File indexing completed on 2025-01-05 03:54:00

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2008-01-09
0007  * Description : Core database searches XML queries manager
0008  *
0009  * SPDX-FileCopyrightText: 2008-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0010  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #ifndef DIGIKAM_CORE_DB_SEARCH_XML_H
0017 #define DIGIKAM_CORE_DB_SEARCH_XML_H
0018 
0019 // Qt includes
0020 
0021 #include <QString>
0022 #include <QDateTime>
0023 #include <QXmlStreamReader>
0024 #include <QXmlStreamWriter>
0025 #include <QStringList>
0026 #include <QVariant>
0027 
0028 // Local includes
0029 
0030 #include "digikam_export.h"
0031 
0032 namespace Digikam
0033 {
0034 
0035 namespace SearchXml
0036 {
0037 
0038 enum Operator
0039 {
0040     And,
0041     Or,
0042     AndNot,
0043     OrNot
0044 };
0045 
0046 enum Element
0047 {
0048     Search,
0049     Group,
0050     GroupEnd,
0051     Field,
0052     FieldEnd,
0053     End
0054 };
0055 
0056 enum Relation
0057 {
0058     Equal,
0059     Unequal,
0060     Like,
0061     NotLike,
0062     LessThan,
0063     GreaterThan,
0064     LessThanOrEqual,
0065     GreaterThanOrEqual,
0066     Interval,          // [a,b]
0067     IntervalOpen,      // (a,b)
0068     OneOf,
0069     AllOf,
0070     InTree,
0071     NotInTree,
0072     Near,
0073     Inside
0074 };
0075 
0076 template <typename T>
0077 bool testRelation(T v1, T v2, Relation rel)
0078 {
0079     if      (rel == Equal)
0080     {
0081         return v1 == v2;
0082     }
0083     else if (rel == Unequal)
0084     {
0085         return v1 != v2;
0086     }
0087     else if (rel == LessThan)
0088     {
0089         return v1 < v2;
0090     }
0091     else if (rel == LessThanOrEqual)
0092     {
0093         return v1 <= v2;
0094     }
0095     else if (rel == GreaterThan)
0096     {
0097         return v1 > v2;
0098     }
0099     else if (rel == GreaterThanOrEqual)
0100     {
0101         return v1 >= v2;
0102     }
0103 
0104     return false;
0105 }
0106 
0107 /**
0108  * General default values for groupOperator() and defaultFieldOperator()
0109  */
0110 inline SearchXml::Operator standardGroupOperator()
0111 {
0112     return SearchXml::Or;
0113 }
0114 
0115 inline SearchXml::Operator standardFieldOperator()
0116 {
0117     return SearchXml::And;
0118 }
0119 
0120 inline SearchXml::Relation standardFieldRelation()
0121 {
0122     return SearchXml::Equal;
0123 }
0124 
0125 } // namespace SearchXml
0126 
0127 // ---------------------------------------------------------------------------------
0128 
0129 class DIGIKAM_DATABASE_EXPORT SearchXmlReader : public QXmlStreamReader
0130 {
0131 public:
0132 
0133     explicit SearchXmlReader(const QString& xml);
0134 
0135     /** Continue parsing the document. Returns the type of the current element.
0136      */
0137     SearchXml::Element  readNext();
0138 
0139     /** Returns if the current element is a group element (start or end element).
0140      */
0141     bool                isGroupElement() const;
0142 
0143     /** Returns if the current element is a field element (start or end element).
0144      */
0145     bool                isFieldElement() const;
0146 
0147     /** Returns the group operator. Only valid if the current element is a group.
0148      */
0149     SearchXml::Operator groupOperator() const;
0150 
0151     /** Returns the (optional) group caption. Only valid if the current element is a group.
0152      */
0153     QString             groupCaption() const;
0154 
0155     /** Returns the default field operator. This operator can be overridden by a specific fieldOperator().
0156      */
0157     SearchXml::Operator defaultFieldOperator() const;
0158 
0159     /** Returns the field attributes. Only valid if the current element is a field.
0160      *  fieldOperator returns the default operator if the field has not specified any.
0161      */
0162     SearchXml::Operator fieldOperator() const;
0163     QString             fieldName() const;
0164     SearchXml::Relation fieldRelation() const;
0165 
0166     /** Returns the field values. Only valid if the current element is a field.
0167      *  This reads to the end element of the field, and converts the found
0168      *  text/elements to the desired output.
0169      */
0170     QString             value();
0171     int                 valueToInt();
0172     qlonglong           valueToLongLong();
0173     double              valueToDouble();
0174     QDateTime           valueToDateTime();
0175     QList<int>          valueToIntList();
0176     QList<qlonglong>    valueToLongLongList();
0177     QList<double>       valueToDoubleList();
0178     QStringList         valueToStringList();
0179     QList<QDateTime>    valueToDateTimeList();
0180 
0181     QList<int>          valueToIntOrIntList();
0182     QList<double>       valueToDoubleOrDoubleList();
0183     QList<QString>      valueToStringOrStringList();
0184 
0185     /** General helper method: Reads XML a start element with the given
0186      *  name is found. The method goes to the next start element, and from
0187      *  there down the hierarchy, but not further up in the hierarchy.
0188      *  Returns false if the element is not found.
0189      */
0190     bool readToStartOfElement(const QString& name);
0191 
0192     /** General helper method: Reads XML until the end element of the current
0193         start element in reached.
0194      */
0195     void readToEndOfElement();
0196 
0197     /** General helper method: Reads XML until the first field
0198      *  of the next or first found group is reached.
0199      */
0200     void readToFirstField();
0201 
0202 protected:
0203 
0204     SearchXml::Operator readOperator(const QString&, SearchXml::Operator) const;
0205     SearchXml::Relation readRelation(const QString&, SearchXml::Relation) const;
0206 
0207 protected:
0208 
0209     SearchXml::Operator m_defaultFieldOperator;
0210 };
0211 
0212 // ---------------------------------------------------------------------------------
0213 
0214 class DIGIKAM_DATABASE_EXPORT SearchXmlWriter : public QXmlStreamWriter
0215 {
0216 public:
0217 
0218     /**
0219      *  Note that SearchXmlWriter and SearchXmlGroupWriter rely on you
0220      *  calling the methods following the restrictions set by the documentation;
0221      *  Otherwise you will not produce the desired output.
0222      */
0223     explicit SearchXmlWriter();
0224 
0225     /** Adds a group. Use the returned group writer to add fields.
0226      */
0227     void writeGroup();
0228 
0229     /** Sets the operator applied to the group as a whole "OR (field1 ... fieldn)".
0230      *  Default value is OR.
0231      */
0232     void setGroupOperator(SearchXml::Operator op);
0233 
0234     /** Sets an optional caption.
0235      */
0236     void setGroupCaption(const QString& caption);
0237 
0238     /** Sets the default operator for fields in this group "(field1 AND field2 AND ... fieldn)".
0239      *  The default operator can in each field be overridden. Default value is AND.
0240      */
0241     void setDefaultFieldOperator(SearchXml::Operator op);
0242 
0243     /** Adds a new field with the given name (entity) and relation, "Rating less than ...".
0244      *  Ensure that you closed the previous field with finishField().
0245      *  For a reference of valid field names, look into ItemQueryBuilder.
0246      *  The general rule is that names are like the database fields, but all lower-case.
0247      */
0248     void writeField(const QString& name, SearchXml::Relation relation);
0249 
0250     /** Adds an optional operator overriding the default field operator of the group.
0251      */
0252     void setFieldOperator(SearchXml::Operator op);
0253 
0254     /** Adds the value, "4" in the case of "Rating less than 4".
0255      */
0256     void writeValue(const QString& value);
0257     void writeValue(int value);
0258     void writeValue(qlonglong value);
0259     void writeValue(float value, int precision = 6);
0260     void writeValue(double value, int precision = 8);
0261     void writeValue(const QDateTime& dateTime);
0262     void writeValue(const QList<int>& valueList);
0263     void writeValue(const QList<qlonglong>& valueList);
0264     void writeValue(const QList<float>& valueList, int precision = 6);
0265     void writeValue(const QList<double>& valueList, int precision = 8);
0266     void writeValue(const QStringList& valueList);
0267     void writeValue(const QList<QDateTime>& valueList);
0268 
0269     /** Finish writing the current field.
0270      *  You shall call this method before adding another field, or closing the group.
0271      */
0272     void finishField();
0273 
0274     /** Finish the current group.
0275      *  You cannot add anymore fields after calling this.
0276      *  Note that you will want to call this before
0277      *  writing another group if you want the group on the same level.
0278      *  You can as well add nested groups and call this to close the group afterwards.
0279      */
0280     void finishGroup();
0281 
0282     /**
0283      *  Finish the XML. No further group can be added after calling this.
0284      *  You need to call this before you can get the resulting XML from xml().
0285      */
0286     void finish();
0287 
0288     /** Get the created XML. The value is only valid if finish() has been called.
0289      */
0290     QString xml() const;
0291 
0292     /** Returns ready-made XML for a query of type "keyword" with the specified
0293      *  text as keyword.
0294      */
0295     static QString keywordSearch(const QString& keyword);
0296 
0297 protected:
0298 
0299     void writeOperator(const QString&, SearchXml::Operator);
0300     void writeRelation(const QString&, SearchXml::Relation);
0301 
0302 protected:
0303 
0304     QString m_xml;
0305 };
0306 
0307 // ---------------------------------------------------------------------------------
0308 
0309 namespace KeywordSearch
0310 {
0311 
0312 /** Splits a given string to a list of keywords.
0313  *  Splits at whitespace, but recognizes quotation marks
0314  *  to group words in a single keyword.
0315  */
0316 DIGIKAM_DATABASE_EXPORT QStringList split(const QString& string);
0317 
0318 /** Reverse of split().
0319  *  From a list of keywords, gives a single string for a text entry field.
0320  */
0321 DIGIKAM_DATABASE_EXPORT QString merge(const QStringList& keywordList);
0322 
0323 /** Assuming previousContent is a string
0324  *  as accepted by split and returned by merge,
0325  *  adds newEntry as another (single) keyword to the string,
0326  *  returning the combined result.
0327  */
0328 DIGIKAM_DATABASE_EXPORT QString merge(const QString& previousContent, const QString& newEntry);
0329 
0330 } // namespace KeywordSearch
0331 
0332 // ---------------------------------------------------------------------------------
0333 
0334 class DIGIKAM_DATABASE_EXPORT KeywordSearchReader : public SearchXmlReader
0335 {
0336 public:
0337 
0338     explicit KeywordSearchReader(const QString& xml);
0339 
0340     /// Returns the keywords from this search, merged in a list.
0341     QStringList keywords();
0342 
0343     /// Checks if the XML is a simple keyword search, compatible with keywords().
0344     bool isSimpleKeywordSearch();
0345 
0346 private:
0347 
0348     void    readGroup(QStringList& list);
0349     bool    isSimpleKeywordSearchGroup();
0350     QString readField();
0351 };
0352 
0353 // ---------------------------------------------------------------------------------
0354 
0355 class DIGIKAM_DATABASE_EXPORT KeywordSearchWriter : public SearchXmlWriter
0356 {
0357 public:
0358 
0359     explicit KeywordSearchWriter();
0360 
0361     QString xml(const QStringList& keywordList);
0362 };
0363 
0364 // ---------------------------------------------------------------------------------
0365 
0366 class DIGIKAM_DATABASE_EXPORT SearchXmlCachingReader : public SearchXmlReader
0367 {
0368 public:
0369 
0370     /** This class has the same semantics as SearchXmlReader,
0371      *  but performs some caching and is thus much more relaxed than SearchXmlReader
0372      *  about the calling order of methods:
0373      *  With this class, you can access properties of a group until the next group
0374      *  is read, access properties and the value of a field until the next field is read,
0375      *  with all calls possible multiple times.
0376      */
0377     explicit SearchXmlCachingReader(const QString& xml);
0378 
0379     SearchXml::Element  readNext();
0380 
0381     SearchXml::Operator groupOperator() const;
0382     QString             groupCaption()  const;
0383 
0384     SearchXml::Operator fieldOperator() const;
0385     QString             fieldName()     const;
0386     SearchXml::Relation fieldRelation() const;
0387     QString             value();
0388     int                 valueToInt();
0389     qlonglong           valueToLongLong();
0390     double              valueToDouble();
0391     QDateTime           valueToDateTime();
0392     QList<int>          valueToIntList();
0393     QList<qlonglong>    valueToLongLongList();
0394     QList<double>       valueToDoubleList();
0395     QStringList         valueToStringList();
0396     QList<QDateTime>    valueToDateTimeList();
0397     QList<int>          valueToIntOrIntList();
0398     QList<double>       valueToDoubleOrDoubleList();
0399     QList<QString>      valueToStringOrStringList();
0400 
0401 protected:
0402 
0403     SearchXml::Operator m_groupOperator;
0404     QString             m_groupCaption;
0405     SearchXml::Operator m_fieldOperator;
0406     QString             m_fieldName;
0407     SearchXml::Relation m_fieldRelation;
0408     QVariant            m_value;
0409     bool                m_readValue;
0410 };
0411 
0412 } // namespace Digikam
0413 
0414 #endif // DIGIKAM_CORE_DB_SEARCH_XML_H