File indexing completed on 2024-11-10 04:40:44
0001 /* 0002 SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "searchquery.h" 0008 #include "akonadicore_debug.h" 0009 0010 #include <QJsonDocument> 0011 #include <QJsonObject> 0012 #include <QVariant> 0013 0014 using namespace Akonadi; 0015 0016 class Akonadi::SearchTermPrivate : public QSharedData 0017 { 0018 public: 0019 bool operator==(const SearchTermPrivate &other) const 0020 { 0021 return relation == other.relation && isNegated == other.isNegated && terms == other.terms && key == other.key && value == other.value 0022 && condition == other.condition; 0023 } 0024 0025 QString key; 0026 QVariant value; 0027 SearchTerm::Condition condition = SearchTerm::CondEqual; 0028 SearchTerm::Relation relation = SearchTerm::RelAnd; 0029 QList<SearchTerm> terms; 0030 bool isNegated = false; 0031 }; 0032 0033 class Akonadi::SearchQueryPrivate : public QSharedData 0034 { 0035 public: 0036 bool operator==(const SearchQueryPrivate &other) const 0037 { 0038 return rootTerm == other.rootTerm && limit == other.limit; 0039 } 0040 0041 static QVariantMap termToJSON(const SearchTerm &term) 0042 { 0043 const QList<SearchTerm> &subTerms = term.subTerms(); 0044 QVariantMap termJSON; 0045 termJSON.insert(QStringLiteral("negated"), term.isNegated()); 0046 if (subTerms.isEmpty()) { 0047 if (!term.isNull()) { 0048 termJSON.insert(QStringLiteral("key"), term.key()); 0049 termJSON.insert(QStringLiteral("value"), term.value()); 0050 termJSON.insert(QStringLiteral("cond"), static_cast<int>(term.condition())); 0051 } 0052 } else { 0053 termJSON.insert(QStringLiteral("rel"), static_cast<int>(term.relation())); 0054 QVariantList subTermsJSON; 0055 subTermsJSON.reserve(subTerms.count()); 0056 for (const SearchTerm &term : std::as_const(subTerms)) { 0057 subTermsJSON.append(termToJSON(term)); 0058 } 0059 termJSON.insert(QStringLiteral("subTerms"), subTermsJSON); 0060 } 0061 0062 return termJSON; 0063 } 0064 0065 static SearchTerm JSONToTerm(const QVariantMap &map) 0066 { 0067 if (map.isEmpty()) { 0068 return SearchTerm(); 0069 } else if (map.contains(QStringLiteral("key"))) { 0070 SearchTerm term(map[QStringLiteral("key")].toString(), 0071 map[QStringLiteral("value")], 0072 static_cast<SearchTerm::Condition>(map[QStringLiteral("cond")].toInt())); 0073 term.setIsNegated(map[QStringLiteral("negated")].toBool()); 0074 return term; 0075 } else if (map.contains(QStringLiteral("rel"))) { 0076 SearchTerm term(static_cast<SearchTerm::Relation>(map[QStringLiteral("rel")].toInt())); 0077 term.setIsNegated(map[QStringLiteral("negated")].toBool()); 0078 const QList<QVariant> list = map[QStringLiteral("subTerms")].toList(); 0079 for (const QVariant &var : list) { 0080 term.addSubTerm(JSONToTerm(var.toMap())); 0081 } 0082 return term; 0083 } else { 0084 qCWarning(AKONADICORE_LOG) << "Invalid JSON for term: " << map; 0085 return SearchTerm(); 0086 } 0087 } 0088 0089 SearchTerm rootTerm; 0090 int limit = -1; 0091 }; 0092 0093 SearchTerm::SearchTerm(SearchTerm::Relation relation) 0094 : d(new SearchTermPrivate) 0095 { 0096 d->relation = relation; 0097 } 0098 0099 SearchTerm::SearchTerm(const QString &key, const QVariant &value, SearchTerm::Condition condition) 0100 : d(new SearchTermPrivate) 0101 { 0102 d->relation = RelAnd; 0103 d->key = key; 0104 d->value = value; 0105 d->condition = condition; 0106 } 0107 0108 SearchTerm::SearchTerm(const SearchTerm &other) 0109 : d(other.d) 0110 { 0111 } 0112 0113 SearchTerm::~SearchTerm() = default; 0114 0115 SearchTerm &SearchTerm::operator=(const SearchTerm &other) 0116 { 0117 d = other.d; 0118 return *this; 0119 } 0120 0121 bool SearchTerm::operator==(const SearchTerm &other) const 0122 { 0123 return *d == *other.d; 0124 } 0125 0126 bool SearchTerm::isNull() const 0127 { 0128 return d->key.isEmpty() && d->value.isNull() && d->terms.isEmpty(); 0129 } 0130 0131 QString SearchTerm::key() const 0132 { 0133 return d->key; 0134 } 0135 0136 QVariant SearchTerm::value() const 0137 { 0138 return d->value; 0139 } 0140 0141 SearchTerm::Condition SearchTerm::condition() const 0142 { 0143 return d->condition; 0144 } 0145 0146 void SearchTerm::setIsNegated(bool negated) 0147 { 0148 d->isNegated = negated; 0149 } 0150 0151 bool SearchTerm::isNegated() const 0152 { 0153 return d->isNegated; 0154 } 0155 0156 void SearchTerm::addSubTerm(const SearchTerm &term) 0157 { 0158 d->terms << term; 0159 } 0160 0161 QList<SearchTerm> SearchTerm::subTerms() const 0162 { 0163 return d->terms; 0164 } 0165 0166 SearchTerm::Relation SearchTerm::relation() const 0167 { 0168 return d->relation; 0169 } 0170 0171 SearchQuery::SearchQuery(SearchTerm::Relation rel) 0172 : d(new SearchQueryPrivate) 0173 { 0174 d->rootTerm = SearchTerm(rel); 0175 } 0176 0177 SearchQuery::SearchQuery(const SearchQuery &other) 0178 : d(other.d) 0179 { 0180 } 0181 0182 SearchQuery::~SearchQuery() 0183 { 0184 } 0185 0186 SearchQuery &SearchQuery::operator=(const SearchQuery &other) 0187 { 0188 d = other.d; 0189 return *this; 0190 } 0191 0192 bool SearchQuery::operator==(const SearchQuery &other) const 0193 { 0194 return *d == *other.d; 0195 } 0196 0197 bool SearchQuery::isNull() const 0198 { 0199 return d->rootTerm.isNull(); 0200 } 0201 0202 SearchTerm SearchQuery::term() const 0203 { 0204 return d->rootTerm; 0205 } 0206 0207 void SearchQuery::addTerm(const QString &key, const QVariant &value, SearchTerm::Condition condition) 0208 { 0209 addTerm(SearchTerm(key, value, condition)); 0210 } 0211 0212 void SearchQuery::addTerm(const SearchTerm &term) 0213 { 0214 d->rootTerm.addSubTerm(term); 0215 } 0216 0217 void SearchQuery::setTerm(const SearchTerm &term) 0218 { 0219 d->rootTerm = term; 0220 } 0221 0222 void SearchQuery::setLimit(int limit) 0223 { 0224 d->limit = limit; 0225 } 0226 0227 int SearchQuery::limit() const 0228 { 0229 return d->limit; 0230 } 0231 0232 QByteArray SearchQuery::toJSON() const 0233 { 0234 QVariantMap root; 0235 if (!d->rootTerm.isNull()) { 0236 root = SearchQueryPrivate::termToJSON(d->rootTerm); 0237 root.insert(QStringLiteral("limit"), d->limit); 0238 } 0239 0240 QJsonObject jo = QJsonObject::fromVariantMap(root); 0241 QJsonDocument jdoc; 0242 jdoc.setObject(jo); 0243 return jdoc.toJson(); 0244 } 0245 0246 SearchQuery SearchQuery::fromJSON(const QByteArray &jsonData) 0247 { 0248 QJsonParseError error; 0249 const QJsonDocument json = QJsonDocument::fromJson(jsonData, &error); 0250 if (error.error != QJsonParseError::NoError || json.isNull()) { 0251 return SearchQuery(); 0252 } 0253 0254 SearchQuery query; 0255 const QJsonObject obj = json.object(); 0256 query.d->rootTerm = SearchQueryPrivate::JSONToTerm(obj.toVariantMap()); 0257 if (obj.contains(QLatin1StringView("limit"))) { 0258 query.d->limit = obj.value(QStringLiteral("limit")).toInt(); 0259 } 0260 return query; 0261 } 0262 0263 static QMap<EmailSearchTerm::EmailSearchField, QString> emailSearchFieldMapping() 0264 { 0265 static QMap<EmailSearchTerm::EmailSearchField, QString> mapping; 0266 if (mapping.isEmpty()) { 0267 mapping.insert(EmailSearchTerm::Body, QStringLiteral("body")); 0268 mapping.insert(EmailSearchTerm::Headers, QStringLiteral("headers")); 0269 mapping.insert(EmailSearchTerm::Subject, QStringLiteral("subject")); 0270 mapping.insert(EmailSearchTerm::Message, QStringLiteral("message")); 0271 mapping.insert(EmailSearchTerm::HeaderFrom, QStringLiteral("from")); 0272 mapping.insert(EmailSearchTerm::HeaderTo, QStringLiteral("to")); 0273 mapping.insert(EmailSearchTerm::HeaderCC, QStringLiteral("cc")); 0274 mapping.insert(EmailSearchTerm::HeaderBCC, QStringLiteral("bcc")); 0275 mapping.insert(EmailSearchTerm::HeaderReplyTo, QStringLiteral("replyto")); 0276 mapping.insert(EmailSearchTerm::HeaderOrganization, QStringLiteral("organization")); 0277 mapping.insert(EmailSearchTerm::HeaderListId, QStringLiteral("listid")); 0278 mapping.insert(EmailSearchTerm::HeaderResentFrom, QStringLiteral("resentfrom")); 0279 mapping.insert(EmailSearchTerm::HeaderXLoop, QStringLiteral("xloop")); 0280 mapping.insert(EmailSearchTerm::HeaderXMailingList, QStringLiteral("xmailinglist")); 0281 mapping.insert(EmailSearchTerm::HeaderXSpamFlag, QStringLiteral("xspamflag")); 0282 mapping.insert(EmailSearchTerm::HeaderDate, QStringLiteral("date")); 0283 mapping.insert(EmailSearchTerm::HeaderOnlyDate, QStringLiteral("onlydate")); 0284 mapping.insert(EmailSearchTerm::MessageStatus, QStringLiteral("messagestatus")); 0285 mapping.insert(EmailSearchTerm::MessageTag, QStringLiteral("messagetag")); 0286 mapping.insert(EmailSearchTerm::ByteSize, QStringLiteral("size")); 0287 mapping.insert(EmailSearchTerm::Attachment, QStringLiteral("attachment")); 0288 } 0289 0290 return mapping; 0291 } 0292 0293 EmailSearchTerm::EmailSearchTerm(EmailSearchTerm::EmailSearchField field, const QVariant &value, SearchTerm::Condition condition) 0294 : SearchTerm(toKey(field), value, condition) 0295 { 0296 } 0297 0298 QString EmailSearchTerm::toKey(EmailSearchTerm::EmailSearchField field) 0299 { 0300 return emailSearchFieldMapping().value(field); 0301 } 0302 0303 EmailSearchTerm::EmailSearchField EmailSearchTerm::fromKey(const QString &key) 0304 { 0305 return emailSearchFieldMapping().key(key); 0306 } 0307 0308 static QMap<ContactSearchTerm::ContactSearchField, QString> contactSearchFieldMapping() 0309 { 0310 static QMap<ContactSearchTerm::ContactSearchField, QString> mapping; 0311 if (mapping.isEmpty()) { 0312 mapping.insert(ContactSearchTerm::Name, QStringLiteral("name")); 0313 mapping.insert(ContactSearchTerm::Nickname, QStringLiteral("nickname")); 0314 mapping.insert(ContactSearchTerm::Email, QStringLiteral("email")); 0315 mapping.insert(ContactSearchTerm::Uid, QStringLiteral("uid")); 0316 mapping.insert(ContactSearchTerm::All, QStringLiteral("all")); 0317 } 0318 return mapping; 0319 } 0320 0321 ContactSearchTerm::ContactSearchTerm(ContactSearchTerm::ContactSearchField field, const QVariant &value, SearchTerm::Condition condition) 0322 : SearchTerm(toKey(field), value, condition) 0323 { 0324 } 0325 0326 QString ContactSearchTerm::toKey(ContactSearchTerm::ContactSearchField field) 0327 { 0328 return contactSearchFieldMapping().value(field); 0329 } 0330 0331 ContactSearchTerm::ContactSearchField ContactSearchTerm::fromKey(const QString &key) 0332 { 0333 return contactSearchFieldMapping().key(key); 0334 } 0335 0336 QMap<IncidenceSearchTerm::IncidenceSearchField, QString> incidenceSearchFieldMapping() 0337 { 0338 static QMap<IncidenceSearchTerm::IncidenceSearchField, QString> mapping; 0339 if (mapping.isEmpty()) { 0340 mapping.insert(IncidenceSearchTerm::All, QStringLiteral("all")); 0341 mapping.insert(IncidenceSearchTerm::PartStatus, QStringLiteral("partstatus")); 0342 mapping.insert(IncidenceSearchTerm::Organizer, QStringLiteral("organizer")); 0343 mapping.insert(IncidenceSearchTerm::Summary, QStringLiteral("summary")); 0344 mapping.insert(IncidenceSearchTerm::Location, QStringLiteral("location")); 0345 } 0346 return mapping; 0347 } 0348 0349 IncidenceSearchTerm::IncidenceSearchTerm(IncidenceSearchTerm::IncidenceSearchField field, const QVariant &value, SearchTerm::Condition condition) 0350 : SearchTerm(toKey(field), value, condition) 0351 { 0352 } 0353 0354 QString IncidenceSearchTerm::toKey(IncidenceSearchTerm::IncidenceSearchField field) 0355 { 0356 return incidenceSearchFieldMapping().value(field); 0357 } 0358 0359 IncidenceSearchTerm::IncidenceSearchField IncidenceSearchTerm::fromKey(const QString &key) 0360 { 0361 return incidenceSearchFieldMapping().key(key); 0362 }