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