KoDocumentRdf.cpp 36 KB
Newer Older
1 2
/* This file is part of the KDE project
   Copyright (C) 2010 KO GmbH <ben.martin@kogmbh.com>
3
   Copyright (C) 2011,2012 Ben Martin <monkeyiq@users.sourceforge.net> hacking for fun!
4

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#include "KoDocumentRdf.h"
22

23
#include "KoRdfPrefixMapping.h"
24
#include "RdfSemanticTreeWidgetSelectAction.h"
25 26 27
#include "InsertSemanticObjectActionBase.h"
#include "InsertSemanticObjectCreateAction.h"
#include "InsertSemanticObjectReferenceAction.h"
28
#include "KoRdfSemanticItemRegistry.h"
29

30 31
#include <KoView.h>
#include <KoDocument.h>
Thomas Zander's avatar
Thomas Zander committed
32 33 34 35 36 37
#include <KoToolManager.h>
#include <KoTextDocument.h>
#include <KoTextRdfCore.h>
#include <KoXmlReader.h>
#include <KoXmlWriter.h>
#include <KoStoreDevice.h>
38
#include <KoDocumentResourceManager.h>
Thomas Zander's avatar
Thomas Zander committed
39
#include <KoTextEditor.h>
40 41 42 43 44 45 46
#include <KoShapeManager.h>
#include <KoSelection.h>
#include <KoTextShapeDataBase.h>
#include <KoCanvasBase.h>
#include <KoTextDocument.h>
#include <KoTextEditor.h>
#include <KoCanvasBase.h>
Thomas Zander's avatar
Thomas Zander committed
47 48 49
#include <KoInlineObject.h>
#include <KoTextInlineRdf.h>
#include <KoInlineTextObjectManager.h>
50
#include <KoTextRangeManager.h>
Thomas Zander's avatar
Thomas Zander committed
51
#include <KoTextMeta.h>
52
#include <KoShapeController.h>
53 54 55 56 57 58

#include <kconfig.h>
#include <kdebug.h>
#include <klocale.h>
#include <kuser.h>

59 60 61 62

#include <QWeakPointer>


63 64
#define DEBUG_RDF

65 66 67 68 69 70
#ifdef DEBUG_RDF
#define RDEBUG kDebug(30015)
#else
#define RDEBUG if(0) kDebug(30015)
#endif

71 72 73
using namespace Soprano;


74

75
class KoDocumentRdfPrivate
76
{
77 78 79
public:

    KoDocumentRdfPrivate()
80
            : model(Soprano::createModel())
81 82 83 84 85 86
            , prefixMapping(0)
    {
    }

    ~KoDocumentRdfPrivate()
    {
87 88
        prefixMapping->deleteLater();
        model->deleteLater();
89 90
    }

91
    QSharedPointer<Soprano::Model> model; ///< Main Model containing all Rdf for doc
92 93 94
    QMap<QString, QWeakPointer<KoTextInlineRdf> > inlineRdfObjects;  ///< Cache of weak pointers to inline Rdf
    KoRdfPrefixMapping *prefixMapping;     ///< prefix -> URI mapping

95
    QMap<QString, QList<hKoRdfSemanticItem> > semanticItems;
96
    QMap<QString,QList<hKoSemanticStylesheet> > userStylesheets;
97
};
98 99


100
KoDocumentRdf::KoDocumentRdf(QObject *parent)
101
        : KoDocumentRdfBase(parent)
102
        , d (new KoDocumentRdfPrivate())
103
{
104 105 106
//    if (!backendIsSane()) {
//        kWarning() << "Looks like the backend is not sane!";
//    }
Thomas Zander's avatar
Thomas Zander committed
107
    d->prefixMapping = new KoRdfPrefixMapping(this);
108 109 110 111
}

KoDocumentRdf::~KoDocumentRdf()
{
112
    RDEBUG;
113
    delete d;
114 115
}

116
QSharedPointer<Soprano::Model> KoDocumentRdf::model() const
117
{
Thomas Zander's avatar
Thomas Zander committed
118
    return d->model;
119 120
}

121
KoRdfPrefixMapping *KoDocumentRdf::prefixMapping() const
122
{
Thomas Zander's avatar
Thomas Zander committed
123
    return d->prefixMapping;
124 125 126 127 128 129
}

/**
 * Graph context used for Rdf stored inline in content.xml
 * in an Rdfa like fashion.
 */
130
Soprano::Node KoDocumentRdf::inlineRdfContext() const
131
{
132
    return Node(QUrl("http://www.calligra.org/Rdf/inline-rdf"));
133 134
}

135
QString KoDocumentRdf::rdfInternalMetadataWithoutSubjectURI() const
136
{
137
    return "http://www.calligra.org/Rdf/internal/content.xml";
138 139
}

