2
This file is part of the Nepomuk KDE project.
3
Copyright (C) 2010 Vishesh Handa <handa.vish@gmail.com>
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.
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.
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/>.
24
#include "changelogmerger.h"
29
#include <QtCore/QMultiHash>
30
#include <QtCore/QHashIterator>
31
#include <QtCore/QThread>
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>
39
#include <Soprano/Node>
40
#include <Soprano/Statement>
41
#include <Soprano/Model>
42
#include <Soprano/QueryResultIterator>
43
#include <Soprano/StatementIterator>
44
#include <Soprano/NodeIterator>
46
#include <Nepomuk/Resource>
47
#include <Nepomuk/Variant>
48
#include <Nepomuk/Types/Property>
49
#include <Nepomuk/ResourceManager>
53
int Nepomuk::ChangeLogMerger::NextId = 0;
55
Nepomuk::ChangeLogMerger::ChangeLogMerger(Nepomuk::ChangeLog log)
62
int Nepomuk::ChangeLogMerger::id()
67
void Nepomuk::ChangeLogMerger::load()
69
kDebug() << "Loading the ChangeLog..." << m_logFile.size();
70
m_hash = ResourceLogMap::fromChangeLog( m_logFile );
72
// The records are stored according to dateTime
73
Q_ASSERT( m_logFile.toList().isEmpty() == false );
74
m_minDateTime = m_logFile.toList().first().dateTime();
80
// Cache the results. This could have very bad consequences if someone updates the ontology
81
// when the service is running
83
bool isMergeable( const KUrl & prop, Soprano::Model * model ) {
85
// trueg: hardcoding the non-mergeable properties here since there are only 2 defined
87
return prop != Soprano::Vocabulary::RDF::type() && prop != Nepomuk::Vocabulary::NIE::url();
90
QList<Nepomuk::ChangeLogRecord> getRecords( const Nepomuk::ResourceLogMap & hash, const KUrl resUri, const KUrl & propUri ) {
92
Nepomuk::ResourceLogMap::const_iterator it = hash.constFind( resUri );
93
if( it == hash.constEnd() ) {
94
return QList<Nepomuk::ChangeLogRecord>();
97
return it->prop.values( propUri );
101
//TODO: Add completed signal
102
void Nepomuk::ChangeLogMerger::mergeChangeLog()
104
m_theGraph = createGraph();
111
kDebug() << "minDateTime : " << m_minDateTime;
112
ChangeLog ownLog;// = LogStorage::instance()->getChangeLog( m_minDateTime );
113
kDebug() << "own Log : " << ownLog.size();
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;
121
kDebug() << "own Hash : " << ownHash.size();
122
kDebug() << "their hash : " << theirHash.size();
124
QHashIterator<KUrl, ResourceLog> it( theirHash );
125
while( it.hasNext() ) {
128
// Check for resource deletions
129
if( handleResourceDeletion( it.key() ) )
132
const KUrl & resUri = it.key();
133
const ResourceLog & resLog = it.value();
135
kDebug() << "Resolving " << resUri;
137
const QList<KUrl> & properties = resLog.prop.uniqueKeys();
138
foreach( const KUrl & propUri, properties ) {
141
if( !isMergeable( propUri, model() ) ) {
142
kDebug() << propUri << " is non Mergeable - IGNORING";
146
Nepomuk::Types::Property prop( propUri );
147
int cardinality = prop.maxCardinality();
149
QList<ChangeLogRecord> theirRecords = resLog.prop.values( propUri );
150
QList<ChangeLogRecord> ownRecords = getRecords( ownHash, resUri, propUri );
151
//kDebug() << "own Records : " << ownRecords.size();
153
// This case shouldn't ever happen, but just to be sure
154
if( theirRecords.empty() )
157
if( cardinality == 1 ) {
158
resolveSingleCardinality( theirRecords, ownRecords );
161
resolveMultipleCardinality( theirRecords, ownRecords );
165
//if( !rs.propHash.isEmpty() )
166
// m_jobs.append( rs );
169
//kDebug() << "Done with merge resolution : " << m_jobs.size();
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() )
181
return Nepomuk::ChangeLogRecord();
186
void Nepomuk::ChangeLogMerger::resolveSingleCardinality(const QList< Nepomuk::ChangeLogRecord >& theirRecords, const QList< Nepomuk::ChangeLogRecord >& ownRecords)
188
kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
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();
196
if( theirMax > ownMax ) {
197
Soprano::Statement statement( theirMax.st().subject(), theirMax.st().predicate(),
198
Soprano::Node(), Soprano::Node() );
200
if( theirMax.added() ) {
201
Soprano::Node object = theirMax.st().object();
202
kDebug() << "Resolved - Adding " << object;
204
if( !model()->containsAnyStatement( statement ) ) {
205
statement.setObject( object );
206
statement.setContext( m_theGraph );
207
model()->addStatement( statement );
211
kDebug() << "Resolved - Removing";
212
model()->removeAllStatements( statement );
223
MergeData( bool add, const QDateTime & dt )
232
void Nepomuk::ChangeLogMerger::resolveMultipleCardinality( const QList<Nepomuk::ChangeLogRecord>& theirRecords, const QList<Nepomuk::ChangeLogRecord>& ownRecords)
234
kDebug() << "MULTIPLE";
235
kDebug() << "O: " << ownRecords.size() << " " << "T:" << theirRecords.size();
237
const Soprano::Statement& reference = theirRecords.first().st();
238
Soprano::Statement baseStatement( reference.subject(), reference.predicate(), Soprano::Node(), Soprano::Node() );
241
// Merge both record lists
243
//TODO: Optimize merging - use merge sort or something equivilant
244
QList<ChangeLogRecord> records = ownRecords;
245
records << theirRecords;
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() ) );
257
if( rec.added() == true && it.value().added == false ) {
258
hash.remove( object );
259
hash.insert( object, MergeData( rec.added(), rec.dateTime() ) );
262
else if( rec.added() == false && it.value().added == true ) {
263
hash.remove( object );
272
// Do the actual merging
274
QHashIterator<Soprano::Node, MergeData> it( hash );
275
while( it.hasNext() ) {
278
Soprano::Statement st( baseStatement );
279
st.setObject( it.key() );
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;
290
kDebug() << "removing " << st;
291
model()->removeAllStatements( st );
295
m_multipleMergers.append( Soprano::Statement( baseStatement.subject(),
296
baseStatement.predicate(),
300
QList< Soprano::Statement > Nepomuk::ChangeLogMerger::multipleMergers() const
302
return m_multipleMergers;
305
bool Nepomuk::ChangeLogMerger::handleResourceDeletion(const KUrl& resUri)
307
ResourceLog & log = m_hash[ resUri ];
308
const KUrl& rdfTypeProp = Soprano::Vocabulary::RDF::type();
310
QList<ChangeLogRecord> records = log.prop.values( rdfTypeProp );
311
if( records.empty() )
315
// Check if rdf:type is being removed
317
bool removed = false;
318
foreach( const ChangeLogRecord & r, records ) {
327
// If removed, remove all records and delete the resource
328
m_hash.remove( resUri );
329
Resource res( resUri );