Baloo

term.cpp
1 /*
2  This file is part of the KDE Baloo Project
3  SPDX-FileCopyrightText: 2013 Vishesh Handa <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7 
8 #include "term.h"
9 
10 #include <QDateTime>
11 
12 namespace Baloo {
13 
14 class Baloo::Term::Private {
15 public:
16  Operation m_op = None;
17  Comparator m_comp = Auto;
18 
19  QString m_property;
20  QVariant m_value;
21 
22  bool m_isNegated = false;
23 
24  QList<Term> m_subTerms;
25  QVariantHash m_userData;
26 };
27 
28 Term::Term()
29  : d(new Private)
30 {
31 }
32 
33 Term::Term(const Term& t)
34  : d(new Private(*t.d))
35 {
36 }
37 
38 Term::Term(const QString& property)
39  : d(new Private)
40 {
41  d->m_property = property;
42 }
43 
44 Term::Term(const QString& property, const QVariant& value, Term::Comparator c)
45  : d(new Private)
46 {
47  d->m_property = property;
48  d->m_value = value;
49 
50  if (c == Auto) {
51  if (value.type() == QVariant::String) {
52  d->m_comp = Contains;
53  } else if (value.type() == QVariant::DateTime) {
54  d->m_comp = Contains;
55  } else {
56  d->m_comp = Equal;
57  }
58  }
59  else {
60  d->m_comp = c;
61  }
62 }
63 
64 /*
65 Term::Term(const QString& property, const QVariant& start, const QVariant& end)
66  : d(new Private)
67 {
68  d->m_property = property;
69  d->m_op = Range;
70 
71  // FIXME: How to save range queries?
72 }
73 */
74 
75 Term::Term(Term::Operation op)
76  : d(new Private)
77 {
78  d->m_op = op;
79 }
80 
81 Term::Term(Term::Operation op, const Term& t)
82  : d(new Private)
83 {
84  d->m_op = op;
85  d->m_subTerms << t;
86 }
87 
88 Term::Term(Term::Operation op, const QList<Term>& t)
89  : d(new Private)
90 {
91  d->m_op = op;
92  d->m_subTerms = t;
93 }
94 
95 Term::Term(const Term& lhs, Term::Operation op, const Term& rhs)
96  : d(new Private)
97 {
98  d->m_op = op;
99  if (lhs.isEmpty()) {
100  *d = *(rhs.d);
101  return;
102  }
103  if (rhs.isEmpty()) {
104  *d = *(lhs.d);
105  return;
106  }
107 
108  if (lhs.operation() == op) {
109  d->m_subTerms << lhs.subTerms();
110  } else {
111  d->m_subTerms << lhs;
112  }
113 
114  if (rhs.operation() == op) {
115  d->m_subTerms << rhs.subTerms();
116  } else {
117  d->m_subTerms << rhs;
118  }
119 }
120 
121 Term::~Term()
122 {
123  delete d;
124 }
125 
126 bool Term::isValid() const
127 {
128  // Terms with an operator but no subterms are still valid
129  if (d->m_op != Term::None) {
130  return true;
131  }
132 
133  if (d->m_comp == Term::Auto) {
134  return false;
135  }
136 
137  return true;
138 }
139 
140 void Term::setNegation(bool isNegated)
141 {
142  d->m_isNegated = isNegated;
143 }
144 
145 bool Term::isNegated() const
146 {
147  return d->m_isNegated;
148 }
149 
150 bool Term::negated() const
151 {
152  return d->m_isNegated;
153 }
154 
155 void Term::addSubTerm(const Term& term)
156 {
157  d->m_subTerms << term;
158 }
159 
160 void Term::setSubTerms(const QList<Term>& terms)
161 {
162  d->m_subTerms = terms;
163 }
164 
165 Term Term::subTerm() const
166 {
167  if (!d->m_subTerms.isEmpty()) {
168  return d->m_subTerms.first();
169  }
170 
171  return Term();
172 }
173 
174 QList<Term> Term::subTerms() const
175 {
176  return d->m_subTerms;
177 }
178 
179 void Term::setOperation(Term::Operation op)
180 {
181  d->m_op = op;
182 }
183 
184 Term::Operation Term::operation() const
185 {
186  return d->m_op;
187 }
188 
189 bool Term::empty() const
190 {
191  return isEmpty();
192 }
193 
194 bool Term::isEmpty() const
195 {
196  return d->m_property.isEmpty() && d->m_value.isNull() && d->m_subTerms.isEmpty();
197 }
198 
199 QString Term::property() const
200 {
201  return d->m_property;
202 }
203 
204 void Term::setProperty(const QString& property)
205 {
206  d->m_property = property;
207 }
208 
209 void Term::setValue(const QVariant& value)
210 {
211  d->m_value = value;
212 }
213 
214 QVariant Term::value() const
215 {
216  return d->m_value;
217 }
218 
219 Term::Comparator Term::comparator() const
220 {
221  return d->m_comp;
222 }
223 
224 void Term::setComparator(Term::Comparator c)
225 {
226  d->m_comp = c;
227 }
228 
229 void Term::setUserData(const QString& name, const QVariant& value)
230 {
231  d->m_userData.insert(name, value);
232 }
233 
234 QVariant Term::userData(const QString& name) const
235 {
236  return d->m_userData.value(name);
237 }
238 
239 QVariantMap Term::toVariantMap() const
240 {
241  QVariantMap map;
242  if (d->m_op != None) {
243  QVariantList variantList;
244  for (const Term& term : std::as_const(d->m_subTerms)) {
245  variantList << QVariant(term.toVariantMap());
246  }
247 
248  if (d->m_op == And) {
249  map[QStringLiteral("$and")] = variantList;
250  } else {
251  map[QStringLiteral("$or")] = variantList;
252  }
253 
254  return map;
255  }
256 
257  QString op;
258  switch (d->m_comp) {
259  case Equal:
260  map[d->m_property] = d->m_value;
261  return map;
262 
263  case Contains:
264  op = QStringLiteral("$ct");
265  break;
266 
267  case Greater:
268  op = QStringLiteral("$gt");
269  break;
270 
271  case GreaterEqual:
272  op = QStringLiteral("$gte");
273  break;
274 
275  case Less:
276  op = QStringLiteral("$lt");
277  break;
278 
279  case LessEqual:
280  op = QStringLiteral("$lte");
281  break;
282 
283  case Auto:
284  Q_ASSERT(0);
285  }
286 
287  QVariantMap m;
288  m[op] = d->m_value;
289  map[d->m_property] = QVariant(m);
290 
291  return map;
292 }
293 
294 namespace {
295  // QJson does not recognize QDate/QDateTime parameters. We try to guess
296  // and see if they can be converted into date/datetime.
297  QVariant tryConvert(const QVariant& var) {
298  if (var.canConvert(QVariant::DateTime)) {
299  QDateTime dt = var.toDateTime();
300  if (!dt.isValid()) {
301  return var;
302  }
303 
304  if (!var.toString().contains(QLatin1Char('T'))) {
305  return QVariant(var.toDate());
306  }
307  return dt;
308  }
309  return var;
310  }
311 }
312 
313 Term Term::fromVariantMap(const QVariantMap& map)
314 {
315  if (map.size() != 1) {
316  return Term();
317  }
318 
319  Term term;
320 
321  QString andOrString;
322  if (map.contains(QLatin1String("$and"))) {
323  andOrString = QStringLiteral("$and");
324  term.setOperation(And);
325  }
326  else if (map.contains(QLatin1String("$or"))) {
327  andOrString = QStringLiteral("$or");
328  term.setOperation(Or);
329  }
330 
331  if (!andOrString.isEmpty()) {
332  QList<Term> subTerms;
333 
334  const QVariantList list = map[andOrString].toList();
335  for (const QVariant &var : list) {
336  subTerms << Term::fromVariantMap(var.toMap());
337  }
338 
339  term.setSubTerms(subTerms);
340  return term;
341  }
342 
343  QString prop = map.cbegin().key();
344  term.setProperty(prop);
345 
346  QVariant value = map.value(prop);
347  if (value.type() == QVariant::Map) {
348  QVariantMap mapVal = value.toMap();
349  if (mapVal.size() != 1) {
350  return term;
351  }
352 
353  QString op = mapVal.cbegin().key();
354  Term::Comparator com;
355  if (op == QLatin1String("$ct")) {
356  com = Contains;
357  } else if (op == QLatin1String("$gt")) {
358  com = Greater;
359  } else if (op == QLatin1String("$gte")) {
360  com = GreaterEqual;
361  } else if (op == QLatin1String("$lt")) {
362  com = Less;
363  } else if (op == QLatin1String("$lte")) {
364  com = LessEqual;
365  } else {
366  return term;
367  }
368 
369  term.setComparator(com);
370  term.setValue(tryConvert(mapVal.value(op)));
371 
372  return term;
373  }
374 
375  term.setComparator(Equal);
376  term.setValue(tryConvert(value));
377 
378  return term;
379 }
380 
381 bool Term::operator==(const Term& rhs) const
382 {
383  if (d->m_op != rhs.d->m_op || d->m_comp != rhs.d->m_comp ||
384  d->m_isNegated != rhs.d->m_isNegated || d->m_property != rhs.d->m_property ||
385  d->m_value != rhs.d->m_value)
386  {
387  return false;
388  }
389 
390  if (d->m_subTerms.size() != rhs.d->m_subTerms.size()) {
391  return false;
392  }
393 
394  if (d->m_subTerms.isEmpty()) {
395  return true;
396  }
397 
398  for (const Term& t : std::as_const(d->m_subTerms)) {
399  if (!rhs.d->m_subTerms.contains(t)) {
400  return false;
401  }
402  }
403 
404  return true;
405 }
406 
407 Term& Term::operator=(const Term& rhs)
408 {
409  *d = *rhs.d;
410  return *this;
411 }
412 
413 char *toString(const Term& term)
414 {
415  QString buffer;
416  QDebug stream(&buffer);
417  stream << term;
418  return qstrdup(buffer.toUtf8().constData());
419 }
420 
421 } // namespace Baloo
422 
423 namespace {
424  QString comparatorToString(Baloo::Term::Comparator c) {
425  switch (c) {
426  case Baloo::Term::Auto:
427  return QStringLiteral("Auto");
428  case Baloo::Term::Equal:
429  return QStringLiteral("=");
430  case Baloo::Term::Contains:
431  return QStringLiteral(":");
432  case Baloo::Term::Less:
433  return QStringLiteral("<");
434  case Baloo::Term::LessEqual:
435  return QStringLiteral("<=");
436  case Baloo::Term::Greater:
437  return QStringLiteral(">");
438  case Baloo::Term::GreaterEqual:
439  return QStringLiteral(">=");
440  }
441 
442  return QString();
443  }
444 
445  QString operationToString(Baloo::Term::Operation op) {
446  switch (op) {
447  case Baloo::Term::None:
448  return QStringLiteral("NONE");
449  case Baloo::Term::And:
450  return QStringLiteral("AND");
451  case Baloo::Term::Or:
452  return QStringLiteral("OR");
453  }
454 
455  return QString();
456  }
457 }
458 
459 QDebug operator <<(QDebug d, const Baloo::Term& t)
460 {
461  QDebugStateSaver saver(d);
462  d.noquote().nospace();
463  if (t.subTerms().isEmpty()) {
464  if (!t.property().isEmpty()) {
465  d << t.property();
466  }
467  d << comparatorToString(t.comparator());
468  if (t.value().type() == QVariant::String) {
469  d << QLatin1Char('"') << t.value().toString() << QLatin1Char('"');
470  } else {
471  d << t.value().typeName() << QLatin1Char('(')
472  << t.value().toString() << QLatin1Char(')');
473  }
474  }
475  else {
476  d << "[" << operationToString(t.operation());
477  const auto subTerms = t.subTerms();
478  for (const Baloo::Term& term : subTerms) {
479  d << QLatin1Char(' ') << term;
480  }
481  d << "]";
482  }
483  return d;
484 }
T & first()
T value() const const
QDebug & nospace()
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QDate toDate() const const
QDebug & noquote()
QVariant::Type type() const const
Implements storage for docIds without any associated data Instantiated for:
Definition: coding.cpp:11
bool isEmpty() const const
QByteArray toUtf8() const const
QMap< QString, QVariant > toMap() const const
bool canConvert(int targetTypeId) const const
QDateTime toDateTime() const const
const char * constData() const const
QString::const_iterator cbegin() const const
bool isValid() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QFuture< void > map(Sequence &sequence, MapFunctor function)
char * toString(const EngineQuery &query)
Helper for QTest.
Definition: enginequery.h:91
QString toString() const const
QVariant property(const char *name) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Dec 5 2023 04:11:50 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.