140
QString KoDocumentRdf::rdfPathContextPrefix() const
141
{
142
    return "http://www.calligra.org/Rdf/path/";
143 144 145 146
}

Soprano::Node KoDocumentRdf::manifestRdfNode() const
{
147
    return Node(QUrl(rdfPathContextPrefix() + "manifest.rdf"));
148 149
}

150
void KoDocumentRdf::freshenBNodes(QSharedPointer<Soprano::Model> m)
151
{
152
    Q_ASSERT(m);
153
    Q_ASSERT(d->model);
154 155 156 157 158
    QList<Soprano::Statement> removeList;
    QList<Soprano::Statement> addList;
    QMap<QString, Soprano::Node> bnodeMap;
    StatementIterator it = m->listStatements();
    QList<Statement> allStatements = it.allElements();
159
    RDEBUG << "freshening model.sz:" << allStatements.size();
160
    foreach (const Soprano::Statement &s, allStatements) {
161 162 163 164 165 166 167
        Soprano::Node subj = s.subject();
        Soprano::Node obj = s.object();
        Soprano::Statement news;
        if (subj.type() == Soprano::Node::BlankNode) {
            QString nodeStr = subj.toString();
            Soprano::Node n = bnodeMap[ nodeStr ];
            if (!n.isValid()) {
Thomas Zander's avatar
Thomas Zander committed
168
                n = d->model->createBlankNode();
169 170 171 172 173 174 175 176 177 178
                bnodeMap[ nodeStr ] = n;
            }
            removeList << s;
            subj = n;
            news = Statement(subj, s.predicate(), obj, s.context());
        }
        if (obj.type() == Soprano::Node::BlankNode) {
            QString nodeStr = obj.toString();
            Soprano::Node n = bnodeMap[ nodeStr ];
            if (!n.isValid()) {
Thomas Zander's avatar
Thomas Zander committed
179
                n = d->model->createBlankNode();
180 181 182 183 184 185 186 187 188 189
                bnodeMap[ nodeStr ] = n;
            }
            removeList << s;
            obj = n;
            news = Statement(subj, s.predicate(), obj, s.context());
        }
        if (news.isValid()) {
            addList << news;
        }
    }
190 191
    RDEBUG << "remove count:" << removeList.size();
    RDEBUG << "add count:" << addList.size();
192 193 194 195
    // Note that as of Jan 2010 you couldn't rely on
    // Soprano::Model::removeStatements() if every entry
    // in removeList did not exist exactly once in the model.
    KoTextRdfCore::removeStatementsIfTheyExist(m, removeList);
196
    RDEBUG << "after remove, model.sz:" << m->statementCount();
197
    m->addStatements(addList);
198
    RDEBUG << "after add,    model.sz:" << m->statementCount();
199 200
}

Thomas Zander's avatar
Thomas Zander committed
201
bool KoDocumentRdf::loadRdf(KoStore *store, const Soprano::Parser *parser, const QString &fileName)
202
{
203
    QSharedPointer<Soprano::Model> tmpmodel(Soprano::createModel());
204 205 206 207
    if (!d->model || !tmpmodel) {
        kWarning(30003) << "No soprano model";
        return false;
    }
208 209
    bool ok = true;
    if (!store->open(fileName)) {
210
        RDEBUG << "Entry " << fileName << " not found!"; // not a warning as embedded stores don't have to have all files
211 212
        return false;
    }
213
    RDEBUG << "Loading external Rdf/XML from:" << fileName;
214
    Soprano::Node context(QUrl(rdfPathContextPrefix() + fileName));
Thomas Zander's avatar
Thomas Zander committed
215
    QUrl BaseURI = QUrl(QString());
216
    QString rdfxmlData(store->device()->readAll());
Thomas Zander's avatar
Thomas Zander committed
217
    Soprano::StatementIterator it = parser->parseString(rdfxmlData, BaseURI,
218 219
                                    Soprano::SerializationRdfXml);
    QList<Statement> allStatements = it.allElements();
220
    RDEBUG << "Found " << allStatements.size() << " triples..." << endl;
221
    foreach (const Soprano::Statement &s, allStatements) {
222 223 224 225 226
        Soprano::Node subj = s.subject();
        Soprano::Node pred = s.predicate();
        Soprano::Node obj  = s.object();
        Error::ErrorCode err = tmpmodel->addStatement(subj, pred, obj, context);
        if (err != Error::ErrorNone) {
227
            RDEBUG << "Error adding triple! s:" << subj << " p:" << pred << " o:" << obj << endl;
228 229 230 231
            ok = false;
            break;
        }
    }
232
    RDEBUG << "calling freshenBNodes(), tmpmodel.sz:" << tmpmodel->statementCount();
Thomas Zander's avatar
Thomas Zander committed
233
#ifdef DEBUG_RDF
234
    dumpModel(fileName, tmpmodel);
Thomas Zander's avatar
Thomas Zander committed
235
#endif
236
    freshenBNodes(tmpmodel);
Thomas Zander's avatar
Thomas Zander committed
237
#ifdef DEBUG_RDF
238
    dumpModel(fileName, tmpmodel);
Thomas Zander's avatar
Thomas Zander committed
239
#endif
240
    RDEBUG << "done with freshenBNodes(), tmpmodel.sz:" << tmpmodel->statementCount();
Thomas Zander's avatar
Thomas Zander committed
241 242 243
    d->model->addStatements(tmpmodel->listStatements().allElements());
    if (fileName == "manifest.rdf" && d->prefixMapping) {
        d->prefixMapping->load(d->model);
244

245 246
        foreach (const QString &semanticClass, KoRdfSemanticItemRegistry::instance()->classNames()) {
            hKoRdfSemanticItem si = KoRdfSemanticItemRegistry::instance()->createSemanticItem(semanticClass, this, this);
Thomas Zander's avatar
Thomas Zander committed
247
            si->loadUserStylesheets(d->model);
248 249 250 251 252 253
        }
    }
    store->close();
    return ok;
}

