| 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 | |