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