Thomas Zander's avatar
Thomas Zander committed
254
bool KoDocumentRdf::loadOasis(KoStore *store)
255 256 257 258 259
{
    if (!store) {
        kWarning(30003) << "No store backend";
        return false;
    }
260 261 262 263
    if (!d->model) {
        kWarning(30003) << "No soprano model";
        return false;
    }
Thomas Zander's avatar
Thomas Zander committed
264
    const Soprano::Parser *parser =
265 266 267 268
        Soprano::PluginManager::instance()->discoverParserForSerialization(
            Soprano::SerializationRdfXml);
    bool ok = loadRdf(store, parser, "manifest.rdf");
    if (ok) {
Thomas Zander's avatar
Thomas Zander committed
269
        QString sparqlQuery = "prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \n"
Thomas Zander's avatar
Thomas Zander committed
270 271 272 273 274 275 276
          "prefix odf: <http://docs.oasis-open.org/opendocument/meta/package/odf#> \n"
          "prefix odfcommon: <http://docs.oasis-open.org/opendocument/meta/package/common#> \n"
          "select ?subj ?fileName \n"
          " where { \n"
          "  ?subj rdf:type odf:MetaDataFile . \n"
          "  ?subj odfcommon:path ?fileName  \n"
          " } \n";
277
        Soprano::QueryResultIterator it =
Thomas Zander's avatar
Thomas Zander committed
278
            d->model->executeQuery(sparqlQuery,
279 280 281 282 283 284 285 286
                                  Soprano::Query::QueryLanguageSparql);
        QList< QString > externalRdfFiles;
        //
        // This is a bit tricky, loadRdf() might block if the
        // sparql query is still being iterated, so we have to
        // store the fileNames and exhaust the binding result
        // iterator first.
        //
287
        while (it.next()) {
288 289 290
            QString fileName = it.binding("fileName").toString();
            externalRdfFiles << fileName;
        }
Thomas Zander's avatar
Thomas Zander committed
291
        foreach (const QString &fileName, externalRdfFiles) {
292
            ok = loadRdf(store, parser, fileName);
293
            if (!ok) break;
294 295 296 297 298
        }
    }
    return ok;
}

