realfactory.h
1 /********************************************************************************
2  * FARSA - Total99 *
3  * Copyright (C) 2008-2011 Tomassino Ferrauto <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 Free Software *
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
18  ********************************************************************************/
19 
20 #ifndef REAL_FACTORY_H
21 #define REAL_FACTORY_H
22 
23 #include "configurationconfig.h"
24 #include "configurationexceptions.h"
25 #include "parametersettable.h"
26 #include "factoryobserver.h"
27 #include <QString>
28 #include <QStringList>
29 #include <QMap>
30 #include <QList>
31 #include <QVector>
32 #include <typeinfo>
33 #include <memory>
34 
35 namespace farsa {
36 
37 class ConfigurationParameters;
38 
45 class FARSA_CONF_TEMPLATE ParameterSettableCreator
46 {
47 public:
52  {
53  }
54 
71  virtual ParameterSettable* create(ConfigurationParameters& settings, QString prefix, bool configure) const = 0;
72 
80  virtual bool canDeferConfiguration() const = 0;
81 };
82 
92 template <class T, bool ConfigureInConstructor>
93 class FARSA_CONF_TEMPLATE ParameterSettableCreatorT : public ParameterSettableCreator
94 {
95 public:
100  {
101  }
102 
115  virtual ParameterSettableInConstructor* create(ConfigurationParameters& settings, QString prefix, bool configure) const;
116 
123  virtual bool canDeferConfiguration() const
124  {
125  return false;
126  }
127 };
128 
138 template <class T>
139 class FARSA_CONF_TEMPLATE ParameterSettableCreatorT<T, false> : public ParameterSettableCreator
140 {
141 public:
146  {
147  }
148 
161  virtual ParameterSettableWithConfigureFunction* create(ConfigurationParameters& settings, QString prefix, bool configure) const;
162 
169  virtual bool canDeferConfiguration() const
170  {
171  return true;
172  }
173 };
174 
186 class FARSA_CONF_API RealFactory {
187 public:
191  RealFactory(ConfigurationParameters& configurationParameters);
192 
196  ~RealFactory();
197 
203  void addObserver(FactoryObserver* observer);
204 
210  void addObservers(const QSet<FactoryObserver*>& observers);
211 
217  const QSet<FactoryObserver*>& getObservers() const;
218 
222  void clearObservers();
223 
251  template <class TypeToCreate>
252  TypeToCreate* create(const QString& className, QString prefix, bool configure = true, bool* actuallyConfigured = NULL);
253 
281  template <class TypeToCreate>
282  TypeToCreate* createFromParameter(QString prefix, bool configure = true, bool* actuallyConfigured = NULL);
283 
299  template <class TypeToCreate>
300  QList<TypeToCreate *> createListFromParameter(QString prefix, QString basename);
301 
317  template <class TypeToCreate>
318  QVector<TypeToCreate *> createVectorFromParameter(QString prefix, QString basename);
319 
329  void objectConfigured(ParameterSettable *object);
330 
339  bool outsideCallsToCreate() const;
340 
347  void callPostConfigureInitializationForConfiguredObjects();
348 
349 private:
353  ConfigurationParameters& m_configurationParameters;
354 
358  QSet<FactoryObserver*> m_observers;
359 
363  unsigned int m_createRecursionLevel;
364 
369  QList<ParameterSettable*> m_objectsConfiguredNotInitialized;
370 
371 private:
381  static bool orderByNumberAfterColon(const QString& s1, const QString& s2);
382 
383 private:
391  RealFactory(const RealFactory &other);
392 
400  RealFactory& operator=(const RealFactory &other);
401 };
402 
403 } // end namespace farsa
404 
405 // Implementation of template functions
406 #include "configurationparameters.h"
407 #include "factory.h"
408 
409 namespace farsa {
410 
411 template <class T, bool ConfigureInConstructor>
413 {
414  QString terminatedPrefix = prefix + ConfigurationParameters::GroupSeparator();
415 
416  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
417  // to be created and configured
418  if (!settings.setObjectFromGroupStatusToCreatingAndConfiguring(terminatedPrefix)) {
419  throw PrefixNotGroupException(prefix.toAscii().data());
420  }
421 
422  std::auto_ptr<T> t(new T(settings, terminatedPrefix));
423 
424  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
425  // created and configured
426  if (!settings.setObjectFromGroupStatusToCreatedAndConfigured(terminatedPrefix, t.get())) {
427  throw PrefixNotGroupException(prefix.toAscii().data());
428  }
429 
430  return t.release();
431 }
432 
433 template <class T>
435 {
436  QString terminatedPrefix = prefix + ConfigurationParameters::GroupSeparator();
437 
438  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
439  // to be created
440  if (!settings.setObjectFromGroupStatusToCreating(terminatedPrefix)) {
441  throw PrefixNotGroupException(prefix.toAscii().data());
442  }
443 
444  std::auto_ptr<T> t(new T());
445 
446  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
447  // created but not yet configured
448  if (!settings.setObjectFromGroupStatusToCreatedNotConfigured(terminatedPrefix, t.get())) {
449  throw PrefixNotGroupException(prefix.toAscii().data());
450  }
451 
452  if (configure) {
453  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix is about
454  // to be configured
455  if (!settings.setObjectFromGroupStatusToConfiguring(terminatedPrefix)) {
456  throw PrefixNotGroupException(prefix.toAscii().data());
457  }
458 
459  t->configure(settings, terminatedPrefix);
460 
461  // Telling the ConfigurationParameters object that the object for the group in terminatedPrefix has been
462  // created and configured
463  if (!settings.setObjectFromGroupStatusToCreatedAndConfigured(terminatedPrefix)) {
464  throw PrefixNotGroupException(prefix.toAscii().data());
465  }
466  }
467 
468  return t.release();
469 }
470 
471 template <class TypeToCreate>
472 TypeToCreate* RealFactory::create(const QString& className, QString prefix, bool configure, bool* actuallyConfigured)
473 {
474  // This is a simple class to implement RAII for the m_createRecursionLevel variable, so that in case
475  // an exception is thrown, its value is correctly restored to the previous one
476  class RecursionLevelRAII
477  {
478  public:
479  RecursionLevelRAII(unsigned int& createRecursionLevel, QList<ParameterSettable*>& objectsConfiguredNotInitialized) :
480  m_createRecursionLevel(createRecursionLevel)
481  {
482  if (m_createRecursionLevel == 0) {
483  // As this is the first call to create for the current m_settings, we remove the list of objects, if
484  // present (this could be here because the previous set of calls ended with an exception)
485  objectsConfiguredNotInitialized.clear();
486  }
487  m_createRecursionLevel++;
488  }
489 
490  ~RecursionLevelRAII() throw()
491  {
492  m_createRecursionLevel--;
493  }
494 
495  private:
496  unsigned int& m_createRecursionLevel;
497 
498  // We put implementations to avoid warning under windows. Anyway, these are private, so
499  // nobody can call them.
500  RecursionLevelRAII(const RecursionLevelRAII& other) : m_createRecursionLevel(other.m_createRecursionLevel) {}
501  RecursionLevelRAII& operator=(const RecursionLevelRAII& /*other*/) { return *this; }
502  };
503 
504  // Incrementing the recursion level for this function
505  RecursionLevelRAII recursionLevelRAII(m_createRecursionLevel, m_objectsConfiguredNotInitialized);
506 
507  // Checking that the class map contains the class name
508  if (!Factory::getInstance().m_classMap.contains(className)) {
509  throw ClassNameNotRegisteredException(className.toAscii().data());
510  }
511 
512  // First of all checking if it is possible to create and not configure the object
513  if (!Factory::getInstance().m_classMap[className]->canDeferConfiguration()) {
514  configure = true;
515  }
516  if (actuallyConfigured != NULL) {
517  (*actuallyConfigured) = configure;
518  }
519 
520  // Using auto_ptr to be sure to delete the object if something goes wrong (RAII)
521  std::auto_ptr<ParameterSettable> newObj(Factory::getInstance().m_classMap[className]->create(m_configurationParameters, prefix, configure));
522  TypeToCreate* obj = dynamic_cast<TypeToCreate*>(newObj.get());
523  // set the typeName to the newly created object
524  obj->setTypeName( className );
525 
526  if (obj == NULL) {
527  throw CannotConvertToTypeException(className.toAscii().data(), typeid(TypeToCreate));
528  }
529 
530  // If the object has been configured, adding it to the list of objects to be initialized
531  if (configure) {
532  m_objectsConfiguredNotInitialized.append(obj);
533  }
534 
535  // If we are outside any call to ConfigurationParameters::getObjectFromGroup() and this is the most external
536  // call to create (m_createRecursionLevel has been incremented from 0 to 1 by the constructor of RecursionLevelRAII),
537  // here we initialize all objects that have been configured. If the most external call to this function have been
538  // performed inside a call to ConfigurationParameters::getObjectFromGroup(), that function will do the job
539  // NOTE: this check should be useless now that create is protected and can only be called by ConfigurationParameters:
540  // calling postConfigureInitialization is always done inside ConfigurationParameters::getObjectFromGroup().
541  // Leaving it here, however, creates no harm (so, just to be sure, we don't touch it)
542  if (m_configurationParameters.outsideCallsToGetObjectFromGroup() && (m_createRecursionLevel == 1)) {
544  }
545 
546  // notify observers
547  foreach(FactoryObserver* obs, m_observers) {
548  obs->onObjectCreation(&m_configurationParameters, obj, prefix, configure);
549  }
550 
551  // Releasing the auto_ptr to prevent object deallocation
552  newObj.release();
553 
554  return obj;
555 }
556 
557 template <class TypeToCreate>
558 TypeToCreate* RealFactory::createFromParameter(QString prefix, bool configure, bool* actuallyConfigured)
559 {
560  QString type = m_configurationParameters.getValue(prefix + ConfigurationParameters::GroupSeparator() + QString("type"), false);
561 
562  if (!type.isNull()) {
563  return create<TypeToCreate>(type, prefix, configure, actuallyConfigured);
564  }
565 
566  throw CannotFindTypeParameterException(prefix.toAscii().data());
567 
568  return NULL;
569 }
570 
571 template <class TypeToCreate>
572 QList<TypeToCreate *> RealFactory::createListFromParameter(QString prefix, QString basename)
573 {
574  // The list of objects to return
575  QList<TypeToCreate *> objects;
576  // Getting the list of groups under prefix
577  QStringList objectsGroups = m_configurationParameters.getGroupsWithPrefixList(prefix, basename);
578 
579  // Now creating all groups
580  for (QStringList::iterator it = objectsGroups.begin(); it != objectsGroups.end(); it++) {
581  objects.push_back(createFromParameter<TypeToCreate>(prefix + ConfigurationParameters::GroupSeparator() + *it));
582  }
583 
584  return objects;
585 }
586 
587 template <class TypeToCreate>
588 QVector<TypeToCreate *> RealFactory::createVectorFromParameter(QString prefix, QString basename)
589 {
590  // The vector of objects to return
591  QVector<TypeToCreate *> objects;
592  // Getting the vector of groups under prefix
593  QStringList objectsGroups = m_configurationParameters.getGroupsWithPrefixList(prefix, basename);
594  // Order the data, so that the index respect the number specified after the colon in the groupname
595  qSort(objectsGroups.begin(), objectsGroups.end(), orderByNumberAfterColon);
596 
597  // Now creating all groups
598  for (QStringList::iterator it = objectsGroups.begin(); it != objectsGroups.end(); it++) {
599  objects.push_back(createFromParameter<TypeToCreate>(prefix + ConfigurationParameters::GroupSeparator() + *it));
600  }
601 
602  return objects;
603 }
604 
605 } // end namespace farsa
606 
607 #endif