]> git.tdb.fi Git - libs/datafile.git/blob - source/collection.h
Fix collection necessity detection
[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 "loader.h"
8
9 namespace Msp {
10 namespace DataFile {
11
12 /**
13 Helper struct to determine whether a Loader has a Collection typedef.
14 */
15 template<typename T>
16 struct NeedsCollection
17 {
18         struct Yes { char c[2]; };
19         struct No { char c; };
20
21         template<typename U>
22         static Yes f(typename U::Collection *);
23         template<typename U>
24         static No f(...);
25
26         enum { value = (sizeof(f<T>(0))==sizeof(Yes)) };
27 };
28
29 class CollectionItemTypeBase;
30
31 template<typename T>
32 class CollectionItemType;
33
34 /**
35 A collection of objects that can be loaded from a datafile.  Each object is
36 identified by a name, which must be unique across the entire collection.
37 */
38 class Collection
39 {
40 public:
41         /**
42         Loads objects into a Collection.
43         */
44         class Loader: public DataFile::Loader
45         {
46                 template<typename T> friend class CollectionItemType;
47
48         private:
49                 template<typename T, typename S, bool = NeedsCollection<typename T::Loader>::value>
50                 struct Add;
51
52                 Collection &coll;
53
54         public:
55                 Loader(Collection &);
56                 Collection &get_object() const { return coll; }
57         private:
58                 template<typename T, typename S, typename C>
59                 void coll_item(const std::string &n)
60                 {
61                         RefPtr<T> it=new T;
62                         load_sub(*it, dynamic_cast<C &>(coll));
63                         coll.add<S>(n, it.get());
64                         it.release();
65                 }
66
67                 template<typename T, typename S>
68                 void item(const std::string &n)
69                 {
70                         RefPtr<T> it=new T;
71                         load_sub(*it);
72                         coll.add<S>(n, it.get());
73                         it.release();
74                 }
75
76                 template<typename, typename, bool> friend class ItemKeyword;
77         };
78
79 private:
80         typedef std::map<std::string, Variant> ItemMap;
81         typedef std::list<CollectionItemTypeBase *> TypeList;
82
83         TypeList types;
84         ItemMap items;
85
86         Collection(const Collection &);
87         Collection &operator=(const Collection &);
88 public:
89         Collection() { }
90         virtual ~Collection();
91
92         /**
93         Adds an object into the collection.  If a name collision occurs, an
94         exception is thrown.  The collection takes ownership of the object.
95         */
96         template<typename T>
97         void add(const std::string &name, T *item)
98         {
99                 if(!item)
100                         throw std::invalid_argument("Collection::add(item)");
101
102                 RefPtr<typename RemoveConst<T>::Type> ptr(item);
103                 try
104                 {
105                         insert_unique(items, name, ptr);
106                 }
107                 catch(...)
108                 {
109                         // Avoid deleting the object
110                         ptr.release();
111                         throw;
112                 }
113         }
114
115         /**
116         Gets an object of a specific type from the collection.
117         */
118         template<typename T>
119         T &get(const std::string &name) const
120         {
121                 typedef typename RemoveConst<T>::Type NCT;
122
123                 return *get_item(items, name).value<RefPtr<NCT> >();
124         }
125
126         /**
127         Gets an object of a specific type from the collection.  If the name is not
128         found in the collection and there is a creator for the item type, it is
129         invoked.
130         */
131         template<typename T>
132         T &get(const std::string &);
133
134         /**
135         Returns a list of the names of objects of a specific type in the collection.
136         */
137         template<typename T>
138         std::list<std::string> get_names() const
139         {
140                 std::list<std::string> result;
141                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
142                         if(i->second.check_type<RefPtr<typename RemoveConst<T>::Type> >())
143                                 result.push_back(i->first);
144                 return result;
145         }
146
147         /**
148         Returns a list of objects of a specific type in the collection.
149         */
150         template<typename T>
151         std::list<T *> get_list() const
152         {
153                 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
154
155                 std::list<T *> result;
156                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
157                         if(i->second.check_type<RPNCT>())
158                                 result.push_back(i->second.value<RPNCT>().get());
159                 return result;
160         }
161
162         /**
163         Checks whether a name exists in the collection.  Does not care about the
164         type of the object.
165         */
166         bool contains(const std::string &n) const;
167
168         /**
169         Returns the name of an item in the collection.
170         */
171         template<typename T>
172         const std::string &get_name(T *d) const
173         {
174                 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
175
176                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
177                         if(i->second.check_type<RPNCT>())
178                                 if(i->second.value<RPNCT>().get()==d)
179                                         return i->first;
180         
181                 // XXX Need better exception class
182                 throw std::runtime_error("Item not found in collection");
183         }
184
185 protected:
186         template<typename T>
187         CollectionItemType<T> &add_type();
188 };
189
190
191 template<typename T, typename S>
192 struct Collection::Loader::Add<T, S, false>
193 {
194         static void add(Loader &loader, const std::string &kwd)
195         { loader.add(kwd, &Loader::item<T, S>); }
196 };
197
198 template<typename T, typename S>
199 struct Collection::Loader::Add<T, S, true>
200 {
201         static void add(Loader &loader, const std::string &kwd)
202         { loader.add(kwd, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
203 };
204
205
206 class CollectionItemTypeBase
207 {
208 protected:
209         class TagBase
210         {
211         protected:
212                 TagBase() { }
213         public:
214                 virtual ~TagBase() { }
215         };
216
217         template<typename T>
218         class Tag: public TagBase
219         {
220         public:
221                 virtual ~Tag() { }
222         };
223
224         std::string kwd;
225         TagBase *tag;
226
227         CollectionItemTypeBase(): tag(0) { }
228 public:
229         virtual ~CollectionItemTypeBase()
230         { delete tag; }
231
232         virtual void add_to_loader(Collection::Loader &) const = 0;
233         virtual bool can_create() const = 0;
234         virtual void create_item(Collection &, const std::string &) const = 0;
235
236         template<typename T>
237         bool check_type() const
238         { return dynamic_cast<Tag<T> *>(tag); }
239 };
240
241
242 template<typename T>
243 class CollectionItemType: public CollectionItemTypeBase
244 {
245 private:
246         class CreatorBase
247         {
248         protected:
249                 CreatorBase() { }
250         public:
251                 virtual ~CreatorBase() { }
252
253                 virtual T *create(Collection &, const std::string &) const = 0;
254         };
255
256         template<typename C>
257         class Creator: public CreatorBase
258         {
259         public:
260                 typedef T *(C::*FuncPtr)(const std::string &);
261
262         private:
263                 FuncPtr func;
264
265         public:
266                 Creator(FuncPtr f): func(f) { }
267
268                 virtual T *create(Collection &coll, const std::string &name) const
269                 { return (static_cast<C &>(coll).*func)(name); }
270         };
271
272         class StoreBase
273         {
274         protected:
275                 StoreBase() { }
276         public:
277                 virtual ~StoreBase() { }
278
279                 virtual void store(Collection &, const std::string &, T *) = 0;
280
281                 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
282         };
283
284         template<typename S>
285         class Store: public StoreBase
286         {
287         public:
288                 virtual void store(Collection &coll, const std::string &name, T *obj)
289                 { coll.add(name, static_cast<S *>(obj)); }
290
291                 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
292                 { Collection::Loader::Add<T, S>::add(loader, kwd); }
293         };
294
295         CreatorBase *creat;
296         StoreBase *store;
297
298 public:
299         CollectionItemType():
300                 creat(0), store(new Store<T>)
301         { tag = new Tag<T>; }
302
303         ~CollectionItemType()
304         {
305                 delete creat;
306                 delete store;
307         }
308
309         CollectionItemType &keyword(const std::string &k)
310         {
311                 kwd = k;
312                 return *this;
313         }
314
315         template<typename C>
316         CollectionItemType &creator(T *(C::*func)(const std::string &))
317         {
318                 delete creat;
319                 creat = new Creator<C>(func);
320                 return *this;
321         }
322
323         template<typename S>
324         CollectionItemType &store_as()
325         {
326                 delete tag;
327                 tag = new Tag<S>;
328                 delete store;
329                 store = new Store<S>;
330                 return *this;
331         }
332
333         virtual void add_to_loader(Collection::Loader &loader) const
334         { store->add_to_loader(loader, kwd); }
335
336         virtual bool can_create() const
337         { return creat!=0; }
338
339         virtual void create_item(Collection &coll, const std::string &name) const
340         {
341                 if(!creat)
342                         throw std::runtime_error("no creator");
343                 T *obj = creat->create(coll, name);
344                 store->store(coll, name, obj);
345         }
346 };
347
348
349 template<typename T>
350 T &Collection::get(const std::string &name)
351 {
352         typedef typename RemoveConst<T>::Type NCT;
353
354         if(!items.count(name))
355         {
356                 for(TypeList::iterator i=types.begin(); i!=types.end(); ++i)
357                         if((*i)->can_create() && (*i)->check_type<NCT>())
358                                 (*i)->create_item(*this, name);
359         }
360
361         return *get_item(items, name).value<RefPtr<NCT> >();
362 }
363
364 template<typename T>
365 CollectionItemType<T> &Collection::add_type()
366 {
367         CollectionItemType<T> *type = new CollectionItemType<T>;
368         types.push_back(type);
369         return *type;
370 }
371
372 } // namespace DataFile
373 } // namespace Msp
374
375 #endif