Halla Rempt's avatar
Halla Rempt committed
299
bool KoDocumentRdf::saveRdf(KoStore *store, KoXmlWriter *manifestWriter, const Soprano::Node &context) const
300 301
{
    bool ok = false;
Thomas Zander's avatar
Thomas Zander committed
302
    QString fileName("manifest.rdf");
303
    if (context.toString() == inlineRdfContext().toString()) {
304
        RDEBUG << "found some internal Rdf, this is handled by augmenting the DOM";
305 306
        return true;
    }
Halla Rempt's avatar
Halla Rempt committed
307
    if (!model()) {
308 309 310
        kWarning(30003) << "No soprano model";
        return false;
    }
311 312 313
    //
    // The context contains the filename to save into
    //
314 315
    if (context.toString().startsWith(rdfPathContextPrefix())) {
        fileName = context.toString().mid(rdfPathContextPrefix().size());
316
    }
317
    RDEBUG << "saving external file:" << fileName;
Thomas Zander's avatar
Thomas Zander committed
318
    if (!store->open(fileName)) {
319 320 321 322
        return false;
    }
    KoStoreDevice dev(store);
    QTextStream oss(&dev);
Thomas Zander's avatar
Thomas Zander committed
323 324
    if (fileName == "manifest.rdf" && d->prefixMapping) {
        d->prefixMapping->save(d->model, context);
325

326 327
        foreach (const QString &semanticClass, KoRdfSemanticItemRegistry::instance()->classNames()) {
            hKoRdfSemanticItem si = KoRdfSemanticItemRegistry::instance()->createSemanticItem(semanticClass, this, const_cast<KoDocumentRdf*>(this));
Thomas Zander's avatar
Thomas Zander committed
328
            si->saveUserStylesheets(d->model, context);
329 330
        }
    }
Halla Rempt's avatar
Halla Rempt committed
331
    Soprano::StatementIterator triples = model()->listStatements(Soprano::Node(),
Thomas Zander's avatar
Thomas Zander committed
332
            Soprano::Node(), Soprano::Node(), context);
Halla Rempt's avatar
Halla Rempt committed
333

Thomas Zander's avatar
Thomas Zander committed
334 335
    const Soprano::Serializer *serializer = Soprano::PluginManager::instance()->
        discoverSerializerForSerialization(Soprano::SerializationRdfXml);
336 337 338 339 340 341
    if (serializer) {
        QString data;
        QTextStream tss(&data);
        if (serializer->serialize(triples, tss, Soprano::SerializationRdfXml)) {
            tss.flush();
            oss << data;
342
            RDEBUG << "fileName:" << fileName << " data.sz:" << data.size();
Halla Rempt's avatar
Halla Rempt committed
343
            RDEBUG << "model.sz:" << model()->statementCount();
344 345
            ok = true;
        } else {
346
            RDEBUG << "serialization of Rdf failed!";
347 348 349 350 351 352 353 354
        }
    }
    oss.flush();
    store->close();
    manifestWriter->addManifestEntry(fileName, "application/rdf+xml");
    return ok;
}

Thomas Zander's avatar
Thomas Zander committed
355
bool KoDocumentRdf::saveOasis(KoStore *store, KoXmlWriter *manifestWriter)
356
{
357
    RDEBUG << "saveOasis() generic";
358 359 360 361
    if (!d->model) {
        kWarning(30003) << "No soprano model";
        return false;
    }
362
    bool ok = true;
363 364
    NodeIterator contextIter = model()->listContexts();
    QList<Node> contexts = contextIter.allElements();
365
    foreach (const Soprano::Node &node, contexts) {
Halla Rempt's avatar
Halla Rempt committed
366
        if (!saveRdf(store, manifestWriter, node)) {
367 368
            ok = false;
        }
369 370 371 372
    }
    return ok;
}

Thomas Zander's avatar
Thomas Zander committed
373
void KoDocumentRdf::updateXmlIdReferences(const QMap<QString, QString> &m)
374
{
375
    Q_ASSERT(d->model);
376

Thomas Zander's avatar
Thomas Zander committed
377 378
    QList<Soprano::Statement> removeList;
    QList<Soprano::Statement> addList;
Halla Rempt's avatar
Halla Rempt committed
379
    StatementIterator it = model()->listStatements(
380 381 382 383
                               Node(),
                               Node(QUrl("http://docs.oasis-open.org/opendocument/meta/package/common#idref")),
                               Node(),
                               Node());
384 385 386
    if (!it.isValid())
        return;

387 388 389
    // new xmlid->inlinerdfobject mapping
    QMap<QString, QWeakPointer<KoTextInlineRdf> > inlineRdfObjects;

390
    QList<Statement> allStatements = it.allElements();
391
    foreach (const Soprano::Statement &s, allStatements) {
392
        RDEBUG << "seeking obj:" << s.object();
393 394
        QMap<QString, QString>::const_iterator mi = m.find(s.object().toString());
        if (mi != m.end()) {
395 396
            const QString &oldID = mi.key();
            const QString &newID = mi.value();
397 398 399 400 401 402
            removeList << s;
            Statement n(s.subject(),
                        s.predicate(),
                        Node(LiteralValue::createPlainLiteral(newID)),
                        s.context());
            addList << n;
403
            RDEBUG << "looking for inlineRdf object for ID:" << oldID;
Thomas Zander's avatar
Thomas Zander committed
404
            if (KoTextInlineRdf *inlineRdf = findInlineRdfByID(oldID)) {
405 406
                RDEBUG << "updating the xmlid of the inline object";
                RDEBUG << "old:" << oldID << " new:" << newID;
407
                inlineRdf->setXmlId(newID);
408
                inlineRdfObjects[newID] = inlineRdf;
409 410 411 412 413
            }
        }
    }
    // out with the old, in with the new
    // remove & add the triple lists.
414 415
    RDEBUG << "addStatements.size:" << addList.size();
    RDEBUG << " remove.size:" << removeList.size();
Thomas Zander's avatar
Thomas Zander committed
416 417
    KoTextRdfCore::removeStatementsIfTheyExist(d->model, removeList);
    d->model->addStatements(addList);
418 419
    d->inlineRdfObjects = inlineRdfObjects;

420 421
}

