configurationparameters.h
1 /***************************************************************************
2  * Copyright (C) 2008-2009 by Tomassino Ferrauto *
3  * 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 *
17  * Free Software Foundation, Inc., *
18  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19  ***************************************************************************/
20 
21 #ifndef CONFIGURATION_PARAMETERS_H
22 #define CONFIGURATION_PARAMETERS_H
23 
24 #include "configurationconfig.h"
25 #include <QMap>
26 #include <QString>
27 #include <QRegExp>
28 #include <QStringList>
29 #include <QList>
30 #include <memory>
31 #include "parametersfileloadersaver.h"
32 #include "resourcesuser.h"
33 
34 namespace farsa {
35 
36 class RealFactory;
37 class ParameterSettable;
38 class ParameterSettableWithConfigureFunction;
39 class ConfigurationNode;
40 class FactoryObserver;
41 
259 class FARSA_CONF_API ConfigurationParameters
260 {
261 public:
268  static QString GroupSeparator() { return QString("/"); };
269 
276  static QString ParentGroup() { return QString(".."); };
277 
292  static bool registerFileFormat(QString format, ParametersFileLoaderSaver *fileLoaderSaver, QString defaultExtension);
293 
294 private:
302  struct FileFormat
303  {
307  FileFormat(const QString &f, ParametersFileLoaderSaver *l, const QString &e) :
308  formatName(f),
309  fileLoaderSaver(l),
310  extension(e),
311  m_destructionResponsability(true)
312  {
313  }
314 
318  FileFormat(const FileFormat& other) :
319  formatName(other.formatName),
320  fileLoaderSaver(other.fileLoaderSaver),
321  extension(other.extension),
322  m_destructionResponsability(other.m_destructionResponsability)
323  {
324  // Releasing copied object from destruction
325  // responsability
326  other.m_destructionResponsability = false;
327  }
328 
332  FileFormat& operator=(const FileFormat &other)
333  {
334  // Checking for self-assignment
335  if (&other == this) {
336  return *this;
337  }
338 
339  formatName = other.formatName;
340  fileLoaderSaver = other.fileLoaderSaver;
341  extension = other.extension;
342  m_destructionResponsability = other.m_destructionResponsability;
343 
344  // Releasing copied object from destruction
345  // responsability
346  other.m_destructionResponsability = false;
347 
348  return *this;
349  }
350 
354  ~FileFormat()
355  {
356  // Destroying if we have to
357  if (m_destructionResponsability) {
358  delete fileLoaderSaver;
359  }
360  }
361 
365  QString formatName;
366 
370  ParametersFileLoaderSaver *fileLoaderSaver;
371 
375  QString extension;
376 
377  private:
384  mutable bool m_destructionResponsability;
385  };
386 
397  static QMap<QString, FileFormat>& getFormatsMap();
398 
409  static QMap<QString, QString>& getFileExtensionsMap();
410 
411 public:
419  ConfigurationParameters(bool caseSensitive = false);
420 
429  ConfigurationParameters(const ConfigurationParameters &other);
430 
442  ConfigurationParameters& operator=(const ConfigurationParameters &other);
443 
447  ~ConfigurationParameters();
448 
454  bool isCaseSensitive() const;
455 
459  void clearAll();
460 
468  QStringList getGroupsList(QString group) const;
469 
480  QStringList getGroupsWithPrefixList(QString group, QString prefix) const;
481 
492  QStringList getFilteredGroupsList(QString group, QRegExp filter) const;
493 
500  void createGroup(QString groupPath);
501 
534  bool deleteGroup(QString groupPath);
535 
573  bool renameGroup(QString oldGroupPath, QString newGroupName);
574 
610  bool copyGroupTree(QString sourceGroup, QString destGroup);
611 
621  QString createSubGroup(QString parentPath, QString groupName) {
622  QString fullpath = parentPath + GroupSeparator() + groupName;
623  createGroup( fullpath );
624  return fullpath;
625  };
626 
635  void createParameter(QString groupPath, QString parameter);
636 
647  void createParameter(QString groupPath, QString parameter, QString value)
648  {
649  createParameter(groupPath, parameter);
650  setValue(groupPath + GroupSeparator() + parameter, value);
651  }
652 
663  void createParameter(QString groupPath, QString parameter, ParameterSettable* object)
664  {
665  createParameter(groupPath, parameter);
666  setValue(groupPath + GroupSeparator() + parameter, object);
667  }
668 
676  void deleteParameter(QString groupPath, QString parameter);
677 
721  bool startObjectParameters(QString groupPath, QString typeName, ParameterSettable* object);
722 
750  QString getValue(QString path, bool alsoMatchParents = false) const;
751 
760  bool setValue(QString path, QString value);
761 
770  bool setValue(QString path, ParameterSettable* object);
771 
778  QStringList getParametersList(QString group) const;
779 
790  QStringList getParametersWithPrefixList(QString group, QString prefix) const;
791 
802  QStringList getFilteredParametersList(QString group, QRegExp filter) const;
803 
821  void startRememberingGroupObjectAssociations();
822 
828  void stopRememberingGroupObjectAssociations();
829 
837  void resetGroupObjectAssociations();
838 
870  template <class TypeToCreate>
871  TypeToCreate* getObjectFromGroup(QString group, bool configure = true, bool forceObjectCreation = false);
872 
911  template <class TypeToCreate>
912  TypeToCreate* getObjectFromParameter(QString param, bool alsoMatchParents = false, bool configure = true, bool forceObjectCreation = false)
913  {
914  // Getting the value for the parameter and calling getObjectFromGroup
915  QString value = getValue(param, alsoMatchParents);
916  // Checking this here because calling getObjectFromGroup with an
917  // empty or null string means root group
918  if (value.isEmpty()) {
919  return NULL;
920  }
921  return getObjectFromGroup<TypeToCreate>(value, configure, forceObjectCreation);
922  }
923 
933  void updateObjectReferences();
934 
947  bool loadParameters(QString filename, bool keepOld = false, QString format = "");
948 
965  bool saveParameters(QString filename, QString format = "", bool append = false);
966 
976  SimpleResourcesUser* getResourcesUserForResource(QString resourceName);
977 
984  void setResourcesUser(ResourcesUser* resourcesUser);
985 
992  void addObserver(FactoryObserver* observer);
993 
996  void shareObserversWith( ConfigurationParameters& params );
997 
998 private:
1006  QString formatFromFilenameExtension(QString filename) const;
1007 
1013  void copyTree(const ConfigurationNode *root);
1014 
1022  void copyNode(const ConfigurationNode *source, ConfigurationNode *target);
1023 
1031  bool setObjectFromGroupStatusToCreating(QString group);
1032 
1045  bool setObjectFromGroupStatusToCreatedNotConfigured(QString group, ParameterSettable *object);
1046 
1056  bool setObjectFromGroupStatusToConfiguring(QString group);
1057 
1068  bool setObjectFromGroupStatusToCreatingAndConfiguring(QString group);
1069 
1083  bool setObjectFromGroupStatusToCreatedAndConfigured(QString group, ParameterSettable *object = NULL);
1084 
1092  bool outsideCallsToGetObjectFromGroup() const
1093  {
1094  return (m_getObjectFromGroupRecursionLevel == 0);
1095  }
1096 
1102  const std::auto_ptr<RealFactory> m_factory;
1103 
1107  std::auto_ptr<ConfigurationNode> m_root;
1108 
1115  unsigned int m_getObjectFromGroupRecursionLevel;
1116 
1125  struct ObjectAndRecursionLevel
1126  {
1130  ParameterSettableWithConfigureFunction* object;
1131 
1135  unsigned int recursionLevel;
1136 
1141  QString groupName;
1142  };
1143 
1148  QList<ObjectAndRecursionLevel> m_objectsToConfigure;
1149 
1155  bool m_dontForgetGroupObjectAssociations;
1156 
1164  QList<SimpleResourcesUser> m_resourcesUserPerRecursionLevel;
1165 
1170  template<class T, bool ConfigureInConstructor>
1172 
1181  friend class RealFactory;
1182 };
1183 
1184 } // end namespace farsa
1185 
1186 // Implementation of template functions
1187 #include "configurationnode.h"
1188 #include "parametersettable.h"
1189 #include "realfactory.h"
1190 #include <memory>
1191 
1192 namespace farsa {
1193 
1194 template <class TypeToCreate>
1195 TypeToCreate* ConfigurationParameters::getObjectFromGroup(QString group, bool configure, bool forceObjectCreation)
1196 {
1197  // This is a simple class to implement RAII for the m_getObjectFromGroupRecursionLevel variable
1198  // and the m_objectsToConfigure list so that they are properly finalized on getObjectFromGroup() exit.
1199  // Note that to configure objects you have to explicitly call "release", the destructor doesn't call
1200  // configure on objects. This also manages m_resouceMangerPerRecursionLevel
1201  class RecursionLevelRAII
1202  {
1203  public:
1204  RecursionLevelRAII(unsigned int *var, QList<ConfigurationParameters::ObjectAndRecursionLevel> *list, QList<SimpleResourcesUser> *resourcesUsers, ConfigurationParameters &params, RealFactory& factory) :
1205  m_var(var),
1206  m_list(list),
1207  m_resourcesUsers(resourcesUsers),
1208  m_params(params),
1209  m_factory(factory)
1210  {
1211  (*m_var)++;
1212  m_resourcesUsers->append(SimpleResourcesUser());
1213  }
1214 
1215  // This doesn't throw because in the release function we don't call configure
1216  ~RecursionLevelRAII() throw()
1217  {
1218  release(false);
1219  }
1220 
1221  void release(bool doConfigure = true)
1222  {
1223  if (m_list != NULL) {
1224  while ((!m_list->isEmpty()) && (m_list->back().recursionLevel > (*m_var))) {
1225  if (doConfigure) {
1226  // Calling configure (appending the separator to the group name)
1227  m_list->back().object->configure(m_params, m_list->back().groupName + GroupSeparator());
1228  // Telling the factory that the object has been configured
1229  m_factory.objectConfigured(m_list->back().object);
1230  }
1231  m_list->pop_back();
1232  }
1233  m_list = NULL;
1234  }
1235  if (m_var != NULL) {
1236  (*m_var)--;
1237  m_var = NULL;
1238  m_resourcesUsers->removeLast();
1239  }
1240  }
1241 
1242  private:
1243  unsigned int *m_var;
1244  QList<ObjectAndRecursionLevel> *m_list;
1245  QList<SimpleResourcesUser> *m_resourcesUsers;
1246  ConfigurationParameters& m_params;
1247  RealFactory& m_factory;
1248 
1249  // We put implementations to avoid warning under windows. Anyway, these are private, so
1250  // nobody can call them. The initialization of m_params is to avoid compilation errors
1251  // under MSVC
1252  RecursionLevelRAII(const RecursionLevelRAII& /*other*/) : m_params(*((ConfigurationParameters *) NULL)), m_factory(*((RealFactory *) NULL)) {}
1253  RecursionLevelRAII& operator=(const RecursionLevelRAII& /*other*/) { return *this; }
1254  };
1255 
1256  // First of all getting the node for the group
1257  ConfigurationNode *node = m_root->getNode(group);
1258 
1259  // If the node doesn't exist, returning NULL
1260  if (node == NULL) {
1261  return NULL;
1262  }
1263 
1264  // Before starting with group creation, resetting all associations between nodes and objects
1265  if ((!m_dontForgetGroupObjectAssociations) && (m_getObjectFromGroupRecursionLevel == 0)) {
1267  }
1268 
1269  // If the recursion level is 0 (this is the most external call to getObjectFromGroup), we always configure
1270  // the object. This check is needed because otherwise, if the most external call to this function has
1271  // the configure parameter set to false, the object returned by that call is never configured
1272  if (m_getObjectFromGroupRecursionLevel == 0) {
1273  configure = true;
1274  }
1275 
1276  // Incrementing the recursion level for this function
1277  RecursionLevelRAII recursionLevelRAII(&m_getObjectFromGroupRecursionLevel, &m_objectsToConfigure, &m_resourcesUserPerRecursionLevel, *this, *m_factory);
1278 
1279  // The pointer to return
1280  TypeToCreate* retObj = NULL;
1281  // This is used only if the object is created during this call
1282  std::auto_ptr<TypeToCreate> retObjRAII(NULL);
1283 
1284  // Now checking if an object is already associated with the node and its status
1286  // This is set to true because we use it to decide whether to add the object to the list or not:
1287  // if true it is not added, if false it is added (an this is only modified when the object is created
1288  // using the factory)
1289  bool actuallyConfigured = true;
1290  switch (object.objectStatus) {
1291  case ObjectNotCreated:
1292  // Creating object
1293  retObj = m_factory->createFromParameter<TypeToCreate>(group, configure, &actuallyConfigured);
1294  retObjRAII.reset(retObj);
1295  break;
1296  case CreatingObject:
1297  // Object is in creation phase, cannot return it and cannot create another object (throwning
1298  // a different exception depending on the situation)
1299  if (forceObjectCreation) {
1300  throw OtherObjectBeingCreatedException(group.toLatin1().data());
1301  } else {
1302  throw CyclicDependencyException(group.toLatin1().data());
1303  }
1304  break;
1305  case ObjectCreatedNotConfigured:
1306  // Checking we haven't been requested a new object (we cannot do this because another object exists
1307  // but is not configured - this is the safest policy, even if perhaps we could do something more smart).
1308  if (forceObjectCreation) {
1309  throw OtherObjectBeingCreatedException(group.toLatin1().data());
1310  }
1311  // Now we can configure the object if we have to and then return the object
1312  retObj = dynamic_cast<TypeToCreate *>(object.object);
1313  if (retObj == NULL) {
1314  throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
1315  }
1316  if (configure) {
1317  // If we are here the object is surely of type ParameterSettableWithConfigureFunction
1318  // (otherwise the cast will fail miserably). We also change the state of the object before
1319  // and after configuration. Despite having casted object.object to TypeToCreate we use
1320  // another cast here because otherwise we would have compilation errors with classes
1321  // deriving from ParameterSettableInConstructor (but with those classes we should never
1322  // get here)
1323  node->setObjectForNode("", retObj, ConfiguringObject);
1325  o->configure(*this, group);
1326  // Telling the factory that the object has been configured
1327  m_factory->objectConfigured(o);
1328  node->setObjectForNode("", retObj, ObjectCreatedAndConfigured);
1329  }
1330  break;
1331  case ConfiguringObject:
1332  // Checking we haven't been requested a new object (we cannot do this because another object exists
1333  // and is being configured - this is the safest policy, even if perhaps we could do something more smart).
1334  if (forceObjectCreation) {
1335  throw OtherObjectBeingCreatedException(group.toLatin1().data());
1336  }
1337  if (configure) {
1338  // Object is in configuration phase, cannot return it
1339  throw CyclicDependencyException(group.toLatin1().data());
1340  } else {
1341  // Ok, returning object
1342  retObj = dynamic_cast<TypeToCreate *>(object.object);
1343  if (retObj == NULL) {
1344  throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
1345  }
1346  }
1347  break;
1348  case CreatingAndConfiguringObject:
1349  // Object is in creation and configuration phase, cannot return it and cannot create another object
1350  // (throwning a different exception depending on the situation)
1351  if (forceObjectCreation) {
1352  throw OtherObjectBeingCreatedException(group.toLatin1().data());
1353  } else {
1354  throw CyclicDependencyException(group.toLatin1().data());
1355  }
1356  break;
1357  case ObjectCreatedAndConfigured:
1358  // If we were requested a new object, creating it, otherwise we return the already existing one
1359  if (forceObjectCreation) {
1360  // Creating object. This will also remove association with the previous object
1361  retObj = m_factory->createFromParameter<TypeToCreate>(group, configure, &actuallyConfigured);
1362  retObjRAII.reset(retObj);
1363  } else {
1364  // Ok, returning object
1365  retObj = dynamic_cast<TypeToCreate *>(object.object);
1366  if (retObj == NULL) {
1367  throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
1368  }
1369  }
1370  break;
1371  }
1372 
1373  // If the object has been created and not configured, appending to the list
1374  if (!actuallyConfigured) {
1375  ObjectAndRecursionLevel o;
1376  o.object = dynamic_cast<ParameterSettableWithConfigureFunction *>(retObj);
1377  o.recursionLevel = m_getObjectFromGroupRecursionLevel;
1378  o.groupName = group;
1379  m_objectsToConfigure.push_back(o);
1380  }
1381 
1382  // Releasing resources (explicitly, so that unconfigured objects that can be configured at this recursion level
1383  // will actually be configured)
1384  recursionLevelRAII.release();
1385 
1386  // Now checking if we have to call postConfigureInitialization() for all configured objects. The check is here (as
1387  // well as inside RealFactory::create) because this function can call RealFactory::create and vice-versa, so we have to check
1388  // which of the two is the most external call
1389  // NOTE: now that the factory cannot be used directly this check should be useless (we always call postConfigureInitialization()
1390  // here. Leaving it here, however, creates no harm (so, just to be sure, we don't touch it)
1391  if (m_factory->outsideCallsToCreate() && (m_getObjectFromGroupRecursionLevel == 0)) {
1392  m_factory->callPostConfigureInitializationForConfiguredObjects();
1393 
1394  // Safety check, just to be sure: when we get here, there should be no object in the list of objects yet to configure
1395  if (m_objectsToConfigure.size() != 0) {
1396  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());
1397  }
1398  }
1399 
1400  retObjRAII.release();
1401  return retObj;
1402 }
1403 
1404 } // end namespace farsa
1405 
1406 #endif
TypeToCreate * getObjectFromParameter(QString param, bool alsoMatchParents=false, bool configure=true, bool forceObjectCreation=false)
Returns the object for the given parameter, creating it if it doesn't exist.
This file contains the common type defitions used on the whole framework.
A simple class to access/declare resources (not thread-safe)
The exception thrown when casting an ParameterSettable object to the requested type fails...
The exception thrown when a cyclic dependency is discovered during object creation using the Configur...
The class containing configuration parameters.
ObjectAndStatus getObjectForNode(QString path) const
Returns the object corresponding to the given node.
virtual void configure(ConfigurationParameters &params, QString prefix)=0
Configures the object using a ConfigurationParameters object.
The base for classes that can be configured/saved using a ConfigurationParameters object...
The class implementing the create function of the class above.
Definition: realfactory.h:93
static QString ParentGroup()
The sequence used to indicate the parent group.
The exception thrown when the user requests a new object for a group but there is another object for ...
void resetGroupObjectAssociations()
Resets all associations between groups and objects.
FactoryObserver class to keep trace on operations done by Factory.
The parent of classes managing resources.
Definition: resourcesuser.h:70
QString createSubGroup(QString parentPath, QString groupName)
Create a sub-group of parent group specified.
The structure with the object for a node and its status.
TypeToCreate * getObjectFromGroup(QString group, bool configure=true, bool forceObjectCreation=false)
Returns the object for the given group, creating it if it doesn't exist.
The base for classes that can be configured using a ConfigurationParameters object.
void createParameter(QString groupPath, QString parameter, QString value)
Create a parameter in the group path specified and set its value.
A node in the configuration tree.
static QString GroupSeparator()
The character used to split path in groups.
Factory class to create ParameterSettable objects.
Definition: realfactory.h:186
void createParameter(QString groupPath, QString parameter, ParameterSettable *object)
Create a parameter in the group path specified and set its value to point to an object.
ConfigurationParameters & operator=(const ConfigurationParameters &other)
Assignment operator.
The base for all classes able to load/save configuration parameters from/to file. ...
bool setObjectForNode(QString path, ParameterSettable *object, ObjectCreationStatus status=ObjectCreatedAndConfigured)
Sets the object corresponding to the given node to object.