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 }