~ubuntu-branches/ubuntu/quantal/kde-runtime/quantal

« back to all changes in this revision

Viewing changes to nepomuk/services/backupsync/gui/changelogmerger.cpp

  • Committer: Package Import Robot
  • Author(s): Philip Muškovac
  • Date: 2012-06-03 21:50:00 UTC
  • mto: This revision was merged to the branch mainline in revision 21.
  • Revision ID: package-import@ubuntu.com-20120603215000-vn7oarsq0ynrydj5
Tags: upstream-4.8.80
Import upstream version 4.8.80

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
   This file is part of the Nepomuk KDE project.
3
 
   Copyright (C) 2010  Vishesh Handa <handa.vish@gmail.com>
4
 
 
5
 
   This library is free software; you can redistribute it and/or
6
 
   modify it under the terms of the GNU Lesser General Public
7
 
   License as published by the Free Software Foundation; either
8
 
   version 2.1 of the License, or (at your option) version 3, or any
9
 
   later version accepted by the membership of KDE e.V. (or its
10
 
   successor approved by the membership of KDE e.V.), which shall
11
 
   act as a proxy defined in Section 6 of version 3 of the license.
12
 
 
13
 
   This library is distributed in the hope that it will be useful,
14
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
   Lesser General Public License for more details.
17
 
 
18
 
   You should have received a copy of the GNU Lesser General Public
19
 
   License along with this library.  If not, see <http://www.gnu.org/licenses/>.
20
 
*/
21
 
 
22
 
 
23
 
 
24
 
#include "changelogmerger.h"
25
 
#include "nrio.h"
26
 
 
27
 
#include <algorithm>
28
 
 
29
 
#include <QtCore/QMultiHash>
30
 
#include <QtCore/QHashIterator>
31
 
#include <QtCore/QThread>
32
 
 
33
 
#include <Soprano/Vocabulary/NAO>
34
 
#include <Soprano/Vocabulary/NRL>
35
 
#include <Soprano/Vocabulary/RDF>
36
 
#include <Nepomuk/Vocabulary/NIE>
37
 
#include <Nepomuk/Vocabulary/NFO>
38
 
 
39
 
#include <Soprano/Node>
40
 
#include <Soprano/Statement>
41
 
#include <Soprano/Model>
42
 
#include <Soprano/QueryResultIterator>
43
 
#include <Soprano/StatementIterator>
44
 
#include <Soprano/NodeIterator>
45
 
 
46
 
#include <Nepomuk/Resource>
47
 
#include <Nepomuk/Variant>
48
 
#include <Nepomuk/Types/Property>
49
 
#include <Nepomuk/ResourceManager>
50
 
 
51
 
#include <KDebug>
52
 
 
53
 
int Nepomuk::ChangeLogMerger::NextId = 0;
54
 
 
55
 
Nepomuk::ChangeLogMerger::ChangeLogMerger(Nepomuk::ChangeLog log)
56
 
    : ResourceMerger(),
57
 
      m_logFile( log )
58
 
{
59
 
    m_id = NextId++;
60
 
}
61
 
 
62
 
int Nepomuk::ChangeLogMerger::id()
63
 
{
64
 
    return m_id;
65
 
}
66
 
 
67
 
void Nepomuk::ChangeLogMerger::load()
68
 
{
69
 
    kDebug() << "Loading the ChangeLog..." << m_logFile.size();
70
 
    m_hash = ResourceLogMap::fromChangeLog( m_logFile );
71
 
 
72
 
    // The records are stored according to dateTime
73
 
    Q_ASSERT( m_logFile.toList().isEmpty() == false );
74
 
    m_minDateTime = m_logFile.toList().first().dateTime();
75
 
}
76
 
 
77
 
namespace {
78
 
 
79
 
    //
80
 
    // Cache the results. This could have very bad consequences if someone updates the ontology
81
 
    // when the service is running
82
 
    //
83
 
    bool isMergeable( const KUrl & prop, Soprano::Model * model ) {
84
 
        //
85
 
        // trueg: hardcoding the non-mergeable properties here since there are only 2 defined
86
 
        //
87
 
        return prop != Soprano::Vocabulary::RDF::type() && prop != Nepomuk::Vocabulary::NIE::url();
88
 
    }
89
 
 
90
 
    QList<Nepomuk::ChangeLogRecord> getRecords( const Nepomuk::ResourceLogMap & hash, const KUrl resUri, const KUrl & propUri ) {
91
 
 
92
 
        Nepomuk::ResourceLogMap::const_iterator it = hash.constFind( resUri );
93
 
        if( it == hash.constEnd() ) {
94
 
            return QList<Nepomuk::ChangeLogRecord>();
95
 
        }
96
 
 
97
 
        return it->prop.values( propUri );
98
 
    }
99
 
}
100
 
 
101
 
