File indexing completed on 2024-05-19 04:49:18

0001 /****************************************************************************************
0002  * Copyright (c) 2007-2008 Maximilian Kossick <maximilian.kossick@googlemail.com>       *
0003  * Copyright (c) 2008 Daniel Caleb Jones <danielcjones@gmail.com>                       *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) any later           *
0008  * version.                                                                             *
0009  *                                                                                      *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0013  *                                                                                      *
0014  * You should have received a copy of the GNU General Public License along with         *
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0016  ****************************************************************************************/
0017 
0018 #include "XmlQueryReader.h"
0019 
0020 #include "core/support/Debug.h"
0021 #include "core-impl/collections/support/CollectionManager.h"
0022 
0023 #include <QString>
0024 
0025 struct XmlQueryReader::Private
0026 {
0027     ReturnValueEnum flag;
0028     Collections::QueryMaker *qm;
0029     QList<Filter> filters;
0030 };
0031 
0032 Collections::QueryMaker*
0033 XmlQueryReader::getQueryMaker( const QString &xmlData, ReturnValueEnum flag )
0034 {
0035     Collections::QueryMaker *qm = CollectionManager::instance()->queryMaker();
0036     XmlQueryReader reader( qm, flag );
0037     if( reader.read( xmlData ) )
0038         return qm;
0039     else
0040         return nullptr;
0041 }
0042 
0043 XmlQueryReader::XmlQueryReader( Collections::QueryMaker *qm, ReturnValueEnum flag )
0044     : QXmlStreamReader()
0045     , d( new Private )
0046 {
0047     d->flag = flag;
0048     d->qm = qm;
0049 }
0050 
0051 XmlQueryReader::~XmlQueryReader()
0052 {
0053     delete d;
0054 }
0055 
0056 const QList<XmlQueryReader::Filter>&
0057 XmlQueryReader::getFilters() const
0058 {
0059     return d->filters;
0060 }
0061 
0062 bool
0063 XmlQueryReader::read( const QString &xmlData )
0064 {
0065     addData( xmlData );
0066     int queryCount = 0;
0067     while( !atEnd() )
0068     {
0069         readNext();
0070 
0071         if( isStartElement() )
0072         {
0073             //we expect exactly one query definition in the xml data.
0074             //so fail if we find more than one
0075             if( name() == "query" )
0076             {
0077                 if( attributes().value( QStringLiteral("version") ) == "1.0" )
0078                 {
0079                     queryCount++;
0080                     readQuery();
0081                 }
0082             }
0083         }
0084     }
0085 
0086     return queryCount == 1 && !error();
0087 }
0088 
0089 void
0090 XmlQueryReader::readQuery()
0091 {
0092     while( !atEnd() )
0093     {
0094         readNext();
0095 
0096         if( isStartElement() )
0097         {
0098             if( name() == "filters" )
0099                 readFilters();
0100             else if( name() == "order" )
0101             {
0102                 QXmlStreamAttributes attr = attributes();
0103                 QStringRef fieldStr =  attr.value( QStringLiteral("field") );
0104                 QStringRef valueStr =  attr.value( QStringLiteral("value") );
0105 
0106                 qint64 field = Meta::fieldForName( fieldStr.toString() );
0107                 bool descending = valueStr == "descending";
0108 
0109                 if( field != 0 )
0110                     d->qm->orderBy( field, descending  );
0111             }
0112             else if( name() == "limit" )
0113             {
0114                 QStringRef value = attributes().value( QStringLiteral("value") );
0115                 if( !value.isEmpty() )
0116                     d->qm->limitMaxResultSize( value.toString().toInt() );
0117             }
0118             else if( name() == "onlyCompilations" )
0119             {
0120                 d->qm->setAlbumQueryMode( Collections::QueryMaker::OnlyCompilations );
0121             }
0122             else if( name() == "onlyNormalAlbums" )
0123             {
0124                 d->qm->setAlbumQueryMode( Collections::QueryMaker::OnlyNormalAlbums );
0125             }
0126             else if( name() == "returnValues" )
0127                 readReturnValues();
0128             //add more container elements here
0129             else
0130                 ignoreElements();
0131         }
0132     }
0133 }
0134 
0135 void
0136 XmlQueryReader::ignoreElements()
0137 {
0138     //let QXmlStreamReader worry about the fell-formedness of the document
0139     int depth = 1;
0140     while( !atEnd() && depth > 0 )
0141     {
0142         readNext();
0143         if( isEndElement() )
0144             depth--;
0145         if( isStartElement() )
0146             depth++;
0147     }
0148 }
0149 
0150 void
0151 XmlQueryReader::readReturnValues()
0152 {
0153     if( d->flag & XmlQueryReader::IgnoreReturnValues )
0154     {
0155         ignoreElements();
0156         return;
0157     }
0158     else
0159     {
0160         bool customQueryStarted = false;
0161         while( !atEnd() )
0162         {
0163             readNext();
0164             if( name() == "tracks" )
0165             {
0166                 d->qm->setQueryType( Collections::QueryMaker::Track );
0167             }
0168             else if( name() == "artists" )
0169             {
0170                 d->qm->setQueryType( Collections::QueryMaker::Artist );
0171             }
0172             else if( name() == "albums" )
0173             {
0174                 d->qm->setQueryType( Collections::QueryMaker::Album );
0175             }
0176             else if( name() == "albumartist" )
0177             {
0178                 d->qm->setQueryType( Collections::QueryMaker::AlbumArtist );
0179             }
0180             else if( name() == "genres" )
0181             {
0182                 d->qm->setQueryType( Collections::QueryMaker::Genre );
0183             }
0184             else if( name() == "composers" )
0185             {
0186                 d->qm->setQueryType( Collections::QueryMaker::Composer );
0187             }
0188             else if( name() == "year" )
0189             {
0190                 d->qm->setQueryType( Collections::QueryMaker::Year );
0191             }
0192             else
0193             {
0194                 if( !customQueryStarted )
0195                 {
0196                     d->qm->setQueryType( Collections::QueryMaker::Custom );
0197                 }
0198                 //TODO write a mapping function somewhere
0199                 if( name() == "title" )
0200                 {
0201                     d->qm->addReturnValue( Meta::valTitle );
0202                 }
0203                 else if( name() == "artist" )
0204                 {
0205                     d->qm->addReturnValue( Meta::valArtist );
0206                 }
0207             }
0208         }
0209     }
0210 }
0211 
0212 void
0213 XmlQueryReader::readAndOr()
0214 {
0215     readFilters();
0216     ignoreElements();
0217     d->qm->endAndOr();
0218 }
0219 
0220 XmlQueryReader::Filter
0221 XmlQueryReader::readFilter(QXmlStreamReader *reader)
0222 {
0223     Filter filter;
0224 
0225     QXmlStreamAttributes attr = reader->attributes();
0226 
0227     filter.exclude = (reader->name() != "include");
0228     filter.field = Meta::fieldForName( attr.value( QStringLiteral("field") ).toString() );
0229     filter.value = attr.value( QStringLiteral("value") ).toString();
0230 
0231     QStringRef compareStr = attr.value( QStringLiteral("compare") );
0232     if( compareStr.isEmpty() )
0233         filter.compare = -1;
0234     else
0235         filter.compare = compareVal( compareStr );
0236 
0237     return filter;
0238 }
0239 
0240 void
0241 XmlQueryReader::readFilters()
0242 {
0243     while( !atEnd() )
0244     {
0245         readNext();
0246         if( isEndElement() )
0247         {
0248             if( name() == "and" || name() == "or" )
0249             {
0250                 d->qm->endAndOr();
0251                 break;
0252             }
0253             else if( name() == "filters" )
0254             {
0255                 break;
0256             }
0257             else
0258                 continue;
0259         }
0260 
0261         if( name() == "include" || name() == "exclude" )
0262         {
0263             Filter filter = readFilter(this);
0264 
0265             if( filter.field == 0 )
0266                 break;
0267 
0268             if( filter.compare != -1 )
0269             {
0270                 qint64 numValue = filter.value.toInt();
0271                 if( !filter.exclude )
0272                 {
0273                     debug() << "XQR: number include filter:";
0274                     d->qm->addNumberFilter( filter.field, numValue,
0275                             (Collections::QueryMaker::NumberComparison)filter.compare );
0276                 }
0277                 else
0278                 {
0279                     debug() << "XQR: number exclude filter: ";
0280                     d->qm->excludeNumberFilter( filter.field, numValue,
0281                             (Collections::QueryMaker::NumberComparison)filter.compare );
0282                 }
0283             }
0284             else
0285             {
0286                 if( !filter.exclude )
0287                 {
0288                     debug() << "XQR: include filter";
0289                     d->qm->addFilter( filter.field, filter.value );
0290                 }
0291                 else
0292                 {
0293                     debug() << "XQR: exclude filter";
0294                     d->qm->excludeFilter( filter.field, filter.value );
0295                 }
0296             }
0297 
0298             d->filters.append( filter );
0299         }
0300         else if( name() == "and" )
0301         {
0302             d->qm->beginAnd();
0303             readFilters();
0304         }
0305         else if( name() == "or" )
0306         {
0307             d->qm->beginOr();
0308             readFilters();
0309         }
0310     }
0311 }
0312 
0313 int
0314 XmlQueryReader::compareVal( QStringRef compare )
0315 {
0316     if( compare == "less" )
0317         return Collections::QueryMaker::LessThan;
0318     else if( compare == "greater" )
0319         return Collections::QueryMaker::GreaterThan;
0320     else if( compare == "equals" )
0321         return Collections::QueryMaker::Equals;
0322     else
0323         return -1;
0324 }