Logo Search packages:      
Sourcecode: webissues version File versions  Download package

datamanager.cpp

/**************************************************************************
* This file is part of the WebIssues program
* Copyright (C) 2006 Michał Męciński
* Copyright (C) 2007 WebIssues Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
**************************************************************************/

#include "datamanager.h"

#include <QApplication>
#include <QFile>

#include "commands/command.h"
#include "rdb/tableiterators.h"
#include "connectionmanager.h"

using namespace WebIssues;

00023 DataManager* WebIssues::dataManager = NULL;

00025 DataManager::DataManager() :
    m_users( 251 ),
    m_members( 251, 31 ),
    m_types( 31 ),
    m_attributes( 251, 31 ),
    m_projects( 31 ),
    m_folders( 127, 31 ),
    m_issues( 8191, 1021 ),
    m_values( 251, 8191 ),
    m_comments( 251, 127 ),
    m_attachments( 251, 127 ),
    m_changes( 1021, 127 ),
    m_foldersState( 127 ),
    m_issuesState( 8191 )
{
}

00042 DataManager::~DataManager()
{
    saveFolderCache();
}

00047 void DataManager::addObserver( QObject* observer )
{
    m_observers.append( observer );
}

00052 void DataManager::removeObserver( QObject* observer )
{
    m_observers.removeAt( m_observers.indexOf( observer ) );
}

void DataManager::notifyObservers( UpdateEvent::Unit unit, int id )
{
    for ( uint i = 0; i < m_observers.count(); i++ ) {
        UpdateEvent* updateEvent = new UpdateEvent( unit, id );
        QApplication::postEvent( m_observers.at( i ), updateEvent );
    }
}

IssueState* DataManager::issueState( int issueId )
{
    IssueState* state = m_issuesState.find( issueId );
    if ( !state ) {
        state = new IssueState( issueId );
        m_issuesState.insert( state );
    }
    return state;
}

FolderState* DataManager::folderState( int folderId )
{
    FolderState* state = m_foldersState.find( folderId );
    if ( !state ) {
        state = new FolderState( folderId );
        m_foldersState.insert( state );
    }
    return state;
}

00085 bool DataManager::folderUpdateNeeded( int folderId )
{
    updateFolderCache( folderId );

    int folderStamp = 0;
    int listStamp = 0;

    FolderRow* row = m_folders.find( folderId );
    if ( row )
        folderStamp = row->stamp();

    FolderState* state = m_foldersState.find( folderId );
    if ( state )
        listStamp = state->listStamp();

    return ( folderStamp == 0 || folderStamp > listStamp );
}

00103 bool DataManager::issueUpdateNeeded( int issueId )
{
    int issueStamp = 0;
    int detailsStamp = 0;

    IssueRow* row = m_issues.find( issueId );
    if ( row )
        issueStamp = row->stamp();

    IssueState* state = m_issuesState.find( issueId );
    if ( state )
        detailsStamp = state->detailsStamp();

    return ( issueStamp == 0 || issueStamp > detailsStamp );
}

00119 void DataManager::lockIssue( int issueId )
{
    IssueState* state = issueState( issueId );

    state->setLockCount( state->lockCount() + 1 );
}

00126 void DataManager::unlockIssue( int issueId )
{
    IssueState* state = issueState( issueId );

    state->setLockCount( state->lockCount() - 1 );
    state->setTimeUsed( QDateTime::currentDateTime() );

    flushIssueCache();
}

void DataManager::flushIssueCache()
{
    for ( ; ; ) {
        int cacheSize = 0;

        int oldestIssueId = 0;
        QDateTime oldestTime = QDateTime::currentDateTime().addSecs( 1 );

        RDB::TableIterator<IssueState> it( &m_issuesState );
        while ( it.next() ) {
            IssueState* state = it.get();
            if ( state->detailsStamp() != 0 ) {
                cacheSize++;
                if ( state->lockCount() == 0 ) {
                    QDateTime time = state->timeUsed();
                    if ( time.isNull() || time < oldestTime ) {
                        oldestIssueId = state->issueId();
                        oldestTime = time;
                    }
                }
            }
        }

        if ( cacheSize <= 20 || oldestIssueId == 0 )
            break;

        m_comments.removeChildren( oldestIssueId );
        m_attachments.removeChildren( oldestIssueId );
        m_changes.removeChildren( oldestIssueId );

        IssueState* state = issueState( oldestIssueId );
        state->setDetailsStamp( 0 );

        notifyObservers( UpdateEvent::Issue, oldestIssueId );
    }
}