//TODO: Add completed signal
102
 
void Nepomuk::ChangeLogMerger::mergeChangeLog()
103
 
{
104
 
    m_theGraph = createGraph();
105
 
 
106
 
    kDebug();
107
 
 
108
 
    //
109
 
    // Get own changeLog
110
 
    //
111
 
    kDebug() << "minDateTime : " << m_minDateTime;
112
 
    ChangeLog ownLog;// = LogStorage::instance()->getChangeLog( m_minDateTime );
113
 
    kDebug() << "own Log : " << ownLog.size();
114
 
 
115
 
    // Get our and their hash
116
 
    // ownHash = current local hash from system's ChangeLog
117
 
    // theirHash = derived from external ChangeLog
118
 
    ResourceLogMap ownHash = ResourceLogMap::fromChangeLog( ownLog );
119
 
    ResourceLogMap & theirHash = m_hash;
120
 
 
121
 
    kDebug() << "own Hash : " << ownHash.size();
122
 
    kDebug() << "their hash : " << theirHash.size();
123
 
 
124
 
    QHashIterator<KUrl, ResourceLog> it( theirHash );
125
 
    while( it.hasNext() ) {
126
 
        it.next();
127
 
 
128
 
        // Check for resource deletions
129
 
        if( handleResourceDeletion( it.key() ) )
130
 
            continue;
131
 
 
132
 
        const KUrl & resUri = it.key();
133
 
        const ResourceLog & resLog = it.value();
134
 
 
135
 
        kDebug() << "Resolving " << resUri;
136
 
 
137
 
        const QList<KUrl> & properties = resLog.prop.uniqueKeys();
138
 
        foreach( const KUrl & propUri, properties ) {
139
 
            kDebug() << propUri;
140
 
 
141
 
            if( !isMergeable( propUri, model() ) ) {
142
 
                kDebug() << propUri << " is non Mergeable - IGNORING";
143
 
                continue;
144
 
            }
145
 
 
146
 
            Nepomuk::Types::Property prop( propUri );
147
 
            int cardinality = prop.maxCardinality();
148
 
 
149
 
            QList<ChangeLogRecord> theirRecords = resLog.prop.values( propUri );
150
 
            QList<ChangeLogRecord> ownRecords = getRecords( ownHash, resUri, propUri );
151
 
            //kDebug() << "own Records : " << ownRecords.size();
152
 
 
153
 
            // This case shouldn't ever happen, but just to be sure
154
 
            if( theirRecords.empty() )
155
 
                continue;
156
 
 
157
 
            if( cardinality == 1 ) {
158
 
                resolveSingleCardinality( theirRecords, ownRecords );
159
 
            }
160
 
            else {
161
 
                resolveMultipleCardinality( theirRecords, ownRecords );
162
 
            }
163
 
        }
164
 
 
165
 
        //if( !rs.propHash.isEmpty() )
166
 
        //    m_jobs.append( rs );
167
 
    }
168
 
    //theirHash.clear();
169
 
    //kDebug() << "Done with merge resolution : " << m_jobs.size();
170
 
 
171
 
    //processJobs();
172
 
}
173
 
 
174
 
 
175
 
namespace {
176
 
 
177
 
    Nepomuk::ChangeLogRecord maxRecord( const QList<Nepomuk::ChangeLogRecord> & records ) {
178
 
        QList<Nepomuk::ChangeLogRecord>::const_iterator it = std::max_element( records.begin(), records.end() );
179
 
        if( it != records.constEnd() )
180
 
            return *it;
181
 
        return Nepomuk::ChangeLogRecord();
182
 
    }
183
 
}
184
 
 
185
 
 
186
 
void Nepomuk::ChangeLogMerger::resolveSingleCardinality(const QList< Nepomuk::ChangeLogRecord >& theirRecords, const QList< Nepomuk::ChangeLogRecord >& ownRecords)
187
 
{
188
 
    kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
189
 
 
190
 
    //Find max on the basis of time stamp
191
 
    ChangeLogRecord theirMax = maxRecord( theirRecords );
192
 
    ChangeLogRecord ownMax = maxRecord( ownRecords );
193
 
    kDebug() << "TheirMax : "<< theirMax.toString();
194
 
    kDebug() << "OwnMax " << ownMax.toString();
195
 
 
196
 
    if( theirMax > ownMax ) {
197
 
        Soprano::Statement statement( theirMax.st().subject(), theirMax.st().predicate(),
198
 
                                      Soprano::Node(), Soprano::Node() );
199
 
 
200
 
        if( theirMax.added() ) {
201
 
            Soprano::Node object = theirMax.st().object();
202
 
            kDebug() << "Resolved - Adding " << object;
203
 
 
204
 
            if( !model()->containsAnyStatement( statement ) ) {
205
 
                statement.setObject( object );
206
 
                statement.setContext( m_theGraph );
207
 
                model()->addStatement( statement );
208
 
            }
209
 
        }
210
 
        else {
211
 
            kDebug() << "Resolved - Removing";
212
 
            model()->removeAllStatements( statement );
213
 
        }
214
 
    }
215
 
}
216
 
 
217
 
