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 
584  QString createSubGroup(QString parentPath, QString groupName) {
585  QString fullpath = parentPath + GroupSeparator() + groupName;
586  createGroup( fullpath );
587  return fullpath;
588  };
589 
598  void createParameter(QString groupPath, QString parameter);
599 
610  void createParameter(QString groupPath, QString parameter, QString value)
611  {
612  createParameter(groupPath, parameter);
613  setValue(groupPath + GroupSeparator() + parameter, value);
614  }
615 
626  void createParameter(QString groupPath, QString parameter, ParameterSettable* object)
627  {
628  createParameter(groupPath, parameter);
629  setValue(groupPath + GroupSeparator() + parameter, object);
630  }
631 
639  void deleteParameter(QString groupPath, QString parameter);
640 
684  bool startObjectParameters(QString groupPath, QString typeName, ParameterSettable* object);
685 
713  QString getValue(QString path, bool alsoMatchParents = false) const;
714 
723  bool setValue(QString path, QString value);
724 
733  bool setValue(QString path, ParameterSettable* object);
734 
741  QStringList getParametersList(QString group) const;
742 
753  QStringList getParametersWithPrefixList(QString group, QString prefix) const;
754 
765  QStringList getFilteredParametersList(QString group, QRegExp filter) const;
766 
784  void startRememberingGroupObjectAssociations();
785 
791  void stopRememberingGroupObjectAssociations();
792 
800  void resetGroupObjectAssociations();
801 
833  template <class TypeToCreate>
834  TypeToCreate* getObjectFromGroup(QString group, bool configure = true, bool forceObjectCreation = false);
835 
874  template <class TypeToCreate>
875  TypeToCreate* getObjectFromParameter(QString param, bool alsoMatchParents = false, bool configure = true, bool forceObjectCreation = false)
876  {
877  // Getting the value for the parameter and calling getObjectFromGroup
878  QString value = getValue(param, alsoMatchParents);
879  // Checking this here because calling getObjectFromGroup with an
880  // empty or null string means root group
881  if (value.isEmpty()) {
882  return NULL;
883  }
884  return getObjectFromGroup<TypeToCreate>(value, configure, forceObjectCreation);
885  }
886 
896  void updateObjectReferences();
897 
910  bool loadParameters(QString filename, bool keepOld = false, QString format = "");
911 
928  bool saveParameters(QString filename, QString format = "", bool append = false);
929 
939  SimpleResourcesUser* getResourcesUserForResource(QString resourceName);
940 
947  void setResourcesUser(ResourcesUser* resourcesUser);
948 
955  void addObserver(FactoryObserver* observer);
956 
959  void shareObserversWith( ConfigurationParameters& params );
960 
961 private:
969  QString formatFromFilenameExtension(QString filename) const;
970 
976  void copyTree(const ConfigurationNode *root);
977 
985  void copyNode(const ConfigurationNode *source, ConfigurationNode *target);
986 
994  bool setObjectFromGroupStatusToCreating(QString group);
995 
1008  bool setObjectFromGroupStatusToCreatedNotConfigured(QString group, ParameterSettable *object);
1009 
1019  bool setObjectFromGroupStatusToConfiguring(QString group);
1020 
1031  bool setObjectFromGroupStatusToCreatingAndConfiguring(QString group);
1032 
1046  bool setObjectFromGroupStatusToCreatedAndConfigured(QString group, ParameterSettable *object = NULL);
1047 
1055  bool outsideCallsToGetObjectFromGroup() const
1056  {
1057  return (m_getObjectFromGroupRecursionLevel == 0);
1058  }
1059 
1065  const std::auto_ptr<RealFactory> m_factory;
1066 
1070  std::auto_ptr<ConfigurationNode> m_root;
1071 
1078  unsigned int m_getObjectFromGroupRecursionLevel;
1079 
1088  struct ObjectAndRecursionLevel
1089  {
1093  ParameterSettableWithConfigureFunction* object;
1094 
1098  unsigned int recursionLevel;
1099 
1104  QString groupName;
1105  };
1106 
1111  QList<ObjectAndRecursionLevel> m_objectsToConfigure;
1112 
1118  bool m_dontForgetGroupObjectAssociations;
1119 
1127  QList<SimpleResourcesUser> m_resourcesUserPerRecursionLevel;
1128 
1133  template<class T, bool ConfigureInConstructor>
1135 
1144  friend class RealFactory;
1145 };
1146 
1147 } // end namespace farsa
1148 
1149 // Implementation of template functions
1150 #include "configurationnode.h"
1151 #include "parametersettable.h"
1152 #include "realfactory.h"
1153 #include <memory>
1154 
1155 namespace farsa {
1156 
1157 template <class TypeToCreate>
1158 TypeToCreate* ConfigurationParameters::getObjectFromGroup(QString group, bool configure, bool forceObjectCreation)
1159 {
1160  // This is a simple class to implement RAII for the m_getObjectFromGroupRecursionLevel variable
1161  // and the m_objectsToConfigure list so that they are properly finalized on getObjectFromGroup() exit.
1162  // Note that to configure objects you have to explicitly call "release", the destructor doesn't call
1163  // configure on objects. This also manages m_resouceMangerPerRecursionLevel
1164  class RecursionLevelRAII
1165  {
1166  public:
1167  RecursionLevelRAII(unsigned int *var, QList<ConfigurationParameters::ObjectAndRecursionLevel> *list, QList<SimpleResourcesUser> *resourcesUsers, ConfigurationParameters &params, RealFactory& factory) :
1168  m_var(var),
1169  m_list(list),
1170  m_resourcesUsers(resourcesUsers),
1171  m_params(params),
1172  m_factory(factory)
1173  {
1174  (*m_var)++;
1175  m_resourcesUsers->append(SimpleResourcesUser());
1176  }
1177 
1178  // This doesn't throw because in the release function we don't call configure
1179  ~RecursionLevelRAII() throw()
1180  {
1181  release(false);
1182  }
1183 
1184  void release(bool doConfigure = true)
1185  {
1186  if (m_list != NULL) {
1187  while ((!m_list->isEmpty()) && (m_list->back().recursionLevel > (*m_var))) {
1188  if (doConfigure) {
1189  // Calling configure (appending the separator to the group name)
1190  m_list->back().object->configure(m_params, m_list->back().groupName + GroupSeparator());
1191  // Telling the factory that the object has been configured
1192  m_factory.objectConfigured(m_list->back().object);
1193  }
1194  m_list->pop_back();
1195  }
1196  m_list = NULL;
1197  }
1198  if (m_var != NULL) {
1199  (*m_var)--;
1200  m_var = NULL;
1201  m_resourcesUsers->removeLast();
1202  }
1203  }
1204 
1205  private:
1206  unsigned int *m_var;
1207  QList<ObjectAndRecursionLevel> *m_list;
1208  QList<SimpleResourcesUser> *m_resourcesUsers;
1209  ConfigurationParameters& m_params;
1210  RealFactory& m_factory;
1211 
1212  // We put implementations to avoid warning under windows. Anyway, these are private, so
1213  // nobody can call them. The initialization of m_params is to avoid compilation errors
1214  // under MSVC
1215  RecursionLevelRAII(const RecursionLevelRAII& other) : m_params(*((ConfigurationParameters *) NULL)), m_factory(*((RealFactory *) NULL)) {}
1216  RecursionLevelRAII& operator=(const RecursionLevelRAII& other) { return *this; }
1217  };
1218 
1219  // First of all getting the node for the group
1220  ConfigurationNode *node = m_root->getNode(group);
1221 
1222  // If the node doesn't exist, returning NULL
1223  if (node == NULL) {
1224  return NULL;
1225  }
1226 
1227  // Before starting with group creation, resetting all associations between nodes and objects
1228  if ((!m_dontForgetGroupObjectAssociations) && (m_getObjectFromGroupRecursionLevel == 0)) {
1230  }
1231 
1232  // If the recursion level is 0 (this is the most external call to getObjectFromGroup), we always configure
1233  // the object. This check is needed because otherwise, if the most external call to this function has
1234  // the configure parameter set to false, the object returned by that call is never configured
1235  if (m_getObjectFromGroupRecursionLevel == 0) {
1236  configure = true;
1237  }
1238 
1239  // Incrementing the recursion level for this function
1240  RecursionLevelRAII recursionLevelRAII(&m_getObjectFromGroupRecursionLevel, &m_objectsToConfigure, &m_resourcesUserPerRecursionLevel, *this, *m_factory);
1241 
1242  // The pointer to return
1243  TypeToCreate* retObj = NULL;
1244  // This is used only if the object is created during this call
1245  std::auto_ptr<TypeToCreate> retObjRAII(NULL);
1246 
1247  // Now checking if an object is already associated with the node and its status
1249  // This is set to true because we use it to decide whether to add the object to the list or not:
1250  // if true it is not added, if false it is added (an this is only modified when the object is created
1251  // using the factory)
1252  bool actuallyConfigured = true;
1253  switch (object.objectStatus) {
1254  case ObjectNotCreated:
1255  // Creating object
1256  retObj = m_factory->createFromParameter<TypeToCreate>(group, configure, &actuallyConfigured);
1257  retObjRAII.reset(retObj);
1258  break;
1259  case CreatingObject:
1260  // Object is in creation phase, cannot return it and cannot create another object (throwning
1261  // a different exception depending on the situation)
1262  if (forceObjectCreation) {
1263  throw OtherObjectBeingCreatedException(group.toAscii().data());
1264  } else {
1265  throw CyclicDependencyException(group.toAscii().data());
1266  }
1267  break;
1268  case ObjectCreatedNotConfigured:
1269  // Checking we haven't been requested a new object (we cannot do this because another object exists
1270  // but is not configured - this is the safest policy, even if perhaps we could do something more smart).
1271  if (forceObjectCreation) {
1272  throw OtherObjectBeingCreatedException(group.toAscii().data());
1273  }
1274  // Now we can configure the object if we have to and then return the object
1275  retObj = dynamic_cast<TypeToCreate *>(object.object);
1276  if (retObj == NULL) {
1277  throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
1278  }
1279  if (configure) {
1280  // If we are here the object is surely of type ParameterSettableWithConfigureFunction
1281  // (otherwise the cast will fail miserably). We also change the state of the object before
1282  // and after configuration. Despite having casted object.object to TypeToCreate we use
1283  // another cast here because otherwise we would have compilation errors with classes
1284  // deriving from ParameterSettableInConstructor (but with those classes we should never
1285  // get here)
1286  node->setObjectForNode("", retObj, ConfiguringObject);
1288  o->configure(*this, group);
1289  // Telling the factory that the object has been configured
1290  m_factory->objectConfigured(o);
1291  node->setObjectForNode("", retObj, ObjectCreatedAndConfigured);
1292  }
1293  break;
1294  case ConfiguringObject:
1295  // Checking we haven't been requested a new object (we cannot do this because another object exists
1296  // and is being configured - this is the safest policy, even if perhaps we could do something more smart).
1297  if (forceObjectCreation) {
1298  throw OtherObjectBeingCreatedException(group.toAscii().data());
1299  }
1300  if (configure) {
1301  // Object is in configuration phase, cannot return it
1302  throw CyclicDependencyException(group.toAscii().data());
1303  } else {
1304  // Ok, returning object
1305  retObj = dynamic_cast<TypeToCreate *>(object.object);
1306  if (retObj == NULL) {
1307  throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
1308  }
1309  }
1310  break;
1311  case CreatingAndConfiguringObject:
1312  // Object is in creation and configuration phase, cannot return it and cannot create another object
1313  // (throwning a different exception depending on the situation)
1314  if (forceObjectCreation) {
1315  throw OtherObjectBeingCreatedException(group.toAscii().data());
1316  } else {
1317  throw CyclicDependencyException(group.toAscii().data());
1318  }
1319  break;
1320  case ObjectCreatedAndConfigured:
1321  // If we were requested a new object, creating it, otherwise we return the already existing one
1322  if (forceObjectCreation) {
1323  // Creating object. This will also remove association with the previous object
1324  retObj = m_factory->createFromParameter<TypeToCreate>(group, configure, &actuallyConfigured);
1325  retObjRAII.reset(retObj);
1326  } else {
1327  // Ok, returning object
1328  retObj = dynamic_cast<TypeToCreate *>(object.object);
1329  if (retObj == NULL) {
1330  throw CannotConvertToTypeException(typeid(object.object).name(), typeid(TypeToCreate));
1331  }
1332  }
1333  break;
1334  }
1335 
1336  // If the object has been created and not configured, appending to the list
1337  if (!actuallyConfigured) {
1338  ObjectAndRecursionLevel o;
1339  o.object = dynamic_cast<ParameterSettableWithConfigureFunction *>(retObj);
1340  o.recursionLevel = m_getObjectFromGroupRecursionLevel;
1341  o.groupName = group;
1342  m_objectsToConfigure.push_back(o);
1343  }
1344 
1345  // Releasing resources (explicitly, so that unconfigured objects that can be configured at this recursion level
1346  // will actually be configured)
1347  recursionLevelRAII.release();
1348 
1349  // Now checking if we have to call postConfigureInitialization() for all configured objects. The check is here (as
1350  // well as inside RealFactory::create) because this function can call RealFactory::create and vice-versa, so we have to check
1351  // which of the two is the most external call
1352  // NOTE: now that the factory cannot be used directly this check should be useless (we always call postConfigureInitialization()
1353  // here. Leaving it here, however, creates no harm (so, just to be sure, we don't touch it)
1354  if (m_factory->outsideCallsToCreate() && (m_getObjectFromGroupRecursionLevel == 0)) {
1355  m_factory->callPostConfigureInitializationForConfiguredObjects();
1356 
1357  // Safety check, just to be sure: when we get here, there should be no object in the list of objects yet to configure
1358  if (m_objectsToConfigure.size() != 0) {
1359  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());
1360  }
1361  }
1362 
1363  retObjRAII.release();
1364  return retObj;
1365 }
1366 
1367 } // end namespace farsa
1368 
1369 #endif