File indexing completed on 2024-04-28 03:51:52
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.typeId() == QMetaType::QString) { 0052 d->m_comp = Contains; 0053 } else if (value.typeId() == QMetaType::QDateTime) { 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() = default; 0122 0123 bool Term::isValid() const 0124 { 0125 // Terms with an operator but no subterms are still valid 0126 if (d->m_op != Term::None) { 0127 return true; 0128 } 0129 0130 if (d->m_comp == Term::Auto) { 0131 return false; 0132 } 0133 0134 return true; 0135 } 0136 0137 void Term::setNegation(bool isNegated) 0138 { 0139 d->m_isNegated = isNegated; 0140 } 0141 0142 bool Term::isNegated() const 0143 { 0144 return d->m_isNegated; 0145 } 0146 0147 bool Term::negated() const 0148 { 0149 return d->m_isNegated; 0150 } 0151 0152 void Term::addSubTerm(const Term& term) 0153 { 0154 d->m_subTerms << term; 0155 } 0156 0157 void Term::setSubTerms(const QList<Term>& terms) 0158 { 0159 d->m_subTerms = terms; 0160 } 0161 0162 Term Term::subTerm() const 0163 { 0164 if (!d->m_subTerms.isEmpty()) { 0165 return d->m_subTerms.first(); 0166 } 0167 0168 return Term(); 0169 } 0170 0171 QList<Term> Term::subTerms() const 0172 { 0173 return d->m_subTerms; 0174 } 0175 0176 void Term::setOperation(Term::Operation op) 0177 { 0178 d->m_op = op; 0179 } 0180 0181 Term::Operation Term::operation() const 0182 { 0183 return d->m_op; 0184 } 0185 0186 bool Term::empty() const 0187 { 0188 return isEmpty(); 0189 } 0190 0191 bool Term::isEmpty() const 0192 { 0193 return d->m_property.isEmpty() && d->m_value.isNull() && d->m_subTerms.isEmpty(); 0194 } 0195 0196 QString Term::property() const 0197 { 0198 return d->m_property; 0199 } 0200 0201 void Term::setProperty(const QString& property) 0202 { 0203 d->m_property = property; 0204 } 0205 0206 void Term::setValue(const QVariant& value) 0207 { 0208 d->m_value = value; 0209 } 0210 0211 QVariant Term::value() const 0212 { 0213 return d->m_value; 0214 } 0215 0216 Term::Comparator Term::comparator() const 0217 { 0218 return d->m_comp; 0219 } 0220 0221 void Term::setComparator(Term::Comparator c) 0222 { 0223 d->m_comp = c; 0224 } 0225 0226 void Term::setUserData(const QString& name, const QVariant& value) 0227 { 0228 d->m_userData.insert(name, value); 0229 } 0230 0231 QVariant Term::userData(const QString& name) const 0232 { 0233 return d->m_userData.value(name); 0234 } 0235 0236 QVariantMap Term::toVariantMap() const 0237 { 0238 QVariantMap map; 0239 if (d->m_op != None) { 0240 QVariantList variantList; 0241 for (const Term& term : std::as_const(d->m_subTerms)) { 0242 variantList << QVariant(term.toVariantMap()); 0243 } 0244 0245 if (d->m_op == And) { 0246 map[QStringLiteral("$and")] = variantList; 0247 } else { 0248 map[QStringLiteral("$or")] = variantList; 0249 } 0250 0251 return map; 0252 } 0253 0254 QString op; 0255 switch (d->m_comp) { 0256 case Equal: 0257 map[d->m_property] = d->m_value; 0258 return map; 0259 0260 case Contains: 0261 op = QStringLiteral("$ct"); 0262 break; 0263 0264 case Greater: 0265 op = QStringLiteral("$gt"); 0266 break; 0267 0268 case GreaterEqual: 0269 op = QStringLiteral("$gte"); 0270 break; 0271 0272 case Less: 0273 op = QStringLiteral("$lt"); 0274 break; 0275 0276 case LessEqual: 0277 op = QStringLiteral("$lte"); 0278 break; 0279 0280 case Auto: 0281 Q_ASSERT(0); 0282 } 0283 0284 QVariantMap m; 0285 m[op] = d->m_value; 0286 map[d->m_property] = QVariant(m); 0287 0288 return map; 0289 } 0290 0291 namespace { 0292 // QJson does not recognize QDate/QDateTime parameters. We try to guess 0293 // and see if they can be converted into date/datetime. 0294 QVariant tryConvert(const QVariant& var) { 0295 if (var.canConvert<QDateTime>()) { 0296 QDateTime dt = var.toDateTime(); 0297 if (!dt.isValid()) { 0298 return var; 0299 } 0300 0301 if (!var.toString().contains(QLatin1Char('T'))) { 0302 return QVariant(var.toDate()); 0303 } 0304 return dt; 0305 } 0306 return var; 0307 } 0308 } 0309 0310 Term Term::fromVariantMap(const QVariantMap& map) 0311 { 0312 if (map.size() != 1) { 0313 return Term(); 0314 } 0315 0316 Term term; 0317 0318 QString andOrString; 0319 if (map.contains(QLatin1String("$and"))) { 0320 andOrString = QStringLiteral("$and"); 0321 term.setOperation(And); 0322 } 0323 else if (map.contains(QLatin1String("$or"))) { 0324 andOrString = QStringLiteral("$or"); 0325 term.setOperation(Or); 0326 } 0327 0328 if (!andOrString.isEmpty()) { 0329 QList<Term> subTerms; 0330 0331 const QVariantList list = map[andOrString].toList(); 0332 for (const QVariant &var : list) { 0333 subTerms << Term::fromVariantMap(var.toMap()); 0334 } 0335 0336 term.setSubTerms(subTerms); 0337 return term; 0338 } 0339 0340 QString prop = map.cbegin().key(); 0341 term.setProperty(prop); 0342 0343 QVariant value = map.value(prop); 0344 if (value.typeId() == QMetaType::QVariantMap) { 0345 QVariantMap mapVal = value.toMap(); 0346 if (mapVal.size() != 1) { 0347 return term; 0348 } 0349 0350 QString op = mapVal.cbegin().key(); 0351 Term::Comparator com; 0352 if (op == QLatin1String("$ct")) { 0353 com = Contains; 0354 } else if (op == QLatin1String("$gt")) { 0355 com = Greater; 0356 } else if (op == QLatin1String("$gte")) { 0357 com = GreaterEqual; 0358 } else if (op == QLatin1String("$lt")) { 0359 com = Less; 0360 } else if (op == QLatin1String("$lte")) { 0361 com = LessEqual; 0362 } else { 0363 return term; 0364 } 0365 0366 term.setComparator(com); 0367 term.setValue(tryConvert(mapVal.value(op))); 0368 0369 return term; 0370 } 0371 0372 term.setComparator(Equal); 0373 term.setValue(tryConvert(value)); 0374 0375 return term; 0376 } 0377 0378 bool Term::operator==(const Term& rhs) const 0379 { 0380 if (d->m_op != rhs.d->m_op || d->m_comp != rhs.d->m_comp || 0381 d->m_isNegated != rhs.d->m_isNegated || d->m_property != rhs.d->m_property || 0382 d->m_value != rhs.d->m_value) 0383 { 0384 return false; 0385 } 0386 0387 if (d->m_subTerms.size() != rhs.d->m_subTerms.size()) { 0388 return false; 0389 } 0390 0391 if (d->m_subTerms.isEmpty()) { 0392 return true; 0393 } 0394 0395 for (const Term& t : std::as_const(d->m_subTerms)) { 0396 if (!rhs.d->m_subTerms.contains(t)) { 0397 return false; 0398 } 0399 } 0400 0401 return true; 0402 } 0403 0404 Term& Term::operator=(const Term& rhs) 0405 { 0406 *d = *rhs.d; 0407 return *this; 0408 } 0409 0410 char *toString(const Term& term) 0411 { 0412 QString buffer; 0413 QDebug stream(&buffer); 0414 stream << term; 0415 return qstrdup(buffer.toUtf8().constData()); 0416 } 0417 0418 } // namespace Baloo 0419 0420 namespace { 0421 QString comparatorToString(Baloo::Term::Comparator c) { 0422 switch (c) { 0423 case Baloo::Term::Auto: 0424 return QStringLiteral("Auto"); 0425 case Baloo::Term::Equal: 0426 return QStringLiteral("="); 0427 case Baloo::Term::Contains: 0428 return QStringLiteral(":"); 0429 case Baloo::Term::Less: 0430 return QStringLiteral("<"); 0431 case Baloo::Term::LessEqual: 0432 return QStringLiteral("<="); 0433 case Baloo::Term::Greater: 0434 return QStringLiteral(">"); 0435 case Baloo::Term::GreaterEqual: 0436 return QStringLiteral(">="); 0437 } 0438 0439 return QString(); 0440 } 0441 0442 QString operationToString(Baloo::Term::Operation op) { 0443 switch (op) { 0444 case Baloo::Term::None: 0445 return QStringLiteral("NONE"); 0446 case Baloo::Term::And: 0447 return QStringLiteral("AND"); 0448 case Baloo::Term::Or: 0449 return QStringLiteral("OR"); 0450 } 0451 0452 return QString(); 0453 } 0454 } 0455 0456 QDebug operator <<(QDebug d, const Baloo::Term& t) 0457 { 0458 QDebugStateSaver saver(d); 0459 d.noquote().nospace(); 0460 if (t.subTerms().isEmpty()) { 0461 if (!t.property().isEmpty()) { 0462 d << t.property(); 0463 } 0464 d << comparatorToString(t.comparator()); 0465 if (t.value().typeId() == QMetaType::QString) { 0466 d << QLatin1Char('"') << t.value().toString() << QLatin1Char('"'); 0467 } else { 0468 d << t.value().typeName() << QLatin1Char('(') 0469 << t.value().toString() << QLatin1Char(')'); 0470 } 0471 } 0472 else { 0473 d << "[" << operationToString(t.operation()); 0474 const auto subTerms = t.subTerms(); 0475 for (const Baloo::Term& term : subTerms) { 0476 d << QLatin1Char(' ') << term; 0477 } 0478 d << "]"; 0479 } 0480 return d; 0481 }