1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Dataquay
5 
6     A C++/Qt library for simple RDF datastore management.
7     Copyright 2009-2012 Chris Cannam.
8 
9     Permission is hereby granted, free of charge, to any person
10     obtaining a copy of this software and associated documentation
11     files (the "Software"), to deal in the Software without
12     restriction, including without limitation the rights to use, copy,
13     modify, merge, publish, distribute, sublicense, and/or sell copies
14     of the Software, and to permit persons to whom the Software is
15     furnished to do so, subject to the following conditions:
16 
17     The above copyright notice and this permission notice shall be
18     included in all copies or substantial portions of the Software.
19 
20     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
24     ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26     WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 
28     Except as contained in this notice, the name of Chris Cannam
29     shall not be used in advertising or otherwise to promote the sale,
30     use or other dealings in this Software without prior written
31     authorization.
32 */
33 
34 #ifndef _DATAQUAY_OBJECT_BUILDER_H_
35 #define _DATAQUAY_OBJECT_BUILDER_H_
36 
37 #include <QHash>
38 #include <QString>
39 #include <QVariant>
40 
41 namespace Dataquay {
42 
43 /**
44  * \class ObjectBuilder ObjectBuilder.h <dataquay/objectmapper/ObjectBuilder.h>
45  *
46  * ObjectBuilder is a singleton object factory capable of constructing
47  * new objects of classes that are subclassed from QObject.  Given the
48  * class name as a string, and optionally a parent object, it will
49  * return a new instance of the class.  To be capable of construction
50  * using ObjectBuilder, a class must be declared using Q_OBJECT as
51  * well as subclassed from QObject.
52  *
53  * All candidate object classes need to be registered with the builder
54  * before they can be constructed.  The only class that ObjectBuilder
55  * is able to construct without registration is QObject itself.
56  *
57  * This class permits code to construct new objects dynamically,
58  * without needing to know anything about them except for their class
59  * names, and without needing their definitions to be visible.  (The
60  * definitions must be visible when the object classes are registered,
61  * but not when the objects are constructed.)
62  */
63 class ObjectBuilder
64 {
65 public:
66     /**
67      * Retrieve the single global instance of ObjectBuilder.
68      */
69     static ObjectBuilder *getInstance();
70 
71     /**
72      * Register type T, a subclass of QObject, as a class that can be
73      * constructed by calling a zero-argument constructor.
74      *
75      * For example, registerClass<QAction>() declares that QAction is
76      * a subclass of QObject that may be built by calling
77      * QAction::QAction().
78      *
79      * A subsequent call to ObjectBuilder::build("QAction") would
80      * return a new QAction built with that constructor (since
81      * "QAction" is the class name of QAction returned by its meta
82      * object).
83      */
84     template <typename T>
registerClass()85     void registerClass() {
86         m_builders[T::staticMetaObject.className()] = new Builder0<T>();
87     }
88 
89     /**
90      * Register type T, a subclass of QObject, as a class that can be
91      * constructed by calling a single-argument constructor whose
92      * argument is of pointer-to-Parent type, where Parent is also a
93      * subclass of QObject.
94      *
95      * For example, registerClass<QWidget, QWidget>() declares that
96      * QWidget is a subclass of QObject that may be built by calling
97      * QWidget::QWidget(QWidget *parent).
98      *
99      * A subsequent call to ObjectBuilder::build("QWidget", parent)
100      * would return a new QWidget built with that constructor (since
101      * "QWidget" is the class name of QWidget returned by its meta
102      * object).
103      */
104     template <typename T, typename Parent>
registerClass()105     void registerClass() {
106         m_builders[T::staticMetaObject.className()] = new Builder1<T, Parent>();
107     }
108 
109     /**
110      * Register type T, a subclass of QObject, as a class that can be
111      * constructed by calling a zero-argument constructor.  Also
112      * declare pointerName to be the meta type name for pointers to
113      * type T, such that QVariant can be used to store such pointers.
114      *
115      * For example, registerClass<QAction>("QAction*") declares that
116      * QAction is a subclass of QObject that may be built by calling
117      * QAction::QAction(), and that "QAction*" has been registered
118      * (using qRegisterMetaType) as the meta type name for
119      * pointer-to-QAction.
120      *
121      * A subsequent call to ObjectBuilder::build("QAction") would
122      * return a new QAction built with that constructor (since
123      * "QAction" is the class name of QAction returned by its meta
124      * object).
125      */
126     template <typename T>
registerClass(QString pointerName)127     void registerClass(QString pointerName) {
128         QString className = T::staticMetaObject.className();
129         m_cpmap[className] = pointerName;
130         m_pcmap[pointerName] = className;
131         m_builders[className] = new Builder0<T>();
132         registerExtractor<T>(pointerName);
133     }
134 
135     /**
136      * Register type T, a subclass of QObject, as a class that can be
137      * constructed by calling a single-argument constructor whose
138      * argument is of pointer-to-Parent type, where Parent is also a
139      * subclass of QObject.  Also declare pointerName to be the meta
140      * type name for pointers to type T, such that QVariant can be
141      * used to store such pointers.
142      *
143      * For example, registerClass<QWidget, QWidget>("QWidget*")
144      * declares that QWidget is a subclass of QObject that may be
145      * built by calling QWidget::QWidget(QWidget *parent), and that
146      * "QWidget*" has been registered (using qRegisterMetaType) as the
147      * meta type name for pointer-to-QWidget.
148      *
149      * A subsequent call to ObjectBuilder::build("QWidget", parent)
150      * would return a new QWidget built with that constructor (since
151      * "QWidget" is the class name of QWidget returned by its meta
152      * object).
153      */
154     template <typename T, typename Parent>
registerClass(QString pointerName)155     void registerClass(QString pointerName) {
156         QString className = T::staticMetaObject.className();
157         m_cpmap[className] = pointerName;
158         m_pcmap[pointerName] = className;
159         m_builders[className] = new Builder1<T, Parent>();
160         registerExtractor<T>(pointerName);
161     }
162 
163     /**
164      * Register type T, a subclass of QObject, as an interface (a pure
165      * virtual class) and pointerName to be the meta type name for
166      * pointers to type T, such that QVariant can be used to store
167      * such pointers.
168      *
169      * For example, registerClass<Command>("Command*") declares that
170      * Command is a subclass of QObject that may not be built directly
171      * but that "Command*" has been registered (using
172      * qRegisterMetaType) as the meta type name for
173      * pointer-to-QAction.
174      *
175      * A subsequent call to ObjectBuilder::extract("Command*", v)
176      * would extract a pointer of type Command* from the QVariant v.
177      */
178     template <typename T>
registerInterface(QString pointerName)179     void registerInterface(QString pointerName) {
180         QString className = T::staticMetaObject.className();
181         m_cpmap[className] = pointerName;
182         m_pcmap[pointerName] = className;
183         registerExtractor<T>(pointerName);
184     }
185 
186     /**
187      * Return true if the class whose class name (according to its
188      * meta object) is className has been registered for building.
189      */
knows(QString className)190     bool knows(QString className) {
191         return m_builders.contains(className);
192     }
193 
194     /**
195      * Return a new object whose class name (according to its meta
196      * object) is className, with the given parent (cast
197      * appropriately) passed to its single argument constructor.
198      */
build(QString className,QObject * parent)199     QObject *build(QString className, QObject *parent) {
200         if (!knows(className)) return 0;
201         return m_builders[className]->build(parent);
202     }
203 
204     /**
205      * Return a new object whose class name (according to its meta
206      * object) is className, constructed with no parent.
207      */
build(QString className)208     QObject *build(QString className) {
209         if (!knows(className)) return 0;
210         return m_builders[className]->build(0);
211     }
212 
213     /**
214      * Return true if the class whose pointer has meta-type name
215      * pointerName has been registered with that pointer name
216      * (i.e. using one of the registerClass(pointerName) methods or
217      * registerInterface).
218      */
canExtract(QString pointerName)219     bool canExtract(QString pointerName) {
220         return m_extractors.contains(pointerName);
221     }
222 
223     /**
224      * Return true if the class whose pointer has meta-type name
225      * pointerName has been registered with that pointer name
226      * (i.e. using one of the registerClass(pointerName) methods or
227      * registerInterface).
228      */
canInject(QString pointerName)229     bool canInject(QString pointerName) {
230         return m_extractors.contains(pointerName);
231     }
232 
233     /**
234      * Provided the given pointerName has been registered using one of
235      * the registerClass(pointerName) methods or registerInterface,
236      * take the given variant containing that pointer type and extract
237      * and return the pointer.
238      */
extract(QString pointerName,QVariant & v)239     QObject *extract(QString pointerName, QVariant &v) {
240         if (!canExtract(pointerName)) return 0;
241         return m_extractors[pointerName]->extract(v);
242     }
243 
244     /**
245      * Provided the given pointerName has been registered using one of
246      * the registerClass(pointerName) methods or registerInterface,
247      * take the given pointer and stuff it into a variant, returning
248      * the result.
249      */
inject(QString pointerName,QObject * p)250     QVariant inject(QString pointerName, QObject *p) {
251         if (!canInject(pointerName)) return QVariant();
252         return m_extractors[pointerName]->inject(p);
253     }
254 
255     /**
256      * Provided the given pointerName has been registered using one of
257      * the registerClass(pointerName) methods, return the name of the
258      * class that was used as the template argument for that method.
259      */
getClassNameForPointerName(QString pointerName)260     QString getClassNameForPointerName(QString pointerName) const {
261         if (m_pcmap.contains(pointerName)) return m_pcmap[pointerName];
262         return "";
263     }
264 
265     /**
266      * If the class whose class name (according to its meta object) is
267      * className has been registered using one of the
268      * registerClass(pointerName) methods, return the pointerName that
269      * was passed to that method.
270      */
getPointerNameForClassName(QString className)271     QString getPointerNameForClassName(QString className) const {
272         if (m_cpmap.contains(className)) return m_cpmap[className];
273         return "";
274     }
275 
276 private:
ObjectBuilder()277     ObjectBuilder() {
278         registerClass<QObject, QObject>("QObject*");
279     }
~ObjectBuilder()280     ~ObjectBuilder() {
281         for (BuilderMap::iterator i = m_builders.begin();
282              i != m_builders.end(); ++i) {
283             delete *i;
284         }
285         for (ExtractorMap::iterator i = m_extractors.begin();
286              i != m_extractors.end(); ++i) {
287             delete *i;
288         }
289     }
290 
291     template <typename T>
292     void
registerExtractor(QString pointerName)293     registerExtractor(QString pointerName) {
294         m_extractors[pointerName] = new Extractor<T *>();
295     }
296 
297     template <typename T>
298     void
registerExtractor(QString pointerName,QString)299     registerExtractor(QString pointerName, QString /* listName */) {
300         m_extractors[pointerName] = new Extractor<T *>();
301     }
302 
303     struct BuilderBase {
~BuilderBaseBuilderBase304         virtual ~BuilderBase() { }
305         virtual QObject *build(QObject *) = 0;
306     };
307 
308     template <typename T> struct Builder0 : public BuilderBase {
buildBuilder0309         virtual QObject *build(QObject *) {
310             return new T();
311         }
312     };
313 
314     template <typename T, typename Parent> struct Builder1 : public BuilderBase {
buildBuilder1315         virtual QObject *build(QObject *p) {
316             return new T(qobject_cast<Parent *>(p));
317         }
318     };
319 
320     typedef QHash<QString, BuilderBase *> BuilderMap;
321     BuilderMap m_builders;
322 
323     struct ExtractorBase {
~ExtractorBaseExtractorBase324         virtual ~ExtractorBase() { }
325         virtual QObject *extract(const QVariant &v) = 0;
326         virtual QVariant inject(QObject *) = 0;
327     };
328 
329     template <typename Pointer> struct Extractor : public ExtractorBase {
extractExtractor330         virtual QObject *extract(const QVariant &v) {
331             return v.value<Pointer>();
332         }
injectExtractor333         virtual QVariant inject(QObject *o) {
334             Pointer p = qobject_cast<Pointer>(o);
335             if (p) return QVariant::fromValue<Pointer>(p);
336             else return QVariant();
337         }
338     };
339 
340     typedef QHash<QString, ExtractorBase *> ExtractorMap;
341     ExtractorMap m_extractors;
342 
343     QHash<QString, QString> m_cpmap;
344     QHash<QString, QString> m_pcmap;
345 };
346 
347 }
348 
349 #endif
350