• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

NepomukDaemons

ontologymanagermodel.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE Project
00002    Copyright (c) 2008 Sebastian Trueg <trueg@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License version 2 as published by the Free Software Foundation.
00007 
00008    This library is distributed in the hope that it will be useful,
00009    but WITHOUT ANY WARRANTY; without even the implied warranty of
00010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011    Library General Public License for more details.
00012 
00013    You should have received a copy of the GNU Library General Public License
00014    along with this library; see the file COPYING.LIB.  If not, write to
00015    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, USA.
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         // 1. all statements need to have a proper context set
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         // 2. make sure we have a proper relation between the data and metadata graphs
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         // check for classes and properties
00151         QStringList classesOrPropertiesSubQueries;
00152         foreach( const QUrl& uri, propertyClasses ) {
00153             classesOrPropertiesSubQueries << QString( "?type = <%1>" ).arg( uri.toString() );
00154         }
00155 
00156         // we cannot use UNION here because redland does not support it!
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         // check for anything that is not a class or property
00164         classesOrPropertiesSubQueries.clear();
00165         foreach( const QUrl& uri, propertyClasses ) {
00166             classesOrPropertiesSubQueries << QString( "?type != <%1>" ).arg( uri.toString() );
00167         }
00168         // owl:Ontologys do not have any influce on our descision
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         // set proper context on all data statements (This is a bit ugly but we cannot iterate and modify at the same time!)
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         // add the metadata
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             // we do not have inference in Nepomuk yet and this way libnepomuk does not get confused when reading types
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     // Create temp memory model
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     // so we do not have to care about deleting out tmpModel anymore.
00301     ObjectGarbageCollector modelGarbageCollector( tmpModel );
00302 
00303     // import the data into our tmp model
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     // all the data has been read into the temp model
00328     // now we make sure it has a proper layout (one main and one metadata graph)
00329     // ------------------------------------
00330     QList<Node> graphs = tmpModel->listContexts().allNodes();
00331     if ( graphs.count() == 0 ) {
00332         // simple: we have to create all data manually
00333         createMetadata( tmpModel, ontoUri );
00334     }
00335     else if ( graphs.count() == 2 ) {
00336         // proper number of graphs. Make sure we have all the necessary information
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     // store the modification date of the ontology file in the metadata graph and reuse it to know if we have to update
00350     // ------------------------------------
00351     QUrl dataGraphUri, metadataGraphUri;
00352     if ( findGraphUris( tmpModel, ontoUri, dataGraphUri, metadataGraphUri ) ) {
00353         // remove any modification date data there is
00354         tmpModel->removeAllStatements( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), Node() );
00355 
00356         // set the new modification date
00357         tmpModel->addStatement( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), LiteralValue( QDateTime::currentDateTime() ), metadataGraphUri );
00358 
00359         // now it is time to merge the new data in
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                 // FIXME: here we should cleanup, but then again, if adding the statement
00371                 // fails, removing will probably also fail. So the only real solution
00372                 // would be a transaction.
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         // now removing the ontology is simple
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"

NepomukDaemons

Skip menu "NepomukDaemons"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KCMShell
  • KNotify
  • KStyles
  • Nepomuk Daemons
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal