00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "ontologymanagermodel.h"
00020
00021 #include <QtCore/QUrl>
00022 #include <QtCore/QDateTime>
00023
00024 #include <Soprano/Backend>
00025 #include <Soprano/StorageModel>
00026 #include <Soprano/PluginManager>
00027 #include <Soprano/Global>
00028 #include <Soprano/NodeIterator>
00029 #include <Soprano/StatementIterator>
00030 #include <Soprano/QueryResultIterator>
00031 #include <Soprano/Vocabulary/RDF>
00032 #include <Soprano/Vocabulary/RDFS>
00033 #include <Soprano/Vocabulary/NRL>
00034 #include <Soprano/Vocabulary/NAO>
00035 #include <Soprano/Vocabulary/XMLSchema>
00036 #include <Soprano/Vocabulary/OWL>
00037
00038 #include <KDebug>
00039
00040
00041 using namespace Soprano;
00042
00043
00044
00045 namespace {
00050 QUrl createMetadataGraphUri( const QUrl& uri ) {
00051 QString s( uri.toString() );
00052 if ( s.endsWith( '#' ) )
00053 s[s.length()-1] = '/';
00054 else if ( !s.endsWith( '/' ) )
00055 s += '/';
00056 s += "metadata";
00057 return QUrl( s );
00058 }
00059
00070 bool findGraphUris( Soprano::Model* model, const QUrl& ns, QUrl& dataGraphUri, QUrl& metaDataGraphUri ) {
00071 QString query = QString( "select ?dg ?mdg where { "
00072 "?dg <%1> \"%2\"^^<%3> . "
00073 "?mdg <%4> ?dg . "
00074 "}" )
00075 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
00076 .arg( ns.toString() )
00077 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
00078 .arg( Soprano::Vocabulary::NRL::coreGraphMetadataFor().toString() );
00079
00080 QueryResultIterator it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
00081 if ( it.next() ) {
00082 metaDataGraphUri = it.binding("mdg").uri();
00083 dataGraphUri = it.binding("dg").uri();
00084 return true;
00085 }
00086 else {
00087 return false;
00088 }
00089 }
00090
00100 bool ensureDataLayout( Soprano::Model* tmpModel, const QUrl& ns )
00101 {
00102
00103 StatementIterator it = tmpModel->listStatements();
00104 while ( it.next() ) {
00105 if ( !it.current().context().isValid() ) {
00106 kDebug() << "Invalid data in ontology" << ns << *it;
00107 return false;
00108 }
00109 }
00110
00111
00112 QUrl dataGraphUri, metaDataGraphUri;
00113 if ( !findGraphUris( tmpModel, ns, dataGraphUri, metaDataGraphUri ) ) {
00114 kDebug() << "Invalid data in ontology" << ns << "Could not find datagraph and metadatagraph relation.";
00115 return false;
00116 }
00117
00118 return true;
00119 }
00120
00121
00126 QUrl guessOntologyType( Soprano::Model* tmpModel )
00127 {
00128 static QList<QUrl> propertyClasses;
00129 if ( propertyClasses.isEmpty() )
00130 propertyClasses << Soprano::Vocabulary::RDFS::Class()
00131 << Soprano::Vocabulary::OWL::Class()
00132 << Soprano::Vocabulary::RDF::Property()
00133 << Soprano::Vocabulary::RDFS::ContainerMembershipProperty()
00134 << Soprano::Vocabulary::OWL::ObjectProperty()
00135 << Soprano::Vocabulary::OWL::DatatypeProperty()
00136 << Soprano::Vocabulary::OWL::AnnotationProperty()
00137 << Soprano::Vocabulary::OWL::FunctionalProperty()
00138 << Soprano::Vocabulary::OWL::DeprecatedProperty()
00139 << Soprano::Vocabulary::OWL::OntologyProperty()
00140 << Soprano::Vocabulary::OWL::TransitiveProperty()
00141 << Soprano::Vocabulary::OWL::SymmetricProperty()
00142 << Soprano::Vocabulary::OWL::InverseFunctionalProperty()
00143 << Soprano::Vocabulary::NRL::TransitiveProperty()
00144 << Soprano::Vocabulary::NRL::SymmetricProperty()
00145 << Soprano::Vocabulary::NRL::AsymmetricProperty()
00146 << Soprano::Vocabulary::NRL::InverseFunctionalProperty()
00147 << Soprano::Vocabulary::NRL::FunctionalProperty()
00148 << Soprano::Vocabulary::NRL::ReflexiveProperty();
00149
00150
00151 QStringList classesOrPropertiesSubQueries;
00152 foreach( const QUrl& uri, propertyClasses ) {
00153 classesOrPropertiesSubQueries << QString( "?type = <%1>" ).arg( uri.toString() );
00154 }
00155
00156
00157 bool haveClassesOrProperties = tmpModel->executeQuery( QString( "ask where { "
00158 "?r a ?type . "
00159 "FILTER(%1) . }" )
00160 .arg( classesOrPropertiesSubQueries.join( " || " ) ),
00161 Soprano::Query::QueryLanguageSparql ).boolValue();
00162
00163
00164 classesOrPropertiesSubQueries.clear();
00165 foreach( const QUrl& uri, propertyClasses ) {
00166 classesOrPropertiesSubQueries << QString( "?type != <%1>" ).arg( uri.toString() );
00167 }
00168
00169 classesOrPropertiesSubQueries << QString( "?type != <%1>" ).arg( Soprano::Vocabulary::OWL::Ontology().toString() );
00170
00171 bool haveInstances = tmpModel->executeQuery( QString( "ask where { "
00172 "?r a ?type . "
00173 "FILTER(%1) . }" )
00174 .arg( classesOrPropertiesSubQueries.join( " && " ) ),
00175 Soprano::Query::QueryLanguageSparql ).boolValue();
00176
00177 if ( haveClassesOrProperties && !haveInstances )
00178 return Soprano::Vocabulary::NRL::Ontology();
00179 else if ( !haveClassesOrProperties && haveInstances )
00180 return Soprano::Vocabulary::NRL::InstanceBase();
00181 else
00182 return Soprano::Vocabulary::NRL::KnowledgeBase();
00183 }
00184
00185
00192 void createMetadata( Soprano::Model* tmpModel, const QUrl& ns )
00193 {
00194 Q_ASSERT( ns.isValid() );
00195 QUrl dataGraphUri( ns );
00196 dataGraphUri.setFragment( QString() );
00197 QUrl metaDataGraphUri = createMetadataGraphUri( dataGraphUri );
00198
00199
00200 QList<Statement> allStatements = tmpModel->listStatements().allStatements();
00201 tmpModel->removeAllStatements();
00202 foreach( Statement s, allStatements ) {
00203 s.setContext( dataGraphUri );
00204 tmpModel->addStatement( s );
00205 }
00206
00207 QUrl graphType = guessOntologyType( tmpModel );
00208
00209 kDebug() << "guessed onto type:" << graphType;
00210
00211
00212 tmpModel->addStatement( Soprano::Statement( metaDataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::GraphMetadata(), metaDataGraphUri ) );
00213 tmpModel->addStatement( Soprano::Statement( metaDataGraphUri, Soprano::Vocabulary::NRL::coreGraphMetadataFor(), dataGraphUri, metaDataGraphUri ) );
00214 tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), graphType, metaDataGraphUri ) );
00215 if ( graphType == Soprano::Vocabulary::NRL::KnowledgeBase() ) {
00216
00217 tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::Ontology(), metaDataGraphUri ) );
00218 tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::InstanceBase(), metaDataGraphUri ) );
00219 }
00220 tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::NAO::hasDefaultNamespace(), LiteralValue( ns.toString() ), metaDataGraphUri ) );
00221 }
00222
00226 class ObjectGarbageCollector
00227 {
00228 public:
00229 ObjectGarbageCollector( QObject* o )
00230 : m_object( o ) {
00231 }
00232 ~ObjectGarbageCollector() {
00233 delete m_object;
00234 }
00235
00236 private:
00237 QObject* m_object;
00238 };
00239 }
00240
00241
00242 class Nepomuk::OntologyManagerModel::Private
00243 {
00244 public:
00245 Private( OntologyManagerModel* p )
00246 : q( p ) {
00247 }
00248
00249 private:
00250 OntologyManagerModel* q;
00251 };
00252
00253
00254
00255
00256
00257 Nepomuk::OntologyManagerModel::OntologyManagerModel( Soprano::Model* parentModel, QObject* parent )
00258 : FilterModel( parentModel ),
00259 d( new Private( this ) )
00260 {
00261 setParent( parent );
00262 }
00263
00264
00265 Nepomuk::OntologyManagerModel::~OntologyManagerModel()
00266 {
00267 delete d;
00268 }
00269
00270
00271 void Nepomuk::OntologyManagerModel::setParentModel( Soprano::Model* parentModel )
00272 {
00273 FilterModel::setParentModel( parentModel );
00274 }
00275
00276
00277 bool Nepomuk::OntologyManagerModel::updateOntology( Soprano::StatementIterator data, const QUrl& ns )
00278 {
00279 clearError();
00280
00281 QTime timer;
00282 timer.start();
00283
00284
00285
00286 const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByFeatures( Soprano::BackendFeatureStorageMemory );
00287 if ( !backend ) {
00288 kDebug() << "No Soprano backend found that can handle memory models!";
00289 setError( "No Soprano backend found that can handle memory models." );
00290 return false;
00291 }
00292
00293 Soprano::Model* tmpModel = backend->createModel( BackendSettings() << BackendSetting( Soprano::BackendOptionStorageMemory ) );
00294 if ( !tmpModel ) {
00295 kDebug() << "Failed to create temp memory model!";
00296 setError( backend->lastError() );
00297 return false;
00298 }
00299
00300
00301 ObjectGarbageCollector modelGarbageCollector( tmpModel );
00302
00303
00304 while ( data.next() ) {
00305 tmpModel->addStatement( *data );
00306 }
00307
00308 QUrl ontoUri = ns;
00309 if ( ontoUri.isEmpty() ) {
00310 StatementIterator it = tmpModel->listStatements();
00311 if ( it.next() ) {
00312 ontoUri = it.current().subject().uri();
00313 if ( !ontoUri.fragment().isEmpty() ) {
00314 ontoUri.setFragment( QString() );
00315 }
00316 else {
00317 ontoUri = ontoUri.toString().left( ontoUri.toString().lastIndexOf( '/' )+1 );
00318 }
00319 }
00320 }
00321 if ( ontoUri.isEmpty() ) {
00322 kDebug() << "Failed to determine ontology URI.";
00323 setError( "Failed to determine ontology URI from data." );
00324 return false;
00325 }
00326
00327
00328
00329
00330 QList<Node> graphs = tmpModel->listContexts().allNodes();
00331 if ( graphs.count() == 0 ) {
00332
00333 createMetadata( tmpModel, ontoUri );
00334 }
00335 else if ( graphs.count() == 2 ) {
00336
00337 if ( !ensureDataLayout( tmpModel, ontoUri ) ) {
00338 setError( "The ontology data contains invalid statements.", Soprano::Error::ErrorInvalidArgument );
00339 return false;
00340 }
00341 }
00342 else {
00343 kDebug() << "Invalid data in ontology" << ontoUri << "We need one data and one metadata graph.";
00344 setError( "The ontology data contains invalid statements.", Soprano::Error::ErrorInvalidArgument );
00345 return false;
00346 }
00347
00348
00349
00350
00351 QUrl dataGraphUri, metadataGraphUri;
00352 if ( findGraphUris( tmpModel, ontoUri, dataGraphUri, metadataGraphUri ) ) {
00353
00354 tmpModel->removeAllStatements( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), Node() );
00355
00356
00357 tmpModel->addStatement( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), LiteralValue( QDateTime::currentDateTime() ), metadataGraphUri );
00358
00359
00360
00361 if ( ontoModificationDate( ontoUri ).isValid() ) {
00362 if ( !removeOntology( ontoUri ) ) {
00363 return false;
00364 }
00365 }
00366
00367 StatementIterator it = tmpModel->listStatements();
00368 while ( it.next() ) {
00369 if ( addStatement( *it ) != Error::ErrorNone ) {
00370
00371
00372
00373 return false;
00374 }
00375 }
00376
00377 kDebug() << "Successfully updated ontology" << ontoUri << QString("(%1ms)").arg(timer.elapsed());
00378 return true;
00379 }
00380 else {
00381 kDebug() << "BUG! BUG! BUG! BUG! BUG! BUG! Could not find data and metadata graph URIs! This should not happen!";
00382 return false;
00383 }
00384 }
00385
00386
00387 bool Nepomuk::OntologyManagerModel::removeOntology( const QUrl& ns )
00388 {
00389 clearError();
00390
00391 QUrl dataGraphUri, metadataGraphUri;
00392 if ( findGraphUris( this, ns, dataGraphUri, metadataGraphUri ) ) {
00393
00394 removeContext( dataGraphUri );
00395 removeContext( metadataGraphUri );
00396 return true;
00397 }
00398 else {
00399 kDebug() << "Could not find data graph URI for" << ns;
00400 setError( "Could not find ontology " + ns.toString(), Error::ErrorInvalidArgument );
00401 return false;
00402 }
00403 }
00404
00405
00406 QDateTime Nepomuk::OntologyManagerModel::ontoModificationDate( const QUrl& uri )
00407 {
00408 QueryResultIterator it = executeQuery( QString( "select ?date where { "
00409 "?onto <%1> \"%2\"^^<%3> . "
00410 "?onto <%4> ?date . "
00411 "FILTER(DATATYPE(?date) = <%5>) . }" )
00412 .arg( Soprano::Vocabulary::NAO::hasDefaultNamespace().toString() )
00413 .arg( uri.toString() )
00414 .arg( Soprano::Vocabulary::XMLSchema::string().toString() )
00415 .arg( Soprano::Vocabulary::NAO::lastModified().toString() )
00416 .arg( Soprano::Vocabulary::XMLSchema::dateTime().toString() ),
00417 Soprano::Query::QueryLanguageSparql );
00418 if ( it.next() ) {
00419 kDebug() << "Found modification date for" << uri << it.binding( "date" ).literal().toDateTime();
00420 return it.binding( "date" ).literal().toDateTime();
00421 }
00422 else {
00423 return QDateTime();
00424 }
00425 }
00426
00427
00428 QUrl Nepomuk::OntologyManagerModel::findOntologyContext( const QUrl& uri )
00429 {
00430 QUrl dataGraphUri, metaDataGraphUri;
00431 if ( findGraphUris( parentModel(), uri, dataGraphUri, metaDataGraphUri ) ) {
00432 return dataGraphUri;
00433 }
00434 else {
00435 return QUrl();
00436 }
00437 }
00438
00439 #include "ontologymanagermodel.moc"