00173 Command* DataManager::updateUsers()
{
    Command* command = new Command();

    command->setKeyword( "LIST USERS" );

    command->setAcceptNullReply( true );
    command->addRule( "U issi", ReplyRule::ZeroOrMore );
    command->addRule( "M iii", ReplyRule::ZeroOrMore );

    connect( command, SIGNAL( commandReply( const Reply& ) ), this, SLOT( updateUsersReply( const Reply& ) ) );

    return command;
}

00188 Command* DataManager::updateTypes()
{
    Command* command = new Command();

    command->setKeyword( "LIST TYPES" );

    command->setAcceptNullReply( true );
    command->addRule( "T is", ReplyRule::ZeroOrMore );
    command->addRule( "A iiss", ReplyRule::ZeroOrMore );

    connect( command, SIGNAL( commandReply( const Reply& ) ), this, SLOT( updateTypesReply( const Reply& ) ) );

    return command;
}

00203 Command* DataManager::updateProjects()
{
    Command* command = new Command();

    command->setKeyword( "LIST PROJECTS" );

    command->setAcceptNullReply( true );
    command->addRule( "P is", ReplyRule::ZeroOrMore );
    command->addRule( "F iisii", ReplyRule::ZeroOrMore );

    connect( command, SIGNAL( commandReply( const Reply& ) ), this, SLOT( updateProjectsReply( const Reply& ) ) );

    return command;
}

00218 Command* DataManager::updateFolder( int folderId )
{
    updateFolderCache( folderId );

    FolderState* state = m_foldersState.find( folderId );
    int stamp = state ? state->listStamp() : 0;

    Command* command = new Command();

    command->setKeyword( "LIST ISSUES" );
    command->addArg( folderId );
    command->addArg( stamp );

    command->setAcceptNullReply( true );
    command->addRule( "I iisiiiii", ReplyRule::ZeroOrMore );
    command->addRule( "V iis", ReplyRule::ZeroOrMore );

    connect( command, SIGNAL( commandReply( const Reply& ) ), this, SLOT( updateFolderReply( const Reply& ) ) );

    return command;
}

00240 Command* DataManager::updateIssue( int issueId )
{
    IssueState* state = m_issuesState.find( issueId );
    int stamp = state ? state->detailsStamp() : 0;

    Command* command = new Command();

    command->setKeyword( "GET DETAILS" );
    command->addArg( issueId );
    command->addArg( stamp );

    command->setAcceptNullReply( true );
    command->addRule( "I iisiiiii", ReplyRule::One );
    command->addRule( "V iis", ReplyRule::ZeroOrMore );
    command->addRule( "C iiiis", ReplyRule::ZeroOrMore );
    command->addRule( "A iisiiis", ReplyRule::ZeroOrMore );
    command->addRule( "H iiiiiss", ReplyRule::ZeroOrMore );

    connect( command, SIGNAL( commandReply( const Reply& ) ), this, SLOT( updateIssueReply( const Reply& ) ) );

    return command;
}

void DataManager::updateUsersReply( const Reply& reply )
{
    m_users.clear();
    m_members.clear();

    for ( int i = 0; i < reply.lines().count(); i++ ) {
        ReplyLine line = reply.lines().at( i );
        if ( line.keyword() == "U" ) {
            UserRow* user = readUserRow( line );
            m_users.insert( user );
        } else {
            MemberRow* member = readMemberRow( line );
            m_members.insert( member );
        }
    }

    notifyObservers( UpdateEvent::Users );
}

void DataManager::updateTypesReply( const Reply& reply )
{
    m_types.clear();
    m_attributes.clear();

    for ( int i = 0; i < reply.lines().count(); i++ ) {
        ReplyLine line = reply.lines().at( i );
        if ( line.keyword() == "T" ) {
            TypeRow* type = readTypeRow( line );
            m_types.insert( type );
        } else {
            AttributeRow* attribute = readAttributeRow( line );
            m_attributes.insert( attribute );
        }
    }

    notifyObservers( UpdateEvent::Types );
}

void DataManager::updateProjectsReply( const Reply& reply )
{
    m_projects.clear();
    m_folders.clear();

    for ( int i = 0; i < reply.lines().count(); i++ ) {
        ReplyLine line = reply.lines().at( i );
        if ( line.keyword() == "P" ) {
            ProjectRow* project = readProjectRow( line );
            m_projects.insert( project );
        } else {
            FolderRow* folder = readFolderRow( line );
            m_folders.insert( folder );
        }
    }

    notifyObservers( UpdateEvent::Projects );
}

void DataManager::updateFolderReply( const Reply& reply )
{
    int folderId = 0;
    int stamp = 0;

    for ( int i = 0; i < reply.lines().count(); i++ ) {
        ReplyLine line = reply.lines().at( i );
        if ( line.keyword() == "I" ) {
            IssueRow* issue = readIssueRow( line );

            folderId = issue->folderId();
            if ( issue->stamp() > stamp )
                stamp = issue->stamp();

            m_issues.remove( issue->issueId() );
            m_issues.insert( issue );

            m_values.removeAll( 1, issue->issueId() );
        } else {
            ValueRow* value = readValueRow( line );
            m_values.insert( value );
        }
    }

    FolderState* state = folderState( folderId );
    state->setListStamp( stamp );

    notifyObservers( UpdateEvent::Folder, folderId );
}

