configuration/include/configurationparameters.h

00001 /***************************************************************************
00002  *   Copyright (C) 2008-2009 by Tomassino Ferrauto                         *
00003  *   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                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020 
00021 #ifndef CONFIGURATION_PARAMETERS_H
00022 #define CONFIGURATION_PARAMETERS_H
00023 
00024 #include "configurationconfig.h"
00025 #include <QMap>
00026 #include <QString>
00027 #include <QRegExp>
00028 #include <QStringList>
00029 #include <QList>
00030 #include <memory>
00031 #include "parametersfileloadersaver.h"
00032 #include "resourcesuser.h"
00033 
00034 namespace farsa {
00035 
00036 class RealFactory;
00037 class ParameterSettable;
00038 class ParameterSettableWithConfigureFunction;
00039 class ConfigurationNode;
00040 class FactoryObserver;
00041 
00259 class FARSA_CONF_API ConfigurationParameters
00260 {
00261 public:
00268     static QString GroupSeparator() { return QString("/"); };
00269 
00276     static QString ParentGroup() { return QString(".."); };
00277 
00292     static bool registerFileFormat(QString format, ParametersFileLoaderSaver *fileLoaderSaver, QString defaultExtension);
00293 
00294 private:
00302     struct FileFormat
00303     {
00307         FileFormat(const QString &f, ParametersFileLoaderSaver *l, const QString &e) :
00308             formatName(f),
00309             fileLoaderSaver(l),
00310             extension(e),
00311             m_destructionResponsability(true)
00312         {
00313         }
00314 
00318         FileFormat(const FileFormat& other) :
00319             formatName(other.formatName),
00320             fileLoaderSaver(other.fileLoaderSaver),
00321             extension(other.extension),
00322             m_destructionResponsability(other.m_destructionResponsability)
00323         {
00324             // Releasing copied object from destruction
00325             // responsability
00326             other.m_destructionResponsability = false;
00327         }
00328 
00332         FileFormat& operator=(const FileFormat &other)
00333         {
00334             // Checking for self-assignment
00335             if (&other == this) {
00336                 return *this;
00337             }
00338 
00339             formatName = other.formatName;
00340             fileLoaderSaver = other.fileLoaderSaver;
00341             extension = other.extension;
00342             m_destructionResponsability = other.m_destructionResponsability;
00343 
00344             // Releasing copied object from destruction
00345             // responsability
00346             other.m_destructionResponsability = false;
00347 
00348             return *this;
00349         }
00350 
00354         ~FileFormat()
00355         {
00356             // Destroying if we have to
00357             if (m_destructionResponsability) {
00358                 delete fileLoaderSaver;
00359             }
00360         }
00361 
00365         QString formatName;
00366 
00370         ParametersFileLoaderSaver *fileLoaderSaver;
00371 
00375         QString extension;
00376 
00377     private:
00384         mutable bool m_destructionResponsability;
00385     };
00386 
00397     static QMap<QString, FileFormat>& getFormatsMap();
00398 
00409     static QMap<QString, QString>& getFileExtensionsMap();
00410 
00411 public:
00419     ConfigurationParameters(bool caseSensitive = false);
00420 
00429     ConfigurationParameters(const ConfigurationParameters &other);
00430 
00442     ConfigurationParameters& operator=(const ConfigurationParameters &other);
00443 
00447     ~ConfigurationParameters();
00448 
00454     bool isCaseSensitive() const;
00455 
00459     void clearAll();
00460 
00468     QStringList getGroupsList(QString group) const;
00469 
00480     QStringList getGroupsWithPrefixList(QString group, QString prefix) const;
00481 
00492     QStringList getFilteredGroupsList(QString group, QRegExp filter) const;
00493 
00500     void createGroup(QString groupPath);
00501 
00534     bool deleteGroup(QString groupPath);
00535 
00573     bool renameGroup(QString oldGroupPath, QString newGroupName);
00574 
00584     QString createSubGroup(QString parentPath, QString groupName) {
00585         QString fullpath = parentPath + GroupSeparator() + groupName;
00586         createGroup( fullpath );
00587         return fullpath;
00588     };
00589 
00598     void createParameter(QString groupPath, QString parameter);
00599 
00610     void createParameter(QString groupPath, QString parameter, QString value)
00611     {
00612         createParameter(groupPath, parameter);
00613         setValue(groupPath + GroupSeparator() + parameter, value);
00614     }
00615 
00626     void createParameter(QString groupPath, QString parameter, ParameterSettable* object)
00627     {
00628         createParameter(groupPath, parameter);
00629         setValue(groupPath + GroupSeparator() + parameter, object);
00630     }
00631 
00639     void deleteParameter(QString groupPath, QString parameter);
00640 
00684     bool startObjectParameters(QString groupPath, QString typeName, ParameterSettable* object);
00685 
00713     QString getValue(QString path, bool alsoMatchParents = false) const;
00714 
00723     bool setValue(QString path, QString value);
00724 
00733     bool setValue(QString path, ParameterSettable* object);
00734 
00741     QStringList getParametersList(QString group) const;
00742 
00753     QStringList getParametersWithPrefixList(QString group, QString prefix) const;
00754 
00765     QStringList getFilteredParametersList(QString group, QRegExp filter) const;
00766 
00784     void startRememberingGroupObjectAssociations();
00785 
00791     void stopRememberingGroupObjectAssociations();
00792 
00800     void resetGroupObjectAssociations();
00801 
00833     template <class TypeToCreate>
00834     TypeToCreate* getObjectFromGroup(QString group, bool configure = true, bool forceObjectCreation = false);
00835 
00874     template <class TypeToCreate>
00875     TypeToCreate* getObjectFromParameter(QString param, bool alsoMatchParents = false, bool configure = true, bool forceObjectCreation = false)
00876     {
00877         // Getting the value for the parameter and calling getObjectFromGroup
00878         QString value = getValue(param, alsoMatchParents);
00879         // Checking this here because calling getObjectFromGroup with an
00880         // empty or null string means root group
00881         if (value.isEmpty()) {
00882             return NULL;
00883         }
00884         return getObjectFromGroup<TypeToCreate>(value, configure, forceObjectCreation);
00885     }
00886 
00896     void updateObjectReferences();
00897 
00910     bool loadParameters(QString filename, bool keepOld = false, QString format = "");
00911 
00928     bool saveParameters(QString filename, QString format = "", bool append = false);
00929 
00939     SimpleResourcesUser* getResourcesUserForResource(QString resourceName);
00940 
00947     void setResourcesUser(ResourcesUser* resourcesUser);
00948 
00955     void addObserver(FactoryObserver* observer);
00956 
00957 private:
00965     QString formatFromFilenameExtension(QString filename) const;
00966 
00972     void copyTree(const ConfigurationNode *root);
00973 
00981     void copyNode(const ConfigurationNode *source, ConfigurationNode *target);
00982 
00990     bool setObjectFromGroupStatusToCreating(QString group);
00991 
01004     bool setObjectFromGroupStatusToCreatedNotConfigured(QString group, ParameterSettable *object);
01005 
01015     bool setObjectFromGroupStatusToConfiguring(QString group);
01016 
01027     bool setObjectFromGroupStatusToCreatingAndConfiguring(QString group);
01028 
01042     bool setObjectFromGroupStatusToCreatedAndConfigured(QString group, ParameterSettable *object = NULL);
01043 
01051     bool outsideCallsToGetObjectFromGroup() const
01052     {
01053         return (m_getObjectFromGroupRecursionLevel == 0);
01054     }
01055 
01061     const std::auto_ptr<RealFactory> m_factory;
01062 
01066     std::auto_ptr<ConfigurationNode> m_root;
01067 
01074     unsigned int m_getObjectFromGroupRecursionLevel;
01075 
01084     struct ObjectAndRecursionLevel
01085     {
01089         ParameterSettableWithConfigureFunction* object;
01090 
01094         unsigned int recursionLevel;
01095 
01100         QString groupName;
01101     };
01102 
01107     QList<ObjectAndRecursionLevel> m_objectsToConfigure;
01108 
01114     bool m_dontForgetGroupObjectAssociations;
01115 
01123     QList<SimpleResourcesUser> m_resourcesUserPerRecursionLevel;
01124 
01129     template<class T, bool ConfigureInConstructor>
01130     friend class ParameterSettableCreatorT;
01131 
01140     friend class RealFactory;
01141 };
01142 
01143 } // end namespace farsa
01144 
01145 // Implementation of template functions
01146 #include "configurationnode.h"
01147 #include "parametersettable.h"
01148 #include "realfactory.h"
01149 #include <memory>
01150 
01151 namespace farsa {
01152 
01153 template <class TypeToCreate>
01154 TypeToCreate* ConfigurationParameters::getObjectFromGroup(QString group, bool configure, bool forceObjectCreation)
01155 {
01156     // This is a simple class to implement RAII for the m_getObjectFromGroupRecursionLevel variable
01157     // and the m_objectsToConfigure list so that they are properly finalized on getObjectFromGroup() exit.
01158     // Note that to configure objects you have to explicitly call "release", the destructor doesn't call
01159     // configure on objects. This also manages m_resouceMangerPerRecursionLevel
01160     class RecursionLevelRAII
01161     {
01162     public:
01163         RecursionLevelRAII(unsigned int *var, QList<ConfigurationParameters::ObjectAndRecursionLevel> *list, QList<SimpleResourcesUser> *resourcesUsers, ConfigurationParameters &params, RealFactory& factory) :
01164             m_var(var),
01165             m_list(list),
01166             m_resourcesUsers(resourcesUsers),
01167             m_params(params),
01168             m_factory(factory)
01169         {
01170             (*m_var)++;
01171             m_resourcesUsers->append(SimpleResourcesUser());
01172         }
01173 
01174         // This doesn't throw because in the release function we don't call configure
01175         ~RecursionLevelRAII() throw()
01176         {
01177             release(false);
01178         }
01179 
01180         void release(bool doConfigure = true)
01181         {
01182             if (m_list != NULL) {
01183                 while ((!m_list->isEmpty()) && (m_list->back().recursionLevel > (*m_var))) {
01184                     if (doConfigure) {
01185                         // Calling configure (appending the separator to the group name)
01186                         m_list->back().object->configure(m_params, m_list->back().groupName + GroupSeparator());
01187                         // Telling the factory that the object has been configured
01188                         m_factory.objectConfigured(m_list->back().object);
01189                     }
01190                     m_list->pop_back();
01191                 }
01192                 m_list = NULL;
01193             }
01194             if (m_var != NULL) {
01195                 (*m_var)--;
01196                 m_var = NULL;
01197                 m_resourcesUsers->removeLast();
01198             }
01199         }
01200 
01201     private:
01202         unsigned int *m_var;
01203         QList<ObjectAndRecursionLevel> *m_list;
01204         QList<SimpleResourcesUser> *m_resourcesUsers;
01205         ConfigurationParameters& m_params;
01206         RealFactory& m_factory;
01207 
01208         // We put implementations to avoid warning under windows. Anyway, these are private, so
01209         // nobody can call them. The initialization of m_params is to avoid compilation errors
01210         // under MSVC
01211         RecursionLevelRAII(const RecursionLevelRAII& other) : m_params(*((ConfigurationParameters *) NULL)), m_factory(*((RealFactory *) NULL)) {}
01212         RecursionLevelRAII& operator=(const RecursionLevelRAII& other) { return *this; }
01213     };
01214 
01215     // First of all getting the node for the group
01216     ConfigurationNode *node = m_root->getNode(group);
01217 
01218     // If the node doesn't exist, returning NULL
01219     if (node == NULL) {
01220         return NULL;
01221     }
01222 
01223     // Before starting with group creation, resetting all associations between nodes and objects
01224     if ((!m_dontForgetGroupObjectAssociations) && (m_getObjectFromGroupRecursionLevel == 0)) {
01225         resetGroupObjectAssociations();
01226     }
01227 
01228     // If the recursion level is 0 (this is the most external call to getObjectFromGroup), we always configure
01229     // the object. This check is needed because otherwise, if the most external call to this function has
01230     // the configure parameter set to false, the object returned by that call is never configured
01231     if (m_getObjectFromGroupRecursionLevel == 0) {
01232         configure = true;
01233     }
01234 
01235     // Incrementing the recursion level for this function
01236     RecursionLevelRAII recursionLevelRAII(&m_getObjectFromGroupRecursionLevel, &m_objectsToConfigure, &m_resourcesUserPerRecursionLevel, *this, *m_factory);
01237 
01238     // The pointer to return
01239     TypeToCreate* retObj = NULL;
01240     // This is used only if the object is created during this call
01241     std::auto_ptr<TypeToCreate> retObjRAII(NULL);
01242 
01243     // Now checking if an object is already associated with the node and its status
01244     ConfigurationNode::ObjectAndStatus object = node->getObjectForNode("");
01245     // This is set to true because we use it to decide whether to add the object to the list or not:
01246     // if true it is not added, if false it is added (an this is only modified when the object is created
01247     // using the factory)
01248     bool actuallyConfigured = true;
01249     switch (object.objectStatus) {
01250         case ObjectNotCreated:
01251             // Creating object
01252             retObj = m_factory->createFromParameter<TypeToCreate>(group, configure, &actuallyConfigured);
01253             retObjRAII.reset(retObj);
01254             break;
01255         case CreatingObject:
01256             // Object is in creation phase, cannot return it and cannot create another object (throwning
01257             // a different exception depending on the situation)
01258             if (forceObjectCreation) {
01259                 throw OtherObjectBeingCreatedException(group.toAscii().data());
01260             } else {
01261                 throw CyclicDependencyException(group.toAscii().data());
01262             }
01263             break;
01264         case ObjectCreatedNotConfigured:
01265             // Checking we haven't been requested a new object (we cannot do this because another object exists
01266             // but is not configured - this is the safest policy, even if perhaps we could do something more smart).
01267             if (forceObjectCreation) {
01268                 throw OtherObjectBeingCreatedException(group.toAscii().data());
01269             }
01270             // Now we can configure the object if we have to and then return the object
01271             retObj = dynamic_cast<TypeToCreate *>(object.object);
01272             if (retObj == NULL) {
01273                 throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
01274             }
01275             if (configure) {
01276                 // If we are here the object is surely of type ParameterSettableWithConfigureFunction
01277                 // (otherwise the cast will fail miserably). We also change the state of the object before
01278                 // and after configuration. Despite having casted object.object to TypeToCreate we use
01279                 // another cast here because otherwise we would have compilation errors with classes
01280                 // deriving from ParameterSettableInConstructor (but with those classes we should never
01281                 // get here)
01282                 node->setObjectForNode("", retObj, ConfiguringObject);
01283                 ParameterSettableWithConfigureFunction *const o = dynamic_cast<ParameterSettableWithConfigureFunction *>(retObj);
01284                 o->configure(*this, group);
01285                 // Telling the factory that the object has been configured
01286                 m_factory->objectConfigured(o);
01287                 node->setObjectForNode("", retObj, ObjectCreatedAndConfigured);
01288             }
01289             break;
01290         case ConfiguringObject:
01291             // Checking we haven't been requested a new object (we cannot do this because another object exists
01292             // and is being configured - this is the safest policy, even if perhaps we could do something more smart).
01293             if (forceObjectCreation) {
01294                 throw OtherObjectBeingCreatedException(group.toAscii().data());
01295             }
01296             if (configure) {
01297                 // Object is in configuration phase, cannot return it
01298                 throw CyclicDependencyException(group.toAscii().data());
01299             } else {
01300                 // Ok, returning object
01301                 retObj = dynamic_cast<TypeToCreate *>(object.object);
01302                 if (retObj == NULL) {
01303                     throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
01304                 }
01305             }
01306             break;
01307         case CreatingAndConfiguringObject:
01308             // Object is in creation and configuration phase, cannot return it and cannot create another object
01309             // (throwning a different exception depending on the situation)
01310             if (forceObjectCreation) {
01311                 throw OtherObjectBeingCreatedException(group.toAscii().data());
01312             } else {
01313                 throw CyclicDependencyException(group.toAscii().data());
01314             }
01315             break;
01316         case ObjectCreatedAndConfigured:
01317             // If we were requested a new object, creating it, otherwise we return the already existing one
01318             if (forceObjectCreation) {
01319                 // Creating object. This will also remove association with the previous object
01320                 retObj = m_factory->createFromParameter<TypeToCreate>(group, configure, &actuallyConfigured);
01321                 retObjRAII.reset(retObj);
01322             } else {
01323                 // Ok, returning object
01324                 retObj = dynamic_cast<TypeToCreate *>(object.object);
01325                 if (retObj == NULL) {
01326                     throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
01327                 }
01328             }
01329             break;
01330     }
01331 
01332     // If the object has been created and not configured, appending to the list
01333     if (!actuallyConfigured) {
01334         ObjectAndRecursionLevel o;
01335         o.object = dynamic_cast<ParameterSettableWithConfigureFunction *>(retObj);
01336         o.recursionLevel = m_getObjectFromGroupRecursionLevel;
01337         o.groupName = group;
01338         m_objectsToConfigure.push_back(o);
01339     }
01340 
01341     // Releasing resources (explicitly, so that unconfigured objects that can be configured at this recursion level
01342     // will actually be configured)
01343     recursionLevelRAII.release();
01344 
01345     // Now checking if we have to call postConfigureInitialization() for all configured objects. The check is here (as
01346     // well as inside RealFactory::create) because this function can call RealFactory::create and vice-versa, so we have to check
01347     // which of the two is the most external call
01348     // NOTE: now that the factory cannot be used directly this check should be useless (we always call postConfigureInitialization()
01349     //       here. Leaving it here, however, creates no harm (so, just to be sure, we don't touch it)
01350     if (m_factory->outsideCallsToCreate() && (m_getObjectFromGroupRecursionLevel == 0)) {
01351         m_factory->callPostConfigureInitializationForConfiguredObjects();
01352 
01353         // Safety check, just to be sure: when we get here, there should be no object in the list of objects yet to configure
01354         if (m_objectsToConfigure.size() != 0) {
01355             qFatal( "INTERNAL ERROR: The list of objects yet to be configured is not empty at the exit of the most external call to ConfigurationParameters::getObjectFromGroup(). The number of objects still in the list is %d", m_objectsToConfigure.size());
01356         }
01357     }
01358 
01359     retObjRAII.release();
01360     return retObj;
01361 }
01362 
01363 } // end namespace farsa
01364 
01365 #endif