configuration/include/realfactory.h

00001 /********************************************************************************
00002  *  FARSA - Total99                                                             *
00003  *  Copyright (C) 2008-2011 Tomassino Ferrauto <t_ferrauto@yahoo.it>            *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #ifndef REAL_FACTORY_H
00021 #define REAL_FACTORY_H
00022 
00023 #include "configurationconfig.h"
00024 #include "configurationexceptions.h"
00025 #include "parametersettable.h"
00026 #include "factoryobserver.h"
00027 #include <QString>
00028 #include <QStringList>
00029 #include <QMap>
00030 #include <QList>
00031 #include <QVector>
00032 #include <typeinfo>
00033 #include <memory>
00034 
00035 namespace farsa {
00036 
00037 class ConfigurationParameters;
00038 
00045 class FARSA_CONF_TEMPLATE ParameterSettableCreator
00046 {
00047 public:
00051     virtual ~ParameterSettableCreator()
00052     {
00053     }
00054 
00071     virtual ParameterSettable* create(ConfigurationParameters& settings, QString prefix, bool configure) const = 0;
00072 
00080     virtual bool canDeferConfiguration() const = 0;
00081 };
00082 
00092 template <class T, bool ConfigureInConstructor>
00093 class FARSA_CONF_TEMPLATE ParameterSettableCreatorT : public ParameterSettableCreator
00094 {
00095 public:
00099     virtual ~ParameterSettableCreatorT()
00100     {
00101     }
00102 
00115     virtual ParameterSettableInConstructor* create(ConfigurationParameters& settings, QString prefix, bool configure) const;
00116 
00123     virtual bool canDeferConfiguration() const
00124     {
00125         return false;
00126     }
00127 };
00128 
00138 template <class T>
00139 class FARSA_CONF_TEMPLATE ParameterSettableCreatorT<T, false> : public ParameterSettableCreator
00140 {
00141 public:
00145     virtual ~ParameterSettableCreatorT()
00146     {
00147     }
00148 
00161     virtual ParameterSettableWithConfigureFunction* create(ConfigurationParameters& settings, QString prefix, bool configure) const;
00162 
00169     virtual bool canDeferConfiguration() const
00170     {
00171         return true;
00172     }
00173 };
00174 
00186 class FARSA_CONF_API RealFactory {
00187 public:
00191     RealFactory(ConfigurationParameters& configurationParameters);
00192 
00196     ~RealFactory();
00197 
00203     void addObserver(FactoryObserver* observer);
00204 
00210     void addObservers(const QSet<FactoryObserver*>& observers);
00211 
00217     const QSet<FactoryObserver*>& getObservers() const;
00218 
00222     void clearObservers();
00223 
00251     template <class TypeToCreate>
00252     TypeToCreate* create(const QString& className, QString prefix, bool configure = true, bool* actuallyConfigured = NULL);
00253 
00281     template <class TypeToCreate>
00282     TypeToCreate* createFromParameter(QString prefix, bool configure = true, bool* actuallyConfigured = NULL);
00283 
00299     template <class TypeToCreate>
00300     QList<TypeToCreate *> createListFromParameter(QString prefix, QString basename);
00301 
00317     template <class TypeToCreate>
00318     QVector<TypeToCreate *> createVectorFromParameter(QString prefix, QString basename);
00319 
00329     void objectConfigured(ParameterSettable *object);
00330 
00339     bool outsideCallsToCreate() const;
00340 
00347     void callPostConfigureInitializationForConfiguredObjects();
00348 
00349 private:
00353     ConfigurationParameters& m_configurationParameters;
00354 
00358     QSet<FactoryObserver*> m_observers;
00359 
00363     unsigned int m_createRecursionLevel;
00364 
00369     QList<ParameterSettable*> m_objectsConfiguredNotInitialized;
00370 
00371 private:
00381     static bool orderByNumberAfterColon(const QString& s1, const QString& s2);
00382 
00383 private:
00391     RealFactory(const RealFactory &other);
00392 
00400     RealFactory& operator=(const RealFactory &other);
00401 };
00402 
00403 } // end namespace farsa
00404 
00405 // Implementation of template functions
00406 #include "configurationparameters.h"
00407 #include "factory.h"
00408 
00409 namespace farsa {
00410 
00411 template <class T, bool ConfigureInConstructor>
00412 ParameterSettableInConstructor* ParameterSettableCreatorT<T, ConfigureInConstructor>::create(ConfigurationParameters& settings, QString prefix, bool /*configure*/) const
00413 {
00414     QString terminatedPrefix = prefix + ConfigurationParameters::GroupSeparator();
00415 
00416     // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
00417     // to be created and configured
00418     if (!settings.setObjectFromGroupStatusToCreatingAndConfiguring(terminatedPrefix)) {
00419         throw PrefixNotGroupException(prefix.toAscii().data());
00420     }
00421 
00422     std::auto_ptr<T> t(new T(settings, terminatedPrefix));
00423 
00424     // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
00425     // created and configured
00426     if (!settings.setObjectFromGroupStatusToCreatedAndConfigured(terminatedPrefix, t.get())) {
00427         throw PrefixNotGroupException(prefix.toAscii().data());
00428     }
00429 
00430     return t.release();
00431 }
00432 
00433 template <class T>
00434 ParameterSettableWithConfigureFunction* ParameterSettableCreatorT<T, false>::create(ConfigurationParameters& settings, QString prefix, bool configure) const
00435 {
00436     QString terminatedPrefix = prefix + ConfigurationParameters::GroupSeparator();
00437 
00438     // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
00439     // to be created
00440     if (!settings.setObjectFromGroupStatusToCreating(terminatedPrefix)) {
00441         throw PrefixNotGroupException(prefix.toAscii().data());
00442     }
00443 
00444     std::auto_ptr<T> t(new T());
00445 
00446     // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
00447     // created but not yet configured
00448     if (!settings.setObjectFromGroupStatusToCreatedNotConfigured(terminatedPrefix, t.get())) {
00449         throw PrefixNotGroupException(prefix.toAscii().data());
00450     }
00451 
00452     if (configure) {
00453         // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
00454         // to be configured
00455         if (!settings.setObjectFromGroupStatusToConfiguring(terminatedPrefix)) {
00456             throw PrefixNotGroupException(prefix.toAscii().data());
00457         }
00458 
00459         t->configure(settings, terminatedPrefix);
00460 
00461         // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
00462         // created and configured
00463         if (!settings.setObjectFromGroupStatusToCreatedAndConfigured(terminatedPrefix)) {
00464             throw PrefixNotGroupException(prefix.toAscii().data());
00465         }
00466     }
00467 
00468     return t.release();
00469 }
00470 
00471 template <class TypeToCreate>
00472 TypeToCreate* RealFactory::create(const QString& className, QString prefix, bool configure, bool* actuallyConfigured)
00473 {
00474     // This is a simple class to implement RAII for the m_createRecursionLevel variable, so that in case
00475     // an exception is thrown, its value is correctly restored to the previous one
00476     class RecursionLevelRAII
00477     {
00478     public:
00479         RecursionLevelRAII(unsigned int& createRecursionLevel, QList<ParameterSettable*>& objectsConfiguredNotInitialized) :
00480             m_createRecursionLevel(createRecursionLevel)
00481         {
00482             if (m_createRecursionLevel == 0) {
00483                 // As this is the first call to create for the current m_settings, we remove the list of objects, if
00484                 // present (this could be here because the previous set of calls ended with an exception)
00485                 objectsConfiguredNotInitialized.clear();
00486             }
00487             m_createRecursionLevel++;
00488         }
00489 
00490         ~RecursionLevelRAII() throw()
00491         {
00492             m_createRecursionLevel--;
00493         }
00494 
00495     private:
00496         unsigned int& m_createRecursionLevel;
00497 
00498         // We put implementations to avoid warning under windows. Anyway, these are private, so
00499         // nobody can call them.
00500         RecursionLevelRAII(const RecursionLevelRAII& other) : m_createRecursionLevel(other.m_createRecursionLevel) {}
00501         RecursionLevelRAII& operator=(const RecursionLevelRAII& other) { return *this; }
00502     };
00503 
00504     // Incrementing the recursion level for this function
00505     RecursionLevelRAII recursionLevelRAII(m_createRecursionLevel, m_objectsConfiguredNotInitialized);
00506 
00507     // Checking that the class map contains the class name
00508     if (!Factory::getInstance().m_classMap.contains(className)) {
00509         throw ClassNameNotRegisteredException(className.toAscii().data());
00510     }
00511 
00512     // First of all checking if it is possible to create and not configure the object
00513     if (!Factory::getInstance().m_classMap[className]->canDeferConfiguration()) {
00514         configure = true;
00515     }
00516     if (actuallyConfigured != NULL) {
00517         (*actuallyConfigured) = configure;
00518     }
00519 
00520     // Using auto_ptr to be sure to delete the object if something goes wrong (RAII)
00521     std::auto_ptr<ParameterSettable> newObj(Factory::getInstance().m_classMap[className]->create(m_configurationParameters, prefix, configure));
00522     TypeToCreate* obj = dynamic_cast<TypeToCreate*>(newObj.get());
00523 
00524     if (obj == NULL) {
00525         throw CannotConvertToTypeException(className.toAscii().data(), typeid(TypeToCreate));
00526     }
00527 
00528     // If the object has been configured, adding it to the list of objects to be initialized
00529     if (configure) {
00530         m_objectsConfiguredNotInitialized.append(obj);
00531     }
00532 
00533     // If we are outside any call to ConfigurationParameters::getObjectFromGroup() and this is the most external
00534     // call to create (m_createRecursionLevel has been incremented from 0 to 1 by the constructor of RecursionLevelRAII),
00535     // here we initialize all objects that have been configured. If the most external call to this function have been
00536     // performed inside a call to ConfigurationParameters::getObjectFromGroup(), that function will do the job
00537     // NOTE: this check should be useless now that create is protected and can only be called by ConfigurationParameters:
00538     //       calling postConfigureInitialization is always done inside ConfigurationParameters::getObjectFromGroup().
00539     //       Leaving it here, however, creates no harm (so, just to be sure, we don't touch it)
00540     if (m_configurationParameters.outsideCallsToGetObjectFromGroup() && (m_createRecursionLevel == 1)) {
00541         callPostConfigureInitializationForConfiguredObjects();
00542     }
00543 
00544     // notify observers
00545     foreach(FactoryObserver* obs, m_observers) {
00546         obs->onObjectCreation(obj, className, prefix, configure);
00547     }
00548 
00549     // Releasing the auto_ptr to prevent object deallocation
00550     newObj.release();
00551 
00552     return obj;
00553 }
00554 
00555 template <class TypeToCreate>
00556 TypeToCreate* RealFactory::createFromParameter(QString prefix, bool configure, bool* actuallyConfigured)
00557 {
00558     QString type = m_configurationParameters.getValue(prefix + ConfigurationParameters::GroupSeparator() + QString("type"), false);
00559 
00560     if (!type.isNull()) {
00561         return create<TypeToCreate>(type, prefix, configure, actuallyConfigured);
00562     }
00563 
00564     throw CannotFindTypeParameterException(prefix.toAscii().data());
00565 
00566     return NULL;
00567 }
00568 
00569 template <class TypeToCreate>
00570 QList<TypeToCreate *> RealFactory::createListFromParameter(QString prefix, QString basename)
00571 {
00572     // The list of objects to return
00573     QList<TypeToCreate *> objects;
00574     // Getting the list of groups under prefix
00575     QStringList objectsGroups = m_configurationParameters.getGroupsWithPrefixList(prefix, basename);
00576 
00577     // Now creating all groups
00578     for (QStringList::iterator it = objectsGroups.begin(); it != objectsGroups.end(); it++) {
00579         objects.push_back(createFromParameter<TypeToCreate>(prefix + ConfigurationParameters::GroupSeparator() + *it));
00580     }
00581 
00582     return objects;
00583 }
00584 
00585 template <class TypeToCreate>
00586 QVector<TypeToCreate *> RealFactory::createVectorFromParameter(QString prefix, QString basename)
00587 {
00588     // The vector of objects to return
00589     QVector<TypeToCreate *> objects;
00590     // Getting the vector of groups under prefix
00591     QStringList objectsGroups = m_configurationParameters.getGroupsWithPrefixList(prefix, basename);
00592     // Order the data, so that the index respect the number specified after the colon in the groupname
00593     qSort(objectsGroups.begin(), objectsGroups.end(), orderByNumberAfterColon);
00594     
00595     // Now creating all groups
00596     for (QStringList::iterator it = objectsGroups.begin(); it != objectsGroups.end(); it++) {
00597         objects.push_back(createFromParameter<TypeToCreate>(prefix + ConfigurationParameters::GroupSeparator() + *it));
00598     }
00599 
00600     return objects;
00601 }
00602 
00603 } // end namespace farsa
00604 
00605 #endif