realfactory.h
1 /********************************************************************************
2  * FARSA - Total99 *
3  * Copyright (C) 2008-2011 Tomassino Ferrauto <t_ferrauto@yahoo.it> *
4  * *
5  * This program is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #ifndef REAL_FACTORY_H
21 #define REAL_FACTORY_H
22 
23 #include "configurationconfig.h"
24 #include "configurationexceptions.h"
25 #include "parametersettable.h"
26 #include "factoryobserver.h"
27 #include <QString>
28 #include <QStringList>
29 #include <QMap>
30 #include <QList>
31 #include <QVector>
32 #include <typeinfo>
33 #include <memory>
34 
35 namespace farsa {
36 
37 class ConfigurationParameters;
38 
45 class FARSA_CONF_TEMPLATE ParameterSettableCreator
46 {
47 public:
52  {
53  }
54 
71  virtual ParameterSettable* create(ConfigurationParameters& settings, QString prefix, bool configure) const = 0;
72 
80  virtual bool canDeferConfiguration() const = 0;
81 };
82 
92 template <class T, bool ConfigureInConstructor>
93 class FARSA_CONF_TEMPLATE ParameterSettableCreatorT : public ParameterSettableCreator
94 {
95 public:
100  {
101  }
102 
115  virtual ParameterSettableInConstructor* create(ConfigurationParameters& settings, QString prefix, bool configure) const;
116 
123  virtual bool canDeferConfiguration() const
124  {
125  return false;
126  }
127 };
128 
138 template <class T>
139 class FARSA_CONF_TEMPLATE ParameterSettableCreatorT<T, false> : public ParameterSettableCreator
140 {
141 public:
146  {
147  }
148 
161  virtual ParameterSettableWithConfigureFunction* create(ConfigurationParameters& settings, QString prefix, bool configure) const;
162 
169  virtual bool canDeferConfiguration() const
170  {
171  return true;
172  }
173 };
174 
186 class FARSA_CONF_API RealFactory {
187 public:
191  RealFactory(ConfigurationParameters& configurationParameters);
192 
196  ~RealFactory();
197 
203  void addObserver(FactoryObserver* observer);
204 
210  void addObservers(const QSet<FactoryObserver*>& observers);
211 
217  const QSet<FactoryObserver*>& getObservers() const;
218 
222  void clearObservers();
223 
251  template <class TypeToCreate>
252  TypeToCreate* create(const QString& className, QString prefix, bool configure = true, bool* actuallyConfigured = NULL);
253 
281  template <class TypeToCreate>
282  TypeToCreate* createFromParameter(QString prefix, bool configure = true, bool* actuallyConfigured = NULL);
283 
299  template <class TypeToCreate>
300  QList<TypeToCreate *> createListFromParameter(QString prefix, QString basename);
301 
317  template <class TypeToCreate>
318  QVector<TypeToCreate *> createVectorFromParameter(QString prefix, QString basename);
319 
329  void objectConfigured(ParameterSettable *object);
330 
339  bool outsideCallsToCreate() const;
340 
347  void callPostConfigureInitializationForConfiguredObjects();
348 
349 private:
353  ConfigurationParameters& m_configurationParameters;
354 
358  QSet<FactoryObserver*> m_observers;
359 
363  unsigned int m_createRecursionLevel;
364 
369  QList<ParameterSettable*> m_objectsConfiguredNotInitialized;
370 
371 private:
381  static bool orderByNumberAfterColon(const QString& s1, const QString& s2);
382 
383 private:
391  RealFactory(const RealFactory &other);
392 
400  RealFactory& operator=(const RealFactory &other);
401 };
402 
403 } // end namespace farsa
404 
405 // Implementation of template functions
406 #include "configurationparameters.h"
407 #include "factory.h"
408 
409 namespace farsa {
410 
411 template <class T, bool ConfigureInConstructor>
413 {
414  QString terminatedPrefix = prefix + ConfigurationParameters::GroupSeparator();
415 
416  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
417  // to be created and configured
418  if (!settings.setObjectFromGroupStatusToCreatingAndConfiguring(terminatedPrefix)) {
419  throw PrefixNotGroupException(prefix.toLatin1().data());
420  }
421 
422  std::auto_ptr<T> t(new T(settings, terminatedPrefix));
423 
424  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
425  // created and configured
426  if (!settings.setObjectFromGroupStatusToCreatedAndConfigured(terminatedPrefix, t.get())) {
427  throw PrefixNotGroupException(prefix.toLatin1().data());
428  }
429 
430  return t.release();
431 }
432 
433 template <class T>
435 {
436  QString terminatedPrefix = prefix + ConfigurationParameters::GroupSeparator();
437 
438  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
439  // to be created
440  if (!settings.setObjectFromGroupStatusToCreating(terminatedPrefix)) {
441  throw PrefixNotGroupException(prefix.toLatin1().data());
442  }
443 
444  std::auto_ptr<T> t(new T());
445 
446  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
447  // created but not yet configured
448  if (!settings.setObjectFromGroupStatusToCreatedNotConfigured(terminatedPrefix, t.get())) {
449  throw PrefixNotGroupException(prefix.toLatin1().data());
450  }
451 
452  if (configure) {
453  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
454  // to be configured
455  if (!settings.setObjectFromGroupStatusToConfiguring(terminatedPrefix)) {
456  throw PrefixNotGroupException(prefix.toLatin1().data());
457  }
458 
459  t->configure(settings, terminatedPrefix);
460 
461  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
462  // created and configured
463  if (!settings.setObjectFromGroupStatusToCreatedAndConfigured(terminatedPrefix)) {
464  throw PrefixNotGroupException(prefix.toLatin1().data());
465  }
466  }
467 
468  return t.release();
469 }
470 
471 template <class TypeToCreate>
472 TypeToCreate* RealFactory::create(const QString& className, QString prefix, bool configure, bool* actuallyConfigured)
473 {
474  // This is a simple class to implement RAII for the m_createRecursionLevel variable, so that in case
475  // an exception is thrown, its value is correctly restored to the previous one
476  class RecursionLevelRAII
477  {
478  public:
479  RecursionLevelRAII(unsigned int& createRecursionLevel, QList<ParameterSettable*>& objectsConfiguredNotInitialized) :
480  m_createRecursionLevel(createRecursionLevel)
481  {
482  if (m_createRecursionLevel == 0) {
483  // As this is the first call to create for the current m_settings, we remove the list of objects, if
484  // present (this could be here because the previous set of calls ended with an exception)
485  objectsConfiguredNotInitialized.clear();
486  }
487  m_createRecursionLevel++;
488  }
489 
490  ~RecursionLevelRAII() throw()
491  {
492  m_createRecursionLevel--;
493  }
494 
495  private:
496  unsigned int& m_createRecursionLevel;
497 
498  // We put implementations to avoid warning under windows. Anyway, these are private, so
499  // nobody can call them.
500  RecursionLevelRAII(const RecursionLevelRAII& other) : m_createRecursionLevel(other.m_createRecursionLevel) {}
501  RecursionLevelRAII& operator=(const RecursionLevelRAII& /*other*/) { return *this; }
502  };
503 
504  // Incrementing the recursion level for this function
505  RecursionLevelRAII recursionLevelRAII(m_createRecursionLevel, m_objectsConfiguredNotInitialized);
506 
507  // Checking that the class map contains the class name
508  if (!Factory::getInstance().m_classMap.contains(className)) {
509  // checking if its abstract
510  if ( Factory::getInstance().m_parentsMap.contains(className) ) {
511  // its an abstract class
512  throw ClassNameIsAbstractException(className.toLatin1().data());
513  } else {
514  throw ClassNameNotRegisteredException(className.toLatin1().data());
515  }
516  }
517 
518  // First of all checking if it is possible to create and not configure the object
519  if (!Factory::getInstance().m_classMap[className]->canDeferConfiguration()) {
520  configure = true;
521  }
522  if (actuallyConfigured != NULL) {
523  (*actuallyConfigured) = configure;
524  }
525 
526  // Using auto_ptr to be sure to delete the object if something goes wrong (RAII)
527  std::auto_ptr<ParameterSettable> newObj(Factory::getInstance().m_classMap[className]->create(m_configurationParameters, prefix, configure));
528  TypeToCreate* obj = dynamic_cast<TypeToCreate*>(newObj.get());
529 
530  if (obj == NULL) {
531  throw CannotConvertToTypeException(className.toLatin1().data(), typeid(TypeToCreate));
532  }
533 
534  // set the typeName to the newly created object
535  obj->setTypeName( className );
536 
537  // If the object has been configured, adding it to the list of objects to be initialized
538  if (configure) {
539  m_objectsConfiguredNotInitialized.append(obj);
540  }
541 
542  // If we are outside any call to ConfigurationParameters::getObjectFromGroup() and this is the most external
543  // call to create (m_createRecursionLevel has been incremented from 0 to 1 by the constructor of RecursionLevelRAII),
544  // here we initialize all objects that have been configured. If the most external call to this function have been
545  // performed inside a call to ConfigurationParameters::getObjectFromGroup(), that function will do the job
546  // NOTE: this check should be useless now that create is protected and can only be called by ConfigurationParameters:
547  // calling postConfigureInitialization is always done inside ConfigurationParameters::getObjectFromGroup().
548  // Leaving it here, however, creates no harm (so, just to be sure, we don't touch it)
549  if (m_configurationParameters.outsideCallsToGetObjectFromGroup() && (m_createRecursionLevel == 1)) {
551  }
552 
553  // notify observers
554  foreach(FactoryObserver* obs, m_observers) {
555  obs->onObjectCreation(&m_configurationParameters, obj, prefix, configure);
556  }
557 
558  // Releasing the auto_ptr to prevent object deallocation
559  newObj.release();
560 
561  return obj;
562 }
563 
564 template <class TypeToCreate>
565 TypeToCreate* RealFactory::createFromParameter(QString prefix, bool configure, bool* actuallyConfigured)
566 {
567  QString type = m_configurationParameters.getValue(prefix + ConfigurationParameters::GroupSeparator() + QString("type"), false);
568 
569  if (!type.isNull()) {
570  return create<TypeToCreate>(type, prefix, configure, actuallyConfigured);
571  }
572 
573  throw CannotFindTypeParameterException(prefix.toLatin1().data());
574 
575  return NULL;
576 }
577 
578 template <class TypeToCreate>
579 QList<TypeToCreate *> RealFactory::createListFromParameter(QString prefix, QString basename)
580 {
581  // The list of objects to return
582  QList<TypeToCreate *> objects;
583  // Getting the list of groups under prefix
584  QStringList objectsGroups = m_configurationParameters.getGroupsWithPrefixList(prefix, basename);
585 
586  // Now creating all groups
587  for (QStringList::iterator it = objectsGroups.begin(); it != objectsGroups.end(); it++) {
588  objects.push_back(createFromParameter<TypeToCreate>(prefix + ConfigurationParameters::GroupSeparator() + *it));
589  }
590 
591  return objects;
592 }
593 
594 template <class TypeToCreate>
595 QVector<TypeToCreate *> RealFactory::createVectorFromParameter(QString prefix, QString basename)
596 {
597  // The vector of objects to return
598  QVector<TypeToCreate *> objects;
599  // Getting the vector of groups under prefix
600  QStringList objectsGroups = m_configurationParameters.getGroupsWithPrefixList(prefix, basename);
601  // Order the data, so that the index respect the number specified after the colon in the groupname
602  qSort(objectsGroups.begin(), objectsGroups.end(), orderByNumberAfterColon);
603 
604  // Now creating all groups
605  for (QStringList::iterator it = objectsGroups.begin(); it != objectsGroups.end(); it++) {
606  objects.push_back(createFromParameter<TypeToCreate>(prefix + ConfigurationParameters::GroupSeparator() + *it));
607  }
608 
609  return objects;
610 }
611 
612 } // end namespace farsa
613 
614 #endif
This file contains the common type defitions used on the whole framework.
virtual bool canDeferConfiguration() const
Returns true if the object that is created can be configured after creation.
Definition: realfactory.h:123
The exception thrown when casting an ParameterSettable object to the requested type fails...
virtual ~ParameterSettableCreatorT()
Destructor.
Definition: realfactory.h:99
virtual ~ParameterSettableCreator()
Destructor.
Definition: realfactory.h:51
The class containing configuration parameters.
The base class for object creators. The implementation is in the template class below.
Definition: realfactory.h:45
QVector< TypeToCreate * > createVectorFromParameter(QString prefix, QString basename)
Creates a vector of objects reading the types from the parameters object.
Definition: realfactory.h:595
virtual ~ParameterSettableCreatorT()
Destructor.
Definition: realfactory.h:145
virtual bool canDeferConfiguration() const
Returns true if the object that is created can be configured after creation.
Definition: realfactory.h:169
virtual ParameterSettableInConstructor * create(ConfigurationParameters &settings, QString prefix, bool configure) const
Creates an instance and configures it.
Definition: realfactory.h:412
The base for classes that can be configured using a ConfigurationParameters object passed to the cons...
The exception thrown when requested class name is registered but cannot be created because it is abst...
The base for classes that can be configured/saved using a ConfigurationParameters object...
QList< TypeToCreate * > createListFromParameter(QString prefix, QString basename)
Creates a list of objects reading the types from the parameters object.
Definition: realfactory.h:579
virtual void onObjectCreation(ConfigurationParameters *confParams, ParameterSettable *object, QString path, bool configured)=0
called when an object has been created
The class implementing the create function of the class above.
Definition: realfactory.h:93
The exception thrown when requested class name is not registered with the factory.
FactoryObserver class to keep trace on operations done by Factory.
QString getValue(QString path, bool alsoMatchParents=false) const
Returns a parameter value.
static Factory & getInstance()
Returns the only instance of this class.
Definition: factory.cpp:26
The base for classes that can be configured using a ConfigurationParameters object.
QStringList getGroupsWithPrefixList(QString group, QString prefix) const
Returns the list of sub-groups in the given group whose name starts with the provided string...
void callPostConfigureInitializationForConfiguredObjects()
Calls postConfigureInitialization on all objects for the given ConfigurationParameters object...
Definition: realfactory.cpp:69
The exception thrown when trying to create an object from a using a prefi that is not a group...
TypeToCreate * createFromParameter(QString prefix, bool configure=true, bool *actuallyConfigured=NULL)
Creates an object reading the type from the parameters object.
Definition: realfactory.h:565
The exception thrown when requested to create an object from a group and the "type" parameter is not ...
static QString GroupSeparator()
The character used to split path in groups.
TypeToCreate * create(const QString &className, QString prefix, bool configure=true, bool *actuallyConfigured=NULL)
Creates an object.
Definition: realfactory.h:472
Factory class to create ParameterSettable objects.
Definition: realfactory.h:186