1 /* This file is part of the KDE project
2    Copyright (C) 2010 KO GmbH <ben.martin@kogmbh.com>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "KoSopranoTableModel.h"
21 
22 #include "KoDocumentRdf.h"
23 #include "KoRdfPrefixMapping.h"
24 // KF5
25 #include <kdebug.h>
26 #include <klocalizedstring.h>
27 
28 #include <algorithm>
29 
KoSopranoTableModel(KoDocumentRdf * rdf)30 KoSopranoTableModel::KoSopranoTableModel(KoDocumentRdf *rdf)
31         : m_rdf(rdf)
32 {
33     Soprano::StatementIterator siter = model()->listStatements();
34     while (siter.next()) {
35         m_statementIndex << *siter;
36     }
37 }
38 
model() const39 QSharedPointer<Soprano::Model> KoSopranoTableModel::model() const
40 {
41     return m_rdf->model();
42 }
43 
URItoPrefexedLocalname(const QString & uri) const44 QString KoSopranoTableModel::URItoPrefexedLocalname(const QString &uri) const
45 {
46     return m_rdf->prefixMapping()->URItoPrefexedLocalname(uri);
47 }
PrefexedLocalnameToURI(const QString & pname)48 QString KoSopranoTableModel::PrefexedLocalnameToURI(const QString &pname)
49 {
50     return m_rdf->prefixMapping()->PrefexedLocalnameToURI(pname);
51 }
52 
data(const QModelIndex & index,int role) const53 QVariant KoSopranoTableModel::data(const QModelIndex &index, int role) const
54 {
55     if (!index.isValid()) {
56         return QVariant();
57     }
58     Soprano::Statement st = m_statementIndex[index.row()];
59     if (index.column() == ColIsValid && role == Qt::CheckStateRole) {
60         return st.isValid();
61     }
62     if (role == Qt::BackgroundRole) {
63         if (!m_statementIndex[index.row()].isValid()) {
64             return QColor("#BB0000");
65         }
66     }
67     if (role != Qt::DisplayRole && role != Qt::EditRole) {
68         return QVariant();
69     }
70     switch (index.column()) {
71     case ColIsValid:
72         return QVariant();
73     case ColSubj:
74         return URItoPrefexedLocalname(st.subject().toString());
75     case ColPred:
76         return URItoPrefexedLocalname(st.predicate().toString());
77     case ColObj:
78         if (st.object().type() == Soprano::Node::ResourceNode)
79             return URItoPrefexedLocalname(st.object().toString());
80         return st.object().toString();
81     case ColObjType:
82         switch (st.object().type()) {
83         case Soprano::Node::EmptyNode:
84             return i18n("Empty");
85         case Soprano::Node::ResourceNode:
86             return i18n("URI");
87         case Soprano::Node::LiteralNode:
88             return i18n("Literal");
89         case Soprano::Node::BlankNode:
90             return i18n("Blank");
91         }
92         return QString();
93     case ColObjXsdType:
94         return st.object().dataType().toString();
95     case ColCtx: {
96         QString ctx = st.context().toString();
97         QString RdfPathContextPrefix = m_rdf->RDF_PATH_CONTEXT_PREFIX;
98         QString InternalContext = m_rdf->inlineRdfContext().toString();
99 
100         kDebug(30015) << "InternalContext:" << InternalContext;
101         kDebug(30015) << "ctx:" << ctx;
102 
103         if (ctx.startsWith(RdfPathContextPrefix)) {
104             ctx.remove(0, RdfPathContextPrefix.size());
105         }
106         if (isInlineRdf(st)) {
107             ctx = "inline";
108         }
109         return ctx;
110     }
111     }
112     return QVariant();
113 }
114 
rowCount(const QModelIndex & parent) const115 int KoSopranoTableModel::rowCount(const QModelIndex &parent) const
116 {
117     Q_UNUSED(parent);
118     return model()->statementCount();
119 }
120 
columnCount(const QModelIndex & parent) const121 int KoSopranoTableModel::columnCount(const QModelIndex &parent) const
122 {
123     Q_UNUSED(parent);
124     return ColCount;
125 }
126 
headerData(int section,Qt::Orientation orientation,int role) const127 QVariant KoSopranoTableModel::headerData(int section, Qt::Orientation orientation, int role) const
128 {
129     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
130         switch (section) {
131         case ColIsValid:
132             return i18n("Valid");
133         case ColSubj:
134             return i18n("Subject");
135         case ColPred:
136             return i18n("Predicate");
137         case ColObj:
138             return i18n("Object");
139         case ColObjType:
140             return i18n("Obj Type");
141         case ColObjXsdType:
142             return i18n("DataType");
143         case ColCtx:
144             return i18n("Stored In");
145         }
146     }
147     return QVariant();
148 }
149 
isInlineRdf(const Soprano::Statement & st) const150 bool KoSopranoTableModel::isInlineRdf(const Soprano::Statement &st) const
151 {
152     return (st.context().toString() == m_rdf->inlineRdfContext().toString());
153 }
154 
flags(const QModelIndex & index) const155 Qt::ItemFlags KoSopranoTableModel::flags(const QModelIndex &index) const
156 {
157     Qt::ItemFlags ret = QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
158     if (index.column() == ColIsValid) {
159         ret |= Qt::ItemIsUserCheckable;
160     }
161     if (index.row() >= 0) {
162         Soprano::Statement st = m_statementIndex[index.row()];
163         if (isInlineRdf(st)) {
164             if (index.column() == ColSubj
165                     || index.column() == ColObjType
166                     || index.column() == ColCtx) {
167                 ret &= (~Qt::ItemIsEditable);
168             }
169         }
170     }
171     return ret;
172 }
173 
174 /**
175  * You MUST use this method if you want to change a Statement.
176  *
177  * Used by setData() to remove the old statement and replace it with the new 'n' one.
178  * The internal m_statementIndex int->statement is updated
179  * as well as the dataChanged signal emitted
180  */
setDataUpdateTriple(const QModelIndex & index,const Soprano::Statement & old,const Soprano::Statement & n)181 bool KoSopranoTableModel::setDataUpdateTriple(const QModelIndex &index, const Soprano::Statement &old, const Soprano::Statement &n)
182 {
183     model()->addStatement(n);
184     model()->removeStatement(old);
185     m_statementIndex[index.row()] = n;
186     emit dataChanged(index, index);
187     return true;
188 }
189 
setData(const QModelIndex & index,const QVariant & value,int role)190 bool KoSopranoTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
191 {
192     Q_UNUSED(role);
193     if (!index.isValid()) {
194         return false;
195     }
196     int r = index.row();
197     Soprano::Statement st = m_statementIndex[r];
198     QString uri = PrefexedLocalnameToURI(value.toString());
199     Soprano::Statement n(st.subject(), st.predicate(), st.object(), st.context());
200     switch (index.column()) {
201     case ColSubj:
202         n.setSubject(Soprano::Node(QUrl(uri)));
203         return setDataUpdateTriple(index, st, n);
204     case ColPred:
205         n.setPredicate(Soprano::Node(QUrl(uri)));
206         return setDataUpdateTriple(index, st, n);
207     case ColObj: {
208         if (st.object().isLiteral()) {
209             n.setObject(
210                 Soprano::Node(
211                     Soprano::LiteralValue(value.toString())));
212         } else {
213             n.setObject(Soprano::Node(QUrl(uri)));
214         }
215         return setDataUpdateTriple(index, st, n);
216     }
217     case ColObjType: {
218         QString v = value.toString();
219         if (v == "URI") {
220             n.setObject(Soprano::Node(QUrl(st.object().toString())));
221         } else if (v == "Literal") {
222             n.setObject(
223                 Soprano::Node(
224                     Soprano::LiteralValue(st.object().toString())));
225         } else {
226             n.setObject(Soprano::Node(QString(st.object().toString())));
227         }
228         return setDataUpdateTriple(index, st, n);
229     }
230     case ColCtx: {
231         QString v = value.toString();
232         if (v == "inline") {
233             QString InternalContext = m_rdf->rdfInternalMetadataWithoutSubjectURI();
234             n.setContext(Soprano::Node(QUrl(InternalContext)));
235         } else {
236             if (!v.endsWith(".rdf"))
237                 v = v + ".rdf";
238             n.setContext(Soprano::Node(QUrl(m_rdf->RDF_PATH_CONTEXT_PREFIX + v)));
239         }
240         return setDataUpdateTriple(index, st, n);
241     }
242     }
243     return false;
244 }
245 
246 /**
247  * Add the statement 'st' to the model as the new last row.
248  */
insertStatement(const Soprano::Statement & st)249 int KoSopranoTableModel::insertStatement(const Soprano::Statement &st)
250 {
251     QModelIndex parent;
252     int newRowNumber = rowCount();
253     kDebug(30015) << "insert, newrow:" << newRowNumber << endl;
254     beginInsertRows(parent, newRowNumber, newRowNumber);
255     model()->addStatement(st);
256     m_statementIndex << st;
257     endInsertRows();
258     return newRowNumber;
259 }
260 
261 /**
262  * Copy all the triples in srclist to be new rows in the model.
263  * Note that the object value is modified to contain a unique
264  * postfix so that the new triple copies can be inserted into
265  * the Rdf model. It is a copy in a looser sense of the word.
266  */
copyTriples(const QModelIndexList & srclist)267 QModelIndexList KoSopranoTableModel::copyTriples(const QModelIndexList &srclist)
268 {
269     QModelIndexList ret;
270     int FirstNewRowNumber = rowCount();
271     int LastNewRowNumber = FirstNewRowNumber + srclist.size() - 1;
272     int currentNewRowNum = FirstNewRowNumber;
273     beginInsertRows(QModelIndex(), FirstNewRowNumber, LastNewRowNumber);
274     kDebug(30015) << " m_statementIndex.sz:" << m_statementIndex.size();
275     kDebug(30015) << " srclist.size():" << srclist.size();
276     kDebug(30015) << " first:" << FirstNewRowNumber;
277     kDebug(30015) << " last:" << LastNewRowNumber;
278     foreach (const QModelIndex &src, srclist) {
279         int r = src.row();
280         kDebug(30015) << "r:" << r;
281         Soprano::Statement st = m_statementIndex[ r ];
282         //
283         // Append a bnode to the object to ensure the "copy"
284         // is unique relative to the original.
285         //
286         Soprano::Node obj(QUrl(st.object().toString() + '-'
287                                + model()->createBlankNode().toString()));
288         Soprano::Statement n(st.subject(), st.predicate(),
289                              obj, st.context());
290         model()->addStatement(n);
291         m_statementIndex << n;
292         QModelIndex newIdx = index(currentNewRowNum, ColSubj);
293         ret << newIdx;
294         ++currentNewRowNum;
295     }
296     endInsertRows();
297     return ret;
298 }
299 
300 /**
301  * Delete all the triples in srclist from the model.
302  */
deleteTriples(const QModelIndexList & srclist)303 void KoSopranoTableModel::deleteTriples(const QModelIndexList &srclist)
304 {
305     //
306     // Because items after a row are shuffled back to fill
307     // it's position, it is easiest to remove each item
308     // starting at the largest row number and working
309     // down by descending row number.
310     //
311     QList<int> rowsToRemoveDesc;
312     foreach (const QModelIndex &src, srclist) {
313         int r = src.row();
314         rowsToRemoveDesc << r;
315     }
316     std:sort(rowsToRemoveDesc.begin(), rowsToRemoveDesc.end(), std::greater<int>());
317     int r;
318     foreach (r, rowsToRemoveDesc) {
319         Soprano::Statement st = m_statementIndex[ r ];
320         int firstRow =  r;
321         int lastRow = r;
322         beginRemoveRows(QModelIndex(), firstRow, lastRow);
323         model()->removeStatement(st);
324         // m_statementIndex[ r ] = Soprano::Statement();
325         for (int i = r; i < m_statementIndex.size() - 1; ++i) {
326             m_statementIndex[ i ] = m_statementIndex[ i + 1 ];
327         }
328         m_statementIndex.removeLast();
329         endRemoveRows();
330     }
331 }
332 
statementAtIndex(const QModelIndex & index) const333 Soprano::Statement KoSopranoTableModel::statementAtIndex(const QModelIndex &index) const
334 {
335     return m_statementIndex[index.row()];
336 }
337 
invalidStatementCount() const338 int KoSopranoTableModel::invalidStatementCount() const
339 {
340     return invalidStatementList().size();
341 }
342 
invalidStatementList() const343 QModelIndexList KoSopranoTableModel::invalidStatementList() const
344 {
345     QModelIndexList ret;
346     for (int r = 0; r < m_statementIndex.size(); ++r)  {
347         const Soprano::Statement &s = m_statementIndex.at(r);
348         if (!s.isValid()) {
349             int col = ColSubj;
350             if (!s.subject().isValid()) {
351                 col = ColSubj;
352             }
353             if (!s.predicate().isValid()) {
354                 col = ColPred;
355             }
356             if (!s.object().isValid()) {
357                 col = ColObj;
358             }
359             QModelIndex idx = index(r, col);
360             ret << idx;
361         }
362     }
363     return ret;
364 }
365 
366