namespace {
218
 
 
219
 
    struct MergeData {
220
 
        bool added;
221
 
        QDateTime dateTime;
222
 
 
223
 
        MergeData( bool add, const QDateTime & dt )
224
 
            : added( add ),
225
 
              dateTime( dt )
226
 
        {}
227
 
    };
228
 
 
229
 
 
230
 
}
231
 
 
232
 
void Nepomuk::ChangeLogMerger::resolveMultipleCardinality( const QList<Nepomuk::ChangeLogRecord>& theirRecords, const QList<Nepomuk::ChangeLogRecord>& ownRecords)
233
 
{
234
 
    kDebug() << "MULTIPLE";
235
 
    kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
236
 
 
237
 
    const Soprano::Statement& reference = theirRecords.first().st();
238
 
    Soprano::Statement baseStatement( reference.subject(), reference.predicate(), Soprano::Node(), Soprano::Node() );
239
 
 
240
 
    //
241
 
    // Merge both record lists
242
 
    //
243
 
    //TODO: Optimize merging - use merge sort or something equivilant
244
 
    QList<ChangeLogRecord> records = ownRecords;
245
 
    records << theirRecords;
246
 
    qSort( records );
247
 
 
248
 
    QHash<Soprano::Node, MergeData> hash;
249
 
    foreach( const ChangeLogRecord rec, records ) {
250
 
        Soprano::Node object = rec.st().object();
251
 
        QHash<Soprano::Node, MergeData>::const_iterator it = hash.constFind( object );
252
 
        if( it == hash.constEnd() ) {
253
 
            hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
254
 
        }
255
 
        else {
256
 
            // +ve after -ve
257
 
            if( rec.added() == true && it.value().added == false ) {
258
 
                hash.remove( object );
259
 
                hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
260
 
            }
261
 
            // -ve after +ve
262
 
            else if( rec.added() == false && it.value().added == true ) {
263
 
                hash.remove( object );
264
 
            }
265
 
            // +ve after +ve
266
 
            // -ve after -ve
267
 
            //    Do nothing
268
 
        }
269
 
    }
270
 
 
271
 
    //
272
 
    // Do the actual merging
273
 
    //
274
 
    QHashIterator<Soprano::Node, MergeData> it( hash );
275
 
    while( it.hasNext() ) {
276
 
        it.next();
277
 
 
278
 
        Soprano::Statement st( baseStatement );
279
 
        st.setObject( it.key() );
280
 
 
281
 
        MergeData data = it.value();
282
 
        if( data.added == true ) {
283
 
            if( !model()->containsAnyStatement( st ) ) {
284
 
                st.setContext( m_theGraph );
285
 
                model()->addStatement( st );
286
 
                kDebug() << "adding - " << st;
287
 
            }
288
 
        }
289
 
        else {
290
 
            kDebug() << "removing " << st;
291
 
            model()->removeAllStatements( st );
292
 
        }
293
 
    }
294
 
 
295
 
    m_multipleMergers.append( Soprano::Statement( baseStatement.subject(),
296
 
                                                  baseStatement.predicate(),
297
 
                                                  Soprano::Node() ) );
298
 
}
299
 
 
300
 
QList< Soprano::Statement > Nepomuk::ChangeLogMerger::multipleMergers() const
301
 
{
302
 
    return m_multipleMergers;
303
 
}
304
 
 
305
 
bool Nepomuk::ChangeLogMerger::handleResourceDeletion(const KUrl& resUri)
306
 
{
307
 
    ResourceLog & log = m_hash[ resUri ];
308
 
    const KUrl& rdfTypeProp = Soprano::Vocabulary::RDF::type();
309
 
 
310
 
    QList<ChangeLogRecord> records = log.prop.values( rdfTypeProp );
311
 
    if( records.empty() )
312
 
        return false;
313
 
 
314
 
    //
315
 
    // Check if rdf:type is being removed
316
 
    //
317
 
    bool removed = false;
318
 
    foreach( const ChangeLogRecord & r, records ) {
319
 
        if( !r.added() ) {
320
 
            removed = true;
321
 
            break;
322
 
        }
323
 
    }
324
 
    if( !removed )
325
 
        return false;
326
 
 
327
 
    // If removed, remove all records and delete the resource
328
 
    m_hash.remove( resUri );
329
 
    Resource res( resUri );
330
 
    res.remove();
331
 
    return true;
332
 
}