422
QList<hKoRdfSemanticItem> KoDocumentRdf::semanticItems(const QString &className, QSharedPointer<Soprano::Model> m)
423 424
{
    if (!m) {
425 426
        m = d->model;
        Q_ASSERT(m);
427 428
    }

429 430 431 432 433
    QList<hKoRdfSemanticItem> items;
    // TODO: improve double lookup
    if (KoRdfSemanticItemRegistry::instance()->classNames().contains(className)) {
        KoRdfSemanticItemRegistry::instance()->updateSemanticItems(d->semanticItems[className], this, className, m);
        items = d->semanticItems[className];
434 435
    }

436
    return items;
437 438
}

439
hKoRdfSemanticItem KoDocumentRdf::createSemanticItem(const QString &semanticClass, QObject *parent) const
440
{
441
    return KoRdfSemanticItemRegistry::instance()->createSemanticItem(semanticClass, this, parent);
442 443
}

444
void KoDocumentRdf::dumpModel(const QString &msg, QSharedPointer<Soprano::Model> m) const
445 446 447 448
{
    if (!m) {
        return;
    }
449

450
    QList<Soprano::Statement> allStatements = m->listStatements().allElements();
Thomas Zander's avatar
Thomas Zander committed
451
    RDEBUG << "----- " << msg << " ----- model size:" << allStatements.size();
452
    foreach (const Soprano::Statement &s, allStatements) {
Thomas Zander's avatar
Thomas Zander committed
453
        RDEBUG << s;
454 455 456
    }
}

Thomas Zander's avatar
Thomas Zander committed
457
Soprano::Statement KoDocumentRdf::toStatement(KoTextInlineRdf *inlineRdf) const
458 459 460 461 462 463 464
{
    if (!inlineRdf) {
        return Soprano::Statement();
    }
    if (inlineRdf->predicate().isEmpty())  {
        return Soprano::Statement();
    }
465

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
    Soprano::Node subj = Soprano::Node::createResourceNode(QUrl(inlineRdf->subject()));
    Soprano::Node pred = Soprano::Node::createResourceNode(QUrl(inlineRdf->predicate()));
    Soprano::Node obj;
    switch (inlineRdf->sopranoObjectType()) {
    case Node::ResourceNode:
        obj = Soprano::Node::createResourceNode(inlineRdf->object());
        break;
    case Node::LiteralNode:
        obj = Soprano::Node::createLiteralNode(inlineRdf->object());
        break;
    case Node::BlankNode:
        obj = Soprano::Node::createBlankNode(inlineRdf->object());
        break;
    }
    if (!inlineRdf->subject().size()) {
481
        subj = inlineRdfContext();
482
    }
483 484 485
    RDEBUG << "subj:"  << subj;
    RDEBUG << " pred:" << pred;
    RDEBUG << " obj:"  << obj;
486
    Soprano::Statement ret(subj, pred, obj, inlineRdfContext());
487 488 489
    return ret;
}

490
void KoDocumentRdf::addStatements(QSharedPointer<Soprano::Model> model, const QString &xmlid)
491
{
492 493
    Q_ASSERT(model);
    Q_ASSERT(d->model);
494 495
    QString sparqlQuery;
    QTextStream queryss(&sparqlQuery);
496

Thomas Zander's avatar
Thomas Zander committed
497
    RDEBUG << "addStatements model.sz:" << d->model->statementCount() << " xmlid:" << xmlid;
498
    queryss << "prefix pkg:  <http://docs.oasis-open.org/opendocument/meta/package/common#> \n"
499 500 501 502 503 504
            << ""
            << "select ?s ?p ?o ?g \n"
            << "where { \n"
            << " graph ?g {  ?s ?p ?o } .  ?s pkg:idref ?xmlid  \n"
            << " filter( str(?xmlid) = \"" << xmlid << "\" ) \n"
            << "}\n";
505
    queryss.flush();
506
    RDEBUG << "sparql:" << sparqlQuery;
Thomas Zander's avatar
Thomas Zander committed
507
    Soprano::QueryResultIterator it = d->model->executeQuery(sparqlQuery,
508
                              Soprano::Query::QueryLanguageSparql);
509 510 511

    while (it.next())
    {
512 513
        Statement s(it.binding("s"),
                    it.binding("p"),
514 515
                    it.binding("o"),
                    it.binding("g"));
516
        model->addStatement(s);
517 518 519
        RDEBUG << "result, s:" << it.binding("s");
        RDEBUG << " p:" << it.binding("p");
        RDEBUG << " o:" << it.binding("o");
520 521 522
    }
}

