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"