00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "ontologyupdatejob.h"
00020
00021 #include <QtCore/QUrl>
00022 #include <QtCore/QThread>
00023 #include <QtCore/QDateTime>
00024
00025 #include <Soprano/Backend>
00026 #include <Soprano/StorageModel>
00027 #include <Soprano/PluginManager>
00028 #include <Soprano/Global>
00029 #include <Soprano/NodeIterator>
00030 #include <Soprano/StatementIterator>
00031 #include <Soprano/QueryResultIterator>
00032 #include <Soprano/Vocabulary/RDF>
00033 #include <Soprano/Vocabulary/RDFS>
00034 #include <Soprano/Vocabulary/NRL>
00035 #include <Soprano/Vocabulary/NAO>
00036 #include <Soprano/Vocabulary/XMLSchema>
00037
00038 #include <KDebug>
00039
00040
00041 using namespace Soprano;
00042
00043 namespace {
00044 QUrl createMetadataGraphUri( const QUrl& uri ) {
00045 QString s( uri.toString() );
00046 if ( s.endsWith( '#' ) )
00047 s[s.length()-1] = '/';
00048 else if ( !s.endsWith( '/' ) )
00049 s += '/';
00050 s += "metadata";
00051 return QUrl( s );
00052 }
00053
00054 bool findGraphUris( Soprano::Model* model, const QUrl& ns, QUrl& dataGraphUri, QUrl& metaDataGraphUri ) {
00055 QString query = QString( "select ?dg ?mdg where { "
00056 "?dg <%1> \"%2\"^^<%3> . "
00057 "?mdg <%4> ?dg . "
00058 "}" )
00059 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
00060 .arg( ns.toString() )
00061 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
00062 .arg( Soprano::Vocabulary::NRL::coreGraphMetadataFor().toString() );
00063
00064 QueryResultIterator it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
00065 if ( it.next() ) {
00066 metaDataGraphUri = it.binding("mdg").uri();
00067 dataGraphUri = it.binding("dg").uri();
00068 return true;
00069 }
00070 else {
00071 return false;
00072 }
00073 }
00074 }
00075
00076
00077 class Nepomuk::OntologyUpdateJob::Private : public QThread
00078 {
00079 public:
00080 Private( Soprano::Model* mainModel, OntologyUpdateJob* job )
00081 : QThread( job ),
00082 m_model( mainModel ),
00083 m_job( job ),
00084 m_success( false ) {
00085 }
00086
00087 void run();
00088 void _k_slotFinished();
00089
00090 QUrl baseUri;
00091 Soprano::Model* m_model;
00092
00093 private:
00094 bool updateOntology();
00095 bool ensureDataLayout( Soprano::Model* tmpModel, const QUrl& ns );
00096 void createMetadata( Soprano::Model* tmpModel, const QUrl& ns );
00097 bool removeOntology( const QUrl& ns );
00098
00099 OntologyUpdateJob* m_job;
00100 bool m_success;
00101 };
00102
00103
00104 void Nepomuk::OntologyUpdateJob::Private::run()
00105 {
00106 m_success = updateOntology();
00107 }
00108
00109
00110 bool Nepomuk::OntologyUpdateJob::Private::ensureDataLayout( Soprano::Model* tmpModel, const QUrl& ns )
00111 {
00112
00113 StatementIterator it = tmpModel->listStatements();
00114 while ( it.next() ) {
00115 if ( !it.current().context().isValid() ) {
00116 kDebug() << "Invalid data in ontology" << ns << *it;
00117 return false;
00118 }
00119 }
00120
00121
00122 QUrl dataGraphUri, metaDataGraphUri;
00123 if ( !findGraphUris( tmpModel, ns, dataGraphUri, metaDataGraphUri ) ) {
00124 kDebug() << "Invalid data in ontology" << ns << "Could not find datagraph and metadatagraph relation.";
00125 return false;
00126 }
00127
00128 return true;
00129 }
00130
00131
00132 void Nepomuk::OntologyUpdateJob::Private::createMetadata( Soprano::Model* tmpModel, const QUrl& ns )
00133 {
00134 Q_ASSERT( ns.isValid() );
00135 QUrl dataGraphUri( ns );
00136 dataGraphUri.setFragment( QString() );
00137 QUrl metaDataGraphUri = createMetadataGraphUri( dataGraphUri );
00138
00139
00140 QList<Statement> allStatements = tmpModel->listStatements().allStatements();
00141 tmpModel->removeAllStatements();
00142 foreach( Statement s, allStatements ) {
00143 s.setContext( dataGraphUri );
00144 tmpModel->addStatement( s );
00145 }
00146
00147
00148 tmpModel->addStatement( Soprano::Statement( metaDataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::GraphMetadata(), metaDataGraphUri ) );
00149 tmpModel->addStatement( Soprano::Statement( metaDataGraphUri, Soprano::Vocabulary::NRL::coreGraphMetadataFor(), dataGraphUri, metaDataGraphUri ) );
00150 tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::Ontology(), metaDataGraphUri ) );
00151 tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::NAO::hasDefaultNamespace(), LiteralValue( ns.toString() ), metaDataGraphUri ) );
00152 }
00153
00154
00155 bool Nepomuk::OntologyUpdateJob::Private::updateOntology()
00156 {
00157
00158
00159 const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByFeatures( Soprano::BackendFeatureStorageMemory );
00160 if ( !backend ) {
00161 kDebug() << "No Soprano backend found that can handle memory models!";
00162 return false;
00163 }
00164
00165 Soprano::Model* tmpModel = backend->createModel( BackendSettings() << BackendSetting( Soprano::BackendOptionStorageMemory ) );
00166 if ( !tmpModel ) {
00167 kDebug() << "Failed to create temp memory model!";
00168 return false;
00169 }
00170
00171 Soprano::StatementIterator it = m_job->data();
00172 while ( it.next() ) {
00173 tmpModel->addStatement( *it );
00174 }
00175
00176 QUrl ontoUri = baseUri;
00177 if ( ontoUri.isEmpty() ) {
00178 it = tmpModel->listStatements();
00179 if ( it.next() ) {
00180 ontoUri = it.current().subject().uri();
00181 if ( !ontoUri.fragment().isEmpty() ) {
00182 ontoUri.setFragment( QString() );
00183 }
00184 else {
00185 ontoUri = ontoUri.toString().left( ontoUri.toString().lastIndexOf( '/' )+1 );
00186 }
00187 }
00188 }
00189 if ( ontoUri.isEmpty() ) {
00190 kDebug() << "Failed to determine ontology URI.";
00191 return false;
00192 }
00193
00194
00195
00196
00197 QList<Node> graphs = tmpModel->listContexts().allNodes();
00198 if ( graphs.count() == 0 ) {
00199
00200 createMetadata( tmpModel, ontoUri );
00201 }
00202 else if ( graphs.count() == 2 ) {
00203
00204 if ( !ensureDataLayout( tmpModel, ontoUri ) ) {
00205 delete tmpModel;
00206 return false;
00207 }
00208 }
00209 else {
00210 kDebug() << "Invalid data in ontology" << ontoUri << "We need one data and one metadata graph.";
00211 delete tmpModel;
00212 return false;
00213 }
00214
00215
00216
00217
00218 QUrl dataGraphUri, metadataGraphUri;
00219 if ( findGraphUris( tmpModel, ontoUri, dataGraphUri, metadataGraphUri ) ) {
00220 tmpModel->addStatement( Statement( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), LiteralValue( QDateTime::currentDateTime() ), metadataGraphUri ) );
00221
00222
00223
00224 if ( ontoModificationDate( m_model, ontoUri ).isValid() ) {
00225 removeOntology( ontoUri );
00226 }
00227
00228 it = tmpModel->listStatements();
00229 while ( it.next() ) {
00230 m_model->addStatement( *it );
00231 }
00232 delete tmpModel;
00233
00234 kDebug() << "Successfully updated ontology" << ontoUri;
00235 return true;
00236 }
00237 else {
00238 kDebug() << "BUG! Could not find data and metadata graph URIs! This should not happen!";
00239 return false;
00240 }
00241 }
00242
00243
00244 bool Nepomuk::OntologyUpdateJob::Private::removeOntology( const QUrl& ns )
00245 {
00246 QUrl dataGraphUri, metadataGraphUri;
00247 if ( findGraphUris( m_model, ns, dataGraphUri, metadataGraphUri ) ) {
00248
00249 m_model->removeContext( dataGraphUri );
00250 m_model->removeContext( metadataGraphUri );
00251 return true;
00252 }
00253 else {
00254 kDebug() << "Could not find data graph URI for" << ns;
00255 return false;
00256 }
00257 }
00258
00259
00260 void Nepomuk::OntologyUpdateJob::Private::_k_slotFinished()
00261 {
00262
00263 m_job->setError( m_success ? KJob::NoError : KJob::UserDefinedError );
00264 emit m_job->emitResult();
00265 }
00266
00267
00268 Nepomuk::OntologyUpdateJob::OntologyUpdateJob( Soprano::Model* mainModel, QObject* parent )
00269 : KJob( parent ),
00270 d( new Private( mainModel, this ) )
00271 {
00272
00273 connect( d, SIGNAL(finished()), this, SLOT(_k_slotFinished()) );
00274 }
00275
00276
00277 Nepomuk::OntologyUpdateJob::~OntologyUpdateJob()
00278 {
00279 delete d;
00280 }
00281
00282
00283 void Nepomuk::OntologyUpdateJob::start()
00284 {
00285 d->start();
00286 }
00287
00288
00289 void Nepomuk::OntologyUpdateJob::setBaseUri( const QUrl& uri )
00290 {
00291 d->baseUri = uri;
00292 }
00293
00294
00295 Soprano::Model* Nepomuk::OntologyUpdateJob::model() const
00296 {
00297 return d->m_model;
00298 }
00299
00300
00301 QDateTime Nepomuk::OntologyUpdateJob::ontoModificationDate( Soprano::Model* model, const QUrl& uri )
00302 {
00303 QueryResultIterator it = model->executeQuery( QString( "select ?date where { ?onto <%1> \"%2\"^^<%3> . ?onto <%4> ?date . }" )
00304 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
00305 .arg( uri.toString() )
00306 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
00307 .arg( Soprano::Vocabulary::NAO::lastModified().toString() ),
00308 Soprano::Query::QueryLanguageSparql );
00309 if ( it.next() ) {
00310 kDebug() << "Found modification date for" << uri << it.binding( "date" ).literal().toDateTime();
00311 return it.binding( "date" ).literal().toDateTime();
00312 }
00313 else {
00314 return QDateTime();
00315 }
00316 }
00317
00318 #include "ontologyupdatejob.moc"