523
void KoDocumentRdf::expandStatementsReferencingSubject(QSharedPointer<Soprano::Model> _model) const
524
{
Halla Rempt's avatar
Halla Rempt committed
525
    Q_ASSERT(_model);
526
    Q_ASSERT(d->model);
527
    QList<Statement> addList;
Halla Rempt's avatar
Halla Rempt committed
528
    QList<Statement> allStatements = _model->listStatements().allElements();
529

530
    foreach (const Soprano::Statement &s, allStatements) {
Halla Rempt's avatar
Halla Rempt committed
531
        QList<Statement> all = model()->listStatements(Node(), Node(), s.subject()).allElements();
532
        addList.append(all);
533
    }
Halla Rempt's avatar
Halla Rempt committed
534
    _model->addStatements(addList);
535 536
}

537
void KoDocumentRdf::expandStatementsSubjectPointsTo(QSharedPointer<Soprano::Model> _model) const
538
{
Halla Rempt's avatar
Halla Rempt committed
539
    Q_ASSERT(_model);
540
    Q_ASSERT(d->model);
541
    QList<Statement> addList;
Halla Rempt's avatar
Halla Rempt committed
542
    QList<Statement> allStatements = _model->listStatements().allElements();
543

544
    foreach (const Soprano::Statement &s, allStatements) {
Halla Rempt's avatar
Halla Rempt committed
545
        QList<Statement> all = model()->listStatements(s.object(), Node(), Node()).allElements();
546
        addList.append(all);
547
    }
Halla Rempt's avatar
Halla Rempt committed
548
    _model->addStatements(addList);
549 550
}

551
void KoDocumentRdf::expandStatementsSubjectPointsTo(QSharedPointer<Soprano::Model> _model, const Soprano::Node &n) const
552
{
Halla Rempt's avatar
Halla Rempt committed
553
    Q_ASSERT(_model);
554
    Q_ASSERT(d->model);
555
    QList<Statement> addList = model()->listStatements(n, Node(), Node()).allElements();
556

Halla Rempt's avatar
Halla Rempt committed
557
    _model->addStatements(addList);
558 559
}

560
void KoDocumentRdf::expandStatementsToIncludeRdfListsRecurse(QSharedPointer<Soprano::Model> _model,
Halla Rempt's avatar
Halla Rempt committed
561
        QList<Statement> &addList, const Soprano::Node &n) const
562
{
Halla Rempt's avatar
Halla Rempt committed
563
    Q_ASSERT(_model);
564
    Q_ASSERT(d->model);
565 566 567
    Node rdfFirst = Node::createResourceNode(QUrl("http://www.w3.org/1999/02/22-rdf-syntax-ns#first"));
    Node rdfRest  = Node::createResourceNode(QUrl("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"));
    QList<Statement> all;
568

Halla Rempt's avatar
Halla Rempt committed
569
    all = model()->listStatements(n, rdfFirst, Node()).allElements();
570
    addList << all;
Halla Rempt's avatar
Halla Rempt committed
571
    all = model()->listStatements(n, rdfRest, Node()).allElements();
572
    addList << all;
573
    foreach (const Soprano::Statement &s, all) {
Halla Rempt's avatar
Halla Rempt committed
574
        expandStatementsToIncludeRdfListsRecurse(_model, addList, s.object());
575 576 577 578
    }
}


579
void KoDocumentRdf::expandStatementsToIncludeRdfLists(QSharedPointer<Soprano::Model> model) const
580
{
581
    Q_ASSERT(model);
582
    RDEBUG << "model.sz:" << model->statementCount();
583 584
    QList<Statement> addList;
    QList<Statement> allStatements = model->listStatements().allElements();
585

586
    foreach (const Soprano::Statement &s, allStatements) {
587 588
        expandStatementsToIncludeRdfListsRecurse(model, addList, s.subject());
    }
589 590
    RDEBUG << "model.sz:" << model->statementCount();
    RDEBUG << "addList.sz:" << addList.size();
591 592 593
    model->addStatements(addList);
}

594
void KoDocumentRdf::expandStatementsToIncludeOtherPredicates(QSharedPointer<Soprano::Model> _model) const
595
{
Halla Rempt's avatar
Halla Rempt committed
596
    Q_ASSERT(_model);
597
    Q_ASSERT(d->model);
Thomas Zander's avatar
Thomas Zander committed
598
    QList<Statement> addList;
Halla Rempt's avatar
Halla Rempt committed
599
    QList<Statement> allStatements = _model->listStatements().allElements();
600

601
    foreach (const Soprano::Statement &s, allStatements) {
Halla Rempt's avatar
Halla Rempt committed
602
        QList<Statement> all = model()->listStatements(s.subject(), Node(), Node()).allElements();
603
        addList.append(all);
604
    }
Halla Rempt's avatar
Halla Rempt committed
605
    _model->addStatements(addList);
606 607
}