void DataManager::updateIssueReply( const Reply& reply )
{
    ReplyLine issueLine = reply.lines().at( 0 );

    IssueRow* issue = readIssueRow( issueLine );
    int issueId = issue->issueId();

    m_issues.remove( issueId );
    m_issues.insert( issue );

    m_values.removeAll( 1, issueId );

    IssueState* state = issueState( issueId );
    state->setDetailsStamp( issue->stamp() );

    for ( int i = 1; i < reply.lines().count(); i++ ) {
        ReplyLine line = reply.lines().at( i );
        if ( line.keyword() == "V" ) {
            ValueRow* value = readValueRow( line );
            m_values.insert( value );
        } else if ( line.keyword() == "C" ) {
            CommentRow* comment = readCommentRow( line );
            m_comments.insert( comment );
        } else if ( line.keyword() == "A" ) {
            AttachmentRow* attachment = readAttachmentRow( line );
            m_attachments.insert( attachment );
        } else {
            ChangeRow* change = readChangeRow( line );
            m_changes.insert( change );
        }
    }

    notifyObservers( UpdateEvent::Issue, issueId );

    flushIssueCache();
}

UserRow* DataManager::readUserRow( const ReplyLine& line )
{
    int userId = line.argInt( 0 );
    QString login = line.argString( 1 );
    QString name = line.argString( 2 );
    Access access = (Access)line.argInt( 3 );

    return new UserRow( userId, login, name, access );
}

MemberRow* DataManager::readMemberRow( const ReplyLine& line )
{
    int userId = line.argInt( 0 );
    int projectId = line.argInt( 1 );
    Access access = (Access)line.argInt( 2 );

    return new MemberRow( userId, projectId, access );
}

TypeRow* DataManager::readTypeRow( const ReplyLine& line )
{
    int typeId = line.argInt( 0 );
    QString name = line.argString( 1 );

    return new TypeRow( typeId, name );
}

AttributeRow* DataManager::readAttributeRow( const ReplyLine& line )
{
    int attributeId = line.argInt( 0 );
    int typeId = line.argInt( 1 );
    QString name = line.argString( 2 );
    QString definition = line.argString( 3 );

    return new AttributeRow( attributeId, typeId, name, definition );
}

ProjectRow* DataManager::readProjectRow( const ReplyLine& line )
{
    int projectId = line.argInt( 0 );
    QString name = line.argString( 1 );

    return new ProjectRow( projectId, name );
}

FolderRow* DataManager::readFolderRow( const ReplyLine& line )
{
    int folderId = line.argInt( 0 );
    int projectId = line.argInt( 1 );
    QString name = line.argString( 2 );
    int typeId = line.argInt( 3 );
    int stamp = line.argInt( 4 );

    return new FolderRow( folderId, projectId, name, typeId, stamp );
}

IssueRow* DataManager::readIssueRow( const ReplyLine& line )
{
    int issueId = line.argInt( 0 );
    int folderId = line.argInt( 1 );
    QString name = line.argString( 2 );
    int stamp = line.argInt( 3 );
    QDateTime createdDate;
    createdDate.setTime_t( line.argInt( 4 ) );
    int createdUser = line.argInt( 5 );
    QDateTime modifiedDate;
    modifiedDate.setTime_t( line.argInt( 6 ) );
    int modifiedUser = line.argInt( 7 );

    return new IssueRow( issueId, folderId, name, stamp, createdDate, createdUser, modifiedDate, modifiedUser );
}

ValueRow* DataManager::readValueRow( const ReplyLine& line )
{
    int attributeId = line.argInt( 0 );
    int issueId = line.argInt( 1 );
    QString value = line.argString( 2 );

    return new ValueRow( attributeId, issueId, value );
}

CommentRow* DataManager::readCommentRow( const ReplyLine& line )
{
    int commentId = line.argInt( 0 );
    int issueId = line.argInt( 1 );
    QDateTime createdDate;
    createdDate.setTime_t( line.argInt( 2 ) );
    int createdUser = line.argInt( 3 );
    QString text = line.argString( 4 );

    return new CommentRow( commentId, issueId, createdDate, createdUser, text );
}

