1 /* This file is part of the KDE Project
2 Copyright (c) 2008 Sebastian Trueg <trueg@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
19 #include "ontologyupdatejob.h"
21 #include <QtCore/QUrl>
22 #include <QtCore/QThread>
23 #include <QtCore/QDateTime>
25 #include <Soprano/Backend>
26 #include <Soprano/StorageModel>
27 #include <Soprano/PluginManager>
28 #include <Soprano/Global>
29 #include <Soprano/NodeIterator>
30 #include <Soprano/StatementIterator>
31 #include <Soprano/QueryResultIterator>
32 #include <Soprano/Vocabulary/RDF>
33 #include <Soprano/Vocabulary/RDFS>
34 #include <Soprano/Vocabulary/NRL>
35 #include <Soprano/Vocabulary/NAO>
36 #include <Soprano/Vocabulary/XMLSchema>
41 using namespace Soprano
;
44 QUrl
createMetadataGraphUri( const QUrl
& uri
) {
45 QString
s( uri
.toString() );
46 if ( s
.endsWith( '#' ) )
47 s
[s
.length()-1] = '/';
48 else if ( !s
.endsWith( '/' ) )
54 bool findGraphUris( Soprano::Model
* model
, const QUrl
& ns
, QUrl
& dataGraphUri
, QUrl
& metaDataGraphUri
) {
55 QString query
= QString( "select ?dg ?mdg where { "
56 "?dg <%1> \"%2\"^^<%3> . "
59 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
61 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
62 .arg( Soprano::Vocabulary::NRL::coreGraphMetadataFor().toString() );
64 QueryResultIterator it
= model
->executeQuery( query
, Soprano::Query::QueryLanguageSparql
);
66 metaDataGraphUri
= it
.binding("mdg").uri();
67 dataGraphUri
= it
.binding("dg").uri();
77 class Nepomuk::OntologyUpdateJob::Private
: public QThread
80 Private( Soprano::Model
* mainModel
, OntologyUpdateJob
* job
)
88 void _k_slotFinished();
91 Soprano::Model
* m_model
;
94 bool updateOntology();
95 bool ensureDataLayout( Soprano::Model
* tmpModel
, const QUrl
& ns
);
96 void createMetadata( Soprano::Model
* tmpModel
, const QUrl
& ns
);
97 bool removeOntology( const QUrl
& ns
);
99 OntologyUpdateJob
* m_job
;
104 void Nepomuk::OntologyUpdateJob::Private::run()
106 m_success
= updateOntology();
110 bool Nepomuk::OntologyUpdateJob::Private::ensureDataLayout( Soprano::Model
* tmpModel
, const QUrl
& ns
)
112 // 1. all statements need to have a proper context set
113 StatementIterator it
= tmpModel
->listStatements();
114 while ( it
.next() ) {
115 if ( !it
.current().context().isValid() ) {
116 kDebug() << "Invalid data in ontology" << ns
<< *it
;
121 // 2. make sure we have a proper relation between the data and metadata graphs
122 QUrl dataGraphUri
, metaDataGraphUri
;
123 if ( !findGraphUris( tmpModel
, ns
, dataGraphUri
, metaDataGraphUri
) ) {
124 kDebug() << "Invalid data in ontology" << ns
<< "Could not find datagraph and metadatagraph relation.";
132 void Nepomuk::OntologyUpdateJob::Private::createMetadata( Soprano::Model
* tmpModel
, const QUrl
& ns
)
134 Q_ASSERT( ns
.isValid() );
135 QUrl
dataGraphUri( ns
);
136 dataGraphUri
.setFragment( QString() );
137 QUrl metaDataGraphUri
= createMetadataGraphUri( dataGraphUri
);
139 // set proper context on all data statements (This is a bit ugly but we cannot iterate and modify at the same time!)
140 QList
<Statement
> allStatements
= tmpModel
->listStatements().allStatements();
141 tmpModel
->removeAllStatements();
142 foreach( Statement s
, allStatements
) {
143 s
.setContext( dataGraphUri
);
144 tmpModel
->addStatement( s
);
148 tmpModel
->addStatement( Soprano::Statement( metaDataGraphUri
, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::GraphMetadata(), metaDataGraphUri
) );
149 tmpModel
->addStatement( Soprano::Statement( metaDataGraphUri
, Soprano::Vocabulary::NRL::coreGraphMetadataFor(), dataGraphUri
, metaDataGraphUri
) );
150 tmpModel
->addStatement( Soprano::Statement( dataGraphUri
, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::Ontology(), metaDataGraphUri
) );
151 tmpModel
->addStatement( Soprano::Statement( dataGraphUri
, Soprano::Vocabulary::NAO::hasDefaultNamespace(), LiteralValue( ns
.toString() ), metaDataGraphUri
) );
155 bool Nepomuk::OntologyUpdateJob::Private::updateOntology()
157 // Create temp memory model
158 // ------------------------------------
159 const Soprano::Backend
* backend
= Soprano::PluginManager::instance()->discoverBackendByFeatures( Soprano::BackendFeatureStorageMemory
);
161 kDebug() << "No Soprano backend found that can handle memory models!";
165 Soprano::Model
* tmpModel
= backend
->createModel( BackendSettings() << BackendSetting( Soprano::BackendOptionStorageMemory
) );
167 kDebug() << "Failed to create temp memory model!";
171 Soprano::StatementIterator it
= m_job
->data();
172 while ( it
.next() ) {
173 tmpModel
->addStatement( *it
);
176 QUrl ontoUri
= baseUri
;
177 if ( ontoUri
.isEmpty() ) {
178 it
= tmpModel
->listStatements();
180 ontoUri
= it
.current().subject().uri();
181 if ( !ontoUri
.fragment().isEmpty() ) {
182 ontoUri
.setFragment( QString() );
185 ontoUri
= ontoUri
.toString().left( ontoUri
.toString().lastIndexOf( '/' )+1 );
189 if ( ontoUri
.isEmpty() ) {
190 kDebug() << "Failed to determine ontology URI.";
194 // all the data has been read into the temp model
195 // now we make sure it has a proper layout (one main and one metadata graph)
196 // ------------------------------------
197 QList
<Node
> graphs
= tmpModel
->listContexts().allNodes();
198 if ( graphs
.count() == 0 ) {
199 // simple: we have to create all data manually
200 createMetadata( tmpModel
, ontoUri
);
202 else if ( graphs
.count() == 2 ) {
203 // proper number of graphs. Make sure we have all the necessary information
204 if ( !ensureDataLayout( tmpModel
, ontoUri
) ) {
210 kDebug() << "Invalid data in ontology" << ontoUri
<< "We need one data and one metadata graph.";
216 // store the modification date of the ontology file in the metadata graph and reuse it to know if we have to update
217 // ------------------------------------
218 QUrl dataGraphUri
, metadataGraphUri
;
219 if ( findGraphUris( tmpModel
, ontoUri
, dataGraphUri
, metadataGraphUri
) ) {
220 tmpModel
->addStatement( Statement( dataGraphUri
, Soprano::Vocabulary::NAO::lastModified(), LiteralValue( QDateTime::currentDateTime() ), metadataGraphUri
) );
222 // now it is time to merge the new data in
223 // ------------------------------------
224 if ( ontoModificationDate( m_model
, ontoUri
).isValid() ) {
225 removeOntology( ontoUri
);
228 it
= tmpModel
->listStatements();
229 while ( it
.next() ) {
230 m_model
->addStatement( *it
);
234 kDebug() << "Successfully updated ontology" << ontoUri
;
238 kDebug() << "BUG! Could not find data and metadata graph URIs! This should not happen!";
244 bool Nepomuk::OntologyUpdateJob::Private::removeOntology( const QUrl
& ns
)
246 QUrl dataGraphUri
, metadataGraphUri
;
247 if ( findGraphUris( m_model
, ns
, dataGraphUri
, metadataGraphUri
) ) {
248 // now removing the ontology is simple
249 m_model
->removeContext( dataGraphUri
);
250 m_model
->removeContext( metadataGraphUri
);
254 kDebug() << "Could not find data graph URI for" << ns
;
260 void Nepomuk::OntologyUpdateJob::Private::_k_slotFinished()
262 // FIXME: more detailed error code and message
263 m_job
->setError( m_success
? KJob::NoError
: KJob::UserDefinedError
);
264 emit m_job
->emitResult();
268 Nepomuk::OntologyUpdateJob::OntologyUpdateJob( Soprano::Model
* mainModel
, QObject
* parent
)
270 d( new Private( mainModel
, this ) )
272 // FIXME: connect the thread for more information
273 connect( d
, SIGNAL(finished()), this, SLOT(_k_slotFinished()) );
277 Nepomuk::OntologyUpdateJob::~OntologyUpdateJob()
283 void Nepomuk::OntologyUpdateJob::start()
289 void Nepomuk::OntologyUpdateJob::setBaseUri( const QUrl
& uri
)
295 Soprano::Model
* Nepomuk::OntologyUpdateJob::model() const
301 QDateTime
Nepomuk::OntologyUpdateJob::ontoModificationDate( Soprano::Model
* model
, const QUrl
& uri
)
303 QueryResultIterator it
= model
->executeQuery( QString( "select ?date where { ?onto <%1> \"%2\"^^<%3> . ?onto <%4> ?date . }" )
304 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
305 .arg( uri
.toString() )
306 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
307 .arg( Soprano::Vocabulary::NAO::lastModified().toString() ),
308 Soprano::Query::QueryLanguageSparql
);
310 kDebug() << "Found modification date for" << uri
<< it
.binding( "date" ).literal().toDateTime();
311 return it
.binding( "date" ).literal().toDateTime();
318 #include "ontologyupdatejob.moc"