608
void KoDocumentRdf::expandStatements(QSharedPointer<Soprano::Model> model) const
609
{
610
    Q_ASSERT(model);
611 612 613 614
    expandStatementsReferencingSubject(model);
    expandStatementsToIncludeOtherPredicates(model);
}

Thomas Zander's avatar
Thomas Zander committed
615
KAction *KoDocumentRdf::createInsertSemanticObjectReferenceAction(KoCanvasBase *host)
616
{
Laurent Montel's avatar
Laurent Montel committed
617
    KAction *ret = new InsertSemanticObjectReferenceAction(host, this, i18n("Reference"));
618
    RDEBUG << "createInsertSemanticObjectReferenceAction";
619 620 621 622 623 624
    return ret;
}

QList<KAction*> KoDocumentRdf::createInsertSemanticObjectNewActions(KoCanvasBase *host)
{
    QList<KAction*> ret;
625
    foreach (const QString &semanticClass, KoRdfSemanticItemRegistry::instance()->classNames()) {
Halla Rempt's avatar
Halla Rempt committed
626
        ret.append(new InsertSemanticObjectCreateAction(host, this, semanticClass));
627 628 629 630
    }
    return ret;
}

Halla Rempt's avatar
Halla Rempt committed
631
QPair<int, int> KoDocumentRdf::findExtent(const QString &xmlid) const
632
{
Thomas Zander's avatar
Thomas Zander committed
633
    KoTextInlineRdf *obj = findInlineRdfByID(xmlid);
634
    if (obj) {
Thomas Zander's avatar
Thomas Zander committed
635
        QPair<int, int> ret = obj->findExtent();
636
        RDEBUG << "(Semantic) have inline obj, extent:" << ret;
637 638
        return ret;
    }
639
    return QPair<int, int>(-1, 0);
640 641
}

642
QPair<int, int> KoDocumentRdf::findExtent(KoTextEditor *handler) const
643
{
644
    Q_ASSERT(d->model);
Thomas Zander's avatar
Thomas Zander committed
645
    RDEBUG << "model.sz:" << d->model->statementCount();
646

647 648 649 650 651
    const QTextDocument *document = handler->document();

    // first check for bookmarks
    KoTextRangeManager *mgr = KoTextDocument(document).textRangeManager();
    Q_ASSERT(mgr);
652
    QHash<int, KoTextRange *> textRanges = mgr->textRangesChangingWithin(handler->document(), 0, handler->selectionEnd(), handler->selectionStart(), -1);
653 654 655 656 657
    foreach (const KoTextRange *range, textRanges) {
        return QPair<int,int>(range->rangeStart(), range->rangeEnd());
    }
/*
    // find the text:meta inline objects
658 659 660 661 662 663 664 665 666
    int startPosition = handler->position();
    KoInlineTextObjectManager *inlineObjectManager
                = KoTextDocument(handler->document()).inlineTextObjectManager();
    Q_ASSERT(inlineObjectManager);

    QTextCursor cursor = document->find(QString(QChar::ObjectReplacementCharacter),
                                        startPosition,
                                        QTextDocument::FindBackward);
    while(!cursor.isNull()) {
667
        RDEBUG <<  "findXmlId" << cursor.position();
668 669 670
        QTextCharFormat fmt = cursor.charFormat();
        KoInlineObject *obj = inlineObjectManager->inlineTextObject(fmt);

671
        if (KoTextMeta *metamark = dynamic_cast<KoTextMeta*>(obj)) {
672 673 674 675 676
            if (metamark->type() == KoTextMeta::StartBookmark) {
                KoTextMeta *endmark = metamark->endBookmark();
                Q_ASSERT(endmark);
                if (endmark->position() > startPosition) {
                    return QPair<int, int>(metamark->position(), endmark->position());
677 678 679
                }
            }
        }
680 681 682
        cursor = document->find(QString(QChar::ObjectReplacementCharacter),
                                cursor.position(),
                                QTextDocument::FindBackward);
683
    }
684
    */
685
    return QPair<int, int>(-1, 0);
686 687
}

