1 | /* |
2 | This file is part of the syndication library |
3 | SPDX-FileCopyrightText: 2006 Frank Osterfeld <osterfeld@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "model.h" |
9 | #include "model_p.h" |
10 | |
11 | namespace Syndication |
12 | { |
13 | namespace RDF |
14 | { |
15 | long Model::ModelPrivate::idCounter = 0; |
16 | |
17 | Model::Model() |
18 | : d(new ModelPrivate) |
19 | { |
20 | } |
21 | |
22 | Model::Model(const Model &other) |
23 | { |
24 | *this = other; |
25 | } |
26 | |
27 | Model::~Model() |
28 | { |
29 | } |
30 | |
31 | Model &Model::operator=(const Model &other) |
32 | { |
33 | d = other.d; |
34 | return *this; |
35 | } |
36 | |
37 | bool Model::operator==(const Model &other) const |
38 | { |
39 | return *d == *(other.d); |
40 | } |
41 | |
42 | PropertyPtr Model::createProperty(const QString &uri) |
43 | { |
44 | PropertyPtr prop; |
45 | |
46 | if (d->properties.contains(key: uri)) { |
47 | prop = d->properties[uri]; |
48 | } else { |
49 | prop = PropertyPtr(new Property(uri)); |
50 | prop->setModel(*this); |
51 | // if there is a resource object with the same uri, replace |
52 | // the resource object by the new property object and reuse the id |
53 | if (d->resources.contains(key: uri)) { |
54 | prop->setId(d->resources[uri]->id()); |
55 | } |
56 | d->addToHashes(node: prop); |
57 | } |
58 | |
59 | return prop; |
60 | } |
61 | |
62 | ResourcePtr Model::createResource(const QString &uri) |
63 | { |
64 | ResourcePtr res; |
65 | |
66 | if (d->resources.contains(key: uri)) { |
67 | res = d->resources[uri]; |
68 | } else { |
69 | res = ResourcePtr(new Resource(uri)); |
70 | res->setModel(*this); |
71 | d->addToHashes(node: res); |
72 | } |
73 | |
74 | return res; |
75 | } |
76 | |
77 | SequencePtr Model::createSequence(const QString &uri) |
78 | { |
79 | SequencePtr seq; |
80 | |
81 | if (d->sequences.contains(key: uri)) { |
82 | seq = d->sequences[uri]; |
83 | } else { |
84 | seq = SequencePtr(new Sequence(uri)); |
85 | seq->setModel(*this); |
86 | // if there is a resource object with the same uri, replace |
87 | // the resource object by the new sequence object and reuse the id |
88 | if (d->resources.contains(key: uri)) { |
89 | seq->setId(d->resources[uri]->id()); |
90 | } |
91 | |
92 | d->addToHashes(node: seq); |
93 | } |
94 | |
95 | return seq; |
96 | } |
97 | |
98 | LiteralPtr Model::createLiteral(const QString &text) |
99 | { |
100 | LiteralPtr lit(new Literal(text)); |
101 | |
102 | d->addToHashes(node: lit); |
103 | return lit; |
104 | } |
105 | |
106 | void Model::removeStatement(StatementPtr statement) |
107 | { |
108 | removeStatement(subject: statement->subject(), predicate: statement->predicate(), object: statement->object()); |
109 | } |
110 | |
111 | void Model::removeStatement(ResourcePtr subject, PropertyPtr predicate, NodePtr object) |
112 | { |
113 | /* clang-format off */ |
114 | QString key = QStringLiteral("%1-%2-%3" ).arg(args: QString::number(subject->id()), |
115 | args: QString::number(predicate->id()), |
116 | args: QString::number(object->id())); |
117 | /* clang-format on */ |
118 | |
119 | d->removeFromHashes(key); |
120 | } |
121 | |
122 | StatementPtr Model::addStatement(ResourcePtr subject, PropertyPtr predicate, NodePtr object) |
123 | { |
124 | d->init(sharedThis: d); |
125 | ResourcePtr subjInternal = subject; |
126 | |
127 | if (!d->nodes.contains(key: subjInternal->id())) { |
128 | subjInternal = ResourcePtr(subject->clone()); |
129 | subjInternal->setModel(*this); |
130 | d->addToHashes(node: subjInternal); |
131 | } |
132 | |
133 | PropertyPtr predInternal = predicate; |
134 | |
135 | if (!d->nodes.contains(key: predInternal->id())) { |
136 | predInternal = PropertyPtr(predicate->clone()); |
137 | predInternal->setModel(*this); |
138 | d->addToHashes(node: predInternal); |
139 | } |
140 | |
141 | NodePtr objInternal = object; |
142 | |
143 | if (!d->nodes.contains(key: objInternal->id())) { |
144 | objInternal = NodePtr(object->clone()); |
145 | objInternal->setModel(*this); |
146 | d->addToHashes(node: objInternal); |
147 | } |
148 | |
149 | // TODO: avoid duplicated stmts with literal objects! |
150 | |
151 | /* clang-format off */ |
152 | QString key = QStringLiteral("%1-%2-%3" ).arg(args: QString::number(subjInternal->id()), |
153 | args: QString::number(predInternal->id()), |
154 | args: QString::number(objInternal->id())); |
155 | /* clang-format on */ |
156 | |
157 | StatementPtr stmt; |
158 | |
159 | if (!d->statements.contains(key)) { |
160 | stmt = StatementPtr(new Statement(subjInternal, predInternal, objInternal)); |
161 | d->addToHashes(stmt, key); |
162 | } else { |
163 | stmt = d->statements[key]; |
164 | } |
165 | |
166 | return stmt; |
167 | } |
168 | |
169 | bool Model::isEmpty() const |
170 | { |
171 | return d->statements.isEmpty(); |
172 | } |
173 | |
174 | bool Model::resourceHasProperty(const Resource *resource, PropertyPtr property) const |
175 | { |
176 | return d->resourceHasProperty(resource, property); |
177 | } |
178 | |
179 | bool Model::ModelPrivate::resourceHasProperty(const Resource *resource, PropertyPtr property) const |
180 | { |
181 | // resource unknown |
182 | if (!resources.contains(key: resource->uri())) { |
183 | return false; |
184 | } |
185 | |
186 | const QList<StatementPtr> &stmts = stmtsBySubject[resource->uri()]; |
187 | |
188 | return std::any_of(first: stmts.cbegin(), last: stmts.cend(), pred: [&property](const StatementPtr &p) { |
189 | return *(p->predicate()) == *property; |
190 | }); |
191 | } |
192 | |
193 | StatementPtr Model::resourceProperty(const Resource *resource, PropertyPtr property) const |
194 | { |
195 | return d->resourceProperty(resource, property); |
196 | } |
197 | |
198 | StatementPtr Model::ModelPrivate::resourceProperty(const Resource *resource, PropertyPtr property) const |
199 | { |
200 | const QList<StatementPtr> &stmts = stmtsBySubject[resource->uri()]; |
201 | auto it = std::find_if(first: stmts.cbegin(), last: stmts.cend(), pred: [&property](const StatementPtr &p) { |
202 | return *(p->predicate()) == *property; |
203 | }); |
204 | return it != stmts.cend() ? *it : nullStatement; |
205 | } |
206 | |
207 | QList<StatementPtr> Model::resourceProperties(const Resource *resource, PropertyPtr property) const |
208 | { |
209 | return d->resourceProperties(resource, property); |
210 | } |
211 | |
212 | QList<StatementPtr> Model::ModelPrivate::resourceProperties(const Resource *resource, PropertyPtr property) const |
213 | { |
214 | QList<StatementPtr> res; |
215 | |
216 | const QList<StatementPtr> &stmts = stmtsBySubject[resource->uri()]; |
217 | for (const auto &p : stmts) { |
218 | if (*(p->predicate()) == *property) { |
219 | res.append(t: p); |
220 | } |
221 | } |
222 | |
223 | return res; |
224 | } |
225 | |
226 | QList<StatementPtr> Model::statements() const |
227 | { |
228 | return d->statements.values(); |
229 | } |
230 | |
231 | QString Model::debugInfo() const |
232 | { |
233 | QString info; |
234 | for (const StatementPtr &stmtPtr : std::as_const(t&: d->statements)) { |
235 | info += QStringLiteral("<%1> <%2> " ).arg(args: stmtPtr->subject()->uri(), args: stmtPtr->predicate()->uri()); |
236 | |
237 | if (stmtPtr->object()->isLiteral()) { |
238 | info += QStringLiteral("\"%1\"\n" ).arg(a: stmtPtr->asString()); |
239 | } else { |
240 | info += QStringLiteral("<%1>\n" ).arg(a: stmtPtr->asResource()->uri()); |
241 | } |
242 | } |
243 | |
244 | return info; |
245 | } |
246 | |
247 | QList<ResourcePtr> Model::resourcesWithType(ResourcePtr type) const |
248 | { |
249 | QList<ResourcePtr> list; |
250 | |
251 | for (const StatementPtr &stmtPtr : std::as_const(t&: d->statements)) { |
252 | if (*(stmtPtr->predicate()) == *(RDFVocab::self()->type()) // |
253 | && stmtPtr->object()->isResource() && *(dynamic_cast<Resource *>(stmtPtr->object().data())) == *type) { |
254 | list.append(t: stmtPtr->subject()); |
255 | } |
256 | } |
257 | |
258 | return list; |
259 | } |
260 | |
261 | NodePtr Model::nodeByID(uint id) const |
262 | { |
263 | return d->nodeByID(id); |
264 | } |
265 | |
266 | NodePtr Model::ModelPrivate::nodeByID(uint _id) const |
267 | { |
268 | if (!nodes.contains(key: _id)) { |
269 | return nullLiteral; |
270 | } else { |
271 | return nodes.value(key: _id); |
272 | } |
273 | } |
274 | |
275 | ResourcePtr Model::resourceByID(uint id) const |
276 | { |
277 | return d->resourceByID(id); |
278 | } |
279 | |
280 | ResourcePtr Model::ModelPrivate::resourceByID(uint _id) const |
281 | { |
282 | if (!nodes.contains(key: _id)) { |
283 | return nullResource; |
284 | } else { |
285 | NodePtr node = nodes.value(key: _id); |
286 | if (node->isResource()) { |
287 | return node.staticCast<Resource>(); |
288 | } else { |
289 | return nullResource; |
290 | } |
291 | } |
292 | } |
293 | |
294 | PropertyPtr Model::propertyByID(uint id) const |
295 | { |
296 | return d->propertyByID(id); |
297 | } |
298 | |
299 | PropertyPtr Model::ModelPrivate::propertyByID(uint _id) const |
300 | { |
301 | if (!nodes.contains(key: _id)) { |
302 | return nullProperty; |
303 | } else { |
304 | NodePtr node = nodes.value(key: _id); |
305 | if (node->isProperty()) { |
306 | return node.staticCast<Property>(); |
307 | } else { |
308 | return nullProperty; |
309 | } |
310 | } |
311 | } |
312 | |
313 | LiteralPtr Model::literalByID(uint id) const |
314 | { |
315 | return d->literalByID(id); |
316 | } |
317 | |
318 | LiteralPtr Model::ModelPrivate::literalByID(uint _id) const |
319 | { |
320 | if (!nodes.contains(key: _id)) { |
321 | return nullLiteral; |
322 | } else { |
323 | NodePtr node = nodes.value(key: _id); |
324 | if (node->isLiteral()) { |
325 | return node.staticCast<Literal>(); |
326 | } else { |
327 | return nullLiteral; |
328 | } |
329 | } |
330 | } |
331 | |
332 | } // namespace RDF |
333 | } // namespace Syndication |
334 | |