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 }