File indexing completed on 2025-01-05 04:25:51

0001 /****************************************************************************************
0002  * Copyright (c) 2009 Maximilian Kossick <maximilian.kossick@googlemail.com>            *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #define DEBUG_PREFIX "AggregateQueryMaker"
0018 
0019 #include "AggregateQueryMaker.h"
0020 
0021 #include "core/meta/Meta.h"
0022 #include "core/support/Debug.h"
0023 #include "core-impl/collections/aggregate/AggregateCollection.h"
0024 #include "core-impl/collections/support/MemoryCustomValue.h"
0025 #include "core-impl/collections/support/MemoryQueryMakerHelper.h"
0026 
0027 #include <QMetaEnum>
0028 #include <QMetaObject>
0029 
0030 #include <typeinfo>
0031 
0032 using namespace Collections;
0033 
0034 AggregateQueryMaker::AggregateQueryMaker( AggregateCollection *collection, const QList<QueryMaker*> &queryMakers )
0035     : QueryMaker()
0036     , m_collection( collection )
0037     , m_builders( queryMakers )
0038     , m_queryDoneCount( 0 )
0039     , m_maxResultSize( -1 )
0040     , m_queryType( QueryMaker::None )
0041     , m_orderDescending( false )
0042     , m_orderField( 0 )
0043     , m_orderByNumberField( false )
0044     , m_queryDoneCountMutex()
0045 {
0046     foreach( QueryMaker *b, m_builders )
0047     {
0048         connect( b, &Collections::QueryMaker::queryDone, this, &AggregateQueryMaker::slotQueryDone );
0049         connect( b, &Collections::QueryMaker::newTracksReady, this, &AggregateQueryMaker::slotNewTracksReady, Qt::QueuedConnection );
0050         connect( b, &Collections::QueryMaker::newArtistsReady, this, &AggregateQueryMaker::slotNewArtistsReady, Qt::QueuedConnection );
0051         connect( b, &Collections::QueryMaker::newAlbumsReady, this, &AggregateQueryMaker::slotNewAlbumsReady, Qt::QueuedConnection );
0052         connect( b, &Collections::QueryMaker::newGenresReady, this, &AggregateQueryMaker::slotNewGenresReady, Qt::QueuedConnection );
0053         connect( b, &Collections::QueryMaker::newComposersReady, this, &AggregateQueryMaker::slotNewComposersReady, Qt::QueuedConnection );
0054         connect( b, &Collections::QueryMaker::newYearsReady, this, &AggregateQueryMaker::slotNewYearsReady, Qt::QueuedConnection );
0055         connect( b, &Collections::QueryMaker::newLabelsReady, this, &AggregateQueryMaker::slotNewLabelsReady, Qt::QueuedConnection );
0056     }
0057 }
0058 
0059 AggregateQueryMaker::~AggregateQueryMaker()
0060 {
0061     qDeleteAll( m_returnFunctions );
0062     qDeleteAll( m_returnValues );
0063     qDeleteAll( m_builders );
0064 }
0065 
0066 void
0067 AggregateQueryMaker::run()
0068 {
0069     foreach( QueryMaker *b, m_builders )
0070         b->run();
0071 }
0072 
0073 void
0074 AggregateQueryMaker::abortQuery()
0075 {
0076     foreach( QueryMaker *b, m_builders )
0077         b->abortQuery();
0078 }
0079 
0080 QueryMaker*
0081 AggregateQueryMaker::setQueryType( QueryType type )
0082 {
0083     m_queryType = type;
0084     if( type != QueryMaker::Custom )
0085     {
0086         foreach( QueryMaker *b, m_builders )
0087             b->setQueryType( type );
0088         return this;
0089     }
0090     else
0091     {
0092         // we cannot forward custom queries as there is no way to integrate the results
0093         // delivered by the QueryMakers. Instead we ask for tracks that match the criterias,
0094         // and then generate the custom result similar to MemoryQueryMaker.
0095         // And yes, this means that we will load all tracks when we simply want the count of tracks
0096         // in the collection. It might be necessary to add some specific logic for that case.
0097         // On second thought, there is no way around loading all objects, as we want to operate on distinct
0098         // elements (for some value of distinct) in AggregateCollection. We can only figure out what the union
0099         // of all elements is after loading them in memory
0100         foreach( QueryMaker *b, m_builders )
0101             b->setQueryType( QueryMaker::Track );
0102         return this;
0103     }
0104 }
0105 
0106 QueryMaker*
0107 AggregateQueryMaker::addReturnValue( qint64 value )
0108 {
0109     //do not forward this call, see comment in setQueryType()
0110     m_returnValues.append( CustomValueFactory::returnValue( value ) );
0111     return this;
0112 }
0113 
0114 QueryMaker*
0115 AggregateQueryMaker::addReturnFunction( ReturnFunction function, qint64 value )
0116 {
0117     //do not forward this call, see comment in setQueryType()
0118     m_returnFunctions.append( CustomValueFactory::returnFunction( function, value ) );
0119     return this;
0120 }
0121 
0122 QueryMaker*
0123 AggregateQueryMaker::orderBy( qint64 value, bool descending )
0124 {
0125     m_orderField = value;
0126     m_orderDescending = descending;
0127     //copied from MemoryQueryMaker. TODO: think of a sensible place to put this code
0128     switch( value )
0129     {
0130         case Meta::valYear:
0131         case Meta::valTrackNr:
0132         case Meta::valDiscNr:
0133         case Meta::valBpm:
0134         case Meta::valLength:
0135         case Meta::valBitrate:
0136         case Meta::valSamplerate:
0137         case Meta::valFilesize:
0138         case Meta::valFormat:
0139         case Meta::valCreateDate:
0140         case Meta::valScore:
0141         case Meta::valRating:
0142         case Meta::valFirstPlayed:
0143         case Meta::valLastPlayed:
0144         case Meta::valPlaycount:
0145         case Meta::valModified:
0146         {
0147             m_orderByNumberField = true;
0148             break;
0149         }
0150         default:
0151             m_orderByNumberField = false;
0152     }
0153     foreach( QueryMaker *b, m_builders )
0154         b->orderBy( value, descending );
0155     return this;
0156 }
0157 
0158 QueryMaker*
0159 AggregateQueryMaker::addFilter( qint64 value, const QString &filter, bool matchBegin, bool matchEnd )
0160 {
0161     foreach( QueryMaker *b, m_builders )
0162         b->addFilter( value, filter, matchBegin, matchEnd );
0163     return this;
0164 }
0165 
0166 QueryMaker*
0167 AggregateQueryMaker::excludeFilter( qint64 value, const QString &filter, bool matchBegin, bool matchEnd )
0168 {
0169     foreach( QueryMaker *b, m_builders )
0170         b->excludeFilter( value, filter, matchBegin, matchEnd );
0171     return this;
0172 }
0173 
0174 QueryMaker*
0175 AggregateQueryMaker::addNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare )
0176 {
0177     foreach( QueryMaker *b, m_builders )
0178         b->addNumberFilter( value, filter, compare);
0179     return this;
0180 }
0181 
0182 QueryMaker*
0183 AggregateQueryMaker::excludeNumberFilter( qint64 value, qint64 filter, QueryMaker::NumberComparison compare )
0184 {
0185     foreach( QueryMaker *b, m_builders )
0186         b->excludeNumberFilter( value, filter, compare );
0187     return this;
0188 }
0189 
0190 QueryMaker*
0191 AggregateQueryMaker::addMatch( const Meta::TrackPtr &track )
0192 {
0193     foreach( QueryMaker *b, m_builders )
0194         b->addMatch( track );
0195     return this;
0196 }
0197 
0198 QueryMaker*
0199 AggregateQueryMaker::addMatch( const Meta::ArtistPtr &artist, QueryMaker::ArtistMatchBehaviour behaviour )
0200 {
0201     foreach( QueryMaker *b, m_builders )
0202         b->addMatch( artist, behaviour );
0203     return this;
0204 }
0205 
0206 QueryMaker*
0207 AggregateQueryMaker::addMatch( const Meta::AlbumPtr &album )
0208 {
0209     foreach( QueryMaker *b, m_builders )
0210         b->addMatch( album );
0211     return this;
0212 }
0213 
0214 QueryMaker*
0215 AggregateQueryMaker::addMatch( const Meta::GenrePtr &genre )
0216 {
0217     foreach( QueryMaker *b, m_builders )
0218         b->addMatch( genre );
0219     return this;
0220 }
0221 
0222 QueryMaker*
0223 AggregateQueryMaker::addMatch( const Meta::ComposerPtr &composer )
0224 {
0225     foreach( QueryMaker *b, m_builders )
0226         b->addMatch( composer );
0227     return this;
0228 }
0229 
0230 QueryMaker*
0231 AggregateQueryMaker::addMatch( const Meta::YearPtr &year )
0232 {
0233     foreach( QueryMaker *b, m_builders )
0234         b->addMatch( year );
0235     return this;
0236 }
0237 
0238 QueryMaker*
0239 AggregateQueryMaker::addMatch( const Meta::LabelPtr &label )
0240 {
0241     foreach( QueryMaker *b, m_builders )
0242         b->addMatch( label );
0243     return this;
0244 }
0245 
0246 QueryMaker*
0247 AggregateQueryMaker::limitMaxResultSize( int size )
0248 {
0249     //forward the call so the m_builders do not have to do work
0250     //that we definitely know is unnecessary (like returning more than size results)
0251     //we have to limit the combined result of all m_builders nevertheless
0252     m_maxResultSize = size;
0253     foreach( QueryMaker *b, m_builders )
0254         b->limitMaxResultSize( size );
0255     return this;
0256 }
0257 
0258 QueryMaker*
0259 AggregateQueryMaker::beginAnd()
0260 {
0261     foreach( QueryMaker *b, m_builders )
0262         b->beginAnd();
0263     return this;
0264 }
0265 
0266 QueryMaker*
0267 AggregateQueryMaker::beginOr()
0268 {
0269     foreach( QueryMaker *b, m_builders )
0270         b->beginOr();
0271     return this;
0272 }
0273 
0274 QueryMaker*
0275 AggregateQueryMaker::endAndOr()
0276 {
0277     foreach( QueryMaker *b, m_builders )
0278         b->endAndOr();
0279     return this;
0280 }
0281 
0282 QueryMaker*
0283 AggregateQueryMaker::setAlbumQueryMode( AlbumQueryMode mode )
0284 {
0285     foreach( QueryMaker *b, m_builders )
0286         b->setAlbumQueryMode( mode );
0287     return this;
0288 }
0289 
0290 QueryMaker*
0291 AggregateQueryMaker::setLabelQueryMode( LabelQueryMode mode )
0292 {
0293     foreach( QueryMaker *b, m_builders )
0294         b->setLabelQueryMode( mode );
0295     return this;
0296 }
0297 
0298 void
0299 AggregateQueryMaker::slotQueryDone()
0300 {
0301     m_queryDoneCountMutex.lock();
0302     m_queryDoneCount++;
0303     if ( m_queryDoneCount == m_builders.size() )
0304     {
0305         //make sure we don't give control to code outside this class while holding the lock
0306         m_queryDoneCountMutex.unlock();
0307         handleResult();
0308         Q_EMIT queryDone();
0309     }
0310     else
0311     {
0312         m_queryDoneCountMutex.unlock();
0313     }
0314 }
0315 
0316 void
0317 AggregateQueryMaker::handleResult()
0318 {
0319     //copied from MemoryQueryMaker::handleResult()
0320     switch( m_queryType )
0321     {
0322         case QueryMaker::Custom :
0323         {
0324             QStringList result;
0325             Meta::TrackList tracks;
0326             foreach( AmarokSharedPointer<Meta::AggregateTrack> track, m_tracks )
0327             {
0328                 tracks.append( Meta::TrackPtr::staticCast( track ) );
0329             }
0330             if( !m_returnFunctions.empty() )
0331             {
0332                 //no sorting necessary
0333                 foreach( CustomReturnFunction *function, m_returnFunctions )
0334                 {
0335                     result.append( function->value( tracks ) );
0336                 }
0337             }
0338             else if( !m_returnValues.empty() )
0339             {
0340                 if( m_orderField )
0341                 {
0342                     if( m_orderByNumberField )
0343                         tracks = MemoryQueryMakerHelper::orderListByNumber( tracks, m_orderField, m_orderDescending );
0344                     else
0345                         tracks = MemoryQueryMakerHelper::orderListByString( tracks, m_orderField, m_orderDescending );
0346                 }
0347 
0348                 int count = 0;
0349                 foreach( const Meta::TrackPtr &track, tracks )
0350                 {
0351                     if ( m_maxResultSize >= 0 && count == m_maxResultSize )
0352                         break;
0353 
0354                     foreach( CustomReturnValue *value, m_returnValues )
0355                     {
0356                         result.append( value->value( track ) );
0357                     }
0358                     count++;
0359                 }
0360             }
0361             Q_EMIT newResultReady( result );
0362             break;
0363         }
0364         case QueryMaker::Track :
0365         {
0366             Meta::TrackList tracks;
0367             foreach( AmarokSharedPointer<Meta::AggregateTrack> track, m_tracks )
0368             {
0369                 tracks.append( Meta::TrackPtr::staticCast( track ) );
0370             }
0371 
0372             if( m_orderField )
0373             {
0374                 if( m_orderByNumberField )
0375                     tracks = MemoryQueryMakerHelper::orderListByNumber( tracks, m_orderField, m_orderDescending );
0376                 else
0377                     tracks = MemoryQueryMakerHelper::orderListByString( tracks, m_orderField, m_orderDescending );
0378             }
0379 
0380             if ( m_maxResultSize >= 0 && tracks.count() > m_maxResultSize )
0381                 tracks = tracks.mid( 0, m_maxResultSize );
0382 
0383             Q_EMIT newTracksReady(tracks);
0384             break;
0385         }
0386         case QueryMaker::Album :
0387         {
0388             Meta::AlbumList albums;
0389             foreach( AmarokSharedPointer<Meta::AggregateAlbum> album, m_albums )
0390             {
0391                 albums.append( Meta::AlbumPtr::staticCast( album ) );
0392             }
0393 
0394             albums = MemoryQueryMakerHelper::orderListByName<Meta::AlbumPtr>( albums, m_orderDescending );
0395 
0396             if ( m_maxResultSize >= 0 && albums.count() > m_maxResultSize )
0397                 albums = albums.mid( 0, m_maxResultSize );
0398 
0399             Q_EMIT newAlbumsReady(albums);
0400             break;
0401         }
0402         case QueryMaker::Artist :
0403         case QueryMaker::AlbumArtist :
0404         {
0405             Meta::ArtistList artists;
0406             foreach( AmarokSharedPointer<Meta::AggregateArtist> artist, m_artists )
0407             {
0408                 artists.append( Meta::ArtistPtr::staticCast( artist ) );
0409             }
0410 
0411             artists = MemoryQueryMakerHelper::orderListByName<Meta::ArtistPtr>( artists, m_orderDescending );
0412 
0413             if ( m_maxResultSize >= 0 && artists.count() > m_maxResultSize )
0414                 artists = artists.mid( 0, m_maxResultSize );
0415 
0416             Q_EMIT newArtistsReady(artists);
0417             break;
0418         }
0419         case QueryMaker::Composer :
0420         {
0421             Meta::ComposerList composers;
0422             foreach( AmarokSharedPointer<Meta::AggregateComposer> composer, m_composers )
0423             {
0424                 composers.append( Meta::ComposerPtr::staticCast( composer ) );
0425             }
0426 
0427             composers = MemoryQueryMakerHelper::orderListByName<Meta::ComposerPtr>( composers, m_orderDescending );
0428 
0429             if ( m_maxResultSize >= 0 && composers.count() > m_maxResultSize )
0430                 composers = composers.mid( 0, m_maxResultSize );
0431 
0432             Q_EMIT newComposersReady(composers);
0433             break;
0434         }
0435         case QueryMaker::Genre :
0436         {
0437             Meta::GenreList genres;
0438             foreach( AmarokSharedPointer<Meta::AggregateGenre> genre, m_genres )
0439             {
0440                 genres.append( Meta::GenrePtr::staticCast( genre ) );
0441             }
0442 
0443             genres = MemoryQueryMakerHelper::orderListByName<Meta::GenrePtr>( genres, m_orderDescending );
0444 
0445             if ( m_maxResultSize >= 0 && genres.count() > m_maxResultSize )
0446                 genres = genres.mid( 0, m_maxResultSize );
0447 
0448             Q_EMIT newGenresReady(genres);
0449             break;
0450         }
0451         case QueryMaker::Year :
0452         {
0453             Meta::YearList years;
0454             foreach( AmarokSharedPointer<Meta::AggreagateYear> year, m_years )
0455             {
0456                 years.append( Meta::YearPtr::staticCast( year ) );
0457             }
0458 
0459             //years have to be ordered as numbers, but orderListByNumber does not work for Meta::YearPtrs
0460             if( m_orderField == Meta::valYear )
0461             {
0462                 years = MemoryQueryMakerHelper::orderListByYear( years, m_orderDescending );
0463             }
0464 
0465             if ( m_maxResultSize >= 0 && years.count() > m_maxResultSize )
0466                 years = years.mid( 0, m_maxResultSize );
0467 
0468             Q_EMIT newYearsReady(years);
0469             break;
0470         }
0471         case QueryMaker::Label :
0472         {
0473             Meta::LabelList labels;
0474             foreach( AmarokSharedPointer<Meta::AggregateLabel> label, m_labels )
0475             {
0476                 labels.append( Meta::LabelPtr::staticCast( label ) );
0477             }
0478 
0479             labels = MemoryQueryMakerHelper::orderListByName<Meta::LabelPtr>( labels, m_orderDescending );
0480 
0481             if ( m_maxResultSize >= 0 && labels.count() > m_maxResultSize )
0482                 labels = labels.mid( 0, m_maxResultSize );
0483 
0484             Q_EMIT newLabelsReady(labels);
0485             break;
0486         }
0487         case QueryMaker::None :
0488             //nothing to do
0489             break;
0490     }
0491     m_tracks.clear();
0492     m_albums.clear();
0493     m_artists.clear();
0494     m_composers.clear();
0495     m_genres.clear();
0496     m_years.clear();
0497 }
0498 
0499 void
0500 AggregateQueryMaker::slotNewTracksReady( const Meta::TrackList &tracks )
0501 {
0502     foreach( const Meta::TrackPtr &track, tracks )
0503     {
0504         m_tracks.insert( AmarokSharedPointer<Meta::AggregateTrack>( m_collection->getTrack( track ) ) );
0505     }
0506 }
0507 
0508 void
0509 AggregateQueryMaker::slotNewArtistsReady( const Meta::ArtistList &artists )
0510 {
0511     foreach( const Meta::ArtistPtr &artist, artists )
0512     {
0513         m_artists.insert( AmarokSharedPointer<Meta::AggregateArtist>( m_collection->getArtist( artist ) ) );
0514     }
0515 }
0516 
0517 void
0518 AggregateQueryMaker::slotNewAlbumsReady( const Meta::AlbumList &albums )
0519 {
0520     foreach( const Meta::AlbumPtr &album, albums )
0521     {
0522         m_albums.insert( AmarokSharedPointer<Meta::AggregateAlbum>( m_collection->getAlbum( album ) ) );
0523     }
0524 }
0525 
0526 void
0527 AggregateQueryMaker::slotNewGenresReady( const Meta::GenreList &genres )
0528 {
0529     foreach( const Meta::GenrePtr &genre, genres )
0530     {
0531         m_genres.insert( AmarokSharedPointer<Meta::AggregateGenre>( m_collection->getGenre( genre ) ) );
0532     }
0533 }
0534 
0535 void
0536 AggregateQueryMaker::slotNewComposersReady( const Meta::ComposerList &composers )
0537 {
0538     foreach( const Meta::ComposerPtr &composer, composers )
0539     {
0540         m_composers.insert( AmarokSharedPointer<Meta::AggregateComposer>( m_collection->getComposer( composer ) ) );
0541     }
0542 }
0543 
0544 void
0545 AggregateQueryMaker::slotNewYearsReady( const Meta::YearList &years )
0546 {
0547     foreach( const Meta::YearPtr &year, years )
0548     {
0549         m_years.insert( AmarokSharedPointer<Meta::AggreagateYear>( m_collection->getYear( year ) ) );
0550     }
0551 }
0552 
0553 void
0554 AggregateQueryMaker::slotNewLabelsReady( const Meta::LabelList &labels )
0555 {
0556     foreach( const Meta::LabelPtr &label, labels )
0557     {
0558         m_labels.insert( AmarokSharedPointer<Meta::AggregateLabel>( m_collection->getLabel( label ) ) );
0559     }
0560 }