delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / nepomuk / services / ontologyloader / ontologyupdatejob.cpp
blobdbe3673720795944ea75282ce768eb5c828cef18
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>
38 #include <KDebug>
41 using namespace Soprano;
43 namespace {
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( '/' ) )
49 s += '/';
50 s += "metadata";
51 return QUrl( s );
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> . "
57 "?mdg <%4> ?dg . "
58 "}" )
59 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
60 .arg( ns.toString() )
61 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
62 .arg( Soprano::Vocabulary::NRL::coreGraphMetadataFor().toString() );
64 QueryResultIterator it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
65 if ( it.next() ) {
66 metaDataGraphUri = it.binding("mdg").uri();
67 dataGraphUri = it.binding("dg").uri();
68 return true;
70 else {
71 return false;
77 class Nepomuk::OntologyUpdateJob::Private : public QThread
79 public:
80 Private( Soprano::Model* mainModel, OntologyUpdateJob* job )
81 : QThread( job ),
82 m_model( mainModel ),
83 m_job( job ),
84 m_success( false ) {
87 void run();
88 void _k_slotFinished();
90 QUrl baseUri;
91 Soprano::Model* m_model;
93 private:
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;
100 bool m_success;
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;
117 return false;
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.";
125 return false;
128 return true;
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 );
147 // add the metadata
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 );
160 if ( !backend ) {
161 kDebug() << "No Soprano backend found that can handle memory models!";
162 return false;
165 Soprano::Model* tmpModel = backend->createModel( BackendSettings() << BackendSetting( Soprano::BackendOptionStorageMemory ) );
166 if ( !tmpModel ) {
167 kDebug() << "Failed to create temp memory model!";
168 return false;
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();
179 if ( it.next() ) {
180 ontoUri = it.current().subject().uri();
181 if ( !ontoUri.fragment().isEmpty() ) {
182 ontoUri.setFragment( QString() );
184 else {
185 ontoUri = ontoUri.toString().left( ontoUri.toString().lastIndexOf( '/' )+1 );
189 if ( ontoUri.isEmpty() ) {
190 kDebug() << "Failed to determine ontology URI.";
191 return false;
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 ) ) {
205 delete tmpModel;
206 return false;
209 else {
210 kDebug() << "Invalid data in ontology" << ontoUri << "We need one data and one metadata graph.";
211 delete tmpModel;
212 return false;
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 );
232 delete tmpModel;
234 kDebug() << "Successfully updated ontology" << ontoUri;
235 return true;
237 else {
238 kDebug() << "BUG! Could not find data and metadata graph URIs! This should not happen!";
239 return false;
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 );
251 return true;
253 else {
254 kDebug() << "Could not find data graph URI for" << ns;
255 return false;
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 )
269 : KJob( 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()
279 delete d;
283 void Nepomuk::OntologyUpdateJob::start()
285 d->start();
289 void Nepomuk::OntologyUpdateJob::setBaseUri( const QUrl& uri )
291 d->baseUri = uri;
295 Soprano::Model* Nepomuk::OntologyUpdateJob::model() const
297 return d->m_model;
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 );
309 if ( it.next() ) {
310 kDebug() << "Found modification date for" << uri << it.binding( "date" ).literal().toDateTime();
311 return it.binding( "date" ).literal().toDateTime();
313 else {
314 return QDateTime();
318 #include "ontologyupdatejob.moc"