Newer
Older
Russell Taylor
committed
#ifndef MANTID_KERNEL_DYNAMICFACTORY_H_
#define MANTID_KERNEL_DYNAMICFACTORY_H_
Russell Taylor
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
Gigg, Martyn Anthony
committed
#include "MantidKernel/DllConfig.h"
#include "MantidKernel/Instantiator.h"
#include "MantidKernel/RegistrationHelper.h"
Gigg, Martyn Anthony
committed
// Boost
#ifndef Q_MOC_RUN
Gigg, Martyn Anthony
committed
// Poco
#include <Poco/Notification.h>
#include <Poco/NotificationCenter.h>
// std
#include <functional>
Matt Clarke
committed
#include <vector>
Russell Taylor
committed
namespace Mantid {
namespace Kernel {
//----------------------------------------------------------------------------
// Forward declarations
//----------------------------------------------------------------------------
class Logger;
typedef std::less<std::string> CaseSensitiveStringComparator;
Russell Taylor
committed
/** @class DynamicFactory DynamicFactory.h Kernel/DynamicFactory.h
The dynamic factory is a base dynamic factory for serving up objects in
response
Gigg, Martyn Anthony
committed
to requests from other classes.
Russell Taylor
committed
@author Nick Draper, Tessella Support Services plc
@date 10/10/2007
Copyright © 2007 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
National Laboratory & European Spallation Source
Russell Taylor
committed
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>
Russell Taylor
committed
*/
template <class Base, class Comparator = CaseInsensitiveStringComparator>
class DynamicFactory {
public:
/// Defines the whether notifications are dispatched
enum NotificationStatus { Enabled, Disabled };
/// Defines replacement behaviour
enum SubscribeAction { ErrorIfExists, OverwriteCurrent };
DynamicFactory(const DynamicFactory &) = delete;
DynamicFactory &operator=(const DynamicFactory &) = delete;
Gigg, Martyn Anthony
committed
/**
* Base class for dynamic factory notifications
*/
class DynamicFactoryNotification : public Poco::Notification {};
Gigg, Martyn Anthony
committed
/**
* A notification that the factory has been updated. This is
* blind to the details.
Gigg, Martyn Anthony
committed
*/
class UpdateNotification : public DynamicFactoryNotification {};
Gigg, Martyn Anthony
committed
/**
* Enable notifications
*/
void enableNotifications() { m_notifyStatus = Enabled; }
/**
*/
void disableNotifications() { m_notifyStatus = Disabled; }
Russell Taylor
committed
/// A typedef for the instantiator
typedef AbstractInstantiator<Base> AbstractFactory;
/// Destroys the DynamicFactory and deletes the instantiators for
Russell Taylor
committed
/// all registered classes.
for (const auto &item : _map) {
delete item.second;
Russell Taylor
committed
}
}
Russell Taylor
committed
/// Creates a new instance of the class with the given name.
/// The class must have been registered with subscribe() (typically done via a
/// macro).
Russell Taylor
committed
/// If the class name is unknown, a NotFoundException is thrown.
Janik Zikovsky
committed
/// @param className :: the name of the class you wish to create
virtual boost::shared_ptr<Base> create(const std::string &className) const {
auto it = _map.find(className);
Russell Taylor
committed
if (it != _map.end())
return it->second->createInstance();
Russell Taylor
committed
else
throw Exception::NotFoundError(
"DynamicFactory: " + className + " is not registered.\n", className);
Russell Taylor
committed
}
Gigg, Martyn Anthony
committed
/// 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).
Gigg, Martyn Anthony
committed
/// If the class name is unknown, a NotFoundException is thrown.
Janik Zikovsky
committed
/// @param className :: the name of the class you wish to create
virtual Base *createUnwrapped(const std::string &className) const {
auto it = _map.find(className);
Gigg, Martyn Anthony
committed
if (it != _map.end())
return it->second->createUnwrappedInstance();
else
throw Exception::NotFoundError(
"DynamicFactory: " + className + " is not registered.\n", className);
Gigg, Martyn Anthony
committed
}
Russell Taylor
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.
Janik Zikovsky
committed
/// @param className :: the name of the class you wish to subscribe
template <class C> void subscribe(const std::string &className) {
Russell Taylor
committed
subscribe(className, new Instantiator<C, Base>);
Russell Taylor
committed
}
Russell Taylor
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.
Janik Zikovsky
committed
/// @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()) {
delete pAbstractFactory;
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;
sendUpdateNotificationIfEnabled();
delete pAbstractFactory;
throw std::runtime_error(className + " is already registered.\n");
Russell Taylor
committed
}
Russell Taylor
committed
}
Russell Taylor
committed
/// Unregisters the given class and deletes the instantiator
/// for the class.
/// Throws a NotFoundException if the class has not been registered.
Janik Zikovsky
committed
/// @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()) {
Russell Taylor
committed
delete it->second;
_map.erase(it);
sendUpdateNotificationIfEnabled();
} else {
throw Exception::NotFoundError(
"DynamicFactory:" + className + " is not registered.\n", className);
Russell Taylor
committed
}
Russell Taylor
committed
}
Russell Taylor
committed
/// Returns true if the given class is currently registered.
Janik Zikovsky
committed
/// @param className :: the name of the class you wish to check
bool exists(const std::string &className) const {
Russell Taylor
committed
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 {
Russell Taylor
committed
std::vector<std::string> names;
_map.cbegin(), _map.cend(), std::back_inserter(names),
[](const std::pair<std::string, AbstractFactory *> &mapPair) {
return mapPair.first;
});
Russell Taylor
committed
return names;
}
Gigg, Martyn Anthony
committed
/// Sends notifications to observers. Observers can subscribe to
/// notificationCenter
Gigg, Martyn Anthony
committed
/// using Poco::NotificationCenter::addObserver(...)
Gigg, Martyn Anthony
committed
Poco::NotificationCenter notificationCenter;
Russell Taylor
committed
protected:
/// Protected constructor for base class
DynamicFactory() : notificationCenter(), _map(), m_notifyStatus(Disabled) {}
Gigg, Martyn Anthony
committed
Russell Taylor
committed
private:
/// Send an update notification if they are enabled
void sendUpdateNotificationIfEnabled() {
if (m_notifyStatus == Enabled)
sendUpdateNotification();
}
/// Send an update notification
notificationCenter.postNotification(new UpdateNotification);
}
/// A typedef for the map of registered classes
typedef std::map<std::string, AbstractFactory *, Comparator> FactoryMap;
Russell Taylor
committed
/// The map holding the registered class names and their instantiators
FactoryMap _map;
/// Flag marking whether we should dispatch notifications
NotificationStatus m_notifyStatus;
Russell Taylor
committed
};
Russell Taylor
committed
} // namespace Kernel
} // namespace Mantid
Russell Taylor
committed
Russell Taylor
committed
#endif /*MANTID_KERNEL_DYNAMICFACTORY_H_*/