1 /****************************************************************************************
2  * Copyright (c) 2010 Ralf Engels <ralf-engels@gmx.de>                                  *
3  *                                                                                      *
4  * This program is free software; you can redistribute it and/or modify it under        *
5  * the terms of the GNU General Public License as published by the Free Software        *
6  * Foundation; either version 2 of the License, or (at your option) any later           *
7  * version.                                                                             *
8  *                                                                                      *
9  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
11  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
12  *                                                                                      *
13  * You should have received a copy of the GNU General Public License along with         *
14  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
15  ****************************************************************************************/
16 
17 #define DEBUG_PREFIX "BiasFactory"
18 
19 #include "BiasFactory.h"
20 
21 #include "App.h"
22 #include "biases/AlbumPlayBias.h"
23 #include "biases/IfElseBias.h"
24 #include "biases/PartBias.h"
25 #include "biases/TagMatchBias.h"
26 #include "biases/SearchQueryBias.h"
27 #include "biases/QuizPlayBias.h"
28 #include "biases/EchoNestBias.h"
29 #include "core/support/Debug.h"
30 #include "core/collections/QueryMaker.h"
31 #include "dynamic/Bias.h"
32 #include "scripting/scriptengine/exporters/ScriptableBiasExporter.h"
33 
34 #include <QFormLayout>
35 #include <QLabel>
36 #include <QList>
37 #include <QXmlStreamReader>
38 
39 Dynamic::BiasPtr
createFromXml(QXmlStreamReader * reader)40 Dynamic::AbstractBiasFactory::createFromXml( QXmlStreamReader *reader )
41 {
42     Dynamic::BiasPtr bias( createBias() );
43     bias->fromXml( reader );
44     return bias;
45 }
46 
47 class RandomBiasFactory : public Dynamic::AbstractBiasFactory
48 {
i18nName() const49     QString i18nName() const override
50     { return i18nc("Name of the random bias", "Random"); }
51 
name() const52     QString name() const override
53     { return Dynamic::RandomBias::sName(); }
54 
i18nDescription() const55     QString i18nDescription() const override
56     { return i18nc("Description of the random bias",
57                    "The random bias adds random tracks from the\n"
58                    "whole collection without any bias."); }
59 
createBias()60     Dynamic::BiasPtr createBias() override
61     { return Dynamic::BiasPtr( new Dynamic::RandomBias() ); }
62 };
63 
64 
65 class AndBiasFactory : public Dynamic::AbstractBiasFactory
66 {
i18nName() const67     QString i18nName() const override
68     { return i18nc("Name of the \"And\" bias", "And"); }
69 
name() const70     QString name() const override
71     { return Dynamic::AndBias::sName(); }
72 
i18nDescription() const73     QString i18nDescription() const override
74     { return i18nc("Description of the \"And\" bias",
75                    "The \"And\" bias adds tracks that match all\n"
76                    "of the sub biases."); }
77 
createBias()78     Dynamic::BiasPtr createBias() override
79     { return Dynamic::BiasPtr( new Dynamic::AndBias() ); }
80 };
81 
82 
83 class OrBiasFactory : public Dynamic::AbstractBiasFactory
84 {
i18nName() const85     QString i18nName() const override
86     { return i18nc("Name of the \"Or\" bias", "Or"); }
87 
name() const88     QString name() const override
89     { return Dynamic::OrBias::sName(); }
90 
i18nDescription() const91     QString i18nDescription() const override
92     { return i18nc("Description of the \"Or\" bias",
93                    "The \"Or\" bias adds tracks that match at\n"
94                    "least one of the sub biases."); }
95 
createBias()96     Dynamic::BiasPtr createBias() override
97     { return Dynamic::BiasPtr( new Dynamic::OrBias() ); }
98 };
99 
100 Dynamic::BiasFactory* Dynamic::BiasFactory::s_instance = nullptr;
101 
102 QList<Dynamic::AbstractBiasFactory*> Dynamic::BiasFactory::s_biasFactories = QList<Dynamic::AbstractBiasFactory*>();
103 
104 Dynamic::BiasFactory*
instance()105 Dynamic::BiasFactory::instance()
106 {
107     if( !s_instance )
108     {
109         // --- build in biases
110         s_biasFactories.append( new Dynamic::SearchQueryBiasFactory() );
111         s_biasFactories.append( new RandomBiasFactory() );
112         s_biasFactories.append( new AndBiasFactory() );
113         s_biasFactories.append( new OrBiasFactory() );
114         s_biasFactories.append( new Dynamic::PartBiasFactory() );
115         s_biasFactories.append( new Dynamic::IfElseBiasFactory() );
116         s_biasFactories.append( new Dynamic::TagMatchBiasFactory() );
117         s_biasFactories.append( new Dynamic::AlbumPlayBiasFactory() );
118         s_biasFactories.append( new Dynamic::QuizPlayBiasFactory() );
119         s_biasFactories.append( new Dynamic::EchoNestBiasFactory() );
120 
121         s_instance = new BiasFactory( pApp );
122     }
123     return s_instance;
124 }
125 
126 
127 
128 // --------------- ReplacementBias -------------
129 
130 
ReplacementBias(const QString & n)131 Dynamic::ReplacementBias::ReplacementBias( const QString &n )
132     : m_name( n )
133 {
134     connect( BiasFactory::instance(), &Dynamic::BiasFactory::changed, this, &ReplacementBias::factoryChanged );
135 }
136 
ReplacementBias(const QString & n,QXmlStreamReader * reader)137 Dynamic::ReplacementBias::ReplacementBias( const QString &n, QXmlStreamReader *reader )
138     : m_name( n )
139 {
140     // -- read the original bias data as one block
141     quint64 start = reader->characterOffset();
142     reader->skipCurrentElement();
143     quint64 end = reader->characterOffset();
144 
145     QIODevice *device = reader->device();
146     if( device->isSequential() )
147     {
148         warning() << "Cannot read xml for bias"<<n<<"from sequential device.";
149         return;
150     }
151     device->seek( start );
152     m_html = device->read( end - start );
153 
154     debug() << "replacement bias for"<<n<<"is"<<m_html;
155 
156     connect( BiasFactory::instance(), &Dynamic::BiasFactory::changed, this, &ReplacementBias::factoryChanged );
157 }
158 
159 void
toXml(QXmlStreamWriter * writer) const160 Dynamic::ReplacementBias::toXml( QXmlStreamWriter *writer ) const
161 {
162     Q_UNUSED( writer );
163     writer->writeComment(QStringLiteral("Replacement")); // we need to force the closing of the bias start tag
164     writer->device()->write( m_html.left( m_html.size() - m_name.length() - 3 ) );
165 }
166 
167 QString
sName()168 Dynamic::ReplacementBias::sName()
169 {
170     return QStringLiteral( "replacementBias" );
171 }
172 
173 QString
name() const174 Dynamic::ReplacementBias::name() const
175 {
176     return m_name;
177 }
178 
179 QString
toString() const180 Dynamic::ReplacementBias::toString() const
181 {
182     return i18n( "Replacement for bias %1", m_name );
183 }
184 
185 QWidget*
widget(QWidget * parent)186 Dynamic::ReplacementBias::widget( QWidget* parent )
187 {
188     QLabel *label = new QLabel( i18n( "Replacement for bias %1", m_name ), parent );
189 
190     return label;
191 }
192 
193 void
factoryChanged()194 Dynamic::ReplacementBias::factoryChanged()
195 {
196     DEBUG_BLOCK;
197 
198     // -- search if there is a new factory with my name
199     foreach( AbstractBiasFactory* factory, BiasFactory::instance()->factories() )
200     {
201         if( factory->name() == m_name )
202         {
203             debug() << "Found new factory for" << m_name;
204 
205             // -- replace myself with the new bias
206             QXmlStreamReader reader( m_html );
207 
208             Dynamic::BiasPtr newBias( factory->createFromXml( &reader ) );
209             replace( newBias );
210             return;
211         }
212     }
213 }
214 
215 
216 // ------------- BiasFactory --------------
217 
BiasFactory(QObject * parent)218 Dynamic::BiasFactory::BiasFactory( QObject *parent )
219     : QObject( parent )
220 { }
221 
~BiasFactory()222 Dynamic::BiasFactory::~BiasFactory()
223 {
224     qDeleteAll(s_biasFactories);
225 }
226 
227 Dynamic::BiasPtr
fromXml(QXmlStreamReader * reader)228 Dynamic::BiasFactory::fromXml( QXmlStreamReader *reader )
229 {
230     QStringRef name = reader->name();
231 
232     instance(); // ensure that we have an instance with the default factories
233     foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
234     {
235         if( name == fac->name() )
236             return fac->createFromXml( reader );
237     }
238     return Dynamic::BiasPtr( new ReplacementBias( name.toString(), reader ) );
239 }
240 
241 Dynamic::BiasPtr
fromName(const QString & name)242 Dynamic::BiasFactory::fromName( const QString &name )
243 {
244     instance(); // ensure that we have an instance with the default factories
245     foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
246     {
247         if( name == fac->name() )
248             return fac->createBias();
249     }
250     return Dynamic::BiasPtr( new ReplacementBias( name ) );
251 }
252 
253 void
registerNewBiasFactory(Dynamic::AbstractBiasFactory * factory)254 Dynamic::BiasFactory::registerNewBiasFactory( Dynamic::AbstractBiasFactory* factory )
255 {
256     instance(); // ensure that we have an instance with the default factories
257     debug() << "new factory of type:" << factory->name();
258     if( !s_biasFactories.contains( factory ) )
259         s_biasFactories.append( factory );
260 
261     /*
262     foreach( const QString &name, s_failedMap.keys() )
263     {
264         if( name == entry->pluginName() ) // lazy loading!
265         {
266             debug() << "found entry loaded without proper custombiasentry. fixing now, with  old weight of" << s_failedMap[ name ]->weight() ;
267             //  need to manually set the weight, as we set it on the old widget which is now being thrown away
268             Dynamic::CustomBiasEntry* cbe = factory->newCustomBiasEntry( s_failedMapXml[ name ] );
269             s_failedMap[ name ]->setCurrentEntry( cbe );
270             s_failedMap.remove( name );
271             s_failedMapXml.remove( name );
272         }
273     }
274     */
275 
276     instance()->emitChanged();
277 }
278 
279 void
removeBiasFactory(Dynamic::AbstractBiasFactory * factory)280 Dynamic::BiasFactory::removeBiasFactory( Dynamic::AbstractBiasFactory* factory )
281 {
282     if( s_biasFactories.contains( factory ) )
283         s_biasFactories.removeAll( factory );
284 
285     instance()->emitChanged();
286 }
287 
288 QList<Dynamic::AbstractBiasFactory*>
factories()289 Dynamic::BiasFactory::factories()
290 {
291     instance(); // ensure that we have an instance with the default factories
292     return s_biasFactories;
293 }
294 
295 void
emitChanged()296 Dynamic::BiasFactory::emitChanged()
297 {
298     Q_EMIT changed();
299 }
300 
301