26 #include <Soprano/Vocabulary/NRL>
27 #include <Soprano/Vocabulary/RDF>
28 #include <Soprano/Vocabulary/RDFS>
29 #include <Soprano/Vocabulary/NAO>
30 #include <Soprano/Vocabulary/XMLSchema>
32 #include <Soprano/StatementIterator>
33 #include <Soprano/QueryResultIterator>
34 #include <Soprano/FilterModel>
35 #include <Soprano/NodeIterator>
36 #include <Soprano/LiteralValue>
37 #include <Soprano/Node>
42 #include <Soprano/Graph>
46 using namespace Soprano::Vocabulary;
47 using namespace Nepomuk2::Vocabulary;
51 QUrl getBlankOrResourceUri(
const Soprano::Node & n ) {
52 if( n.isResource() ) {
55 else if( n.isBlank() ) {
56 return QString( QLatin1String(
"_:") + n.identifier() );
62 return QUrl( Soprano::Vocabulary::XMLSchema::xsdNamespace().toString() + QLatin1String(
"duration") );
65 template<
typename T> QStringList nodesToN3(
const T &nodes ) {
67 foreach(
const Soprano::Node& node, nodes ) {
73 template<
typename T> QStringList urlsToN3(
const T &urls ) {
75 foreach(
const QUrl& uri, urls ) {
76 list << Soprano::Node::resourceToN3(uri);
81 QList<QUrl> nodeListToUriList(
const QList<Soprano::Node>& nodeList ) {
83 urls.reserve( nodeList.size() );
84 foreach(
const Soprano::Node& node, nodeList )
91 const Nepomuk2::StoreResourcesFlags& flags,
bool discardable )
94 m_discardbale = discardable;
101 metadataProperties.reserve( 4 );
102 metadataProperties.insert( NAO::lastModified() );
103 metadataProperties.insert( NAO::userVisible() );
104 metadataProperties.insert( NAO::created() );
105 metadataProperties.insert( NAO::creator() );
115 m_mappings = mappings;
126 if( resHash.isEmpty() || graph.isEmpty() )
135 const QString preQuery = QString::fromLatin1(
"sparql insert into %1 { ")
136 .arg( Soprano::Node::resourceToN3( graph ) );
139 QHashIterator<KUrl, Sync::SyncResource> it( resHash );
140 while( it.hasNext() ) {
143 const QString resN3 = Soprano::Node::resourceToN3( res.
uri() );
146 QList<QUrl> propertiesToRemove;
147 QList<KUrl> properties = res.uniqueKeys();
148 foreach(
const QUrl& prop, properties ) {
149 QList<Soprano::Node> values = res.values( prop );
150 const QString propN3 = Soprano::Node::resourceToN3( prop );
152 if( lazy || overwrite || overwriteAll ) {
154 QString query = QString::fromLatin1(
"select ?o where { %1 %2 ?o . }")
155 .arg( resN3, propN3 );
157 Soprano::QueryResultIterator it = m_model->executeQuery(query, Soprano::Query::QueryLanguageSparqlNoInference);
159 m_resRemoveHash[ res.
uri() ].insert( prop, it[0] );
161 propertiesToRemove << prop;
165 values = QList<Soprano::Node>() << values.first();
169 query += QString::fromLatin1(
" %1 %2 ;").arg( propN3, nodesToN3(values).join(QString(
", ")) );
172 query[ query.length() - 1 ] =
'.';
175 if( propertiesToRemove.count() ) {
176 QString query = QString::fromLatin1(
"sparql delete { graph ?g { %1 ?p ?o.} } where { "
177 " graph ?g { %1 ?p ?o. } FILTER(?p in(%2)) . }")
178 .arg( resN3, urlsToN3(propertiesToRemove).join(
",") );
180 m_model->executeQuery( query, Soprano::Query::QueryLanguageUser, QLatin1String(
"sql") );
184 if( query.size() >= 500 ) {
185 QString command = QString::fromLatin1(
"%1 %2 }").arg( preQuery, query );
186 m_model->executeQuery( command, Soprano::Query::QueryLanguageUser, QLatin1String(
"sql") );
187 if( m_model->lastError() ) {
188 setError( m_model->lastError() );
195 if( !query.isEmpty() ) {
196 QString command = QString::fromLatin1(
"%1 %2 }").arg( preQuery, query );
199 m_model->executeQuery( command, Soprano::Query::QueryLanguageUser, QLatin1String(
"sql") );
200 if( m_model->lastError() ) {
201 setError( m_model->lastError() );
211 bool Nepomuk2::ResourceMerger::isOfType(
const Soprano::Node & node,
const QUrl& type,
const QList<QUrl> & newTypes)
const
214 ClassAndPropertyTree * tree = m_model->classAndPropertyTree();
216 QList<QUrl> types( newTypes );
217 if( !node.isBlank() ) {
218 types << m_model->typeCache()->types( node.uri() );
222 if( types.isEmpty() ) {
223 kDebug() << node <<
" does not have a type!!";
227 foreach(
const QUrl & uri, types ) {
228 if( uri == type || tree->isChildOf( uri, type ) ) {
236 Soprano::Node Nepomuk2::ResourceMerger::resolveMappedNode(
const Soprano::Node& node)
239 const QUrl uri = node.isBlank() ? node.toN3() : node.uri();
241 if( it != m_mappings.constEnd() ) {
249 if( uri.scheme() == QLatin1String(
"nepomuk") &&
250 !m_model->containsAnyStatement( uri, Soprano::Node(), Soprano::Node() ) ) {
251 QString error = QString::fromLatin1(
"Could not resolve %1. "
252 "You cannot create nepomuk uris using this method")
253 .arg( Soprano::Node::resourceToN3( uri ) );
254 setError( error, Soprano::Error::ErrorInvalidArgument );
255 return Soprano::Node();
261 Soprano::Node Nepomuk2::ResourceMerger::resolveBlankNode(
const Soprano::Node& node)
263 if( !node.isBlank() )
266 const QUrl nodeN3( node.toN3() );
268 if( it != m_mappings.constEnd() ) {
272 const QUrl newUri = m_model->createUri( DataManagementModel::ResourceUri );
273 m_mappings.insert( nodeN3, newUri );
274 m_newUris.insert( newUri );
282 Sync::ResourceHash resHash;
284 QHashIterator<KUrl, Sync::SyncResource> it( resHash_ );
285 while( it.hasNext() ) {
286 Sync::SyncResource res = it.next().value();
288 res.
setUri( resolveBlankNode( res.uriNode() ) );
290 QMutableHashIterator<KUrl, Soprano::Node> it( res );
291 while( it.hasNext() ) {
293 it.setValue( resolveBlankNode(it.value()) );
296 resHash.insert( res.uri(), res );
309 QHashIterator<KUrl, Sync::SyncResource> it_( resHash_ );
310 while( it_.hasNext() ) {
312 res.
setUri( resolveMappedNode(res.
uri()) );
316 QMutableHashIterator<KUrl, Soprano::Node> iter( res );
317 while( iter.hasNext() ) {
318 Soprano::Node&
object = iter.next().value();
319 if( (
object.isResource() &&
object.uri().scheme() == QLatin1String(
"nepomuk") ) ||
object.isBlank() )
320 object = resolveMappedNode(
object );
326 resHash.insert( res.
uri(), res );
329 Soprano::LiteralValue currentDateTime( QDateTime::currentDateTime() );
335 QStringList resN3List;
337 QMutableHashIterator<KUrl, Sync::SyncResource> it( resHash );
338 while( it.hasNext() ) {
345 if( fit != res.end() ) {
346 metadataRes.insert( NAO::lastModified(), fit.value() );
350 metadataRes.insert( NAO::lastModified(), currentDateTime );
354 resN3List << Soprano::Node::resourceToN3(res.
uri());
356 fit = res.find( NAO::created() );
357 if( fit != res.end() ) {
360 metadataRes.insert( NAO::created(), fit.value() );
366 metadataRes.insert( NAO::created(), currentDateTime );
370 fit = res.find( NIE::url() );
371 if( fit != res.end() ) {
372 metadataRes.insert( NIE::url(), fit.value() );
376 resMetadataHash.insert( metadataRes.uri(), metadataRes );
378 if( !hasValidData( resHash, res ) )
384 if( resN3List.count() ) {
385 QString removeCommand = QString::fromLatin1(
"sparql delete from %1 { ?r nao:lastModified ?m . } where"
386 "{ graph %1 { ?r nao:lastModified ?m . FILTER(?r in (%2)) .} }")
387 .arg( Soprano::Node::resourceToN3(m_model->nepomukGraph()),
388 resN3List.join(
",") );
389 m_model->executeQuery( removeCommand, Soprano::Query::QueryLanguageUser, QLatin1String(
"sql") );
394 if( !resHash.isEmpty() ) {
395 m_graph = m_model->fetchGraph(m_app, m_discardbale);
396 if( m_graph.isEmpty() || m_model->lastError() )
402 resHash = resolveBlankNodes( resHash );
403 resMetadataHash = resolveBlankNodes( resMetadataHash );
410 if( !push( m_graph, resHash ) )
413 if( !push( m_model->nepomukGraph(), resMetadataHash ) )
421 QSetIterator<QUrl> newUriIt( m_newUris );
422 while( newUriIt.hasNext() ) {
423 const QUrl newUri = newUriIt.next();
425 QList<Soprano::Node> types = resHash[ newUri ].values( RDF::type() );
426 QSet<QUrl> allTypes = ClassAndPropertyTree::self()->allParents( nodeListToUriList(types) );
427 m_rvm->createResource( newUri, allTypes );
431 QHashIterator<KUrl, Sync::SyncResource> iter( resHash );
432 while( iter.hasNext() ) {
437 const QList<KUrl> properties = res.uniqueKeys();
438 foreach(
const KUrl& propUri, properties ) {
439 if( metadataProperties.contains( propUri ) )
442 const QList<Soprano::Node> added = res.values( propUri );
443 const QList<Soprano::Node> removed = removedRes.values( propUri );
445 m_rvm->changeProperty( res.
uri(), propUri, added, removed );
457 QList<Soprano::Node> resNodeTypes = res.values( RDF::type() );
458 QList<QUrl> resTypes = nodeListToUriList( resNodeTypes );
459 res.remove( RDF::type() );
462 QList<KUrl> properties = res.uniqueKeys();
463 foreach(
const KUrl& propUri, properties ) {
464 QList<Soprano::Node> objectValues = res.values( propUri );
472 if( !lazyCardinalities ) {
473 int maxCardinality = ClassAndPropertyTree::self()->maxCardinality( propUri );
474 if( maxCardinality > 0 ) {
476 QStringList filterStringList;
477 QStringList objectN3 = nodesToN3( objectValues );
478 foreach(
const QString &n3, objectN3 )
479 filterStringList << QString::fromLatin1(
"?v!=%1").arg( n3 );
481 int existingCardinality = 0;
483 if( !res.
isBlank() && !overwriteProperties ) {
484 if( maxCardinality > 1 ) {
485 const QString query = QString::fromLatin1(
"select count(distinct ?v) where {"
486 " %1 %2 ?v . FILTER( %3 ) . }")
487 .arg( Soprano::Node::resourceToN3( res.
uri() ),
488 Soprano::Node::resourceToN3( propUri ),
489 filterStringList.join( QLatin1String(
" && ") ) );
491 Soprano::QueryResultIterator exCarIt =
492 m_model->executeQuery( query, Soprano::Query::QueryLanguageSparql );
493 if( exCarIt.next() ) {
494 existingCardinality = exCarIt[0].literal().toInt();
498 QString query = QString::fromLatin1(
"ask where { %1 %2 ?v . FILTER(%3) .}")
499 .arg( Soprano::Node::resourceToN3( res.
uri() ),
500 Soprano::Node::resourceToN3( propUri ),
501 filterStringList.first() );
503 bool e = m_model->executeQuery( query, Soprano::Query::QueryLanguageSparqlNoInference ).boolValue();
505 existingCardinality = 1;
509 const int newCardinality = objectValues.size() + existingCardinality;
510 if( newCardinality > maxCardinality ) {
514 QString query = QString::fromLatin1(
"select distinct ?v where {" " %1 %2 ?v ."
516 .arg( Soprano::Node::resourceToN3( res.
uri() ),
517 Soprano::Node::resourceToN3( propUri ),
518 filterStringList.join( QLatin1String(
" && ") ) );
519 QList< Soprano::Node > existingValues = m_model->executeQuery( query,
520 Soprano::Query::QueryLanguageSparql ).iterateBindings(0).allNodes();
522 QString error = QString::fromLatin1(
"%1 has a max cardinality of %2. Provided "
523 "%3 values - %4. Existing - %5")
525 QString::number(maxCardinality),
526 QString::number(objectN3.size()),
527 objectN3.join(QLatin1String(
", ")),
528 nodesToN3(existingValues).join(QLatin1String(
", ")) );
529 setError( error, Soprano::Error::ErrorInvalidStatement );
538 ClassAndPropertyTree* tree = ClassAndPropertyTree::self();
539 QUrl domain = tree->propertyDomain( propUri );
540 QUrl range = tree->propertyRange( propUri );
546 if( !domain.isEmpty() && !isOfType( res.
uriNode(), domain, resTypes ) ) {
548 QList<QUrl> allTypes = ( resTypes + m_model->typeCache()->types(res.
uri()) );
550 QString error = QString::fromLatin1(
"%1 has a rdfs:domain of %2. "
551 "%3 only has the following types %4" )
552 .arg( Soprano::Node::resourceToN3( propUri ),
553 Soprano::Node::resourceToN3( domain ),
554 Soprano::Node::resourceToN3( res.
uri() ),
556 setError( error, Soprano::Error::ErrorInvalidArgument);
561 if( range.isEmpty() )
564 foreach(
const Soprano::Node&
object, objectValues ) {
565 if(
object.isResource() ||
object.isBlank() ) {
566 const QUrl objUri = getBlankOrResourceUri(
object );
567 QList<QUrl> objectNewTypes = nodeListToUriList( resHash[ objUri ].values( RDF::type() ) );
569 if( !isOfType(
object, range, objectNewTypes ) ) {
571 QList<QUrl> allTypes = ( objectNewTypes + m_model->typeCache()->types(objUri) );
573 QString error = QString::fromLatin1(
"%1 has a rdfs:range of %2. "
574 "%3 only has the following types %4" )
575 .arg( Soprano::Node::resourceToN3( propUri ),
576 Soprano::Node::resourceToN3( range ),
577 Soprano::Node::resourceToN3( objUri ),
579 setError( error, Soprano::Error::ErrorInvalidArgument );
583 else if(
object.isLiteral() ) {
584 const Soprano::LiteralValue lv =
object.literal();
586 if( lv.isUnsignedInt() && range ==
xsdDuration() ) {
589 if( (!lv.isPlain() && lv.dataTypeUri() != range) || (lv.isPlain() && range != RDFS::Literal()) ) {
591 QString error = QString::fromLatin1(
"%1 has a rdfs:range of %2. Provided %3")
592 .arg( Soprano::Node::resourceToN3( propUri ),
593 Soprano::Node::resourceToN3( range ),
594 Soprano::Node::literalToN3(lv) );
595 setError( error, Soprano::Error::ErrorInvalidArgument);
604 foreach(
const Soprano::Node& node, resNodeTypes )
605 res.insert( RDF::type(), node );
612 m_error = Soprano::Error::Error();
627 m_error = Soprano::Error::Error( errorMessage, code );
ResourceMerger(Nepomuk2::DataManagementModel *model, const QString &app, const StoreResourcesFlags &flags, bool discardable)
By default storeResources() will only append data and fail if properties with cardinality 1 already h...
When lazy cardinalities are enabled any value that would violate a cardinality restriction is simply ...
QHash< QUrl, QUrl > mappings() const
By default storeResources will only append data and fail if properties with cardinality 1 already hav...
void setError(const Soprano::Error::Error &error)
ResourceWatcherManager * resourceWatcherManager() const
used by the unit tests
void setMappings(const QHash< QUrl, QUrl > &mappings)
Soprano::Error::Error lastError()
void setUri(const Soprano::Node &node)
If node is resource node the uri is set to the node's uri Otherwise if node is a blank node then the ...
bool merge(const Sync::ResourceHash &resHash)
QStringList resourcesToN3(const T &urls)
Convert a list or set or QUrls into a list of N3 formatted strings.
int maxCardinality(const QUrl &type) const
A SyncResource is a convenient way of storing a set of properties and objects for a common subject...
Soprano::Node uriNode() const
A SyncResource is a convenient way of representing a list of Soprano::Statements or a Soprano::Graph...