File indexing completed on 2024-05-26 16:15:24
0001 /* This file is part of the KDE project 0002 Copyright (C) 2010 KO GmbH <ben.martin@kogmbh.com> 0003 Copyright (C) 2011,2012 Ben Martin <monkeyiq@users.sourceforge.net> hacking for fun! 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "KoDocumentRdf.h" 0022 0023 #include "KoRdfPrefixMapping.h" 0024 #include "RdfSemanticTreeWidgetSelectAction.h" 0025 #include "InsertSemanticObjectActionBase.h" 0026 #include "InsertSemanticObjectCreateAction.h" 0027 #include "InsertSemanticObjectReferenceAction.h" 0028 #include "KoRdfSemanticItemRegistry.h" 0029 0030 #include <KoTextDocument.h> 0031 #include <KoTextRdfCore.h> 0032 #include <KoXmlWriter.h> 0033 #include <KoStoreDevice.h> 0034 #include <KoCanvasBase.h> 0035 #include <KoTextEditor.h> 0036 #include <KoInlineObject.h> 0037 #include <KoTextInlineRdf.h> 0038 #include <KoInlineTextObjectManager.h> 0039 #include <KoTextRangeManager.h> 0040 #include <KoTextMeta.h> 0041 0042 #include <kdebug.h> 0043 #include <klocalizedstring.h> 0044 #include <QAction> 0045 0046 #include <QWeakPointer> 0047 0048 // #define DEBUG_RDF 0049 0050 #ifdef DEBUG_RDF 0051 #define RDEBUG kDebug(30015) 0052 #else 0053 #define RDEBUG if(0) kDebug(30015) 0054 #endif 0055 0056 using namespace Soprano; 0057 0058 class KoDocumentRdfPrivate 0059 { 0060 public: 0061 0062 KoDocumentRdfPrivate() 0063 : model(Soprano::createModel()) 0064 , prefixMapping(0) 0065 { 0066 } 0067 0068 ~KoDocumentRdfPrivate() 0069 { 0070 prefixMapping->deleteLater(); 0071 model->deleteLater(); 0072 } 0073 0074 QSharedPointer<Soprano::Model> model; ///< Main Model containing all Rdf for doc 0075 QMap<QString, QWeakPointer<KoTextInlineRdf> > inlineRdfObjects; ///< Cache of weak pointers to inline Rdf 0076 KoRdfPrefixMapping *prefixMapping; ///< prefix -> URI mapping 0077 0078 QMap<QString, QList<hKoRdfBasicSemanticItem> > semanticItems; 0079 QMap<QString, QList<hKoSemanticStylesheet> > userStylesheets; 0080 }; 0081 0082 const QString KoDocumentRdf::RDF_PATH_CONTEXT_PREFIX = "http://www.calligra.org/Rdf/path/"; 0083 0084 KoDocumentRdf::KoDocumentRdf(QObject *parent) 0085 : KoDocumentRdfBase(parent) 0086 , d (new KoDocumentRdfPrivate()) 0087 { 0088 // if (!backendIsSane()) { 0089 // kWarning() << "Looks like the backend is not sane!"; 0090 // } 0091 d->prefixMapping = new KoRdfPrefixMapping(this); 0092 } 0093 0094 KoDocumentRdf::~KoDocumentRdf() 0095 { 0096 RDEBUG; //FIXME: looks like unused call to kDebug() 0097 delete d; 0098 } 0099 0100 QSharedPointer<Soprano::Model> KoDocumentRdf::model() const 0101 { 0102 return d->model; 0103 } 0104 0105 KoRdfPrefixMapping *KoDocumentRdf::prefixMapping() const 0106 { 0107 return d->prefixMapping; 0108 } 0109 0110 /** 0111 * Graph context used for Rdf stored inline in content.xml 0112 * in an Rdfa like fashion. 0113 */ 0114 Soprano::Node KoDocumentRdf::inlineRdfContext() const 0115 { 0116 return Node(QUrl("http://www.calligra.org/Rdf/inline-rdf")); 0117 } 0118 0119 QString KoDocumentRdf::rdfInternalMetadataWithoutSubjectURI() const 0120 { 0121 return "http://www.calligra.org/Rdf/internal/content.xml"; 0122 } 0123 0124 Soprano::Node KoDocumentRdf::manifestRdfNode() const 0125 { 0126 return Node(QUrl(RDF_PATH_CONTEXT_PREFIX + "manifest.rdf")); 0127 } 0128 0129 void KoDocumentRdf::freshenBNodes(QSharedPointer<Soprano::Model> m) 0130 { 0131 Q_ASSERT(m); 0132 Q_ASSERT(d->model); 0133 0134 QList<Soprano::Statement> removeList; 0135 QList<Soprano::Statement> addList; 0136 QMap<QString, Soprano::Node> bnodeMap; 0137 StatementIterator it = m->listStatements(); 0138 QList<Statement> allStatements = it.allElements(); 0139 0140 RDEBUG << "freshening model.sz:" << allStatements.size(); 0141 0142 foreach (const Soprano::Statement &s, allStatements) { 0143 Soprano::Node subj = s.subject(); 0144 Soprano::Node obj = s.object(); 0145 Soprano::Statement news; 0146 0147 if (subj.type() == Soprano::Node::BlankNode) { 0148 QString nodeStr = subj.toString(); 0149 Soprano::Node n = bnodeMap[nodeStr]; 0150 if (!n.isValid()) { 0151 n = d->model->createBlankNode(); 0152 bnodeMap[nodeStr] = n; 0153 } 0154 removeList << s; 0155 subj = n; 0156 news = Statement(subj, s.predicate(), obj, s.context()); 0157 } 0158 0159 if (obj.type() == Soprano::Node::BlankNode) { 0160 QString nodeStr = obj.toString(); 0161 Soprano::Node n = bnodeMap[ nodeStr ]; 0162 if (!n.isValid()) { 0163 n = d->model->createBlankNode(); 0164 bnodeMap[ nodeStr ] = n; 0165 } 0166 removeList << s; 0167 obj = n; 0168 news = Statement(subj, s.predicate(), obj, s.context()); 0169 } 0170 0171 if (news.isValid()) { 0172 addList << news; 0173 } 0174 } 0175 RDEBUG << "remove count:" << removeList.size(); 0176 RDEBUG << "add count:" << addList.size(); 0177 // Note that as of Jan 2010 you couldn't rely on 0178 // Soprano::Model::removeStatements() if every entry 0179 // in removeList did not exist exactly once in the model. 0180 KoTextRdfCore::removeStatementsIfTheyExist(m, removeList); 0181 RDEBUG << "after remove, model.sz:" << m->statementCount(); 0182 m->addStatements(addList); 0183 RDEBUG << "after add, model.sz:" << m->statementCount(); 0184 } 0185 0186 bool KoDocumentRdf::loadRdf(KoStore *store, const Soprano::Parser *parser, const QString &fileName) 0187 { 0188 QSharedPointer<Soprano::Model> tmpmodel(Soprano::createModel()); 0189 if (!d->model || !tmpmodel) { 0190 kWarning(30003) << "No soprano model"; 0191 return false; 0192 } 0193 0194 bool ok = true; 0195 if (!store->open(fileName)) { 0196 RDEBUG << "Entry " << fileName << " not found!"; // not a warning as embedded stores don't have to have all files 0197 return false; 0198 } 0199 0200 RDEBUG << "Loading external Rdf/XML from:" << fileName; 0201 0202 Soprano::Node context(QUrl(RDF_PATH_CONTEXT_PREFIX + fileName)); 0203 QUrl BaseURI = QUrl(QString()); 0204 QString rdfxmlData(store->device()->readAll()); 0205 Soprano::StatementIterator it = parser->parseString(rdfxmlData, BaseURI, Soprano::SerializationRdfXml); 0206 QList<Statement> allStatements = it.allElements(); 0207 RDEBUG << "Found " << allStatements.size() << " triples..." << endl; 0208 foreach (const Soprano::Statement &s, allStatements) { 0209 Soprano::Node subj = s.subject(); 0210 Soprano::Node pred = s.predicate(); 0211 Soprano::Node obj = s.object(); 0212 Error::ErrorCode err = tmpmodel->addStatement(subj, pred, obj, context); 0213 if (err != Error::ErrorNone) { 0214 RDEBUG << "Error adding triple! s:" << subj << " p:" << pred << " o:" << obj << endl; 0215 ok = false; 0216 break; 0217 } 0218 } 0219 RDEBUG << "calling freshenBNodes(), tmpmodel.sz:" << tmpmodel->statementCount(); 0220 0221 #ifdef DEBUG_RDF 0222 dumpModel(fileName, tmpmodel); 0223 #endif 0224 freshenBNodes(tmpmodel); 0225 #ifdef DEBUG_RDF 0226 dumpModel(fileName, tmpmodel); 0227 #endif 0228 0229 RDEBUG << "done with freshenBNodes(), tmpmodel.sz:" << tmpmodel->statementCount(); 0230 0231 d->model->addStatements(tmpmodel->listStatements().allElements()); 0232 if (fileName == "manifest.rdf" && d->prefixMapping) { 0233 d->prefixMapping->load(d->model); 0234 0235 foreach (const QString &semanticClass, KoRdfSemanticItemRegistry::instance()->classNames()) { 0236 if (!KoRdfSemanticItemRegistry::instance()->isBasic(semanticClass)) { 0237 hKoRdfSemanticItem si(static_cast<KoRdfSemanticItem *>( 0238 KoRdfSemanticItemRegistry::instance()->createSemanticItem(semanticClass, this, this).data() 0239 )); 0240 si->loadUserStylesheets(d->model); 0241 } 0242 } 0243 } 0244 store->close(); 0245 return ok; 0246 } 0247 0248 bool KoDocumentRdf::loadOasis(KoStore *store) 0249 { 0250 if (!store) { 0251 kWarning(30003) << "No store backend"; 0252 return false; 0253 } 0254 0255 if (!d->model) { 0256 kWarning(30003) << "No soprano model"; 0257 return false; 0258 } 0259 0260 const Soprano::Parser *parser = 0261 Soprano::PluginManager::instance()->discoverParserForSerialization( 0262 Soprano::SerializationRdfXml); 0263 bool ok = loadRdf(store, parser, "manifest.rdf"); 0264 if (ok) { 0265 QString sparqlQuery = 0266 "prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n" 0267 "prefix odf: <http://docs.oasis-open.org/ns/office/1.2/meta/odf#> \n" 0268 "prefix pkg: <http://docs.oasis-open.org/ns/office/1.2/meta/pkg#> \n" 0269 "select ?subj ?fileName \n" 0270 " where { \n" 0271 " ?subj rdf:type odf:MetaDataFile . \n" 0272 " ?subj pkg:path ?fileName \n" 0273 " } \n"; 0274 Soprano::QueryResultIterator it = 0275 d->model->executeQuery(sparqlQuery, 0276 Soprano::Query::QueryLanguageSparql); 0277 0278 QList< QString > externalRdfFiles; 0279 // 0280 // This is a bit tricky, loadRdf() might block if the 0281 // sparql query is still being iterated, so we have to 0282 // store the fileNames and exhaust the binding result 0283 // iterator first. 0284 // 0285 while (it.next()) { 0286 QString fileName = it.binding("fileName").toString(); 0287 externalRdfFiles << fileName; 0288 } 0289 0290 foreach (const QString &fileName, externalRdfFiles) { 0291 ok = loadRdf(store, parser, fileName); 0292 if (!ok) { 0293 break; 0294 } 0295 } 0296 } 0297 return ok; 0298 } 0299 0300 bool KoDocumentRdf::saveRdf(KoStore *store, KoXmlWriter *manifestWriter, const Soprano::Node &context) const 0301 { 0302 bool ok = false; 0303 QString fileName("manifest.rdf"); 0304 if (context.toString() == inlineRdfContext().toString()) { 0305 RDEBUG << "found some internal Rdf, this is handled by augmenting the DOM"; 0306 return true; 0307 } 0308 0309 if (!model()) { 0310 kWarning(30003) << "No soprano model"; 0311 return false; 0312 } 0313 0314 // 0315 // The context contains the filename to save into 0316 // 0317 if (context.toString().startsWith(RDF_PATH_CONTEXT_PREFIX)) { 0318 fileName = context.toString().mid(RDF_PATH_CONTEXT_PREFIX.size()); 0319 } 0320 0321 RDEBUG << "saving external file:" << fileName; 0322 if (!store->open(fileName)) { 0323 return false; 0324 } 0325 0326 KoStoreDevice dev(store); 0327 QTextStream oss(&dev); 0328 if (fileName == "manifest.rdf" && d->prefixMapping) { 0329 d->prefixMapping->save(d->model, context); 0330 0331 foreach (const QString &semanticClass, KoRdfSemanticItemRegistry::instance()->classNames()) { 0332 if (!KoRdfSemanticItemRegistry::instance()->isBasic(semanticClass)) { 0333 hKoRdfSemanticItem si(static_cast<KoRdfSemanticItem *>( 0334 KoRdfSemanticItemRegistry::instance()->createSemanticItem( 0335 semanticClass, 0336 this, 0337 const_cast<KoDocumentRdf*>(this) 0338 ).data() 0339 )); 0340 si->saveUserStylesheets(d->model, context); 0341 } 0342 } 0343 } 0344 Soprano::StatementIterator triples = model()->listStatements(Soprano::Node(), 0345 Soprano::Node(), Soprano::Node(), context); 0346 0347 const Soprano::Serializer *serializer = Soprano::PluginManager::instance()-> 0348 discoverSerializerForSerialization(Soprano::SerializationRdfXml); 0349 0350 if (serializer) { 0351 QString data; 0352 QTextStream tss(&data); 0353 if (serializer->serialize(triples, tss, Soprano::SerializationRdfXml)) { 0354 tss.flush(); 0355 oss << data; 0356 RDEBUG << "fileName:" << fileName << " data.sz:" << data.size(); 0357 RDEBUG << "model.sz:" << model()->statementCount(); 0358 ok = true; 0359 } else { 0360 RDEBUG << "serialization of Rdf failed!"; 0361 } 0362 } 0363 oss.flush(); 0364 store->close(); 0365 manifestWriter->addManifestEntry(fileName, "application/rdf+xml"); 0366 return ok; 0367 } 0368 0369 bool KoDocumentRdf::saveOasis(KoStore *store, KoXmlWriter *manifestWriter) 0370 { 0371 RDEBUG << "saveOasis() generic"; 0372 if (!d->model) { 0373 kWarning(30003) << "No soprano model"; 0374 return false; 0375 } 0376 bool ok = true; 0377 NodeIterator contextIter = model()->listContexts(); 0378 QList<Node> contexts = contextIter.allElements(); 0379 foreach (const Soprano::Node &node, contexts) { 0380 if (!saveRdf(store, manifestWriter, node)) { 0381 ok = false; 0382 } 0383 } 0384 return ok; 0385 } 0386 0387 void KoDocumentRdf::updateXmlIdReferences(const QMap<QString, QString> &m) 0388 { 0389 Q_ASSERT(d->model); 0390 0391 QList<Soprano::Statement> removeList; 0392 QList<Soprano::Statement> addList; 0393 StatementIterator it = model()->listStatements( 0394 Node(), 0395 Node(QUrl("http://docs.oasis-open.org/ns/office/1.2/meta/pkg#idref")), 0396 Node(), 0397 Node()); 0398 if (!it.isValid()) 0399 return; 0400 0401 // new xmlid->inlinerdfobject mapping 0402 QMap<QString, QWeakPointer<KoTextInlineRdf> > inlineRdfObjects; 0403 0404 QList<Statement> allStatements = it.allElements(); 0405 foreach (const Soprano::Statement &s, allStatements) { 0406 RDEBUG << "seeking obj:" << s.object(); 0407 QMap<QString, QString>::const_iterator mi = m.find(s.object().toString()); 0408 if (mi != m.end()) { 0409 const QString &oldID = mi.key(); 0410 const QString &newID = mi.value(); 0411 removeList << s; 0412 Statement n(s.subject(), 0413 s.predicate(), 0414 Node(LiteralValue::createPlainLiteral(newID)), 0415 s.context()); 0416 addList << n; 0417 RDEBUG << "looking for inlineRdf object for ID:" << oldID; 0418 if (KoTextInlineRdf *inlineRdf = findInlineRdfByID(oldID)) { 0419 RDEBUG << "updating the xmlid of the inline object"; 0420 RDEBUG << "old:" << oldID << " new:" << newID; 0421 inlineRdf->setXmlId(newID); 0422 inlineRdfObjects[newID] = inlineRdf; 0423 } 0424 } 0425 } 0426 // out with the old, in with the new 0427 // remove & add the triple lists. 0428 RDEBUG << "addStatements.size:" << addList.size(); 0429 RDEBUG << " remove.size:" << removeList.size(); 0430 KoTextRdfCore::removeStatementsIfTheyExist(d->model, removeList); 0431 d->model->addStatements(addList); 0432 d->inlineRdfObjects = inlineRdfObjects; 0433 0434 } 0435 0436 QList<hKoRdfBasicSemanticItem> KoDocumentRdf::semanticItems(const QString &className, QSharedPointer<Soprano::Model> m) 0437 { 0438 if (!m) { 0439 m = d->model; 0440 Q_ASSERT(m); 0441 } 0442 0443 QList<hKoRdfBasicSemanticItem> items; 0444 // TODO: improve double lookup 0445 if (KoRdfSemanticItemRegistry::instance()->classNames().contains(className)) { 0446 KoRdfSemanticItemRegistry::instance()->updateSemanticItems(d->semanticItems[className], this, className, m); 0447 items = d->semanticItems[className]; 0448 } 0449 0450 return items; 0451 } 0452 0453 hKoRdfBasicSemanticItem KoDocumentRdf::createSemanticItem(const QString &semanticClass, QObject *parent) const 0454 { 0455 return KoRdfSemanticItemRegistry::instance()->createSemanticItem(semanticClass, this, parent); 0456 } 0457 0458 void KoDocumentRdf::dumpModel(const QString &msg, QSharedPointer<Soprano::Model> m) const 0459 { 0460 if (!m) { 0461 return; 0462 } 0463 0464 QList<Soprano::Statement> allStatements = m->listStatements().allElements(); 0465 RDEBUG << "----- " << msg << " ----- model size:" << allStatements.size(); 0466 foreach (const Soprano::Statement &s, allStatements) { 0467 RDEBUG << s; 0468 } 0469 } 0470 0471 Soprano::Statement KoDocumentRdf::toStatement(KoTextInlineRdf *inlineRdf) const 0472 { 0473 if (!inlineRdf) { 0474 return Soprano::Statement(); 0475 } 0476 if (inlineRdf->predicate().isEmpty()) { 0477 return Soprano::Statement(); 0478 } 0479 0480 Soprano::Node subj = Soprano::Node::createResourceNode(QUrl(inlineRdf->subject())); 0481 Soprano::Node pred = Soprano::Node::createResourceNode(QUrl(inlineRdf->predicate())); 0482 Soprano::Node obj; 0483 0484 switch (inlineRdf->sopranoObjectType()) { 0485 case Node::ResourceNode: 0486 obj = Soprano::Node::createResourceNode(inlineRdf->object()); 0487 break; 0488 case Node::LiteralNode: 0489 obj = Soprano::Node::createLiteralNode(inlineRdf->object()); 0490 break; 0491 case Node::BlankNode: 0492 obj = Soprano::Node::createBlankNode(inlineRdf->object()); 0493 break; 0494 } 0495 0496 if (!inlineRdf->subject().size()) { 0497 subj = inlineRdfContext(); 0498 } 0499 0500 RDEBUG << "subj:" << subj; 0501 RDEBUG << " pred:" << pred; 0502 RDEBUG << " obj:" << obj; 0503 return Soprano::Statement(subj, pred, obj, inlineRdfContext()); 0504 } 0505 0506 void KoDocumentRdf::addStatements(QSharedPointer<Soprano::Model> model, const QString &xmlid) 0507 { 0508 Q_ASSERT(model); 0509 Q_ASSERT(d->model); 0510 QString sparqlQuery; 0511 0512 RDEBUG << "addStatements model.sz:" << d->model->statementCount() << " xmlid:" << xmlid; 0513 0514 sparqlQuery = "prefix pkg: <http://docs.oasis-open.org/ns/office/1.2/meta/pkg#> \n" 0515 "\n" 0516 "select ?s ?p ?o ?g \n" 0517 "where { \n" 0518 " graph ?g { ?s ?p ?o } . ?s pkg:idref ?xmlid \n" 0519 " filter( str(?xmlid) = \"" + xmlid + "\" ) \n" 0520 "}\n"; 0521 0522 RDEBUG << "sparql:" << sparqlQuery; 0523 Soprano::QueryResultIterator it = d->model->executeQuery(sparqlQuery, 0524 Soprano::Query::QueryLanguageSparql); 0525 0526 while (it.next()) { 0527 Statement s(it.binding("s"), 0528 it.binding("p"), 0529 it.binding("o"), 0530 it.binding("g")); 0531 model->addStatement(s); 0532 RDEBUG << "result, s:" << it.binding("s"); 0533 RDEBUG << " p:" << it.binding("p"); 0534 RDEBUG << " o:" << it.binding("o"); 0535 } 0536 } 0537 0538 void KoDocumentRdf::expandStatementsReferencingSubject(QSharedPointer<Soprano::Model> _model) const 0539 { 0540 Q_ASSERT(_model); 0541 Q_ASSERT(d->model); 0542 QList<Statement> addList; 0543 QList<Statement> allStatements = _model->listStatements().allElements(); 0544 0545 foreach (const Soprano::Statement &s, allStatements) { 0546 QList<Statement> all = d->model->listStatements(Node(), Node(), s.subject()).allElements(); 0547 addList.append(all); 0548 } 0549 _model->addStatements(addList); 0550 } 0551 0552 void KoDocumentRdf::expandStatementsSubjectPointsTo(QSharedPointer<Soprano::Model> _model) const 0553 { 0554 Q_ASSERT(_model); 0555 Q_ASSERT(d->model); 0556 QList<Statement> addList; 0557 QList<Statement> allStatements = _model->listStatements().allElements(); 0558 0559 foreach (const Soprano::Statement &s, allStatements) { 0560 QList<Statement> all = d->model->listStatements(s.object(), Node(), Node()).allElements(); 0561 addList.append(all); 0562 } 0563 _model->addStatements(addList); 0564 } 0565 0566 void KoDocumentRdf::expandStatementsSubjectPointsTo(QSharedPointer<Soprano::Model> _model, const Soprano::Node &n) const 0567 { 0568 Q_ASSERT(_model); 0569 Q_ASSERT(d->model); 0570 QList<Statement> addList = d->model->listStatements(n, Node(), Node()).allElements(); 0571 0572 _model->addStatements(addList); 0573 } 0574 0575 void KoDocumentRdf::expandStatementsToIncludeRdfListsRecurse(QSharedPointer<Soprano::Model> _model, 0576 QList<Statement> &addList, const Soprano::Node &n) const 0577 { 0578 Q_ASSERT(_model); 0579 Q_ASSERT(d->model); 0580 Node rdfFirst = Node::createResourceNode(QUrl("http://www.w3.org/1999/02/22-rdf-syntax-ns#first")); 0581 Node rdfRest = Node::createResourceNode(QUrl("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest")); 0582 QList<Statement> all; 0583 0584 all = model()->listStatements(n, rdfFirst, Node()).allElements(); 0585 addList << all; 0586 all = model()->listStatements(n, rdfRest, Node()).allElements(); 0587 addList << all; 0588 foreach (const Soprano::Statement &s, all) { 0589 expandStatementsToIncludeRdfListsRecurse(_model, addList, s.object()); 0590 } 0591 } 0592 0593 void KoDocumentRdf::expandStatementsToIncludeRdfLists(QSharedPointer<Soprano::Model> model) const 0594 { 0595 Q_ASSERT(model); 0596 RDEBUG << "model.sz:" << model->statementCount(); 0597 QList<Statement> addList; 0598 QList<Statement> allStatements = model->listStatements().allElements(); 0599 0600 foreach (const Soprano::Statement &s, allStatements) { 0601 expandStatementsToIncludeRdfListsRecurse(model, addList, s.subject()); 0602 } 0603 RDEBUG << "model.sz:" << model->statementCount(); 0604 RDEBUG << "addList.sz:" << addList.size(); 0605 model->addStatements(addList); 0606 } 0607 0608 void KoDocumentRdf::expandStatementsToIncludeOtherPredicates(QSharedPointer<Soprano::Model> _model) const 0609 { 0610 Q_ASSERT(_model); 0611 Q_ASSERT(d->model); 0612 QList<Statement> addList; 0613 QList<Statement> allStatements = _model->listStatements().allElements(); 0614 0615 foreach (const Soprano::Statement &s, allStatements) { 0616 QList<Statement> all = model()->listStatements(s.subject(), Node(), Node()).allElements(); 0617 addList.append(all); 0618 } 0619 _model->addStatements(addList); 0620 } 0621 0622 void KoDocumentRdf::expandStatements(QSharedPointer<Soprano::Model> model) const 0623 { 0624 Q_ASSERT(model); 0625 expandStatementsReferencingSubject(model); 0626 expandStatementsToIncludeOtherPredicates(model); 0627 } 0628 0629 QAction *KoDocumentRdf::createInsertSemanticObjectReferenceAction(KoCanvasBase *host) 0630 { 0631 QAction *ret = new InsertSemanticObjectReferenceAction(host, this, i18n("Reference")); 0632 RDEBUG << "createInsertSemanticObjectReferenceAction"; 0633 return ret; 0634 } 0635 0636 QList<QAction *> KoDocumentRdf::createInsertSemanticObjectNewActions(KoCanvasBase *host) 0637 { 0638 QList<QAction *> ret; 0639 foreach (const QString &semanticClass, KoRdfSemanticItemRegistry::instance()->classNames()) { 0640 if (!KoRdfSemanticItemRegistry::instance()->isBasic(semanticClass)) { 0641 ret.append(new InsertSemanticObjectCreateAction(host, this, semanticClass)); 0642 } 0643 } 0644 return ret; 0645 } 0646 0647 QPair<int, int> KoDocumentRdf::findExtent(const QString &xmlid) const 0648 { 0649 KoTextInlineRdf *obj = findInlineRdfByID(xmlid); 0650 if (obj) { 0651 QPair<int, int> ret = obj->findExtent(); 0652 RDEBUG << "(Semantic) have inline obj, extent:" << ret; 0653 return ret; 0654 } 0655 return QPair<int, int>(-1, 0); 0656 } 0657 0658 QPair<int, int> KoDocumentRdf::findExtent(KoTextEditor *handler) const 0659 { 0660 Q_ASSERT(d->model); 0661 RDEBUG << "model.sz:" << d->model->statementCount(); 0662 0663 const QTextDocument *document = handler->document(); 0664 0665 // first check for bookmarks 0666 KoTextRangeManager *mgr = KoTextDocument(document).textRangeManager(); 0667 Q_ASSERT(mgr); 0668 QHash<int, KoTextRange *> textRanges = mgr->textRangesChangingWithin(handler->document(), 0, handler->selectionEnd(), handler->selectionStart(), -1); 0669 foreach (const KoTextRange *range, textRanges) { 0670 return QPair<int,int>(range->rangeStart(), range->rangeEnd()); 0671 } 0672 /* 0673 // find the text:meta inline objects 0674 int startPosition = handler->position(); 0675 KoInlineTextObjectManager *inlineObjectManager 0676 = KoTextDocument(handler->document()).inlineTextObjectManager(); 0677 Q_ASSERT(inlineObjectManager); 0678 0679 QTextCursor cursor = document->find(QString(QChar::ObjectReplacementCharacter), 0680 startPosition, 0681 QTextDocument::FindBackward); 0682 while(!cursor.isNull()) { 0683 RDEBUG << "findXmlId" << cursor.position(); 0684 QTextCharFormat fmt = cursor.charFormat(); 0685 KoInlineObject *obj = inlineObjectManager->inlineTextObject(fmt); 0686 0687 if (KoTextMeta *metamark = dynamic_cast<KoTextMeta*>(obj)) { 0688 if (metamark->type() == KoTextMeta::StartBookmark) { 0689 KoTextMeta *endmark = metamark->endBookmark(); 0690 Q_ASSERT(endmark); 0691 if (endmark->position() > startPosition) { 0692 return QPair<int, int>(metamark->position(), endmark->position()); 0693 } 0694 } 0695 } 0696 cursor = document->find(QString(QChar::ObjectReplacementCharacter), 0697 cursor.position(), 0698 QTextDocument::FindBackward); 0699 } 0700 */ 0701 return QPair<int, int>(-1, 0); 0702 } 0703 0704 QString KoDocumentRdf::findXmlId(KoTextEditor *handler) const 0705 { 0706 int startPosition = handler->position(); 0707 Q_UNUSED(startPosition); 0708 0709 KoTextInlineRdf *inlineRdf = 0; 0710 0711 const QTextDocument *document = handler->document(); 0712 0713 // first check for bookmarks 0714 KoTextRangeManager *mgr = KoTextDocument(document).textRangeManager(); 0715 Q_ASSERT(mgr); 0716 QHash<int, KoTextRange *> textRanges = mgr->textRangesChangingWithin(document, 0, handler->selectionEnd(), handler->selectionStart(), -1); 0717 foreach (const KoTextRange *range, textRanges) { 0718 inlineRdf = range->inlineRdf(); 0719 if (inlineRdf) { 0720 return inlineRdf->xmlId(); 0721 } 0722 } 0723 0724 /* 0725 // find the text:meta inline objects 0726 KoInlineTextObjectManager *inlineObjectManager 0727 = KoTextDocument(document).inlineTextObjectManager(); 0728 Q_ASSERT(inlineObjectManager); 0729 QTextCursor cursor = document->find(QString(QChar::ObjectReplacementCharacter), 0730 startPosition, 0731 QTextDocument::FindBackward); 0732 while(!cursor.isNull()) { 0733 RDEBUG << "Cursor position" << cursor.position(); 0734 QTextCharFormat fmt = cursor.charFormat(); 0735 KoInlineObject *obj = inlineObjectManager->inlineTextObject(fmt); 0736 RDEBUG << "obj" << obj; 0737 0738 if (KoTextMeta *metamark = dynamic_cast<KoTextMeta*>(obj)) { 0739 if (metamark->type() == KoTextMeta::StartBookmark) { 0740 KoTextMeta *endmark = metamark->endBookmark(); 0741 // we used to assert on endmark, but we cannot keep people from 0742 // inserting a startbookmark and only then creating and inserting 0743 // the endmark 0744 if (endmark && endmark->position() > startPosition) { 0745 inlineRdf = metamark->inlineRdf(); 0746 } 0747 } 0748 } 0749 0750 // if we've got inline rdf, we've found the nearest xmlid wrapping our current position 0751 if (inlineRdf) { 0752 break; 0753 } 0754 0755 if( cursor.position() <= 0 ) 0756 break; 0757 0758 // else continue with the next inline object 0759 cursor = document->find(QString(QChar::ObjectReplacementCharacter), 0760 cursor.position()-1, 0761 QTextDocument::FindBackward); 0762 } 0763 */ 0764 // we couldn't find inline rdf object... So try to see whether there's 0765 // inlineRdf in the charformat for the current cursor position. It's 0766 // unlikely, of course. Maybe this should be the first check, though? 0767 if (!inlineRdf) { 0768 inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(handler); 0769 } 0770 0771 if (inlineRdf) { 0772 return inlineRdf->xmlId(); 0773 } 0774 0775 return QString(); 0776 } 0777 0778 QSharedPointer<Soprano::Model> KoDocumentRdf::findStatements(const QString &xmlid, int depth) 0779 { 0780 QSharedPointer<Soprano::Model> ret(Soprano::createModel()); 0781 Q_ASSERT(ret); 0782 addStatements(ret, xmlid); 0783 for (int i = 1; i < depth; ++i) { 0784 expandStatements(ret); 0785 } 0786 return ret; 0787 } 0788 0789 QSharedPointer<Soprano::Model> KoDocumentRdf::findStatements(KoTextEditor *handler, int depth) 0790 { 0791 Q_ASSERT(d->model); 0792 0793 QSharedPointer<Soprano::Model> ret(Soprano::createModel()); 0794 Q_ASSERT(ret); 0795 0796 QString xmlid = findXmlId(handler); 0797 KoTextInlineRdf *inlineRdf = findInlineRdfByID(xmlid); 0798 0799 RDEBUG << "1 model.sz:" << d->model->statementCount() 0800 << " ret.sz:" << ret->statementCount(); 0801 if (inlineRdf) { 0802 RDEBUG << "have inlineRdf1...xmlid:" << inlineRdf->xmlId(); 0803 RDEBUG << " ret.sz:" << ret->statementCount(); 0804 ret->addStatement(toStatement(inlineRdf)); 0805 RDEBUG << "have inlineRdf2...xmlid:" << inlineRdf->xmlId(); 0806 RDEBUG << " ret.sz:" << ret->statementCount(); 0807 QString xmlid = inlineRdf->xmlId(); 0808 addStatements(ret, xmlid); 0809 } 0810 0811 RDEBUG << "2 ret.sz:" << ret->statementCount(); 0812 RDEBUG << "checking for block inlineRdf..."; 0813 inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(handler); 0814 0815 if (inlineRdf) { 0816 RDEBUG << "inlineRdf:" << (void*)inlineRdf; 0817 ret->addStatement(toStatement(inlineRdf)); 0818 QString xmlid = inlineRdf->xmlId(); 0819 addStatements(ret, xmlid); 0820 RDEBUG << "have block inlineRdf...xmlid:" << inlineRdf->xmlId(); 0821 } 0822 0823 RDEBUG << "3 ret.sz:" << ret->statementCount(); 0824 RDEBUG << "expanding statements..."; 0825 0826 for (int i = 1; i < depth; ++i) { 0827 expandStatements(ret); 0828 } 0829 0830 return ret; 0831 } 0832 0833 KoTextInlineRdf *KoDocumentRdf::findInlineRdfByID(const QString &xmlid) const 0834 { 0835 if (d->inlineRdfObjects.contains(xmlid)) { 0836 QWeakPointer<KoTextInlineRdf> inlineRdf = d->inlineRdfObjects[xmlid]; 0837 if (!inlineRdf.isNull()) { 0838 return inlineRdf.data(); 0839 } 0840 else { 0841 d->inlineRdfObjects.remove(xmlid); 0842 } 0843 } 0844 return 0; 0845 } 0846 0847 0848 void KoDocumentRdf::rememberNewInlineRdfObject(KoTextInlineRdf *inlineRdf) 0849 { 0850 if (!inlineRdf) { 0851 return; 0852 } 0853 d->inlineRdfObjects[inlineRdf->xmlId()] = inlineRdf; 0854 } 0855 0856 void KoDocumentRdf::updateInlineRdfStatements(const QTextDocument *qdoc) 0857 { 0858 RDEBUG << "top"; 0859 KoInlineTextObjectManager *textObjectManager = KoTextDocument(qdoc).inlineTextObjectManager(); 0860 KoTextRangeManager *textRangeManager = KoTextDocument(qdoc).textRangeManager(); 0861 d->inlineRdfObjects.clear(); 0862 if(!textObjectManager || !textRangeManager) { 0863 return; 0864 } 0865 // 0866 // Rdf from inline objects 0867 // 0868 QList<KoInlineObject*> kiocol = textObjectManager->inlineTextObjects(); 0869 foreach (KoInlineObject *kio, kiocol) { 0870 if (KoTextInlineRdf *inlineRdf = kio->inlineRdf()) { 0871 rememberNewInlineRdfObject(inlineRdf); 0872 } 0873 } 0874 // 0875 // Rdf from textranges 0876 // 0877 QList<KoTextRange *> rangelist = textRangeManager->textRanges(); 0878 foreach (KoTextRange *range, rangelist) { 0879 if (KoTextInlineRdf *inlineRdf = range->inlineRdf()) { 0880 rememberNewInlineRdfObject(inlineRdf); 0881 } 0882 } 0883 // 0884 // Browse the blocks and see if any of them have Rdf attached 0885 // 0886 QVector<QTextFormat> formats = qdoc->allFormats(); 0887 foreach (const QTextFormat& tf, formats) { 0888 if (KoTextInlineRdf *inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(tf)) { 0889 rememberNewInlineRdfObject(inlineRdf); 0890 } 0891 } 0892 if (!d->model) { 0893 return; 0894 } 0895 Soprano::Node context = inlineRdfContext(); 0896 RDEBUG << "removing"; 0897 // delete all inline Rdf statements from model 0898 d->model->removeAllStatements(Soprano::Node(), Soprano::Node(), Soprano::Node(), context); 0899 RDEBUG << "adding, count:" << d->inlineRdfObjects.size(); 0900 0901 // add statements from inlineRdfObjects to model 0902 foreach (const QString &xmlid, d->inlineRdfObjects.keys()) { 0903 QWeakPointer<KoTextInlineRdf> sp = d->inlineRdfObjects[xmlid]; 0904 if (sp.isNull()) { 0905 d->inlineRdfObjects.remove(xmlid); 0906 } 0907 else { 0908 Soprano::Statement st = toStatement(sp.data()); 0909 if (st.isValid()) { 0910 d->model->addStatement(st); 0911 } 0912 } 0913 } 0914 RDEBUG << "done"; 0915 } 0916 0917 void KoDocumentRdf::emitSemanticObjectAdded(hKoRdfBasicSemanticItem item) const 0918 { 0919 emit semanticObjectAdded(item); 0920 } 0921 0922 void KoDocumentRdf::emitSemanticObjectAddedConst(const hKoRdfBasicSemanticItem item) const 0923 { 0924 emit semanticObjectAdded(item); 0925 } 0926 0927 void KoDocumentRdf::emitSemanticObjectUpdated(hKoRdfBasicSemanticItem item) 0928 { 0929 if (item && !KoRdfSemanticItemRegistry::instance()->isBasic(item->className())) { 0930 // 0931 // reflow the formatting for each view of the semanticItem, in reverse document order 0932 // 0933 QMap<int, reflowItem> col; 0934 hKoRdfSemanticItem si(static_cast<KoRdfSemanticItem *>(item.data())); 0935 RDEBUG << "xmlids:" << item->xmlIdList() << " reflow item:" << si->name(); 0936 insertReflow(col, si); 0937 applyReflow(col); 0938 } 0939 emit semanticObjectUpdated(item); 0940 } 0941 0942 void KoDocumentRdf::emitSemanticObjectViewSiteUpdated(hKoRdfBasicSemanticItem baseItem, const QString &xmlid) 0943 { 0944 if (KoRdfSemanticItemRegistry::instance()->isBasic(baseItem->className())) { 0945 return; 0946 } 0947 0948 hKoRdfSemanticItem item(static_cast<KoRdfSemanticItem *>(baseItem.data())); 0949 if (item) { 0950 RDEBUG << "xmlid:" << xmlid << " reflow item:" << item->name(); 0951 emit semanticObjectViewSiteUpdated(item, xmlid); 0952 } 0953 } 0954 0955 0956 bool KoDocumentRdf::completeLoading(KoStore *) 0957 { 0958 return true; 0959 } 0960 0961 bool KoDocumentRdf::completeSaving(KoStore *, KoXmlWriter *, KoShapeSavingContext *) 0962 { 0963 return true; 0964 } 0965 0966 KoDocumentRdf::reflowItem::reflowItem(hKoRdfSemanticItem si, const QString &xmlid, hKoSemanticStylesheet ss, const QPair< int, int > &extent) 0967 : m_si(si) 0968 , m_ss(ss) 0969 , m_xmlid(xmlid) 0970 , m_extent(extent) 0971 { 0972 } 0973 0974 void KoDocumentRdf::insertReflow(QMap<int, reflowItem> &col, hKoRdfSemanticItem obj, hKoSemanticStylesheet ss) 0975 { 0976 RDEBUG << "reflowing object:" << obj->name(); 0977 QStringList xmlidlist = obj->xmlIdList(); 0978 foreach (const QString &xmlid, xmlidlist) { 0979 QPair< int, int > extent = findExtent(xmlid); 0980 RDEBUG << "format(), adding reflow xmlid location:" << xmlid << " extent:" << extent; 0981 reflowItem item(obj, xmlid, ss, extent); 0982 col.insert(extent.first, item); 0983 } 0984 } 0985 0986 void KoDocumentRdf::insertReflow(QMap<int, reflowItem> &col, hKoRdfSemanticItem obj, 0987 const QString &sheetType, const QString &stylesheetName) 0988 { 0989 hKoSemanticStylesheet ss = obj->findStylesheetByName(sheetType, stylesheetName); 0990 insertReflow(col, obj, ss); 0991 } 0992 0993 void KoDocumentRdf::insertReflow(QMap<int, reflowItem> &col, hKoRdfSemanticItem obj) 0994 { 0995 RDEBUG << "reflowing object:" << obj->name(); 0996 foreach (const QString &xmlid, obj->xmlIdList()) { 0997 QPair<int, int> extent = findExtent(xmlid); 0998 RDEBUG << "format(), adding reflow xmlid location:" << xmlid << " extent:" << extent; 0999 reflowItem item(obj, xmlid, hKoSemanticStylesheet(0), extent); 1000 col.insert(extent.first, item); 1001 } 1002 } 1003 1004 void KoDocumentRdf::applyReflow(const QMap<int, reflowItem> &col) 1005 { 1006 QMapIterator< int, reflowItem > i(col); 1007 i.toBack(); 1008 while (i.hasPrevious()) { 1009 const reflowItem &item = i.previous().value(); 1010 RDEBUG << "format(), extent:" << item.m_extent; 1011 RDEBUG << "xmlid:" << item.m_xmlid; 1012 RDEBUG << "format(), semitem:" << item.m_si.constData(); 1013 RDEBUG << "format(), semitem.name:" << item.m_si->name(); 1014 if (item.m_ss) { 1015 KoRdfSemanticItemViewSite vs(item.m_si, item.m_xmlid); 1016 vs.setStylesheetWithoutReflow(item.m_ss); 1017 } 1018 emitSemanticObjectViewSiteUpdated(item.m_si, item.m_xmlid); 1019 } 1020 } 1021 1022 QList<hKoSemanticStylesheet> KoDocumentRdf::userStyleSheetList(const QString& className) const 1023 { 1024 return d->userStylesheets[className]; 1025 } 1026 1027 void KoDocumentRdf::setUserStyleSheetList(const QString& className,const QList<hKoSemanticStylesheet>& l) 1028 { 1029 d->userStylesheets[className] = l; 1030 } 1031 1032 bool KoDocumentRdf::backendIsSane() 1033 { 1034 const Soprano::Backend *backend = Soprano::discoverBackendByFeatures( 1035 Soprano::BackendFeatureContext | 1036 Soprano::BackendFeatureQuery | 1037 Soprano::BackendFeatureStorageMemory); 1038 if (!backend) { 1039 // without a backend with the desired features, this test fails 1040 kWarning() << "No suitable backend found."; 1041 return false; 1042 } 1043 kWarning() << "Found a backend: " << backend->pluginName(); 1044 1045 Soprano::setUsedBackend(backend); 1046 Soprano::BackendSettings backendSettings; 1047 backendSettings << Soprano::BackendOptionStorageMemory; 1048 Soprano::StorageModel* model = backend->createModel(backendSettings); 1049 if (!model) { 1050 // if model creation failed, this test fails 1051 kWarning() << "No model could be created."; 1052 return false; 1053 } 1054 1055 model->addStatement(Soprano::Statement( 1056 QUrl("subject"), QUrl("predicate"), QUrl("object"), 1057 QUrl("context"))); 1058 1059 Soprano::QueryResultIterator it = model->executeQuery( 1060 "SELECT ?g ?s ?p ?o WHERE { GRAPH ?g { ?s ?p ?o } }", 1061 Soprano::Query::QueryLanguageSparql); 1062 1063 if (!it.next()) { 1064 // there should be exactly one result statement 1065 kWarning() << "Query returned too few results."; 1066 delete model; 1067 return false; 1068 } 1069 if (it.next()) { 1070 // there should be exactly one result statement 1071 kWarning() << "Query returned too many results."; 1072 delete model; 1073 return false; 1074 } 1075 1076 delete model; 1077 return true; 1078 1079 } 1080 1081 QStringList KoDocumentRdf::idrefList() const 1082 { 1083 Q_ASSERT(d->model); 1084 QStringList idrefs; 1085 1086 StatementIterator it = model()->listStatements( 1087 Node(), 1088 Node(QUrl("http://docs.oasis-open.org/ns/office/1.2/meta/pkg#idref")), 1089 Node(), 1090 Node()); 1091 if (!it.isValid()) { 1092 return idrefs; 1093 } 1094 1095 QList<Statement> allStatements = it.allElements(); 1096 foreach (const Soprano::Statement &s, allStatements) { 1097 idrefs << s.object().toString(); 1098 } 1099 return idrefs; 1100 }