Syndication

rdf/document.cpp
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 "document.h"
9#include "dublincore.h"
10#include "image.h"
11#include "item.h"
12#include "model.h"
13#include "model_p.h"
14#include "resource.h"
15#include "rssvocab.h"
16#include "sequence.h"
17#include "statement.h"
18#include "syndicationinfo.h"
19#include "textinput.h"
20
21#include <documentvisitor.h>
22#include <tools.h>
23
24#include <QList>
25#include <QString>
26#include <QStringList>
27#include <QVector>
28
29#include <algorithm>
30
31namespace Syndication
32{
33namespace RDF
34{
35class SYNDICATION_NO_EXPORT Document::Private
36{
37public:
38 Private()
39 : itemTitleContainsMarkup(false)
40 , itemTitlesGuessed(false)
41 , itemDescriptionContainsMarkup(false)
42 , itemDescGuessed(false)
43 {
44 }
45 mutable bool itemTitleContainsMarkup;
46 mutable bool itemTitlesGuessed;
47 mutable bool itemDescriptionContainsMarkup;
48 mutable bool itemDescGuessed;
50};
51
52Document::Document()
53 : Syndication::SpecificDocument()
54 , ResourceWrapper()
55 , d(new Private)
56{
57 d->modelPrivate = resource()->model().d;
58}
59
60Document::Document(ResourcePtr resource)
61 : Syndication::SpecificDocument()
62 , ResourceWrapper(resource)
63 , d(new Private)
64{
65 d->modelPrivate = resource->model().d;
66}
67
68Document::Document(const Document &other)
69 : SpecificDocument(other)
70 , ResourceWrapper(other)
71 , d(new Private)
72{
73 *d = *(other.d);
74}
75
76Document::~Document() = default;
77
78bool Document::operator==(const Document &other) const
79{
80 return ResourceWrapper::operator==(other);
81}
82
83Document &Document::operator=(const Document &other)
84{
85 ResourceWrapper::operator=(other);
86 *d = *(other.d);
87
88 return *this;
89}
90
91bool Document::accept(DocumentVisitor *visitor)
92{
93 return visitor->visitRDFDocument(this);
94}
95
96bool Document::isValid() const
97{
98 return !isNull();
99}
100
101QString Document::title() const
102{
103 const QString str = resource()->property(RSSVocab::self()->title())->asString();
104 return normalize(str);
105}
106
107QString Document::description() const
108{
109 const QString str = resource()->property(RSSVocab::self()->description())->asString();
110 return normalize(str);
111}
112
113QString Document::link() const
114{
115 return resource()->property(RSSVocab::self()->link())->asString();
116}
117
118DublinCore Document::dc() const
119{
120 return DublinCore(resource());
121}
122
123SyndicationInfo Document::syn() const
124{
125 return SyndicationInfo(resource());
126}
127
128struct SortItem {
129 Item item;
130 int index;
131};
132
133struct LessThanByIndex {
134 bool operator()(const SortItem &lhs, const SortItem &rhs) const
135 {
136 return lhs.index < rhs.index;
137 }
138};
139
140static QList<Item> sortListToMatchSequence(QList<Item> items, const QStringList &uriSequence)
141{
142 QVector<SortItem> toSort;
143 toSort.reserve(items.size());
144 for (const Item &i : items) {
145 SortItem item;
146 item.item = i;
147 item.index = uriSequence.indexOf(i.resource()->uri());
148 toSort.append(item);
149 }
150 std::sort(toSort.begin(), toSort.end(), LessThanByIndex());
151
152 int i = 0;
153 for (const SortItem &sortItem : std::as_const(toSort)) {
154 items[i] = sortItem.item;
155 i++;
156 }
157
158 return items;
159}
160
161struct UriLessThan {
162 bool operator()(const RDF::ResourcePtr &lhs, const RDF::ResourcePtr &rhs) const
163 {
164 return lhs->uri() < rhs->uri();
165 }
166};
167
168QList<Item> Document::items() const
169{
170 QList<ResourcePtr> items = resource()->model().resourcesWithType(RSSVocab::self()->item());
171 // if there is no sequence, ensure sorting by URI to have a defined and deterministic order
172 // important for unit tests
173 std::sort(items.begin(), items.end(), UriLessThan());
174
175 DocumentPtr doccpy(new Document(*this));
176
178 list.reserve(items.count());
179
180 for (const ResourcePtr &i : std::as_const(items)) {
181 list.append(Item(i, doccpy));
182 }
183
184 if (resource()->hasProperty(RSSVocab::self()->items())) {
185 NodePtr n = resource()->property(RSSVocab::self()->items())->object();
186 if (n->isSequence()) {
187 const SequencePtr seq = n.staticCast<Sequence>();
188
189 const QList<NodePtr> seqItems = seq->items();
190
191 QStringList uriSequence;
192 uriSequence.reserve(seqItems.size());
193
194 for (const NodePtr &i : seqItems) {
195 if (i->isResource()) {
196 uriSequence.append(i.staticCast<Resource>()->uri());
197 }
198 }
199 list = sortListToMatchSequence(list, uriSequence);
200 }
201 }
202
203 return list;
204}
205
206Image Document::image() const
207{
208 ResourcePtr img = resource()->property(RSSVocab::self()->image())->asResource();
209
210 return img ? Image(img) : Image();
211}
212
213TextInput Document::textInput() const
214{
215 ResourcePtr ti = resource()->property(RSSVocab::self()->textinput())->asResource();
216
217 return ti ? TextInput(ti) : TextInput();
218}
219
220void Document::getItemTitleFormatInfo(bool *containsMarkup) const
221{
222 if (!d->itemTitlesGuessed) {
224 QList<Item> litems = items();
225
226 if (litems.isEmpty()) {
227 d->itemTitlesGuessed = true;
228 return;
229 }
230
231 const int nmax = std::min<int>(litems.size(), 10); // we check a maximum of 10 items
232 int i = 0;
233
234 for (const auto &item : litems) {
235 if (i++ >= nmax) {
236 break;
237 }
238 titles += item.originalTitle();
239 }
240
241 d->itemTitleContainsMarkup = stringContainsMarkup(titles);
242 d->itemTitlesGuessed = true;
243 }
244 if (containsMarkup != nullptr) {
245 *containsMarkup = d->itemTitleContainsMarkup;
246 }
247}
248
249void Document::getItemDescriptionFormatInfo(bool *containsMarkup) const
250{
251 if (!d->itemDescGuessed) {
252 QString desc;
253 QList<Item> litems = items();
254
255 if (litems.isEmpty()) {
256 d->itemDescGuessed = true;
257 return;
258 }
259
260 const int nmax = std::min<int>(litems.size(), 10); // we check a maximum of 10 items
261 int i = 0;
262
263 for (const auto &item : litems) {
264 if (i++ >= nmax) {
265 break;
266 }
267 desc += item.originalDescription();
268 }
269
270 d->itemDescriptionContainsMarkup = stringContainsMarkup(desc);
271 d->itemDescGuessed = true;
272 }
273
274 if (containsMarkup != nullptr) {
275 *containsMarkup = d->itemDescriptionContainsMarkup;
276 }
277}
278
279QString Document::debugInfo() const
280{
281 QString info;
282 info += QLatin1String("### Document: ###################\n");
283 info += QLatin1String("title: #") + title() + QLatin1String("#\n");
284 info += QLatin1String("link: #") + link() + QLatin1String("#\n");
285 info += QLatin1String("description: #") + description() + QLatin1String("#\n");
286 info += dc().debugInfo();
287 info += syn().debugInfo();
288 Image img = image();
289 if (!img.resource() == 0L) {
290 info += img.debugInfo();
291 }
292 TextInput input = textInput();
293 if (!input.isNull()) {
294 info += input.debugInfo();
295 }
296
297 const QList<Item> itlist = items();
298 for (const auto &item : itlist) {
299 info += item.debugInfo();
300 }
301
302 info += QLatin1String("### Document end ################\n");
303 return info;
304}
305
306} // namespace RDF
307} // namespace Syndication
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
QString normalize(QStringView str)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KEDUVOCDOCUMENT_EXPORT QStringList titles(const QString &language=QString())
void append(QList< T > &&value)
iterator begin()
qsizetype count() const const
iterator end()
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:15 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.