Skip to content
Snippets Groups Projects
DynamicFactory.h 8.86 KiB
Newer Older
#ifndef MANTID_KERNEL_DYNAMICFACTORY_H_
#define MANTID_KERNEL_DYNAMICFACTORY_H_

//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidKernel/Exception.h"
#include "MantidKernel/Instantiator.h"
#include "MantidKernel/RegistrationHelper.h"
Samuel Jackson's avatar
Samuel Jackson committed
#include "MantidKernel/CaseInsensitiveMap.h"
#include <boost/shared_ptr.hpp>

// Poco
#include <Poco/Notification.h>
#include <Poco/NotificationCenter.h>

// std
namespace Mantid {
namespace Kernel {

//----------------------------------------------------------------------------
// Forward declarations
//----------------------------------------------------------------------------
class Logger;

typedef std::less<std::string> CaseSensitiveStringComparator;
/** @class DynamicFactory DynamicFactory.h Kernel/DynamicFactory.h

    The dynamic factory is a base dynamic factory for serving up objects in
   response
    @author Nick Draper, Tessella Support Services plc
    @date 10/10/2007

    Copyright &copy; 2007 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
   National Laboratory & European Spallation Source

    This file is part of Mantid.

    Mantid is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Mantid is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    File change history is stored at: <https://github.com/mantidproject/mantid>.
    Code Documentation is available at: <http://doxygen.mantidproject.org>
template <class Base, class Comparator = CaseInsensitiveStringComparator>
class DynamicFactory {
  /// Defines the whether notifications are dispatched
  enum NotificationStatus { Enabled, Disabled };
  enum SubscribeAction { ErrorIfExists, OverwriteCurrent };
  DynamicFactory(const DynamicFactory &) = delete;
  DynamicFactory &operator=(const DynamicFactory &) = delete;
  /**
   * Base class for dynamic factory notifications
   */
  class DynamicFactoryNotification : public Poco::Notification {};
   * A notification that the factory has been updated. This is
   * blind to the details.
  class UpdateNotification : public DynamicFactoryNotification {};
  void enableNotifications() { m_notifyStatus = Enabled; }
   * Disable notifications
  void disableNotifications() { m_notifyStatus = Disabled; }
  /// A typedef for the instantiator
  typedef AbstractInstantiator<Base> AbstractFactory;
  /// Destroys the DynamicFactory and deletes the instantiators for
  virtual ~DynamicFactory() {
    for (const auto &item : _map) {
      delete item.second;
  /// Creates a new instance of the class with the given name.
  /// The class must have been registered with subscribe() (typically done via a
  /// macro).
  /// If the class name is unknown, a NotFoundException is thrown.
  /// @param className :: the name of the class you wish to create
Nick Draper's avatar
Nick Draper committed
  /// @return a shared pointer ot the base class
  virtual boost::shared_ptr<Base> create(const std::string &className) const {
    auto it = _map.find(className);
    if (it != _map.end())
      return it->second->createInstance();
      throw Exception::NotFoundError(
          "DynamicFactory: " + className + " is not registered.\n", className);

  /// Creates a new instance of the class with the given name, which
  /// is not wrapped in a boost shared_ptr. This should be used with
  /// extreme care (or, better, not used)! The caller owns the returned
  /// instance.
  /// The class must have been registered with subscribe() (typically done via a
  /// macro).
  /// If the class name is unknown, a NotFoundException is thrown.
  /// @param className :: the name of the class you wish to create
Nick Draper's avatar
Nick Draper committed
  /// @return a pointer to the base class
  virtual Base *createUnwrapped(const std::string &className) const {
    auto it = _map.find(className);
    if (it != _map.end())
      return it->second->createUnwrappedInstance();
    else
      throw Exception::NotFoundError(
          "DynamicFactory: " + className + " is not registered.\n", className);
  /// Registers the instantiator for the given class with the DynamicFactory.
  /// The DynamicFactory takes ownership of the instantiator and deletes
  /// it when it's no longer used.
  /// If the class has already been registered, an ExistsException is thrown
  /// and the instantiator is deleted.
  /// @param className :: the name of the class you wish to subscribe
  template <class C> void subscribe(const std::string &className) {
    subscribe(className, new Instantiator<C, Base>);
Dickon Champion's avatar
Dickon Champion committed

  /// Registers the instantiator for the given class with the DynamicFactory.
  /// The DynamicFactory takes ownership of the instantiator and deletes
  /// it when it's no longer used.
  /// If the class has already been registered, an ExistsException is thrown
  /// and the instantiator is deleted.
  /// @param className :: the name of the class you wish to subscribe
  /// @param pAbstractFactory :: A pointer to an abstractFactory for this class
  /// @param replace :: If ReplaceExisting then the given AbstractFactory
  /// replaces any existing
  ///                   factory with the same className, else throws
  ///                   std::runtime_error (default=ThrowOnExisting)
  void subscribe(const std::string &className,
                 AbstractFactory *pAbstractFactory,
                 SubscribeAction replace = ErrorIfExists) {
    if (className.empty()) {
      throw std::invalid_argument("Cannot register empty class name");
    }
    auto it = _map.find(className);
    if (it == _map.end() || replace == OverwriteCurrent) {
      if (it != _map.end() && it->second)
        delete it->second;
Matt Clarke's avatar
Matt Clarke committed
      _map[className] = pAbstractFactory;
Dickon Champion's avatar
Dickon Champion committed
      throw std::runtime_error(className + " is already registered.\n");
  /// Unregisters the given class and deletes the instantiator
  /// for the class.
  /// Throws a NotFoundException if the class has not been registered.
  /// @param className :: the name of the class you wish to unsubscribe
  void unsubscribe(const std::string &className) {
    auto it = _map.find(className);
    if (!className.empty() && it != _map.end()) {
    } else {
      throw Exception::NotFoundError(
          "DynamicFactory:" + className + " is not registered.\n", className);
  /// Returns true if the given class is currently registered.
  /// @param className :: the name of the class you wish to check
Nick Draper's avatar
Nick Draper committed
  /// @returns true is the class is subscribed
  bool exists(const std::string &className) const {
    return _map.find(className) != _map.end();
  }

  /// Returns the keys in the map
  /// @return A string vector of keys
  virtual const std::vector<std::string> getKeys() const {
Nick Draper's avatar
Nick Draper committed
    names.reserve(_map.size());
    std::transform(
        _map.cbegin(), _map.cend(), std::back_inserter(names),
        [](const std::pair<std::string, AbstractFactory *> &mapPair) {
          return mapPair.first;
        });
  /// Sends notifications to observers. Observers can subscribe to
  /// notificationCenter
  /// using Poco::NotificationCenter::addObserver(...)
Nick Draper's avatar
Nick Draper committed
  /// @return nothing
  Poco::NotificationCenter notificationCenter;
protected:
  /// Protected constructor for base class
  DynamicFactory() : notificationCenter(), _map(), m_notifyStatus(Disabled) {}
  /// Send an update notification if they are enabled
  void sendUpdateNotificationIfEnabled() {
    if (m_notifyStatus == Enabled)
      sendUpdateNotification();
  void sendUpdateNotification() {
    notificationCenter.postNotification(new UpdateNotification);
  }

  /// A typedef for the map of registered classes
  typedef std::map<std::string, AbstractFactory *, Comparator> FactoryMap;
  /// The map holding the registered class names and their instantiators
  FactoryMap _map;
  /// Flag marking whether we should dispatch notifications
  NotificationStatus m_notifyStatus;