File indexing completed on 2025-01-05 03:53:59

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 #include "coredbsearchxml.h"
0017 
0018 #include <QRegularExpression>
0019 
0020 // Local includes
0021 
0022 #include "digikam_globals.h"
0023 
0024 namespace Digikam
0025 {
0026 
0027 SearchXmlReader::SearchXmlReader(const QString& xml)
0028     : QXmlStreamReader(xml)
0029 {
0030     m_defaultFieldOperator = SearchXml::And;
0031 
0032     // read in root element "search"
0033     readNext();
0034 }
0035 
0036 SearchXml::Element SearchXmlReader::readNext()
0037 {
0038     while (!atEnd())
0039     {
0040         QXmlStreamReader::readNext();
0041 
0042         if (isEndElement())
0043         {
0044             if (isGroupElement())
0045             {
0046                 return SearchXml::GroupEnd;
0047             }
0048             else if (isFieldElement())
0049             {
0050                 return SearchXml::FieldEnd;
0051             }
0052         }
0053 
0054         if (isStartElement())
0055         {
0056             if (isGroupElement())
0057             {
0058                 // get possible default operator
0059                 m_defaultFieldOperator = readOperator(QLatin1String("fieldoperator"), SearchXml::standardFieldOperator());
0060                 return SearchXml::Group;
0061             }
0062             else if (isFieldElement())
0063             {
0064                 return SearchXml::Field;
0065             }
0066             else if (name() == QLatin1String("search"))
0067             {
0068                 // root element
0069                 return SearchXml::Search;
0070             }
0071         }
0072     }
0073 
0074     return SearchXml::End;
0075 }
0076 
0077 // ---------------------------------------------------------------------------------
0078 
0079 bool SearchXmlReader::isGroupElement() const
0080 {
0081     return (name() == QLatin1String("group"));
0082 }
0083 
0084 bool SearchXmlReader::isFieldElement() const
0085 {
0086     return (name() == QLatin1String("field"));
0087 }
0088 
0089 SearchXml::Operator SearchXmlReader::groupOperator() const
0090 {
0091     return readOperator(QLatin1String("operator"), SearchXml::standardGroupOperator());
0092 }
0093 
0094 QString SearchXmlReader::groupCaption() const
0095 {
0096     return attributes().value(QLatin1String("caption")).toString();
0097 }
0098 
0099 SearchXml::Operator SearchXmlReader::defaultFieldOperator() const
0100 {
0101     return m_defaultFieldOperator;
0102 }
0103 
0104 SearchXml::Operator SearchXmlReader::fieldOperator() const
0105 {
0106     return readOperator(QLatin1String("operator"), m_defaultFieldOperator);
0107 }
0108 
0109 QString SearchXmlReader::fieldName() const
0110 {
0111     return attributes().value(QLatin1String("name")).toString();
0112 }
0113 
0114 SearchXml::Relation SearchXmlReader::fieldRelation() const
0115 {
0116     return readRelation(QLatin1String("relation"), SearchXml::standardFieldRelation());
0117 }
0118 
0119 QString SearchXmlReader::value()
0120 {
0121     return readElementText();
0122 }
0123 
0124 int SearchXmlReader::valueToInt()
0125 {
0126     return readElementText().toInt();
0127 }
0128 
0129 qlonglong SearchXmlReader::valueToLongLong()
0130 {
0131     return readElementText().toLongLong();
0132 }
0133 
0134 double SearchXmlReader::valueToDouble()
0135 {
0136     return readElementText().toDouble();
0137 }
0138 
0139 QDateTime SearchXmlReader::valueToDateTime()
0140 {
0141     return QDateTime::fromString(readElementText(), Qt::ISODate);
0142 }
0143 
0144 QList<int> SearchXmlReader::valueToIntList()
0145 {
0146     QList<int> list;
0147 
0148     while (!atEnd())
0149     {
0150         QXmlStreamReader::readNext();
0151 
0152         if (name() != QLatin1String("listitem"))
0153         {
0154             break;
0155         }
0156 
0157         if (isStartElement())
0158         {
0159             list << readElementText().toInt();
0160         }
0161     }
0162 
0163     return list;
0164 }
0165 
0166 QList<qlonglong> SearchXmlReader::valueToLongLongList()
0167 {
0168     QList<qlonglong> list;
0169 
0170     while (!atEnd())
0171     {
0172         QXmlStreamReader::readNext();
0173 
0174         if (name() != QLatin1String("listitem"))
0175         {
0176             break;
0177         }
0178 
0179         if (isStartElement())
0180         {
0181             list << readElementText().toLongLong();
0182         }
0183     }
0184 
0185     return list;
0186 }
0187 
0188 QList<double> SearchXmlReader::valueToDoubleList()
0189 {
0190     QList<double> list;
0191 
0192     while (!atEnd())
0193     {
0194         QXmlStreamReader::readNext();
0195 
0196         if (name() != QLatin1String("listitem"))
0197         {
0198             break;
0199         }
0200 
0201         if (isStartElement())
0202         {
0203             list << readElementText().toDouble();
0204         }
0205     }
0206 
0207     return list;
0208 }
0209 
0210 QStringList SearchXmlReader::valueToStringList()
0211 {
0212     QStringList list;
0213 
0214     while (!atEnd())
0215     {
0216         QXmlStreamReader::readNext();
0217 
0218         if (name() != QLatin1String("listitem"))
0219         {
0220             break;
0221         }
0222 
0223         if (isStartElement())
0224         {
0225             list << readElementText();
0226         }
0227     }
0228 
0229     return list;
0230 }
0231 
0232 QList<QDateTime> SearchXmlReader::valueToDateTimeList()
0233 {
0234     QList<QDateTime> list;
0235 
0236     while (!atEnd())
0237     {
0238         QXmlStreamReader::readNext();
0239 
0240         if (name() != QLatin1String("listitem"))
0241         {
0242             break;
0243         }
0244 
0245         if (isStartElement())
0246         {
0247             list << QDateTime::fromString(readElementText(), Qt::ISODate);
0248         }
0249     }
0250 
0251     return list;
0252 }
0253 
0254 QList<int> SearchXmlReader::valueToIntOrIntList()
0255 {
0256     QList<int> list;
0257 
0258     // poke at next token
0259     QXmlStreamReader::TokenType token = QXmlStreamReader::readNext();
0260 
0261     // Found text? Treat text as with valueToInt(), return single element list
0262     if (token == QXmlStreamReader::Characters)
0263     {
0264         list << text().toString().toInt();
0265         readNext();
0266 
0267         return list;
0268     }
0269 
0270     // treat as with valueToIntList()
0271     while (!atEnd())
0272     {
0273         if (token != QXmlStreamReader::StartElement || name() != QLatin1String("listitem"))
0274         {
0275             break;
0276         }
0277 
0278         list << readElementText().toInt();
0279 
0280         token = QXmlStreamReader::readNext();
0281     }
0282 
0283     return list;
0284 }
0285 
0286 QList<double> SearchXmlReader::valueToDoubleOrDoubleList()
0287 {
0288     QList<double> list;
0289 
0290     // poke at next token
0291     QXmlStreamReader::TokenType token = QXmlStreamReader::readNext();
0292 
0293     // Found text? Treat text as with valueToInt(), return single element list
0294     if (token == QXmlStreamReader::Characters)
0295     {
0296         list << text().toString().toDouble();
0297         readNext();
0298 
0299         return list;
0300     }
0301 
0302     // treat as with valueToIntList()
0303     while (!atEnd())
0304     {
0305         if (token != QXmlStreamReader::StartElement || name() != QLatin1String("listitem"))
0306         {
0307             break;
0308         }
0309 
0310         list << readElementText().toDouble();
0311 
0312         token = QXmlStreamReader::readNext();
0313     }
0314 
0315     return list;
0316 }
0317 
0318 QList<QString> SearchXmlReader::valueToStringOrStringList()
0319 {
0320     QList<QString> list;
0321 
0322     // poke at next token
0323     QXmlStreamReader::TokenType token = QXmlStreamReader::readNext();
0324 
0325     // Found text? Treat text as with valueToString(), return single element list
0326     if (token == QXmlStreamReader::Characters)
0327     {
0328         list << text().toString();
0329         readNext();
0330 
0331         return list;
0332     }
0333 
0334     // treat as with valueToStringList()
0335     while (!atEnd())
0336     {
0337         if (token != QXmlStreamReader::StartElement || name() != QLatin1String("listitem"))
0338         {
0339             break;
0340         }
0341 
0342         list << readElementText();
0343 
0344         token = QXmlStreamReader::readNext();
0345     }
0346 
0347     return list;
0348 }
0349 
0350 SearchXml::Operator SearchXmlReader::readOperator(const QString& attributeName,
0351                                                   SearchXml::Operator defaultOperator) const
0352 {
0353     QStringView op = attributes().value(attributeName);
0354 
0355     if      (op == QLatin1String("and"))
0356     {
0357         return SearchXml::And;
0358     }
0359     else if (op == QLatin1String("or"))
0360     {
0361         return SearchXml::Or;
0362     }
0363     else if (op == QLatin1String("andnot"))
0364     {
0365         return SearchXml::AndNot;
0366     }
0367     else if (op == QLatin1String("ornot"))
0368     {
0369         return SearchXml::OrNot;
0370     }
0371 
0372     return defaultOperator;
0373 }
0374 
0375 SearchXml::Relation SearchXmlReader::readRelation(const QString& attributeName,
0376                                                   SearchXml::Relation defaultRelation) const
0377 {
0378     QStringView relation = attributes().value(attributeName);
0379 
0380     if (relation == QLatin1String("equal"))
0381     {
0382         return SearchXml::Equal;
0383     }
0384 
0385     if      (relation == QLatin1String("unequal"))
0386     {
0387         return SearchXml::Unequal;
0388     }
0389     else if (relation == QLatin1String("like"))
0390     {
0391         return SearchXml::Like;
0392     }
0393     else if (relation == QLatin1String("notlike"))
0394     {
0395         return SearchXml::NotLike;
0396     }
0397     else if (relation == QLatin1String("lessthan"))
0398     {
0399         return SearchXml::LessThan;
0400     }
0401     else if (relation == QLatin1String("greaterthan"))
0402     {
0403         return SearchXml::GreaterThan;
0404     }
0405     else if (relation == QLatin1String("lessthanequal"))
0406     {
0407         return SearchXml::LessThanOrEqual;
0408     }
0409     else if (relation == QLatin1String("greaterthanequal"))
0410     {
0411         return SearchXml::GreaterThanOrEqual;
0412     }
0413     else if (relation == QLatin1String("interval"))
0414     {
0415         return SearchXml::Interval;
0416     }
0417     else if (relation == QLatin1String("intervalopen"))
0418     {
0419         return SearchXml::IntervalOpen;
0420     }
0421     else if (relation == QLatin1String("oneof"))
0422     {
0423         return SearchXml::OneOf;
0424     }
0425     else if (relation == QLatin1String("allof"))
0426     {
0427         return SearchXml::AllOf;
0428     }
0429     else if (relation == QLatin1String("intree"))
0430     {
0431         return SearchXml::InTree;
0432     }
0433     else if (relation == QLatin1String("notintree"))
0434     {
0435         return SearchXml::NotInTree;
0436     }
0437     else if (relation == QLatin1String("near"))
0438     {
0439         return SearchXml::Near;
0440     }
0441     else if (relation == QLatin1String("inside"))
0442     {
0443         return SearchXml::Inside;
0444     }
0445 
0446     return defaultRelation;
0447 }
0448 
0449 bool SearchXmlReader::readToStartOfElement(const QString& elementName)
0450 {
0451     // go to next start element
0452     Q_FOREVER
0453     {
0454         bool atStart = isStartElement();
0455 
0456         if (atStart)
0457         {
0458             break;
0459         }
0460 
0461         switch (QXmlStreamReader::readNext())
0462         {
0463             case StartElement:
0464             {
0465                 atStart = true;
0466                 (void)atStart;  // Remove clang warning.
0467                 break;
0468             }
0469             case EndDocument:
0470             {
0471                 return false;
0472             }
0473             default:
0474             {
0475                 break;
0476             }
0477         }
0478     }
0479 
0480     int stack = 1;
0481 
0482     Q_FOREVER
0483     {
0484         switch (QXmlStreamReader::readNext())
0485         {
0486             case StartElement:
0487             {
0488                 if (name() == elementName)
0489                 {
0490                     return true;
0491                 }
0492 
0493                 ++stack;
0494                 break;
0495             }
0496             case EndElement:
0497             {
0498                 if (!--stack)
0499                 {
0500                     return false;
0501                 }
0502 
0503                 break;
0504             }
0505             case EndDocument:
0506             {
0507                 return false;
0508             }
0509             default:
0510             {
0511                 break;
0512             }
0513         }
0514     }
0515 
0516     return false;
0517 }
0518 
0519 void SearchXmlReader::readToEndOfElement()
0520 {
0521     if (isStartElement())
0522     {
0523         int stack = 1;
0524 
0525         Q_FOREVER
0526         {
0527             switch (QXmlStreamReader::readNext())
0528             {
0529                 case StartElement:
0530                 {
0531                     ++stack;
0532                     break;
0533                 }
0534                 case EndElement:
0535                 {
0536                     if (!--stack)
0537                     {
0538                         return;
0539                     }
0540 
0541                     break;
0542                 }
0543                 case EndDocument:
0544                 {
0545                     return;
0546                 }
0547                 default:
0548                 {
0549                     break;
0550                 }
0551             }
0552         }
0553     }
0554 }
0555 
0556 void SearchXmlReader::readToFirstField()
0557 {
0558     SearchXml::Element element;
0559     bool hasGroup = false;
0560 
0561     while (!atEnd())
0562     {
0563         element = readNext();
0564 
0565         if      (element == SearchXml::Group)
0566         {
0567             hasGroup = true;
0568         }
0569         else if (hasGroup && element == SearchXml::Field)
0570         {
0571             return;
0572         }
0573     }
0574 }
0575 
0576 // ---------------------------------------------------------------------------------
0577 
0578 SearchXmlWriter::SearchXmlWriter()
0579     : QXmlStreamWriter(&m_xml)
0580 {
0581     writeStartDocument();
0582     writeStartElement(QLatin1String("search"));
0583 }
0584 
0585 QString SearchXmlWriter::xml() const
0586 {
0587     return m_xml;
0588 }
0589 
0590 void SearchXmlWriter::writeGroup()
0591 {
0592     writeStartElement(QLatin1String("group"));
0593 }
0594 
0595 void SearchXmlWriter::setGroupOperator(SearchXml::Operator op)
0596 {
0597     if (op != SearchXml::Or)
0598     {
0599         writeOperator(QLatin1String("operator"), op);
0600     }
0601 }
0602 
0603 void SearchXmlWriter::setGroupCaption(const QString& caption)
0604 {
0605     if (!caption.isNull())
0606     {
0607         writeAttribute(QLatin1String("caption"), caption);
0608     }
0609 }
0610 
0611 void SearchXmlWriter::setDefaultFieldOperator(SearchXml::Operator op)
0612 {
0613     if (op != SearchXml::And)
0614     {
0615         writeOperator(QLatin1String("fieldoperator"), op);
0616     }
0617 }
0618 
0619 void SearchXmlWriter::writeField(const QString& name, SearchXml::Relation relation)
0620 {
0621     writeStartElement(QLatin1String("field"));
0622     writeAttribute(QLatin1String("name"),    name);
0623     writeRelation(QLatin1String("relation"), relation);
0624 }
0625 
0626 void SearchXmlWriter::setFieldOperator(SearchXml::Operator op)
0627 {
0628     writeOperator(QLatin1String("operator"), op);
0629 }
0630 
0631 void SearchXmlWriter::writeValue(const QString& value)
0632 {
0633     writeCharacters(value);
0634 }
0635 
0636 void SearchXmlWriter::writeValue(int value)
0637 {
0638     writeCharacters(QString::number(value));
0639 }
0640 
0641 void SearchXmlWriter::writeValue(qlonglong value)
0642 {
0643     writeCharacters(QString::number(value));
0644 }
0645 
0646 void SearchXmlWriter::writeValue(float value, int precision)
0647 {
0648     writeCharacters(QString::number(value, 'g', precision));
0649 }
0650 
0651 void SearchXmlWriter::writeValue(double value, int precision)
0652 {
0653     writeCharacters(QString::number(value, 'g', precision));
0654 }
0655 
0656 void SearchXmlWriter::writeValue(const QDateTime& dateTime)
0657 {
0658     writeCharacters(dateTime.toString(Qt::ISODate));
0659 }
0660 
0661 void SearchXmlWriter::writeValue(const QList<int>& valueList)
0662 {
0663     QString listitem(QLatin1String("listitem"));
0664 
0665     Q_FOREACH (int i, valueList)
0666     {
0667         writeTextElement(listitem, QString::number(i));
0668     }
0669 }
0670 
0671 void SearchXmlWriter::writeValue(const QList<qlonglong>& valueList)
0672 {
0673     QString listitem(QLatin1String("listitem"));
0674 
0675     Q_FOREACH (int i, valueList)
0676     {
0677         writeTextElement(listitem, QString::number(i));
0678     }
0679 }
0680 
0681 void SearchXmlWriter::writeValue(const QList<float>& valueList, int precision)
0682 {
0683     QString listitem(QLatin1String("listitem"));
0684 
0685     Q_FOREACH (double i, valueList)
0686     {
0687         writeTextElement(listitem, QString::number(i, 'g', precision));
0688     }
0689 }
0690 
0691 void SearchXmlWriter::writeValue(const QList<double>& valueList, int precision)
0692 {
0693     QString listitem(QLatin1String("listitem"));
0694 
0695     Q_FOREACH (double i, valueList)
0696     {
0697         writeTextElement(listitem, QString::number(i, 'g', precision));
0698     }
0699 }
0700 
0701 void SearchXmlWriter::writeValue(const QList<QDateTime>& valueList)
0702 {
0703     QString listitem(QLatin1String("listitem"));
0704 
0705     Q_FOREACH (const QDateTime& dt, valueList)
0706     {
0707         writeTextElement(listitem, dt.toString(Qt::ISODate));
0708     }
0709 }
0710 
0711 void SearchXmlWriter::writeValue(const QStringList& valueList)
0712 {
0713     QString listitem(QLatin1String("listitem"));
0714 
0715     Q_FOREACH (const QString& str, valueList)
0716     {
0717         writeTextElement(listitem, str);
0718     }
0719 }
0720 
0721 void SearchXmlWriter::finishField()
0722 {
0723     writeEndElement();
0724 }
0725 
0726 void SearchXmlWriter::finishGroup()
0727 {
0728     writeEndElement();
0729 }
0730 
0731 void SearchXmlWriter::finish()
0732 {
0733     writeEndDocument();
0734 }
0735 
0736 void SearchXmlWriter::writeOperator(const QString& attributeName, SearchXml::Operator op)
0737 {
0738     switch (op)
0739     {
0740         default:
0741         case SearchXml::And:
0742             writeAttribute(attributeName, QLatin1String("and"));
0743             break;
0744         case SearchXml::Or:
0745             writeAttribute(attributeName, QLatin1String("or"));
0746             break;
0747         case SearchXml::AndNot:
0748             writeAttribute(attributeName, QLatin1String("andnot"));
0749             break;
0750         case SearchXml::OrNot:
0751             writeAttribute(attributeName, QLatin1String("ornot"));
0752             break;
0753     }
0754 }
0755 
0756 void SearchXmlWriter::writeRelation(const QString& attributeName, SearchXml::Relation op)
0757 {
0758     switch (op)
0759     {
0760         default:
0761         case SearchXml::Equal:
0762             writeAttribute(attributeName, QLatin1String("equal"));
0763             break;
0764         case SearchXml::Unequal:
0765             writeAttribute(attributeName, QLatin1String("unequal"));
0766             break;
0767         case SearchXml::Like:
0768             writeAttribute(attributeName, QLatin1String("like"));
0769             break;
0770         case SearchXml::NotLike:
0771             writeAttribute(attributeName, QLatin1String("notlike"));
0772             break;
0773         case SearchXml::LessThan:
0774             writeAttribute(attributeName, QLatin1String("lessthan"));
0775             break;
0776         case SearchXml::GreaterThan:
0777             writeAttribute(attributeName, QLatin1String("greaterthan"));
0778             break;
0779         case SearchXml::LessThanOrEqual:
0780             writeAttribute(attributeName, QLatin1String("lessthanequal"));
0781             break;
0782         case SearchXml::GreaterThanOrEqual:
0783             writeAttribute(attributeName, QLatin1String("greaterthanequal"));
0784             break;
0785         case SearchXml::Interval:
0786             writeAttribute(attributeName, QLatin1String("interval"));
0787             break;
0788         case SearchXml::IntervalOpen:
0789             writeAttribute(attributeName, QLatin1String("intervalopen"));
0790             break;
0791         case SearchXml::OneOf:
0792             writeAttribute(attributeName, QLatin1String("oneof"));
0793             break;
0794         case SearchXml::AllOf:
0795             writeAttribute(attributeName, QLatin1String("allof"));
0796             break;
0797         case SearchXml::InTree:
0798             writeAttribute(attributeName, QLatin1String("intree"));
0799             break;
0800         case SearchXml::NotInTree:
0801             writeAttribute(attributeName, QLatin1String("notintree"));
0802             break;
0803         case SearchXml::Near:
0804             writeAttribute(attributeName, QLatin1String("near"));
0805             break;
0806         case SearchXml::Inside:
0807             writeAttribute(attributeName, QLatin1String("inside"));
0808             break;
0809     }
0810 }
0811 
0812 QString SearchXmlWriter::keywordSearch(const QString& keyword)
0813 {
0814     SearchXmlWriter writer;
0815     writer.writeGroup();
0816     writer.writeField(QLatin1String("keyword"), SearchXml::Like);
0817     writer.writeValue(keyword);
0818     writer.finishField();
0819     writer.finishGroup();
0820     writer.finish();
0821 
0822     return writer.xml();
0823 }
0824 
0825 // ---------------------------------------------------------------------------------
0826 
0827 QStringList KeywordSearch::split(const QString& keywords)
0828 {
0829     // get groups with quotation marks
0830     QStringList quotationMarkList = keywords.split(QLatin1Char('"'), QT_KEEP_EMPTY_PARTS);
0831 
0832     // split down to single words
0833     QStringList keywordList;
0834     int quotationMarkCount = (keywords.startsWith(QLatin1Char('"')) ? 1 : 0);
0835 
0836     Q_FOREACH (const QString& group, quotationMarkList)
0837     {
0838         if (quotationMarkCount % 2)
0839         {
0840             // inside marks: leave as is
0841             if (!group.isEmpty())
0842             {
0843                 keywordList << group;
0844             }
0845         }
0846         else
0847         {
0848             // not in quotation marks: split by whitespace
0849             keywordList << group.split(QRegularExpression(QLatin1String("\\s+")), QT_SKIP_EMPTY_PARTS);
0850         }
0851 
0852         ++quotationMarkCount;
0853     }
0854 
0855     return keywordList;
0856 }
0857 
0858 QString KeywordSearch::merge(const QStringList& keywordList)
0859 {
0860     QStringList list(keywordList);
0861 
0862     // group keyword with spaces in quotation marks
0863     for (QStringList::iterator it = list.begin() ; it != list.end() ; ++it)
0864     {
0865         if ((*it).contains(QLatin1Char(' ')))
0866         {
0867             *it = (*it).prepend(QLatin1Char('"')).append(QLatin1Char('"'));
0868         }
0869     }
0870 
0871     // join in a string
0872     return list.join(QLatin1Char(' '));
0873 }
0874 
0875 QString KeywordSearch::merge(const QString& previousContent, const QString& newEntry)
0876 {
0877     QString ne(newEntry);
0878     QString pc(previousContent);
0879 
0880     if (ne.contains(QLatin1Char(' ')))
0881     {
0882         ne = ne.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
0883     }
0884 
0885     return pc.append(QLatin1Char(' ')).append(ne);
0886 }
0887 
0888 // ---------------------------------------------------------------------------------
0889 
0890 KeywordSearchReader::KeywordSearchReader(const QString& xml)
0891     : SearchXmlReader(xml)
0892 {
0893 }
0894 
0895 QStringList KeywordSearchReader::keywords()
0896 {
0897     QStringList list;
0898 
0899     SearchXml::Element element;
0900 
0901     while (!atEnd())
0902     {
0903         element = readNext();
0904 
0905         if (element == SearchXml::Group)
0906         {
0907             readGroup(list);
0908         }
0909     }
0910 
0911     return list;
0912 }
0913 
0914 void KeywordSearchReader::readGroup(QStringList& list)
0915 {
0916     SearchXml::Element element;
0917 
0918     while (!atEnd())
0919     {
0920         element = readNext();
0921 
0922         if (element == SearchXml::Field)
0923         {
0924             QString value = readField();
0925 
0926             if (!value.isEmpty())
0927             {
0928                 list << value;
0929             }
0930         }
0931 
0932         if (element == SearchXml::GroupEnd)
0933         {
0934             return;
0935         }
0936     }
0937 }
0938 
0939 QString KeywordSearchReader::readField()
0940 {
0941     if (fieldName() == QLatin1String("keyword"))
0942     {
0943         return value();
0944     }
0945 
0946     return QString();
0947 }
0948 
0949 bool KeywordSearchReader::isSimpleKeywordSearch()
0950 {
0951     // Find out if this XML conforms to a simple keyword search,
0952     // as created with KeywordSearchWriter
0953     SearchXml::Element element;
0954     int groupCount = 0;
0955 
0956     while (!atEnd())
0957     {
0958         element = readNext();
0959 
0960         if (element == SearchXml::Group)
0961         {
0962             // only one group please
0963             if (++groupCount > 1)
0964             {
0965                 return false;
0966             }
0967 
0968             if (!isSimpleKeywordSearchGroup())
0969             {
0970                 return false;
0971             }
0972         }
0973     }
0974 
0975     return true;
0976 }
0977 
0978 bool KeywordSearchReader::isSimpleKeywordSearchGroup()
0979 {
0980     // Find out if the current group conforms to a simple keyword search,
0981     // as created with KeywordSearchWriter
0982 
0983     if (groupOperator() != SearchXml::standardGroupOperator())
0984     {
0985         return false;
0986     }
0987 
0988     if (defaultFieldOperator() != SearchXml::standardFieldOperator())
0989     {
0990         return false;
0991     }
0992 
0993     SearchXml::Element element;
0994 
0995     while (!atEnd())
0996     {
0997         element = readNext();
0998 
0999         // subgroups not allowed
1000         if (element == SearchXml::Group)
1001         {
1002             return false;
1003         }
1004 
1005         // only "keyword" fields allowed
1006         if (element == SearchXml::Field)
1007         {
1008             if (fieldName() != QLatin1String("keyword"))
1009             {
1010                 return false;
1011             }
1012 
1013             if (fieldRelation() != SearchXml::Like)
1014             {
1015                 return false;
1016             }
1017 
1018             if (fieldOperator() != SearchXml::standardFieldOperator())
1019             {
1020                 return false;
1021             }
1022         }
1023 
1024         if (element == SearchXml::GroupEnd)
1025         {
1026             return true;
1027         }
1028     }
1029 
1030     return true;
1031 }
1032 
1033 // ---------------------------------------------------------------------------------
1034 
1035 KeywordSearchWriter::KeywordSearchWriter()
1036     : SearchXmlWriter()
1037 {
1038 }
1039 
1040 QString KeywordSearchWriter::xml(const QStringList& keywordList)
1041 {
1042     writeGroup();
1043 
1044     Q_FOREACH (const QString& keyword, keywordList)
1045     {
1046         writeField(QLatin1String("keyword"), SearchXml::Like);
1047         writeValue(keyword);
1048         finishField();
1049     }
1050 
1051     finishGroup();
1052     finish();
1053 
1054     return SearchXmlWriter::xml();
1055 }
1056 
1057 // ---------------------------------------------------------------------------------
1058 
1059 SearchXmlCachingReader::SearchXmlCachingReader(const QString& xml)
1060     : SearchXmlReader(xml),
1061       m_groupOperator(SearchXml::And),
1062       m_fieldOperator(SearchXml::And),
1063       m_fieldRelation(SearchXml::Equal),
1064       m_readValue(false)
1065 {
1066 }
1067 
1068 SearchXml::Element SearchXmlCachingReader::readNext()
1069 {
1070     SearchXml::Element element = SearchXmlReader::readNext();
1071 
1072     if (element == SearchXml::Group)
1073     {
1074         m_groupOperator = SearchXmlReader::groupOperator();
1075         m_groupCaption  = SearchXmlReader::groupCaption();
1076     }
1077     else if (element == SearchXml::Field)
1078     {
1079         m_fieldOperator = SearchXmlReader::fieldOperator();
1080         m_fieldName     = SearchXmlReader::fieldName();
1081         m_fieldRelation = SearchXmlReader::fieldRelation();
1082         m_readValue     = false;
1083     }
1084 
1085     return element;
1086 }
1087 
1088 SearchXml::Operator SearchXmlCachingReader::groupOperator() const
1089 {
1090     return m_groupOperator;
1091 }
1092 
1093 QString SearchXmlCachingReader::groupCaption() const
1094 {
1095     return m_groupCaption;
1096 }
1097 
1098 SearchXml::Operator SearchXmlCachingReader::fieldOperator() const
1099 {
1100     return m_fieldOperator;
1101 }
1102 
1103 QString SearchXmlCachingReader::fieldName() const
1104 {
1105     return m_fieldName;
1106 }
1107 
1108 SearchXml::Relation SearchXmlCachingReader::fieldRelation() const
1109 {
1110     return m_fieldRelation;
1111 }
1112 
1113 QString SearchXmlCachingReader::value()
1114 {
1115     if (!m_readValue)
1116     {
1117         m_value     = SearchXmlReader::value();
1118         m_readValue = true;
1119     }
1120 
1121     return m_value.toString();
1122 }
1123 
1124 int SearchXmlCachingReader::valueToInt()
1125 {
1126     if (!m_readValue)
1127     {
1128         m_value     = SearchXmlReader::valueToInt();
1129         m_readValue = true;
1130     }
1131 
1132     return m_value.toInt();
1133 }
1134 
1135 qlonglong SearchXmlCachingReader::valueToLongLong()
1136 {
1137     if (!m_readValue)
1138     {
1139         m_value     = SearchXmlReader::valueToLongLong();
1140         m_readValue = true;
1141     }
1142 
1143     return m_value.toLongLong();
1144 }
1145 
1146 double SearchXmlCachingReader::valueToDouble()
1147 {
1148     if (!m_readValue)
1149     {
1150         m_value     = SearchXmlReader::valueToDouble();
1151         m_readValue = true;
1152     }
1153 
1154     return m_value.toDouble();
1155 }
1156 
1157 QDateTime SearchXmlCachingReader::valueToDateTime()
1158 {
1159     if (!m_readValue)
1160     {
1161         m_value     = SearchXmlReader::valueToDateTime();
1162         m_readValue = true;
1163     }
1164 
1165     return m_value.toDateTime();
1166 }
1167 
1168 QList<int> SearchXmlCachingReader::valueToIntList()
1169 {
1170     // with no QVariant support for QList<int>,
1171     // we convert here from string list (equivalent result)
1172     QStringList list = valueToStringList();
1173     QList<int> intList;
1174 
1175     Q_FOREACH (const QString& s, list)
1176     {
1177         double val = s.toDouble();
1178         intList << (int)val;
1179     }
1180 
1181     return intList;
1182 }
1183 
1184 QList<qlonglong> SearchXmlCachingReader::valueToLongLongList()
1185 {
1186     // with no QVariant support for QList<qlonglong>,
1187     // we convert here from string list (equivalent result)
1188     QStringList list = valueToStringList();
1189     QList<qlonglong> qlonglongList;
1190 
1191     Q_FOREACH (const QString& s, list)
1192     {
1193         double val = s.toDouble();
1194         qlonglongList << (qlonglong)val;
1195     }
1196 
1197     return qlonglongList;
1198 }
1199 
1200 QList<double> SearchXmlCachingReader::valueToDoubleList()
1201 {
1202     // with no QVariant support for QList<double>,
1203     // we convert here from string list (equivalent result)
1204     QStringList list = valueToStringList();
1205     QList<double> doubleList;
1206 
1207     Q_FOREACH (const QString& s, list)
1208     {
1209         doubleList << s.toDouble();
1210     }
1211 
1212     return doubleList;
1213 }
1214 
1215 QList<QDateTime> SearchXmlCachingReader::valueToDateTimeList()
1216 {
1217     // with no QVariant support for QList<QDateTime>,
1218     // we convert here from string list (equivalent result)
1219     QStringList list = valueToStringList();
1220     QList<QDateTime> doubleList;
1221 
1222     Q_FOREACH (const QString& s, list)
1223     {
1224         doubleList << QDateTime::fromString(s, Qt::ISODate);
1225     }
1226 
1227     return doubleList;
1228 }
1229 
1230 QStringList SearchXmlCachingReader::valueToStringList()
1231 {
1232     if (!m_readValue)
1233     {
1234         m_value     = SearchXmlReader::valueToStringList();
1235         m_readValue = true;
1236     }
1237 
1238     return m_value.toStringList();
1239 }
1240 
1241 QList<int> SearchXmlCachingReader::valueToIntOrIntList()
1242 {
1243     if (!m_readValue)
1244     {
1245         QList<int> intList = SearchXmlReader::valueToIntOrIntList();
1246         QList<QVariant> varList;
1247 
1248         Q_FOREACH (int v, intList)
1249         {
1250             varList << v;
1251         }
1252 
1253         m_value     = varList;
1254         m_readValue = true;
1255 
1256         return intList;
1257     }
1258 
1259     QList<int> intList;
1260     QList<QVariant> varList = m_value.toList();
1261 
1262     Q_FOREACH (const QVariant& var, varList)
1263     {
1264         intList << var.toInt();
1265     }
1266 
1267     return intList;
1268 }
1269 
1270 QList<double> SearchXmlCachingReader::valueToDoubleOrDoubleList()
1271 {
1272     if (!m_readValue)
1273     {
1274         QList<double> doubleList = SearchXmlReader::valueToDoubleOrDoubleList();
1275         QList<QVariant> varList;
1276 
1277         Q_FOREACH (double v, doubleList)
1278         {
1279             varList << v;
1280         }
1281 
1282         m_value     = varList;
1283         m_readValue = true;
1284 
1285         return doubleList;
1286     }
1287 
1288     QList<double> doubleList;
1289     QList<QVariant> varList = m_value.toList();
1290 
1291     Q_FOREACH (const QVariant& var, varList)
1292     {
1293         doubleList << var.toDouble();
1294     }
1295 
1296     return doubleList;
1297 }
1298 
1299 QList<QString> SearchXmlCachingReader::valueToStringOrStringList()
1300 {
1301     if (!m_readValue)
1302     {
1303         QList<QString> QStringList = SearchXmlReader::valueToStringOrStringList();
1304         QList<QVariant> varList;
1305 
1306         Q_FOREACH (const QString& v, QStringList)
1307         {
1308             varList << v;
1309         }
1310 
1311         m_value     = varList;
1312         m_readValue = true;
1313         return QStringList;
1314     }
1315 
1316     QList<QString> lst;
1317     QList<QVariant> varList = m_value.toList();
1318 
1319     Q_FOREACH (const QVariant& var, varList)
1320     {
1321         lst << var.toString();
1322     }
1323 
1324     return lst;
1325 }
1326 
1327 } // namespace Digikam