File indexing completed on 2024-04-28 15:17:41
0001 /* 0002 This file is part of the KDE Baloo Project 0003 SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "term.h" 0009 0010 #include <QDateTime> 0011 0012 namespace Baloo { 0013 0014 class Baloo::Term::Private { 0015 public: 0016 Operation m_op = None; 0017 Comparator m_comp = Auto; 0018 0019 QString m_property; 0020 QVariant m_value; 0021 0022 bool m_isNegated = false; 0023 0024 QList<Term> m_subTerms; 0025 QVariantHash m_userData; 0026 }; 0027 0028 Term::Term() 0029 : d(new Private) 0030 { 0031 } 0032 0033 Term::Term(const Term& t) 0034 : d(new Private(*t.d)) 0035 { 0036 } 0037 0038 Term::Term(const QString& property) 0039 : d(new Private) 0040 { 0041 d->m_property = property; 0042 } 0043 0044 Term::Term(const QString& property, const QVariant& value, Term::Comparator c) 0045 : d(new Private) 0046 { 0047 d->m_property = property; 0048 d->m_value = value; 0049 0050 if (c == Auto) { 0051 if (value.type() == QVariant::String) { 0052 d->m_comp = Contains; 0053 } else if (value.type() == QVariant::DateTime) { 0054 d->m_comp = Contains; 0055 } else { 0056 d->m_comp = Equal; 0057 } 0058 } 0059 else { 0060 d->m_comp = c; 0061 } 0062 } 0063 0064 /* 0065 Term::Term(const QString& property, const QVariant& start, const QVariant& end) 0066 : d(new Private) 0067 { 0068 d->m_property = property; 0069 d->m_op = Range; 0070 0071 // FIXME: How to save range queries? 0072 } 0073 */ 0074 0075 Term::Term(Term::Operation op) 0076 : d(new Private) 0077 { 0078 d->m_op = op; 0079 } 0080 0081 Term::Term(Term::Operation op, const Term& t) 0082 : d(new Private) 0083 { 0084 d->m_op = op; 0085 d->m_subTerms << t; 0086 } 0087 0088 Term::Term(Term::Operation op, const QList<Term>& t) 0089 : d(new Private) 0090 { 0091 d->m_op = op; 0092 d->m_subTerms = t; 0093 } 0094 0095 Term::Term(const Term& lhs, Term::Operation op, const Term& rhs) 0096 : d(new Private) 0097 { 0098 d->m_op = op; 0099 if (lhs.isEmpty()) { 0100 *d = *(rhs.d); 0101 return; 0102 } 0103 if (rhs.isEmpty()) { 0104 *d = *(lhs.d); 0105 return; 0106 } 0107 0108 if (lhs.operation() == op) { 0109 d->m_subTerms << lhs.subTerms(); 0110 } else { 0111 d->m_subTerms << lhs; 0112 } 0113 0114 if (rhs.operation() == op) { 0115 d->m_subTerms << rhs.subTerms(); 0116 } else { 0117 d->m_subTerms << rhs; 0118 } 0119 } 0120 0121 Term::~Term() 0122 { 0123 delete d; 0124 } 0125 0126 bool Term::isValid() const 0127 { 0128 // Terms with an operator but no subterms are still valid 0129 if (d->m_op != Term::None) { 0130 return true; 0131 } 0132 0133 if (d->m_comp == Term::Auto) { 0134 return false; 0135 } 0136 0137 return true; 0138 } 0139 0140 void Term::setNegation(bool isNegated) 0141 { 0142 d->m_isNegated = isNegated; 0143 } 0144 0145 bool Term::isNegated() const 0146 { 0147 return d->m_isNegated; 0148 } 0149 0150 bool Term::negated() const 0151 { 0152 return d->m_isNegated; 0153 } 0154 0155 void Term::addSubTerm(const Term& term) 0156 { 0157 d->m_subTerms << term; 0158 } 0159 0160 void Term::setSubTerms(const QList<Term>& terms) 0161 { 0162 d->m_subTerms = terms; 0163 } 0164 0165 Term Term::subTerm() const 0166 { 0167 if (!d->m_subTerms.isEmpty()) { 0168 return d->m_subTerms.first(); 0169 } 0170 0171 return Term(); 0172 } 0173 0174 QList<Term> Term::subTerms() const 0175 { 0176 return d->m_subTerms; 0177 } 0178 0179 void Term::setOperation(Term::Operation op) 0180 { 0181 d->m_op = op; 0182 } 0183 0184 Term::Operation Term::operation() const 0185 { 0186 return d->m_op; 0187 } 0188 0189 bool Term::empty() const 0190 { 0191 return isEmpty(); 0192 } 0193 0194 bool Term::isEmpty() const 0195 { 0196 return d->m_property.isEmpty() && d->m_value.isNull() && d->m_subTerms.isEmpty(); 0197 } 0198 0199 QString Term::property() const 0200 { 0201 return d->m_property; 0202 } 0203 0204 void Term::setProperty(const QString& property) 0205 { 0206 d->m_property = property; 0207 } 0208 0209 void Term::setValue(const QVariant& value) 0210 { 0211 d->m_value = value; 0212 } 0213 0214 QVariant Term::value() const 0215 { 0216 return d->m_value; 0217 } 0218 0219 Term::Comparator Term::comparator() const 0220 { 0221 return d->m_comp; 0222 } 0223 0224 void Term::setComparator(Term::Comparator c) 0225 { 0226 d->m_comp = c; 0227 } 0228 0229 void Term::setUserData(const QString& name, const QVariant& value) 0230 { 0231 d->m_userData.insert(name, value); 0232 } 0233 0234 QVariant Term::userData(const QString& name) const 0235 { 0236 return d->m_userData.value(name); 0237 } 0238 0239 QVariantMap Term::toVariantMap() const 0240 { 0241 QVariantMap map; 0242 if (d->m_op != None) { 0243 QVariantList variantList; 0244 for (const Term& term : std::as_const(d->m_subTerms)) { 0245 variantList << QVariant(term.toVariantMap()); 0246 } 0247 0248 if (d->m_op == And) { 0249 map[QStringLiteral("$and")] = variantList; 0250 } else { 0251 map[QStringLiteral("$or")] = variantList; 0252 } 0253 0254 return map; 0255 } 0256 0257 QString op; 0258 switch (d->m_comp) { 0259 case Equal: 0260 map[d->m_property] = d->m_value; 0261 return map; 0262 0263 case Contains: 0264 op = QStringLiteral("$ct"); 0265 break; 0266 0267 case Greater: 0268 op = QStringLiteral("$gt"); 0269 break; 0270 0271 case GreaterEqual: 0272 op = QStringLiteral("$gte"); 0273 break; 0274 0275 case Less: 0276 op = QStringLiteral("$lt"); 0277 break; 0278 0279 case LessEqual: 0280 op = QStringLiteral("$lte"); 0281 break; 0282 0283 case Auto: 0284 Q_ASSERT(0); 0285 } 0286 0287 QVariantMap m; 0288 m[op] = d->m_value; 0289 map[d->m_property] = QVariant(m); 0290 0291 return map; 0292 } 0293 0294 namespace { 0295 // QJson does not recognize QDate/QDateTime parameters. We try to guess 0296 // and see if they can be converted into date/datetime. 0297 QVariant tryConvert(const QVariant& var) { 0298 if (var.canConvert(QVariant::DateTime)) { 0299 QDateTime dt = var.toDateTime(); 0300 if (!dt.isValid()) { 0301 return var; 0302 } 0303 0304 if (!var.toString().contains(QLatin1Char('T'))) { 0305 return QVariant(var.toDate()); 0306 } 0307 return dt; 0308 } 0309 return var; 0310 } 0311 } 0312 0313 Term Term::fromVariantMap(const QVariantMap& map) 0314 { 0315 if (map.size() != 1) { 0316 return Term(); 0317 } 0318 0319 Term term; 0320 0321 QString andOrString; 0322 if (map.contains(QLatin1String("$and"))) { 0323 andOrString = QStringLiteral("$and"); 0324 term.setOperation(And); 0325 } 0326 else if (map.contains(QLatin1String("$or"))) { 0327 andOrString = QStringLiteral("$or"); 0328 term.setOperation(Or); 0329 } 0330 0331 if (!andOrString.isEmpty()) { 0332 QList<Term> subTerms; 0333 0334 const QVariantList list = map[andOrString].toList(); 0335 for (const QVariant &var : list) { 0336 subTerms << Term::fromVariantMap(var.toMap()); 0337 } 0338 0339 term.setSubTerms(subTerms); 0340 return term; 0341 } 0342 0343 QString prop = map.cbegin().key(); 0344 term.setProperty(prop); 0345 0346 QVariant value = map.value(prop); 0347 if (value.type() == QVariant::Map) { 0348 QVariantMap mapVal = value.toMap(); 0349 if (mapVal.size() != 1) { 0350 return term; 0351 } 0352 0353 QString op = mapVal.cbegin().key(); 0354 Term::Comparator com; 0355 if (op == QLatin1String("$ct")) { 0356 com = Contains; 0357 } else if (op == QLatin1String("$gt")) { 0358 com = Greater; 0359 } else if (op == QLatin1String("$gte")) { 0360 com = GreaterEqual; 0361 } else if (op == QLatin1String("$lt")) { 0362 com = Less; 0363 } else if (op == QLatin1String("$lte")) { 0364 com = LessEqual; 0365 } else { 0366 return term; 0367 } 0368 0369 term.setComparator(com); 0370 term.setValue(tryConvert(mapVal.value(op))); 0371 0372 return term; 0373 } 0374 0375 term.setComparator(Equal); 0376 term.setValue(tryConvert(value)); 0377 0378 return term; 0379 } 0380 0381 bool Term::operator==(const Term& rhs) const 0382 { 0383 if (d->m_op != rhs.d->m_op || d->m_comp != rhs.d->m_comp || 0384 d->m_isNegated != rhs.d->m_isNegated || d->m_property != rhs.d->m_property || 0385 d->m_value != rhs.d->m_value) 0386 { 0387 return false; 0388 } 0389 0390 if (d->m_subTerms.size() != rhs.d->m_subTerms.size()) { 0391 return false; 0392 } 0393 0394 if (d->m_subTerms.isEmpty()) { 0395 return true; 0396 } 0397 0398 for (const Term& t : std::as_const(d->m_subTerms)) { 0399 if (!rhs.d->m_subTerms.contains(t)) { 0400 return false; 0401 } 0402 } 0403 0404 return true; 0405 } 0406 0407 Term& Term::operator=(const Term& rhs) 0408 { 0409 *d = *rhs.d; 0410 return *this; 0411 } 0412 0413 char *toString(const Term& term) 0414 { 0415 QString buffer; 0416 QDebug stream(&buffer); 0417 stream << term; 0418 return qstrdup(buffer.toUtf8().constData()); 0419 } 0420 0421 } // namespace Baloo 0422 0423 namespace { 0424 QString comparatorToString(Baloo::Term::Comparator c) { 0425 switch (c) { 0426 case Baloo::Term::Auto: 0427 return QStringLiteral("Auto"); 0428 case Baloo::Term::Equal: 0429 return QStringLiteral("="); 0430 case Baloo::Term::Contains: 0431 return QStringLiteral(":"); 0432 case Baloo::Term::Less: 0433 return QStringLiteral("<"); 0434 case Baloo::Term::LessEqual: 0435 return QStringLiteral("<="); 0436 case Baloo::Term::Greater: 0437 return QStringLiteral(">"); 0438 case Baloo::Term::GreaterEqual: 0439 return QStringLiteral(">="); 0440 } 0441 0442 return QString(); 0443 } 0444 0445 QString operationToString(Baloo::Term::Operation op) { 0446 switch (op) { 0447 case Baloo::Term::None: 0448 return QStringLiteral("NONE"); 0449 case Baloo::Term::And: 0450 return QStringLiteral("AND"); 0451 case Baloo::Term::Or: 0452 return QStringLiteral("OR"); 0453 } 0454 0455 return QString(); 0456 } 0457 } 0458 0459 QDebug operator <<(QDebug d, const Baloo::Term& t) 0460 { 0461 QDebugStateSaver saver(d); 0462 d.noquote().nospace(); 0463 if (t.subTerms().isEmpty()) { 0464 if (!t.property().isEmpty()) { 0465 d << t.property(); 0466 } 0467 d << comparatorToString(t.comparator()); 0468 if (t.value().type() == QVariant::String) { 0469 d << QLatin1Char('"') << t.value().toString() << QLatin1Char('"'); 0470 } else { 0471 d << t.value().typeName() << QLatin1Char('(') 0472 << t.value().toString() << QLatin1Char(')'); 0473 } 0474 } 0475 else { 0476 d << "[" << operationToString(t.operation()); 0477 const auto subTerms = t.subTerms(); 0478 for (const Baloo::Term& term : subTerms) { 0479 d << QLatin1Char(' ') << term; 0480 } 0481 d << "]"; 0482 } 0483 return d; 0484 }