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.toAscii().data());
1301  } else {
1302  throw CyclicDependencyException(group.toAscii().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.toAscii().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.toAscii().data());
1336  }
1337  if (configure) {
1338  // Object is in configuration phase, cannot return it
1339  throw CyclicDependencyException(group.toAscii().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.toAscii().data());
1353  } else {
1354  throw CyclicDependencyException(group.toAscii().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