File indexing completed on 2024-05-05 04:48:29

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Ralf Engels <ralf-engels@gmx.de>                                  *
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 "BiasFactory"
0018 
0019 #include "BiasFactory.h"
0020 
0021 #include "App.h"
0022 #include "biases/AlbumPlayBias.h"
0023 #include "biases/IfElseBias.h"
0024 #include "biases/PartBias.h"
0025 #include "biases/TagMatchBias.h"
0026 #include "biases/SearchQueryBias.h"
0027 #include "biases/QuizPlayBias.h"
0028 #include "biases/EchoNestBias.h"
0029 #include "core/support/Debug.h"
0030 #include "core/collections/QueryMaker.h"
0031 #include "dynamic/Bias.h"
0032 #include "scripting/scriptengine/exporters/ScriptableBiasExporter.h"
0033 
0034 #include <QFormLayout>
0035 #include <QLabel>
0036 #include <QList>
0037 #include <QXmlStreamReader>
0038 
0039 Dynamic::BiasPtr
0040 Dynamic::AbstractBiasFactory::createFromXml( QXmlStreamReader *reader )
0041 {
0042     Dynamic::BiasPtr bias( createBias() );
0043     bias->fromXml( reader );
0044     return bias;
0045 }
0046 
0047 class RandomBiasFactory : public Dynamic::AbstractBiasFactory
0048 {
0049     QString i18nName() const override
0050     { return i18nc("Name of the random bias", "Random"); }
0051 
0052     QString name() const override
0053     { return Dynamic::RandomBias::sName(); }
0054 
0055     QString i18nDescription() const override
0056     { return i18nc("Description of the random bias",
0057                    "The random bias adds random tracks from the\n"
0058                    "whole collection without any bias."); }
0059 
0060     Dynamic::BiasPtr createBias() override
0061     { return Dynamic::BiasPtr( new Dynamic::RandomBias() ); }
0062 };
0063 
0064 
0065 class AndBiasFactory : public Dynamic::AbstractBiasFactory
0066 {
0067     QString i18nName() const override
0068     { return i18nc("Name of the \"And\" bias", "And"); }
0069 
0070     QString name() const override
0071     { return Dynamic::AndBias::sName(); }
0072 
0073     QString i18nDescription() const override
0074     { return i18nc("Description of the \"And\" bias",
0075                    "The \"And\" bias adds tracks that match all\n"
0076                    "of the sub biases."); }
0077 
0078     Dynamic::BiasPtr createBias() override
0079     { return Dynamic::BiasPtr( new Dynamic::AndBias() ); }
0080 };
0081 
0082 
0083 class OrBiasFactory : public Dynamic::AbstractBiasFactory
0084 {
0085     QString i18nName() const override
0086     { return i18nc("Name of the \"Or\" bias", "Or"); }
0087 
0088     QString name() const override
0089     { return Dynamic::OrBias::sName(); }
0090 
0091     QString i18nDescription() const override
0092     { return i18nc("Description of the \"Or\" bias",
0093                    "The \"Or\" bias adds tracks that match at\n"
0094                    "least one of the sub biases."); }
0095 
0096     Dynamic::BiasPtr createBias() override
0097     { return Dynamic::BiasPtr( new Dynamic::OrBias() ); }
0098 };
0099 
0100 Dynamic::BiasFactory* Dynamic::BiasFactory::s_instance = nullptr;
0101 
0102 QList<Dynamic::AbstractBiasFactory*> Dynamic::BiasFactory::s_biasFactories = QList<Dynamic::AbstractBiasFactory*>();
0103 
0104 Dynamic::BiasFactory*
0105 Dynamic::BiasFactory::instance()
0106 {
0107     if( !s_instance )
0108     {
0109         // --- build in biases
0110         s_biasFactories.append( new Dynamic::SearchQueryBiasFactory() );
0111         s_biasFactories.append( new RandomBiasFactory() );
0112         s_biasFactories.append( new AndBiasFactory() );
0113         s_biasFactories.append( new OrBiasFactory() );
0114         s_biasFactories.append( new Dynamic::PartBiasFactory() );
0115         s_biasFactories.append( new Dynamic::IfElseBiasFactory() );
0116         s_biasFactories.append( new Dynamic::TagMatchBiasFactory() );
0117         s_biasFactories.append( new Dynamic::AlbumPlayBiasFactory() );
0118         s_biasFactories.append( new Dynamic::QuizPlayBiasFactory() );
0119         s_biasFactories.append( new Dynamic::EchoNestBiasFactory() );
0120 
0121         s_instance = new BiasFactory( pApp );
0122     }
0123     return s_instance;
0124 }
0125 
0126 
0127 
0128 // --------------- ReplacementBias -------------
0129 
0130 
0131 Dynamic::ReplacementBias::ReplacementBias( const QString &n )
0132     : m_name( n )
0133 {
0134     connect( BiasFactory::instance(), &Dynamic::BiasFactory::changed, this, &ReplacementBias::factoryChanged );
0135 }
0136 
0137 Dynamic::ReplacementBias::ReplacementBias( const QString &n, QXmlStreamReader *reader )
0138     : m_name( n )
0139 {
0140     // -- read the original bias data as one block
0141     quint64 start = reader->characterOffset();
0142     reader->skipCurrentElement();
0143     quint64 end = reader->characterOffset();
0144 
0145     QIODevice *device = reader->device();
0146     if( device->isSequential() )
0147     {
0148         warning() << "Cannot read xml for bias"<<n<<"from sequential device.";
0149         return;
0150     }
0151     device->seek( start );
0152     m_html = device->read( end - start );
0153 
0154     debug() << "replacement bias for"<<n<<"is"<<m_html;
0155 
0156     connect( BiasFactory::instance(), &Dynamic::BiasFactory::changed, this, &ReplacementBias::factoryChanged );
0157 }
0158 
0159 void
0160 Dynamic::ReplacementBias::toXml( QXmlStreamWriter *writer ) const
0161 {
0162     Q_UNUSED( writer );
0163     writer->writeComment(QStringLiteral("Replacement")); // we need to force the closing of the bias start tag
0164     writer->device()->write( m_html.left( m_html.size() - m_name.length() - 3 ) );
0165 }
0166 
0167 QString
0168 Dynamic::ReplacementBias::sName()
0169 {
0170     return QStringLiteral( "replacementBias" );
0171 }
0172 
0173 QString
0174 Dynamic::ReplacementBias::name() const
0175 {
0176     return m_name;
0177 }
0178 
0179 QString
0180 Dynamic::ReplacementBias::toString() const
0181 {
0182     return i18n( "Replacement for bias %1", m_name );
0183 }
0184 
0185 QWidget*
0186 Dynamic::ReplacementBias::widget( QWidget* parent )
0187 {
0188     QLabel *label = new QLabel( i18n( "Replacement for bias %1", m_name ), parent );
0189 
0190     return label;
0191 }
0192 
0193 void
0194 Dynamic::ReplacementBias::factoryChanged()
0195 {
0196     DEBUG_BLOCK;
0197 
0198     // -- search if there is a new factory with my name
0199     foreach( AbstractBiasFactory* factory, BiasFactory::instance()->factories() )
0200     {
0201         if( factory->name() == m_name )
0202         {
0203             debug() << "Found new factory for" << m_name;
0204 
0205             // -- replace myself with the new bias
0206             QXmlStreamReader reader( m_html );
0207 
0208             Dynamic::BiasPtr newBias( factory->createFromXml( &reader ) );
0209             replace( newBias );
0210             return;
0211         }
0212     }
0213 }
0214 
0215 
0216 // ------------- BiasFactory --------------
0217 
0218 Dynamic::BiasFactory::BiasFactory( QObject *parent )
0219     : QObject( parent )
0220 { }
0221 
0222 Dynamic::BiasFactory::~BiasFactory()
0223 {
0224     qDeleteAll(s_biasFactories);
0225 }
0226 
0227 Dynamic::BiasPtr
0228 Dynamic::BiasFactory::fromXml( QXmlStreamReader *reader )
0229 {
0230     QStringRef name = reader->name();
0231 
0232     instance(); // ensure that we have an instance with the default factories
0233     foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
0234     {
0235         if( name == fac->name() )
0236             return fac->createFromXml( reader );
0237     }
0238     return Dynamic::BiasPtr( new ReplacementBias( name.toString(), reader ) );
0239 }
0240 
0241 Dynamic::BiasPtr
0242 Dynamic::BiasFactory::fromName( const QString &name )
0243 {
0244     instance(); // ensure that we have an instance with the default factories
0245     foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
0246     {
0247         if( name == fac->name() )
0248             return fac->createBias();
0249     }
0250     return Dynamic::BiasPtr( new ReplacementBias( name ) );
0251 }
0252 
0253 void
0254 Dynamic::BiasFactory::registerNewBiasFactory( Dynamic::AbstractBiasFactory* factory )
0255 {
0256     instance(); // ensure that we have an instance with the default factories
0257     debug() << "new factory of type:" << factory->name();
0258     if( !s_biasFactories.contains( factory ) )
0259         s_biasFactories.append( factory );
0260 
0261     /*
0262     foreach( const QString &name, s_failedMap.keys() )
0263     {
0264         if( name == entry->pluginName() ) // lazy loading!
0265         {
0266             debug() << "found entry loaded without proper custombiasentry. fixing now, with  old weight of" << s_failedMap[ name ]->weight() ;
0267             //  need to manually set the weight, as we set it on the old widget which is now being thrown away
0268             Dynamic::CustomBiasEntry* cbe = factory->newCustomBiasEntry( s_failedMapXml[ name ] );
0269             s_failedMap[ name ]->setCurrentEntry( cbe );
0270             s_failedMap.remove( name );
0271             s_failedMapXml.remove( name );
0272         }
0273     }
0274     */
0275 
0276     instance()->emitChanged();
0277 }
0278 
0279 void
0280 Dynamic::BiasFactory::removeBiasFactory( Dynamic::AbstractBiasFactory* factory )
0281 {
0282     if( s_biasFactories.contains( factory ) )
0283         s_biasFactories.removeAll( factory );
0284 
0285     instance()->emitChanged();
0286 }
0287 
0288 QList<Dynamic::AbstractBiasFactory*>
0289 Dynamic::BiasFactory::factories()
0290 {
0291     instance(); // ensure that we have an instance with the default factories
0292     return s_biasFactories;
0293 }
0294 
0295 void
0296 Dynamic::BiasFactory::emitChanged()
0297 {
0298     Q_EMIT changed();
0299 }
0300