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