File indexing completed on 2024-05-19 04:49:17
0001 /**************************************************************************************** 0002 * Copyright (c) 2007-2009 Maximilian Kossick <maximilian.kossick@googlemail.com> * 0003 * Copyright (c) 2007-2009 Nikolaj Hald Nielsen <nhn@kde.org> * 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 "MemoryQueryMaker.h" 0019 #include "MemoryCustomValue.h" 0020 #include "MemoryFilter.h" 0021 #include "MemoryMatcher.h" 0022 #include "MemoryQueryMakerHelper.h" 0023 #include "MemoryQueryMakerInternal.h" 0024 #include "core/support/Debug.h" 0025 0026 #include <ThreadWeaver/Queue> 0027 0028 #include <QList> 0029 #include <QSet> 0030 #include <QStack> 0031 #include <QtAlgorithms> 0032 //#include <QRandomGenerator> 0033 0034 #include <KSortableList> 0035 0036 using namespace Collections; 0037 0038 //QueryJob 0039 0040 class QueryJob : public QObject, public ThreadWeaver::Job 0041 { 0042 Q_OBJECT 0043 public: 0044 QueryJob( MemoryQueryMakerInternal *qmInternal ) 0045 : QObject() 0046 , ThreadWeaver::Job() 0047 , queryMakerInternal( qmInternal ) 0048 { 0049 //nothing to do 0050 } 0051 0052 ~QueryJob() override 0053 { 0054 delete queryMakerInternal; 0055 } 0056 0057 protected: 0058 void defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) override 0059 { 0060 Q_EMIT started(self); 0061 ThreadWeaver::Job::defaultBegin(self, thread); 0062 } 0063 0064 void defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread) override 0065 { 0066 ThreadWeaver::Job::defaultEnd(self, thread); 0067 if (!self->success()) { 0068 Q_EMIT failed(self); 0069 } 0070 Q_EMIT done(self); 0071 } 0072 void run(ThreadWeaver::JobPointer self = QSharedPointer<ThreadWeaver::Job>(), ThreadWeaver::Thread *thread=nullptr) override 0073 { 0074 Q_UNUSED(self); 0075 Q_UNUSED(thread); 0076 queryMakerInternal->runQuery(); 0077 // setFinished( true ); 0078 setStatus(Status_Success); 0079 } 0080 0081 public: 0082 MemoryQueryMakerInternal *queryMakerInternal; 0083 0084 Q_SIGNALS: 0085 /** This signal is emitted when this job is being processed by a thread. */ 0086 void started(ThreadWeaver::JobPointer); 0087 /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */ 0088 void done(ThreadWeaver::JobPointer); 0089 /** This job has failed. 0090 * This signal is emitted when success() returns false after the job is executed. */ 0091 void failed(ThreadWeaver::JobPointer); 0092 0093 }; 0094 0095 struct MemoryQueryMaker::Private { 0096 QueryMaker::QueryType type; 0097 bool returnDataPtrs; 0098 MemoryMatcher* matcher; 0099 QueryJob *job; 0100 int maxsize; 0101 QStack<ContainerMemoryFilter*> containerFilters; 0102 QList<CustomReturnFunction*> returnFunctions; 0103 QList<CustomReturnValue*> returnValues; 0104 bool usingFilters; 0105 qint64 orderByField; 0106 bool orderDescending; 0107 bool orderByNumberField; 0108 AlbumQueryMode albumQueryMode; 0109 LabelQueryMode labelQueryMode; 0110 QString collectionId; 0111 }; 0112 0113 MemoryQueryMaker::MemoryQueryMaker( const QWeakPointer<MemoryCollection> &mc, const QString &collectionId ) 0114 : QueryMaker() 0115 , m_collection( mc ) 0116 , d( new Private ) 0117 { 0118 d->collectionId = collectionId; 0119 d->matcher = nullptr; 0120 d->job = nullptr; 0121 d->type = QueryMaker::None; 0122 d->returnDataPtrs = false; 0123 d->job = nullptr; 0124 d->job = nullptr; 0125 d->maxsize = -1; 0126 d->containerFilters.push( new AndContainerMemoryFilter() ); 0127 d->usingFilters = false; 0128 d->orderByField = 0; 0129 d->orderDescending = false; 0130 d->orderByNumberField = false; 0131 d->albumQueryMode = AllAlbums; 0132 d->labelQueryMode = QueryMaker::NoConstraint; 0133 } 0134 0135 MemoryQueryMaker::~MemoryQueryMaker() 0136 { 0137 disconnect(); 0138 abortQuery(); 0139 if( !d->containerFilters.isEmpty() ) 0140 delete d->containerFilters.first(); 0141 delete d; 0142 } 0143 0144 void 0145 MemoryQueryMaker::run() 0146 { 0147 if ( d->type == QueryMaker::None ) 0148 //TODO error handling 0149 return; 0150 else if( d->job && !d->job->isFinished() ) 0151 { 0152 //the worker thread seems to be running 0153 //TODO: wait or job to complete 0154 } 0155 else 0156 { 0157 MemoryQueryMakerInternal *qmi = new MemoryQueryMakerInternal( m_collection ); 0158 if( d->usingFilters ) 0159 { 0160 qmi->setFilters( d->containerFilters.first() ); 0161 d->containerFilters.clear(); //will be deleted by MemoryQueryMakerInternal 0162 } 0163 qmi->setMatchers( d->matcher ); 0164 d->matcher = nullptr; //will be deleted by MemoryQueryMakerInternal 0165 qmi->setMaxSize( d->maxsize ); 0166 qmi->setType( d->type ); 0167 qmi->setCustomReturnFunctions( d->returnFunctions ); 0168 d->returnFunctions.clear(); //will be deleted by MemoryQueryMakerInternal 0169 qmi->setCustomReturnValues( d->returnValues ); 0170 d->returnValues.clear(); //will be deleted by MemoryQueryMakerInternal 0171 qmi->setAlbumQueryMode( d->albumQueryMode ); 0172 qmi->setLabelQueryMode( d->labelQueryMode ); 0173 qmi->setOrderDescending( d->orderDescending ); 0174 qmi->setOrderByNumberField( d->orderByNumberField ); 0175 qmi->setOrderByField( d->orderByField ); 0176 qmi->setCollectionId( d->collectionId ); 0177 0178 connect( qmi, &Collections::MemoryQueryMakerInternal::newAlbumsReady, this, &MemoryQueryMaker::newAlbumsReady, Qt::DirectConnection ); 0179 connect( qmi, &Collections::MemoryQueryMakerInternal::newArtistsReady, this, &MemoryQueryMaker::newArtistsReady, Qt::DirectConnection ); 0180 connect( qmi, &Collections::MemoryQueryMakerInternal::newGenresReady, this, &MemoryQueryMaker::newGenresReady, Qt::DirectConnection ); 0181 connect( qmi, &Collections::MemoryQueryMakerInternal::newComposersReady, this, &MemoryQueryMaker::newComposersReady, Qt::DirectConnection ); 0182 connect( qmi, &Collections::MemoryQueryMakerInternal::newYearsReady, this, &MemoryQueryMaker::newYearsReady, Qt::DirectConnection ); 0183 connect( qmi, &Collections::MemoryQueryMakerInternal::newTracksReady, this, &MemoryQueryMaker::newTracksReady, Qt::DirectConnection ); 0184 connect( qmi, &Collections::MemoryQueryMakerInternal::newResultReady, this, &MemoryQueryMaker::newResultReady, Qt::DirectConnection ); 0185 connect( qmi, &Collections::MemoryQueryMakerInternal::newLabelsReady, this, &MemoryQueryMaker::newLabelsReady, Qt::DirectConnection ); 0186 0187 d->job = new QueryJob( qmi ); 0188 connect( d->job, &QueryJob::done, this, &MemoryQueryMaker::done ); 0189 ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(d->job) ); 0190 } 0191 } 0192 0193 void 0194 MemoryQueryMaker::abortQuery() 0195 { 0196 if( d->job ) 0197 { 0198 d->job->requestAbort(); 0199 d->job->disconnect( this ); 0200 if( d->job->queryMakerInternal ) 0201 d->job->queryMakerInternal->disconnect( this ); 0202 } 0203 } 0204 0205 QueryMaker* 0206 MemoryQueryMaker::setQueryType( QueryType type ) 0207 { 0208 switch( type ) { 0209 case QueryMaker::Track: 0210 if ( d->type == QueryMaker::None ) 0211 d->type = QueryMaker::Track; 0212 return this; 0213 0214 case QueryMaker::Artist: 0215 if ( d->type == QueryMaker::None ) 0216 d->type = QueryMaker::Artist; 0217 return this; 0218 0219 case QueryMaker::Album: 0220 if ( d->type == QueryMaker::None ) 0221 d->type = QueryMaker::Album; 0222 return this; 0223 0224 case QueryMaker::AlbumArtist: 0225 if ( d->type == QueryMaker::None ) 0226 d->type = QueryMaker::AlbumArtist; 0227 return this; 0228 0229 case QueryMaker::Composer: 0230 if ( d->type == QueryMaker::None ) 0231 d->type = QueryMaker::Composer; 0232 return this; 0233 0234 case QueryMaker::Genre: 0235 if ( d->type == QueryMaker::None ) 0236 d->type = QueryMaker::Genre; 0237 return this; 0238 0239 case QueryMaker::Year: 0240 if ( d->type == QueryMaker::None ) 0241 d->type = QueryMaker::Year; 0242 return this; 0243 0244 case QueryMaker::Custom: 0245 if ( d->type == QueryMaker::None ) 0246 d->type = QueryMaker::Custom; 0247 return this; 0248 0249 case QueryMaker::Label: 0250 if( d->type == QueryMaker::None ) 0251 d->type = QueryMaker::Label; 0252 return this; 0253 0254 case QueryMaker::None: 0255 return this; 0256 } 0257 return this; 0258 } 0259 0260 QueryMaker* 0261 MemoryQueryMaker::addReturnValue( qint64 value ) 0262 { 0263 //MQM can not deliver sensible results if both a custom return value and a return function is selected 0264 if( d->returnFunctions.empty() ) 0265 { 0266 CustomReturnValue *returnValue = CustomValueFactory::returnValue( value ); 0267 if( returnValue ) 0268 d->returnValues.append( returnValue ); 0269 } 0270 return this; 0271 } 0272 0273 QueryMaker* 0274 MemoryQueryMaker::addReturnFunction( ReturnFunction function, qint64 value ) 0275 { 0276 //MQM can not deliver sensible results if both a custom return value and a return function is selected 0277 if( d->returnValues.empty() ) 0278 { 0279 CustomReturnFunction *returnFunction = CustomValueFactory::returnFunction( function, value ); 0280 if( returnFunction ) 0281 d->returnFunctions.append( returnFunction ); 0282 } 0283 return this; 0284 } 0285 0286 QueryMaker* 0287 MemoryQueryMaker::orderBy( qint64 value, bool descending ) 0288 { 0289 d->orderByField = value; 0290 d->orderDescending = descending; 0291 switch( value ) 0292 { 0293 case Meta::valYear: 0294 case Meta::valDiscNr: 0295 case Meta::valTrackNr: 0296 case Meta::valScore: 0297 case Meta::valRating: 0298 case Meta::valPlaycount: 0299 case Meta::valFilesize: 0300 case Meta::valSamplerate: 0301 case Meta::valBitrate: 0302 case Meta::valLength: 0303 { 0304 d->orderByNumberField = true; 0305 break; 0306 } 0307 //TODO: what about Meta::valFirstPlayed, Meta::valCreateDate or Meta::valLastPlayed?? 0308 0309 default: 0310 d->orderByNumberField = false; 0311 } 0312 return this; 0313 } 0314 0315 QueryMaker* 0316 MemoryQueryMaker::addMatch( const Meta::TrackPtr &track ) 0317 { 0318 MemoryMatcher *trackMatcher = new TrackMatcher( track ); 0319 if ( d->matcher == nullptr ) 0320 d->matcher = trackMatcher; 0321 else 0322 { 0323 MemoryMatcher *tmp = d->matcher; 0324 while ( !tmp->isLast() ) 0325 tmp = tmp->next(); 0326 tmp->setNext( trackMatcher ); 0327 } 0328 return this; 0329 } 0330 0331 QueryMaker* 0332 MemoryQueryMaker::addMatch( const Meta::ArtistPtr &artist, ArtistMatchBehaviour behaviour ) 0333 { 0334 MemoryMatcher *artistMatcher = new ArtistMatcher( artist, behaviour ); 0335 if ( d->matcher == nullptr ) 0336 d->matcher = artistMatcher; 0337 else 0338 { 0339 MemoryMatcher *tmp = d->matcher; 0340 while ( !tmp->isLast() ) 0341 tmp = tmp->next(); 0342 tmp->setNext( artistMatcher ); 0343 } 0344 return this; 0345 } 0346 0347 QueryMaker* 0348 MemoryQueryMaker::addMatch( const Meta::AlbumPtr &album ) 0349 { 0350 MemoryMatcher *albumMatcher = new AlbumMatcher( album ); 0351 if ( d->matcher == nullptr ) 0352 d->matcher = albumMatcher; 0353 else 0354 { 0355 MemoryMatcher *tmp = d->matcher; 0356 while ( !tmp->isLast() ) 0357 tmp = tmp->next(); 0358 tmp->setNext( albumMatcher ); 0359 } 0360 return this; 0361 } 0362 0363 QueryMaker* 0364 MemoryQueryMaker::addMatch( const Meta::GenrePtr &genre ) 0365 { 0366 MemoryMatcher *genreMatcher = new GenreMatcher( genre ); 0367 if ( d->matcher == nullptr ) 0368 d->matcher = genreMatcher; 0369 else 0370 { 0371 MemoryMatcher *tmp = d->matcher; 0372 while ( !tmp->isLast() ) 0373 tmp = tmp->next(); 0374 tmp->setNext( genreMatcher ); 0375 } 0376 return this; 0377 } 0378 0379 QueryMaker* 0380 MemoryQueryMaker::addMatch( const Meta::ComposerPtr &composer ) 0381 { 0382 MemoryMatcher *composerMatcher = new ComposerMatcher( composer ); 0383 if ( d->matcher == nullptr ) 0384 d->matcher = composerMatcher; 0385 else 0386 { 0387 MemoryMatcher *tmp = d->matcher; 0388 while ( !tmp->isLast() ) 0389 tmp = tmp->next(); 0390 tmp->setNext( composerMatcher ); 0391 } 0392 return this; 0393 } 0394 0395 QueryMaker* 0396 MemoryQueryMaker::addMatch( const Meta::YearPtr &year ) 0397 { 0398 MemoryMatcher *yearMatcher = new YearMatcher( year ); 0399 if ( d->matcher == nullptr ) 0400 d->matcher = yearMatcher; 0401 else 0402 { 0403 MemoryMatcher *tmp = d->matcher; 0404 while ( !tmp->isLast() ) 0405 tmp = tmp->next(); 0406 tmp->setNext( yearMatcher ); 0407 } 0408 return this; 0409 } 0410 0411 QueryMaker* 0412 MemoryQueryMaker::addMatch( const Meta::LabelPtr &label ) 0413 { 0414 MemoryMatcher *labelMatcher = new LabelMatcher( label ); 0415 if( d->matcher == nullptr ) 0416 { 0417 d->matcher = labelMatcher; 0418 } 0419 else 0420 { 0421 MemoryMatcher *tmp = d->matcher; 0422 while( !tmp->isLast() ) 0423 { 0424 tmp = tmp->next(); 0425 } 0426 tmp->setNext( labelMatcher ); 0427 } 0428 return this; 0429 } 0430 0431 QueryMaker* 0432 MemoryQueryMaker::addFilter( qint64 value, const QString &filter, bool matchBegin, bool matchEnd ) 0433 { 0434 d->containerFilters.top()->addFilter( FilterFactory::filter( value, filter, matchBegin, matchEnd ) ); 0435 d->usingFilters = true; 0436 return this; 0437 } 0438 0439 QueryMaker* 0440 MemoryQueryMaker::excludeFilter( qint64 value, const QString &filter, bool matchBegin, bool matchEnd ) 0441 { 0442 MemoryFilter *tmp = FilterFactory::filter( value, filter, matchBegin, matchEnd ); 0443 d->containerFilters.top()->addFilter( new NegateMemoryFilter( tmp ) ); 0444 d->usingFilters = true; 0445 return this; 0446 } 0447 0448 QueryMaker* 0449 MemoryQueryMaker::addNumberFilter( qint64 value, qint64 filter, NumberComparison compare ) 0450 { 0451 d->containerFilters.top()->addFilter( FilterFactory::numberFilter( value, filter, compare ) ); 0452 d->usingFilters = true; 0453 return this; 0454 } 0455 0456 QueryMaker* 0457 MemoryQueryMaker::excludeNumberFilter( qint64 value, qint64 filter, NumberComparison compare ) 0458 { 0459 MemoryFilter *tmp = FilterFactory::numberFilter( value, filter, compare ); 0460 d->containerFilters.top()->addFilter( new NegateMemoryFilter( tmp ) ); 0461 d->usingFilters = true; 0462 return this; 0463 } 0464 0465 QueryMaker* 0466 MemoryQueryMaker::limitMaxResultSize( int size ) 0467 { 0468 d->maxsize = size; 0469 return this; 0470 } 0471 0472 QueryMaker* 0473 MemoryQueryMaker::beginAnd() 0474 { 0475 ContainerMemoryFilter *filter = new AndContainerMemoryFilter(); 0476 d->containerFilters.top()->addFilter( filter ); 0477 d->containerFilters.push( filter ); 0478 return this; 0479 } 0480 0481 QueryMaker* 0482 MemoryQueryMaker::beginOr() 0483 { 0484 ContainerMemoryFilter *filter = new OrContainerMemoryFilter(); 0485 d->containerFilters.top()->addFilter( filter ); 0486 d->containerFilters.push( filter ); 0487 return this; 0488 } 0489 0490 QueryMaker* 0491 MemoryQueryMaker::endAndOr() 0492 { 0493 d->containerFilters.pop(); 0494 return this; 0495 } 0496 0497 void 0498 MemoryQueryMaker::done( ThreadWeaver::JobPointer job ) 0499 { 0500 ThreadWeaver::Queue::instance()->dequeue( job ); 0501 d->job = nullptr; 0502 Q_EMIT queryDone(); 0503 } 0504 0505 QueryMaker * MemoryQueryMaker::setAlbumQueryMode( AlbumQueryMode mode ) 0506 { 0507 d->albumQueryMode = mode; 0508 return this; 0509 } 0510 0511 QueryMaker* 0512 MemoryQueryMaker::setLabelQueryMode( LabelQueryMode mode ) 0513 { 0514 d->labelQueryMode = mode; 0515 return this; 0516 } 0517 0518 #include "MemoryQueryMaker.moc"