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