Halla Rempt's avatar
Halla Rempt committed
688
QString KoDocumentRdf::findXmlId(KoTextEditor *handler) const
689
{
690
    int startPosition = handler->position();
Halla Rempt's avatar
Halla Rempt committed
691
    Q_UNUSED(startPosition);
692

Halla Rempt's avatar
Halla Rempt committed
693
    KoTextInlineRdf *inlineRdf = 0;
694 695

    const QTextDocument *document = handler->document();
696 697 698 699

    // first check for bookmarks
    KoTextRangeManager *mgr = KoTextDocument(document).textRangeManager();
    Q_ASSERT(mgr);
700
    QHash<int, KoTextRange *> textRanges = mgr->textRangesChangingWithin(document, 0, handler->selectionEnd(), handler->selectionStart(), -1);
701 702 703 704 705 706 707 708 709 710 711 712
    foreach (const KoTextRange *range, textRanges) {
        inlineRdf = range->inlineRdf();
        if (inlineRdf) {
            return inlineRdf->xmlId();
        }
    }

/*
    // find the text:meta inline objects
    KoInlineTextObjectManager *inlineObjectManager
                = KoTextDocument(document).inlineTextObjectManager();
    Q_ASSERT(inlineObjectManager);
713 714 715 716
    QTextCursor cursor = document->find(QString(QChar::ObjectReplacementCharacter),
                                        startPosition,
                                        QTextDocument::FindBackward);
    while(!cursor.isNull()) {
717
        RDEBUG << "Cursor position" << cursor.position();
718 719
        QTextCharFormat fmt = cursor.charFormat();
        KoInlineObject *obj = inlineObjectManager->inlineTextObject(fmt);
720
        RDEBUG << "obj" << obj;
721

722
        if (KoTextMeta *metamark = dynamic_cast<KoTextMeta*>(obj)) {
723 724
            if (metamark->type() == KoTextMeta::StartBookmark) {
                KoTextMeta *endmark = metamark->endBookmark();
725 726 727 728
                // we used to assert on endmark, but we cannot keep people from
                // inserting a startbookmark and only then creating and inserting
                // the endmark
                if (endmark && endmark->position() > startPosition) {
729
                    inlineRdf = metamark->inlineRdf();
730 731 732
                }
            }
        }
733 734 735 736 737 738

        // if we've got inline rdf, we've found the nearest xmlid wrapping our current position
        if (inlineRdf) {
            break;
        }

739 740
        if( cursor.position() <= 0 )
            break;
741

742 743
        // else continue with the next inline object
        cursor = document->find(QString(QChar::ObjectReplacementCharacter),
744
                                cursor.position()-1,
745
                                QTextDocument::FindBackward);
746
    }
747
*/
748 749 750
    // we couldn't find inline rdf object... So try to see whether there's
    // inlineRdf in the charformat for the current cursor position. It's
    // unlikely, of course. Maybe this should be the first check, though?
751
    if (!inlineRdf) {
752
        inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(handler);
753
    }
754

755 756 757
    if (inlineRdf) {
        return inlineRdf->xmlId();
    }
758

759
    return QString();
760 761
}

762

763

764
QSharedPointer<Soprano::Model> KoDocumentRdf::findStatements(const QString &xmlid, int depth)
765
{
766
    QSharedPointer<Soprano::Model> ret(Soprano::createModel());
767
    Q_ASSERT(ret);
768 769 770 771 772 773 774
    addStatements(ret, xmlid);
    for (int i = 1; i < depth; ++i) {
        expandStatements(ret);
    }
    return ret;
}

775
QSharedPointer<Soprano::Model> KoDocumentRdf::findStatements(KoTextEditor *handler, int depth)
776
{
777
    Q_ASSERT(d->model);
778

779
    QSharedPointer<Soprano::Model> ret(Soprano::createModel());
780
    Q_ASSERT(ret);
781 782 783 784

    QString xmlid = findXmlId(handler);
    KoTextInlineRdf *inlineRdf = findInlineRdfByID(xmlid);

Thomas Zander's avatar
Thomas Zander committed
785
    RDEBUG << "1 model.sz:" << d->model->statementCount()
Thomas Zander's avatar
Thomas Zander committed
786
        << " ret.sz:" << ret->statementCount();
787
    if (inlineRdf) {
788 789
        RDEBUG << "have inlineRdf1...xmlid:" << inlineRdf->xmlId();
        RDEBUG << " ret.sz:" << ret->statementCount();
790
        ret->addStatement(toStatement(inlineRdf));
791 792
        RDEBUG << "have inlineRdf2...xmlid:" << inlineRdf->xmlId();
        RDEBUG << " ret.sz:" << ret->statementCount();
793 794 795
        QString xmlid = inlineRdf->xmlId();
        addStatements(ret, xmlid);
    }
796

797 798
    RDEBUG << "2 ret.sz:" << ret->statementCount();
    RDEBUG << "checking for block inlineRdf...";
799
    inlineRdf = KoTextInlineRdf::tryToGetInlineRdf(handler);
800

801
    if (inlineRdf) {
802
        RDEBUG << "inlineRdf:" << (void*)inlineRdf;