AttachmentRow* DataManager::readAttachmentRow( const ReplyLine& line )
{
    int attachmentId = line.argInt( 0 );
    int issueId = line.argInt( 1 );
    QString name = line.argString( 2 );
    QDateTime createdDate;
    createdDate.setTime_t( line.argInt( 3 ) );
    int createdUser = line.argInt( 4 );
    int size = line.argInt( 5 );
    QString description = line.argString( 6 );

    return new AttachmentRow( attachmentId, issueId, name, createdDate, createdUser, size, description );
}

ChangeRow* DataManager::readChangeRow( const ReplyLine& line )
{
    int changeId = line.argInt( 0 );
    int issueId = line.argInt( 1 );
    QDateTime modifiedDate;
    modifiedDate.setTime_t( line.argInt( 2 ) );
    int modifiedUser = line.argInt( 3 );
    int attributeId = line.argInt( 4 );
    QString oldValue = line.argString( 5 );
    QString newValue = line.argString( 6 );

    return new ChangeRow( changeId, issueId, modifiedDate, modifiedUser, attributeId, oldValue, newValue );
}

00508 int DataManager::findItem( int itemId )
{
    const IssueRow* issue = m_issues.find( itemId );
    if ( issue != NULL )
        return itemId;

    const CommentRow* comment = m_comments.find( itemId );
    if ( comment != NULL )
        return comment->issueId();

    const AttachmentRow* attachment = m_attachments.find( itemId );
    if ( attachment != NULL )
        return attachment->issueId();

    return 0;
}

void DataManager::updateFolderCache( int folderId )
{
    FolderState* state = folderState( folderId );
    if ( !state->cached() ) {
        readFolderCache( folderId );
        state->setCached( true );
    }
}

void DataManager::readFolderCache( int folderId )
{
    QString name = QString( "folder-cache/%1.cache" ).arg( folderId );
    QString path = connectionManager->locateCacheFile( name );

    QFile file( path );
    if ( !file.open( QIODevice::ReadOnly ) )
        return;

    QDataStream stream( &file );
    stream.setVersion( 5 );

    qint32 version;
    stream >> version;

    if ( version != 1 )
        return;

    qint32 listStamp;
    stream >> listStamp;

    FolderState* state = folderState( folderId );
    state->setListStamp( listStamp );

    qint32 issueId;
    while ( !( stream >> issueId ).atEnd() && issueId != 0 ) {
        QString name;
        stream >> name;
        qint32 stamp;
        stream >> stamp;
        QDateTime createdDate;
        stream >> createdDate;
        qint32 createdUser;
        stream >> createdUser;
        QDateTime modifiedDate;
        stream >> modifiedDate;
        qint32 modifiedUser;
        stream >> modifiedUser;

        const IssueRow* currentIssue = m_issues.find( issueId );

        if ( !currentIssue ) {
            IssueRow* issue = new IssueRow( issueId, folderId, name, stamp, createdDate, createdUser, modifiedDate, modifiedUser );
            m_issues.insert( issue );
        }

        qint32 attributeId;
        while ( !( stream >> attributeId ).atEnd() && attributeId != 0 ) {
            QString value;
            stream >> value;

            if ( !currentIssue ) {
                ValueRow* valueRow = new ValueRow( attributeId, issueId, value );
                m_values.insert( valueRow );
            }
        }
    }

    notifyObservers( UpdateEvent::Folder, folderId );
}

void DataManager::saveFolderCache()
{
    RDB::TableConstIterator<FolderRow> it( &m_folders );
    while ( it.next() )
        writeFolderCache( it.get()->folderId() );
}

void DataManager::writeFolderCache( int folderId )
{
    FolderState* state = folderState( folderId );
    if ( state->listStamp() == 0 )
        return;

    QString name = QString( "folder-cache/%1.cache" ).arg( folderId );
    QString path = connectionManager->locateCacheFile( name );

    QFile file( path );
    if ( !file.open( QIODevice::WriteOnly ) )
        return;

    QDataStream stream( &file );
    stream.setVersion( 5 );

    stream << (qint32)1; // increment version when adding/changing fields

    stream << (qint32)state->listStamp();

    RDB::ForeignConstIterator<IssueRow> it( m_issues.parentIndex(), folderId );
    while ( it.next() ) {
        const IssueRow* issue = it.get();

        stream << (qint32)issue->issueId();
        stream << issue->name();
        stream << (qint32)issue->stamp();
        stream << issue->createdDate();
        stream << (qint32)issue->createdUser();
        stream << issue->modifiedDate();
        stream << (qint32)issue->modifiedUser();

        RDB::ForeignConstIterator<ValueRow> it2( m_values.otherIndex(), issue->issueId() );
        while ( it2.next() ) {
            const ValueRow* value = it2.get();
            stream << (qint32)value->attributeId();
            stream << value->value();
        }
        stream << (qint32)0;
    }
    stream << (qint32)0;
}

#include "datamanager.moc"

Generated by  Doxygen 1.6.0   Back to index