Syndication

rdf/document.cpp
1 /*
2  This file is part of the syndication library
3  SPDX-FileCopyrightText: 2006 Frank Osterfeld <[email protected]>
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 
31 namespace Syndication
32 {
33 namespace RDF
34 {
35 class SYNDICATION_NO_EXPORT Document::Private
36 {
37 public:
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 
52 Document::Document()
53  : Syndication::SpecificDocument()
54  , ResourceWrapper()
55  , d(new Private)
56 {
57  d->modelPrivate = resource()->model().d;
58 }
59 
60 Document::Document(ResourcePtr resource)
61  : Syndication::SpecificDocument()
62  , ResourceWrapper(resource)
63  , d(new Private)
64 {
65  d->modelPrivate = resource->model().d;
66 }
67 
68 Document::Document(const Document &other)
69  : SpecificDocument(other)
70  , ResourceWrapper(other)
71  , d(new Private)
72 {
73  *d = *(other.d);
74 }
75 
76 Document::~Document() = default;
77 
78 bool Document::operator==(const Document &other) const
79 {
80  return ResourceWrapper::operator==(other);
81 }
82 
83 Document &Document::operator=(const Document &other)
84 {
85  ResourceWrapper::operator=(other);
86  *d = *(other.d);
87 
88  return *this;
89 }
90 
91 bool Document::accept(DocumentVisitor *visitor)
92 {
93  return visitor->visitRDFDocument(this);
94 }
95 
96 bool Document::isValid() const
97 {
98  return !isNull();
99 }
100 
101 QString Document::title() const
102 {
103  const QString str = resource()->property(RSSVocab::self()->title())->asString();
104  return normalize(str);
105 }
106 
107 QString Document::description() const
108 {
109  const QString str = resource()->property(RSSVocab::self()->description())->asString();
110  return normalize(str);
111 }
112 
113 QString Document::link() const
114 {
115  return resource()->property(RSSVocab::self()->link())->asString();
116 }
117 
118 DublinCore Document::dc() const
119 {
120  return DublinCore(resource());
121 }
122 
123 SyndicationInfo Document::syn() const
124 {
125  return SyndicationInfo(resource());
126 }
127 
128 struct SortItem {
129  Item item;
130  int index;
131 };
132 
133 struct LessThanByIndex {
134  bool operator()(const SortItem &lhs, const SortItem &rhs) const
135  {
136  return lhs.index < rhs.index;
137  }
138 };
139 
140 static 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 
161 struct UriLessThan {
162  bool operator()(const RDF::ResourcePtr &lhs, const RDF::ResourcePtr &rhs) const
163  {
164  return lhs->uri() < rhs->uri();
165  }
166 };
167 
168 QList<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 
206 Image Document::image() const
207 {
208  ResourcePtr img = resource()->property(RSSVocab::self()->image())->asResource();
209 
210  return img ? Image(img) : Image();
211 }
212 
213 TextInput Document::textInput() const
214 {
215  ResourcePtr ti = resource()->property(RSSVocab::self()->textinput())->asResource();
216 
217  return ti ? TextInput(ti) : TextInput();
218 }
219 
220 void Document::getItemTitleFormatInfo(bool *containsMarkup) const
221 {
222  if (!d->itemTitlesGuessed) {
223  QString titles;
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 
249 void 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 
279 QString 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
void append(const T &value)
QVector::iterator begin()
int count(const T &value) const const
void append(const T &value)
QString normalize(QStringView str)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
void reserve(int alloc)
KIOCORE_EXPORT CopyJob * link(const QList< QUrl > &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
int size() const const
bool isEmpty() const const
int indexOf(QStringView str, int from) const const
void reserve(int size)
QVector::iterator end()
QList::iterator begin()
virtual bool hasProperty(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
QList::iterator end()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Jun 6 2023 03:56:27 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.