• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdelibs API Reference
  • KDE Home
  • Contact Us
 

Nepomuk-Core

  • sources
  • kde-4.12
  • kdelibs
  • nepomuk-core
  • services
  • storage
ontologymanagermodel.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE Project
2  Copyright (c) 2008-2012 Sebastian Trueg <trueg@kde.org>
3 
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.
7 
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.
12 
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.
17 */
18 
19 #include "ontologymanagermodel.h"
20 
21 #include <QtCore/QUrl>
22 #include <QtCore/QDateTime>
23 #include <QtCore/QScopedPointer>
24 
25 #include <Soprano/Backend>
26 #include <Soprano/Version>
27 #include <Soprano/StorageModel>
28 #include <Soprano/PluginManager>
29 #include <Soprano/Global>
30 #include <Soprano/NodeIterator>
31 #include <Soprano/StatementIterator>
32 #include <Soprano/QueryResultIterator>
33 #include <Soprano/Vocabulary/RDF>
34 #include <Soprano/Vocabulary/RDFS>
35 #include <Soprano/Vocabulary/NRL>
36 #include <Soprano/Vocabulary/NAO>
37 #include <Soprano/Vocabulary/XMLSchema>
38 #include <Soprano/Vocabulary/OWL>
39 
40 #include <KDebug>
41 
42 
43 using namespace Soprano;
44 using namespace Soprano::Vocabulary;
45 
46 
47 
48 namespace {
53  QUrl createMetadataGraphUri( const QUrl& uri ) {
54  QString s( uri.toString() );
55  if ( s.endsWith( '#' ) )
56  s[s.length()-1] = '/';
57  else if ( !s.endsWith( '/' ) )
58  s += '/';
59  s += "metadata";
60  return QUrl( s );
61  }
62 
73  bool findGraphUris( Soprano::Model* model, const QUrl& ns, QUrl& dataGraphUri, QUrl& metaDataGraphUri ) {
74  // We use a FILTER(STR(?ns)...) to support both Soprano 2.3 (with plain literals) and earlier (with only typed ones)
75  QString query = QString( "select ?dg ?mdg where { "
76  "?dg %1 ?ns . "
77  "?mdg %3 ?dg . "
78  "FILTER(REGEX(STR(?ns), \"^%2\")) . "
79  "}" )
80  .arg( Soprano::Node::resourceToN3(NAO::hasDefaultNamespace()),
81  ns.toString(),
82  Soprano::Node::resourceToN3(NRL::coreGraphMetadataFor()) );
83  QueryResultIterator it = model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
84  if ( it.next() ) {
85  metaDataGraphUri = it.binding("mdg").uri();
86  dataGraphUri = it.binding("dg").uri();
87  return true;
88  }
89  else {
90  return false;
91  }
92  }
93 
103  bool ensureDataLayout( Soprano::Model* tmpModel, const QUrl& ns )
104  {
105  // make sure we have a proper relation between the data and metadata graphs
106  QUrl dataGraphUri, metaDataGraphUri;
107  if ( !findGraphUris( tmpModel, ns, dataGraphUri, metaDataGraphUri ) ) {
108  kDebug() << "Invalid data in ontology" << ns << "Could not find datagraph and metadatagraph relation.";
109  return false;
110  }
111 
112  return true;
113  }
114 
115 
120  QUrl guessOntologyType( Soprano::Model* tmpModel )
121  {
122  static QList<QUrl> propertyClasses;
123  if ( propertyClasses.isEmpty() )
124  propertyClasses << Soprano::Vocabulary::RDFS::Class()
125  << Soprano::Vocabulary::OWL::Class()
126  << Soprano::Vocabulary::RDF::Property()
127  << Soprano::Vocabulary::RDFS::ContainerMembershipProperty()
128  << Soprano::Vocabulary::OWL::ObjectProperty()
129  << Soprano::Vocabulary::OWL::DatatypeProperty()
130  << Soprano::Vocabulary::OWL::AnnotationProperty()
131  << Soprano::Vocabulary::OWL::FunctionalProperty()
132  << Soprano::Vocabulary::OWL::DeprecatedProperty()
133  << Soprano::Vocabulary::OWL::OntologyProperty()
134  << Soprano::Vocabulary::OWL::TransitiveProperty()
135  << Soprano::Vocabulary::OWL::SymmetricProperty()
136  << Soprano::Vocabulary::OWL::InverseFunctionalProperty()
137  << Soprano::Vocabulary::NRL::TransitiveProperty()
138  << Soprano::Vocabulary::NRL::SymmetricProperty()
139  << Soprano::Vocabulary::NRL::AsymmetricProperty()
140  << Soprano::Vocabulary::NRL::InverseFunctionalProperty()
141  << Soprano::Vocabulary::NRL::FunctionalProperty()
142  << Soprano::Vocabulary::NRL::ReflexiveProperty();
143 
144  // check for classes and properties
145  QStringList classesOrPropertiesSubQueries;
146  foreach( const QUrl& uri, propertyClasses ) {
147  classesOrPropertiesSubQueries << QString( "?type = <%1>" ).arg( uri.toString() );
148  }
149 
150  // we cannot use UNION here because redland does not support it!
151  bool haveClassesOrProperties = tmpModel->executeQuery( QString( "ask where { "
152  "?r a ?type . "
153  "FILTER(%1) . }" )
154  .arg( classesOrPropertiesSubQueries.join( " || " ) ),
155  Soprano::Query::QueryLanguageSparql ).boolValue();
156 
157  // check for anything that is not a class or property
158  classesOrPropertiesSubQueries.clear();
159  foreach( const QUrl& uri, propertyClasses ) {
160  classesOrPropertiesSubQueries << QString( "?type != <%1>" ).arg( uri.toString() );
161  }
162  // owl:Ontologys do not have any influce on our descision
163  classesOrPropertiesSubQueries << QString( "?type != <%1>" ).arg( Soprano::Vocabulary::OWL::Ontology().toString() );
164 
165  bool haveInstances = tmpModel->executeQuery( QString( "ask where { "
166  "?r a ?type . "
167  "FILTER(%1) . }" )
168  .arg( classesOrPropertiesSubQueries.join( " && " ) ),
169  Soprano::Query::QueryLanguageSparql ).boolValue();
170 
171  if ( haveClassesOrProperties && !haveInstances )
172  return Soprano::Vocabulary::NRL::Ontology();
173  else if ( !haveClassesOrProperties && haveInstances )
174  return Soprano::Vocabulary::NRL::InstanceBase();
175  else
176  return Soprano::Vocabulary::NRL::KnowledgeBase();
177  }
178 
179 
186  void createMetadata( Soprano::Model* tmpModel, const QUrl& ns )
187  {
188  Q_ASSERT( ns.isValid() );
189  QUrl dataGraphUri( ns );
190  dataGraphUri.setFragment( QString() );
191  QUrl metaDataGraphUri = createMetadataGraphUri( dataGraphUri );
192 
193  // set proper context on all data statements (This is a bit ugly but we cannot iterate and modify at the same time!)
194  QList<Statement> allStatements = tmpModel->listStatements().allStatements();
195  tmpModel->removeAllStatements();
196  foreach( Statement s, allStatements ) { // krazy:exclude=foreach
197  s.setContext( dataGraphUri );
198  tmpModel->addStatement( s );
199  }
200 
201  QUrl graphType = guessOntologyType( tmpModel );
202 
203  kDebug() << "guessed onto type:" << graphType;
204 
205  // add the metadata
206  tmpModel->addStatement( Soprano::Statement( metaDataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::GraphMetadata(), metaDataGraphUri ) );
207  tmpModel->addStatement( Soprano::Statement( metaDataGraphUri, Soprano::Vocabulary::NRL::coreGraphMetadataFor(), dataGraphUri, metaDataGraphUri ) );
208  tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), graphType, metaDataGraphUri ) );
209  if ( graphType == Soprano::Vocabulary::NRL::KnowledgeBase() ) {
210  // just to make sure. TODO: Since we now have inference in Nepomuk we could probably remove this.
211  tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::Ontology(), metaDataGraphUri ) );
212  tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::RDF::type(), Soprano::Vocabulary::NRL::InstanceBase(), metaDataGraphUri ) );
213  }
214  tmpModel->addStatement( Soprano::Statement( dataGraphUri, Soprano::Vocabulary::NAO::hasDefaultNamespace(), LiteralValue::createPlainLiteral( ns.toString() ), metaDataGraphUri ) );
215  }
216 }
217 
218 
219 class Nepomuk2::OntologyManagerModel::Private
220 {
221 public:
222  Private( OntologyManagerModel* p )
223  : q( p ) {
224  }
225 
226 private:
227  OntologyManagerModel* q;
228 };
229 
230 
231 
232 
233 
234 Nepomuk2::OntologyManagerModel::OntologyManagerModel( Soprano::Model* parentModel, QObject* parent )
235  : FilterModel(parentModel),
236  d( new Private( this ) )
237 {
238  setParent( parent );
239 }
240 
241 
242 Nepomuk2::OntologyManagerModel::~OntologyManagerModel()
243 {
244  delete d;
245 }
246 
247 
248 bool Nepomuk2::OntologyManagerModel::updateOntology( Soprano::StatementIterator data, const QUrl& ns )
249 {
250  clearError();
251 
252  QTime timer;
253  timer.start();
254 
255  // Create temp memory model
256  // ------------------------------------
257  const Soprano::Backend* backend = Soprano::PluginManager::instance()->discoverBackendByFeatures( Soprano::BackendFeatureStorageMemory );
258  if ( !backend ) {
259  kDebug() << "No Soprano backend found that can handle memory models!";
260  setError( "No Soprano backend found that can handle memory models." );
261  return false;
262  }
263 
264  Soprano::Model* tmpModel = backend->createModel( BackendSettings() << BackendSetting( Soprano::BackendOptionStorageMemory ) );
265  if ( !tmpModel ) {
266  kDebug() << "Failed to create temp memory model!";
267  setError( backend->lastError() );
268  return false;
269  }
270 
271  // so we do not have to care about deleting out tmpModel anymore.
272  QScopedPointer<Soprano::Model> modelGarbageCollector( tmpModel );
273 
274  // import the data into our tmp model
275  while ( data.next() ) {
276  Soprano::Statement st = *data;
277  if( !st.context().isValid() ) {
278  QString errorString = QString::fromLatin1("Invalid data in ontology %1 - %2 %3 %4 %5")
279  .arg( Soprano::Node::resourceToN3(ns),
280  st.subject().toN3(),
281  st.predicate().toN3(),
282  st.object().toN3(),
283  st.context().toN3() );
284 
285  kDebug() << errorString;
286  setError( errorString );
287  return false;
288  }
289  tmpModel->addStatement( *data );
290  }
291 
292  QUrl ontoUri = ns;
293  if ( ontoUri.isEmpty() ) {
294  StatementIterator it = tmpModel->listStatements();
295  if ( it.next() ) {
296  ontoUri = it.current().subject().uri();
297  if ( !ontoUri.fragment().isEmpty() ) {
298  ontoUri.setFragment( QString() );
299  }
300  else {
301  ontoUri = ontoUri.toString().left( ontoUri.toString().lastIndexOf( '/' )+1 );
302  }
303  }
304  }
305  if ( ontoUri.isEmpty() ) {
306  kDebug() << "Failed to determine ontology URI.";
307  setError( "Failed to determine ontology URI from data." );
308  return false;
309  }
310 
311  // all the data has been read into the temp model
312  // now we make sure it has a proper layout (one main and one metadata graph)
313  // ------------------------------------
314  QList<Node> graphs = tmpModel->listContexts().allNodes();
315  if ( graphs.count() == 0 ) {
316  // simple: we have to create all data manually
317  createMetadata( tmpModel, ontoUri );
318  }
319  else if ( graphs.count() == 2 ) {
320  // proper number of graphs. Make sure we have all the necessary information
321  if ( !ensureDataLayout( tmpModel, ontoUri ) ) {
322  setError( "The ontology data contains invalid statements.", Soprano::Error::ErrorInvalidArgument );
323  return false;
324  }
325  }
326  else {
327  kDebug() << "Invalid data in ontology" << ontoUri << "We need one data and one metadata graph.";
328  setError( "The ontology data contains invalid statements.", Soprano::Error::ErrorInvalidArgument );
329  return false;
330  }
331 
332 
333  // store the modification date of the ontology file in the metadata graph and reuse it to know if we have to update
334  // ------------------------------------
335  QUrl dataGraphUri, metadataGraphUri;
336  if ( findGraphUris( tmpModel, ontoUri, dataGraphUri, metadataGraphUri ) ) {
337  // remove any modification date data there is
338  tmpModel->removeAllStatements( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), Node() );
339 
340  // set the new modification date
341  tmpModel->addStatement( dataGraphUri, Soprano::Vocabulary::NAO::lastModified(), LiteralValue( QDateTime::currentDateTime() ), metadataGraphUri );
342 
343  // now it is time to merge the new data in
344  // ------------------------------------
345  if ( ontoModificationDate( ontoUri ).isValid() ) {
346  if ( !removeOntology( ontoUri ) ) {
347  return false;
348  }
349  }
350 
351  StatementIterator it = tmpModel->listStatements();
352  while ( it.next() ) {
353  if ( addStatement( *it ) != Error::ErrorNone ) {
354  // FIXME: here we should cleanup, but then again, if adding the statement
355  // fails, removing will probably also fail. So the only real solution
356  // would be a transaction.
357  return false;
358  }
359  }
360 
361  kDebug() << "Successfully updated ontology" << ontoUri << QString("(%1ms)").arg(timer.elapsed());
362  return true;
363  }
364  else {
365  kDebug() << "BUG! BUG! BUG! BUG! BUG! BUG! Could not find data and metadata graph URIs! This should not happen!";
366  return false;
367  }
368 }
369 
370 
371 bool Nepomuk2::OntologyManagerModel::removeOntology( const QUrl& ns )
372 {
373  clearError();
374 
375  QUrl dataGraphUri, metadataGraphUri;
376  if ( findGraphUris( this, ns, dataGraphUri, metadataGraphUri ) ) {
377  // now removing the ontology is simple
378  removeContext( dataGraphUri );
379  removeContext( metadataGraphUri );
380  // be sure we remove any junk from buggy versions
381  removeAllStatements( dataGraphUri, Soprano::Node(), Soprano::Node() );
382  return true;
383  }
384  else {
385  kDebug() << "Could not find data graph URI for" << ns;
386  setError( "Could not find ontology " + ns.toString(), Error::ErrorInvalidArgument );
387  return false;
388  }
389 }
390 
391 
392 QDateTime Nepomuk2::OntologyManagerModel::ontoModificationDate( const QUrl& uri )
393 {
394  // We use a FILTER(STR(?ns)...) to support both Soprano 2.3 (with plain literals) and earlier (with only typed ones)
395  QString query = QString( "select ?date where { "
396  "?onto %1 ?ns . "
397  "?onto %3 ?date . "
398  "FILTER(STR(?ns) = \"%2\") . "
399  "FILTER(DATATYPE(?date) = %4) . } LIMIT 1" )
400  .arg( Soprano::Node::resourceToN3(NAO::hasDefaultNamespace()),
401  uri.toString(),
402  Soprano::Node::resourceToN3(NAO::lastModified()),
403  Soprano::Node::resourceToN3(XMLSchema::dateTime()) );
404  QueryResultIterator it = executeQuery( query, Soprano::Query::QueryLanguageSparql );
405  if ( it.next() ) {
406  //kDebug() << "Found modification date for" << uri << it.binding( "date" ).literal().toDateTime();
407  return it[0].literal().toDateTime();
408  }
409  else {
410  return QDateTime();
411  }
412 }
413 
414 
415 QUrl Nepomuk2::OntologyManagerModel::findOntologyContext( const QUrl& uri )
416 {
417  QUrl dataGraphUri, metaDataGraphUri;
418  if ( findGraphUris( parentModel(), uri, dataGraphUri, metaDataGraphUri ) ) {
419  return dataGraphUri;
420  }
421  else {
422  return QUrl();
423  }
424 }
425 
426 #include "ontologymanagermodel.moc"
Nepomuk2::OntologyManagerModel::~OntologyManagerModel
~OntologyManagerModel()
Destructor.
Definition: ontologymanagermodel.cpp:242
QObject
Nepomuk2::errorString
QString errorString(ErrorCode code)
Definition: resource.cpp:489
Nepomuk2::OntologyManagerModel::OntologyManagerModel
OntologyManagerModel(Soprano::Model *parentModel=0, QObject *parent=0)
Create a new model.
Definition: ontologymanagermodel.cpp:234
Nepomuk2::OntologyManagerModel::updateOntology
bool updateOntology(Soprano::StatementIterator data, const QUrl &ns=QUrl())
Update an ontology.
Definition: ontologymanagermodel.cpp:248
Nepomuk2::OntologyManagerModel::findOntologyContext
QUrl findOntologyContext(const QUrl &uri)
Tries to find the ontology uri in the local Nepomuk store.
Definition: ontologymanagermodel.cpp:415
Nepomuk2::OntologyManagerModel::removeOntology
bool removeOntology(const QUrl &ns)
Remove an ontology from the model.
Definition: ontologymanagermodel.cpp:371
Nepomuk2::OntologyManagerModel::ontoModificationDate
QDateTime ontoModificationDate(const QUrl &uri)
Determine the modification time of a stored ontology.
Definition: ontologymanagermodel.cpp:392
ontologymanagermodel.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:48:08 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Nepomuk-Core

Skip menu "Nepomuk-Core"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal