]> git.tdb.fi Git - libs/datafile.git/blob - source/collection.h
Move most of Collection::get implementation to collection.cpp
[libs/datafile.git] / source / collection.h
1 #ifndef MSP_DATAFILE_COLLECTION_H_
2 #define MSP_DATAFILE_COLLECTION_H_
3
4 #include <msp/core/maputils.h>
5 #include <msp/core/meta.h>
6 #include <msp/core/refptr.h>
7 #include "collectionsource.h"
8 #include "loader.h"
9
10 /* XXX This file is a big mess with too many things in it.  However, the
11 dependencies between those things make it difficult to split up. */
12
13 namespace Msp {
14 namespace DataFile {
15
16 /**
17 Helper struct to determine whether a Loader has a Collection typedef.
18 */
19 template<typename T>
20 struct NeedsCollection
21 {
22         struct Yes { char c[2]; };
23         struct No { char c; };
24
25         template<typename U>
26         static Yes f(typename U::Collection *);
27         template<typename U>
28         static No f(...);
29
30         enum { value = (sizeof(f<T>(0))==sizeof(Yes)) };
31 };
32
33 class CollectionItemTypeBase;
34
35 template<typename T>
36 class CollectionItemType;
37
38 /**
39 A collection of objects that can be loaded from a datafile.  Each object is
40 identified by a name, which must be unique across the entire collection.
41
42 While this class can be instantiated by itself and used for storing objects,
43 loading requires that a subclass defines the supported types.  See the add_type
44 method for details.
45
46 Collections can have sources for loading objects on demand.  Automatic loading
47 only works on a non-const Collection.  See class CollectionSource for details.
48 */
49 class Collection
50 {
51 public:
52         /**
53         Loads objects into a Collection.  Automatically picks up keywords from
54         defined item types.
55         */
56         class Loader: public DataFile::Loader
57         {
58                 template<typename T> friend class CollectionItemType;
59
60         private:
61                 Collection &coll;
62
63         public:
64                 Loader(Collection &);
65                 Collection &get_object() const { return coll; }
66         private:
67                 template<typename T, typename S>
68                 void item(const std::string &n)
69                 {
70                         RefPtr<T> it = new T;
71                         ItemLoader<T> ldr(*it, coll);
72                         load_sub_with(ldr);
73                         coll.add<S>(n, it.get());
74                         it.release();
75                 }
76         };
77
78 protected:
79         template<typename T, bool = NeedsCollection<typename T::Loader>::value>
80         class ItemLoader;
81
82 private:
83         typedef std::map<std::string, Variant> ItemMap;
84         typedef std::list<CollectionItemTypeBase *> TypeList;
85         typedef std::list<CollectionSource *> SourceList;
86
87         TypeList types;
88         ItemMap items;
89         SourceList sources;
90
91         Collection(const Collection &);
92         Collection &operator=(const Collection &);
93 public:
94         Collection() { }
95         virtual ~Collection();
96
97         /** Adds an object into the collection.  The name must not pre-exist.  The
98         collection takes ownership of the object. */
99         template<typename T>
100         void add(const std::string &name, T *item)
101         {
102                 if(!item)
103                         throw std::invalid_argument("Collection::add(item)");
104
105                 insert_unique(items, name, RefPtr<typename RemoveConst<T>::Type>(item));
106         }
107
108         /// Gets a typed object from the collection.
109         template<typename T>
110         T &get(const std::string &name) const
111         {
112                 return extract<T>(get_item(items, name));
113         }
114
115         /** Gets a typed object from the collection.  If the name is not found,
116         automatic creation with the type's creator function (if defined) or from
117         sources (if present) is attempted. */
118         template<typename T>
119         T &get(const std::string &name)
120         {
121                 return extract<T>(get_var(name, get_type<T>()));
122         }
123
124 private:
125         const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
126
127         template<typename T>
128         T &extract(const Variant &var) const
129         {
130                 return *var.value<RefPtr<typename RemoveConst<T>::Type> >();
131         }
132
133         template<typename T>
134         void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
135         {
136                 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
137
138                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
139                         if(i->second.check_type<RPNCT>())
140                         {
141                                 if(objects)
142                                         objects->push_back(i->second.value<RPNCT>().get());
143                                 if(names)
144                                         names->push_back(i->first);
145                         }
146
147                 if(future_names)
148                         if(CollectionItemTypeBase *type = get_type<T>())
149                         {
150                                 for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
151                                 {
152                                         std::list<std::string> available_names = (*i)->get_names(*type);
153                                         for(std::list<std::string>::iterator j=available_names.begin(); j!=available_names.end(); ++j)
154                                                 if(!items.count(*j))
155                                                         future_names->push_back(*j);
156                                 }
157                         }
158         }
159
160 public:
161         /// Returns a list of the names of objects of one type in the collection.
162         template<typename T>
163         std::list<std::string> get_names() const
164         {
165                 std::list<std::string> result;
166                 collect_items<T>(0, &result, 0);
167                 return result;
168         }
169
170         /** Returns a list of the names of objects of one type in the collection or
171         available from sources. */
172         template<typename T>
173         std::list<std::string> get_names()
174         {
175                 std::list<std::string> result;
176                 collect_items<T>(0, &result, &result);
177                 return result;
178         }
179
180         /// Returns a list of objects of one type in the collection.
181         template<typename T>
182         std::list<T *> get_list() const
183         {
184                 std::list<T *> result;
185                 collect_items<T>(&result, 0, 0);
186                 return result;
187         }
188
189         /** Returns a list of objects of one type, loading them from sources if
190         necessary. */
191         template<typename T>
192         std::list<T *> get_list()
193         {
194                 std::list<T *> result;
195                 std::list<std::string> future;
196                 collect_items<T>(&result, 0, &future);
197                 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
198                         result.push_back(&get<T>(*i));
199                 return result;
200         }
201
202 private:
203         template<typename T>
204         unsigned get_status(const std::string &name) const
205         {
206                 ItemMap::const_iterator i = items.find(name);
207                 if(i==items.end())
208                 {
209                         if(CollectionItemTypeBase *type = get_type<T>())
210                         {
211                                 for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j)
212                                         if((*j)->is_loadable(*type, name))
213                                                 return 2;
214                         }
215                         return 0;
216                 }
217
218                 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
219                 if(!i->second.check_type<RPNCT>())
220                         return 0;
221
222                 return 1;
223         }
224
225 public:
226         /// Checks whether a typed object exists in the collection.
227         template<typename T>
228         bool contains(const std::string &name) const
229         { return get_status<T>(name)==1; }
230
231         /** Checks whether a typed object exists in the collection or is loadable
232         from a source. */
233         template<typename T>
234         bool contains(const std::string &name)
235         { return get_status<T>(name)>0; }
236
237         /// Returns the name of an item in the collection.
238         template<typename T>
239         const std::string &get_name(T *d) const
240         {
241                 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
242
243                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
244                         if(i->second.check_type<RPNCT>())
245                                 if(i->second.value<RPNCT>().get()==d)
246                                         return i->first;
247         
248                 // XXX Need better exception class
249                 throw std::runtime_error("Item not found in collection");
250         }
251
252 protected:
253         /** Adds a type to the collection.  The returned descriptor object reference
254         can be used to define how objects of that type can be loaded. */
255         template<typename T>
256         CollectionItemType<T> &add_type();
257
258         /** Returns the descriptor for a type, or null if one isn't defined. */
259         template<typename T>
260         CollectionItemTypeBase *get_type() const;
261
262         void add_source(CollectionSource &);
263 };
264
265 template<typename T>
266 class Collection::ItemLoader<T, false>: public T::Loader
267 {
268 public:
269         ItemLoader(T &o, Collection &):
270                 T::Loader(o)
271         { }
272 };
273
274 template<typename T>
275 class Collection::ItemLoader<T, true>: public T::Loader
276 {
277 public:
278         ItemLoader(T &o, Collection &c):
279                 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
280         { }
281 };
282
283
284 class CollectionItemTypeBase
285 {
286 protected:
287         class TagBase
288         {
289         protected:
290                 TagBase() { }
291         public:
292                 virtual ~TagBase() { }
293         };
294
295         template<typename T>
296         class Tag: public TagBase
297         { };
298
299         std::string kwd;
300         std::vector<std::string> suffixes;
301         TagBase *tag;
302
303         CollectionItemTypeBase();
304 public:
305         virtual ~CollectionItemTypeBase();
306
307         void set_keyword(const std::string &);
308         const std::string &get_keyword() const { return kwd; }
309         void add_suffix(const std::string &);
310         bool match_name(const std::string &) const;
311         virtual void add_to_loader(Collection::Loader &) const = 0;
312         virtual bool can_create() const = 0;
313         virtual void create_item(Collection &, const std::string &) const = 0;
314         virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
315
316         template<typename T>
317         bool check_type() const
318         { return dynamic_cast<Tag<T> *>(tag); }
319 };
320
321
322 /**
323 Describes a type of item that can be loaded by a Collection.  These are created
324 by Collection::add_type.
325 */
326 template<typename T>
327 class CollectionItemType: public CollectionItemTypeBase
328 {
329 private:
330         class CreatorBase
331         {
332         protected:
333                 CreatorBase() { }
334         public:
335                 virtual ~CreatorBase() { }
336
337                 virtual T *create(Collection &, const std::string &) const = 0;
338         };
339
340         template<typename C>
341         class Creator: public CreatorBase
342         {
343         public:
344                 typedef T *(C::*FuncPtr)(const std::string &);
345
346         private:
347                 FuncPtr func;
348
349         public:
350                 Creator(FuncPtr f): func(f) { }
351
352                 virtual T *create(Collection &coll, const std::string &name) const
353                 { return (static_cast<C &>(coll).*func)(name); }
354         };
355
356         class StoreBase
357         {
358         protected:
359                 StoreBase() { }
360         public:
361                 virtual ~StoreBase() { }
362
363                 virtual void store(Collection &, const std::string &, T *) = 0;
364
365                 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
366         };
367
368         template<typename S>
369         class Store: public StoreBase
370         {
371         public:
372                 virtual void store(Collection &coll, const std::string &name, T *obj)
373                 { coll.add(name, static_cast<S *>(obj)); }
374
375                 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
376                 { loader.add(kwd, &Collection::Loader::item<T, S>); }
377         };
378
379         CreatorBase *creat;
380         StoreBase *store;
381
382 public:
383         CollectionItemType():
384                 creat(0), store(new Store<T>)
385         { tag = new Tag<T>; }
386
387         ~CollectionItemType()
388         {
389                 delete creat;
390                 delete store;
391         }
392
393         /** Sets a datafile keyword for this item type.  The Collection's loader
394         will accept a statement with this keyword and a single string argument - the
395         item's name. */
396         CollectionItemType &keyword(const std::string &k)
397         {
398                 set_keyword(k);
399                 return *this;
400         }
401
402         /** Adds a suffix that is used to match names when looking for future
403         objects.  There is no implied separator; a name matches if it ends with the
404         suffix.  If a keyword is defined before any suffixes, then "."+keyword is
405         added as a suffix. */
406         CollectionItemType &suffix(const std::string &s)
407         {
408                 add_suffix(s);
409                 return *this;
410         }
411
412         /** Attaches a creator function to this item type.  If an item is not found
413         in the Collection, the creator function for its type is called to create it.
414         The function must be a member of the Collection subclass containing the
415         type.  It must return the created object, or null if it could not be
416         created.  It's also permissible to load the item via other means and then
417         return null. */
418         template<typename C>
419         CollectionItemType &creator(T *(C::*func)(const std::string &))
420         {
421                 delete creat;
422                 creat = new Creator<C>(func);
423                 return *this;
424         }
425
426         /** Specifies the storage type for items of this type.  It must be a base
427         class of the actual type.  */
428         template<typename S>
429         CollectionItemType &store_as()
430         {
431                 delete tag;
432                 tag = new Tag<S>;
433                 delete store;
434                 store = new Store<S>;
435                 return *this;
436         }
437
438         virtual void add_to_loader(Collection::Loader &loader) const
439         { store->add_to_loader(loader, kwd); }
440
441         virtual bool can_create() const
442         { return creat!=0; }
443
444         virtual void create_item(Collection &coll, const std::string &name) const
445         {
446                 if(!creat)
447                         throw std::runtime_error("no creator");
448                 T *obj = creat->create(coll, name);
449                 if(obj)
450                         store->store(coll, name, obj);
451         }
452
453         virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
454         {
455                 RefPtr<T> obj = new T;
456                 Collection::ItemLoader<T> ldr(*obj, coll);
457                 ldr.load(parser);
458                 store->store(coll, name, obj.get());
459                 obj.release();
460         }
461 };
462
463
464 template<typename T>
465 CollectionItemType<T> &Collection::add_type()
466 {
467         CollectionItemType<T> *type = new CollectionItemType<T>;
468         types.push_back(type);
469         return *type;
470 }
471
472 template<typename T>
473 CollectionItemTypeBase *Collection::get_type() const
474 {
475         for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
476                 if((*j)->check_type<typename RemoveConst<T>::Type>())
477                         return *j;
478         return 0;
479 }
480
481 } // namespace DataFile
482 } // namespace Msp
483
484 #endif