/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Rosegarden A MIDI and audio sequencer and musical notation editor. Copyright 2000-2011 the Rosegarden development team. This program 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 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #ifndef _ROSEGARDEN_THING_FACTORY_H_ #define _ROSEGARDEN_THING_FACTORY_H_ #include #include #include #include #include #include "UrlHash.h" namespace Rosegarden { template class AbstractThingBuilder; class UnknownThingException { }; class UnknownTagException { }; class DuplicateThingException { }; /** * A factory for objects from classes that share a common base class, * have identical single-argument constructors, can be identified by * URI, and that can register their existence with the factory (so * that the factory does not have to know about all buildable * classes). * * ** How to use these classes ** * * Given a base class A with many subclasses B, C, D, etc, all of * which need to be passed parameters class P in their constructor: * * -- in a header associated with A, * * - Create a template class ABuilder which inherits from * ConcreteThingBuilder. This is your class which will * be specialised to provide a builder for each subclass of A. * Its constructor must accept a QUrl containing the URI that * identifies the class of object being built, which is passed * to the parent class's constructor. Optionally, it may also * accept and pass to the parent class a QStringList of "tags", * which are strings used to identify the sorts of facility this * builder's object supports -- for example, file extensions or * MIME types that the object can parse. If two builders * register support for the same tag, only the first to register * will be used (note that which one this is may depend on * static object construction ordering, so it's generally better * if tags are unique to a builder). * * - You may also wish to typedef ThingFactory to something * like AFactory, for convenience. * * -- in a .cpp file associated with each of B, C, D etc, * * - Define a static variable of class ABuilder, ABuilder, * ABuilder, etc, passing the class's identifying URI and * optional supported tag list to its constructor. (If you * like, this could be a static member of some class.) * * You can then do the following: * * -- call AFactory::getInstance()->getURIs() to retrieve a list of * all registered URIs for this factory. * * -- call AFactory::getInstance()->create(uri, parameters), where * parameters is an object of type P, to construct a new object * whose class is that associated with the URI uri. * * -- call AFactory::getInstance()->getURIFor(tag), where tag is a * QString corresponding to one of the tags supported by some * builder, to obtain the URI of the first builder to have * registered its support for the given tag. * * -- call AFactory::getInstance()->createFor(tag, parameters), where * tag is a QString corresponding to one of the tags supported by * some builder and parameters is an object of type P, to * construct a new object whose class is that built by the first * builder to have registered its support for the given tag. */ template class ThingFactory { protected: typedef AbstractThingBuilder Builder; typedef QHash BuilderMap; typedef QHash TagURIMap; public: typedef QSet URISet; static ThingFactory *getInstance() { static QMutex mutex; QMutexLocker locker(&mutex); if (!m_instance) m_instance = new ThingFactory(); return m_instance; } URISet getURIs() const { QList keys = m_registry.keys(); URISet s; for (int i = 0; i < keys.size(); ++i) s.insert(keys[i]); return s; } QStringList getTags() const { return m_tags.keys(); } QUrl getURIFor(QString tag) const { if (!m_tags.contains(tag)) { throw UnknownTagException(); } return m_tags[tag]; } Thing *create(QUrl uri, Parameters p) const { if (!m_registry.contains(uri)) { throw UnknownThingException(); } return m_registry[uri]->build(p); } Thing *createFor(QString tag, Parameters p) const { return create(getURIFor(tag), p); } void registerBuilder(QUrl uri, Builder *builder) { if (m_registry.contains(uri)) { throw DuplicateThingException(); } m_registry[uri] = builder; } void registerBuilder(QUrl uri, Builder *builder, QStringList tags) { if (m_registry.contains(uri)) { throw DuplicateThingException(); } m_registry[uri] = builder; for (int i = 0; i < tags.size(); ++i) { if (m_tags.contains(tags[i])) continue; m_tags[tags[i]] = uri; } } protected: static ThingFactory *m_instance; BuilderMap m_registry; TagURIMap m_tags; }; template class AbstractThingBuilder { public: virtual ~AbstractThingBuilder() { } virtual Thing *build(Parameters) = 0; }; template class ConcreteThingBuilder : public AbstractThingBuilder { public: ConcreteThingBuilder(QUrl uri) { ThingFactory::getInstance()->registerBuilder(uri, this); } ConcreteThingBuilder(QUrl uri, QStringList tags) { ThingFactory::getInstance()->registerBuilder(uri, this, tags); } virtual Thing *build(Parameters p) { return new ConcreteThing(p); } }; } #endif