]> git.tdb.fi Git - libs/datafile.git/blob - source/collection.h
Bugfixes
[libs/datafile.git] / source / collection.h
1 /* $Id$
2
3 This file is part of libmspdatafile
4 Copyright © 2006  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #ifndef MSP_DATAFILE_COLLECTION_H_
9 #define MSP_DATAFILE_COLLECTION_H_
10
11 #include <msp/core/refptr.h>
12 #include "loader.h"
13
14 namespace Msp {
15 namespace DataFile {
16
17 /**
18 Helper struct to determine whether a Loader has a Collection typedef.
19 */
20 template<typename T>
21 struct NeedsCollection
22 {
23         struct Yes { char c[2]; };
24         struct No { char c; };
25
26         template<typename U>
27         static Yes f(typename U::Collection *);
28         template<typename U>
29         static No f(...);
30
31         enum { result=(sizeof(f<T>(0))==sizeof(Yes)) };
32 };
33
34 template<typename T>
35 struct RemoveConst
36 { typedef T Type; };
37
38 template<typename T>
39 struct RemoveConst<const T>
40 { typedef T Type; };
41
42 /**
43 A collection of objects that can be loaded from a datafile.  Each object is
44 identified by a name, which must be unique across the entire collection.
45 */
46 class Collection
47 {
48 public:
49         class Loader;
50
51 private:
52         /* XXX I don't really like sticking all this stuff in here, but there's some
53            complex inter-class relationships, especially between ItemKeyword and
54            Collection::Loader. */
55
56         struct ItemBase
57         {
58                 virtual ~ItemBase() { }
59         };
60
61         template<typename T>
62         struct Item: public ItemBase
63         {
64                 T *data;
65
66                 Item(T *d): data(d) { }
67                 ~Item() { delete data; }
68         };
69
70         /**
71         Used to store keywords for types that can be loaded.
72         */
73         struct ItemKeywordBase
74         {
75                 virtual void add_to_loader(Loader &) const { };
76         };
77
78         template<typename T, typename S, bool need_coll=NeedsCollection<typename T::Loader>::result>
79         struct ItemKeyword: public ItemKeywordBase
80         {
81                 std::string keyword;
82
83                 ItemKeyword(const std::string &kw): keyword(kw) { }
84
85                 void add_to_loader(Loader &ldr) const
86                 { ldr.add(keyword, &Loader::item<T, S>); }
87         };
88
89         template<typename T, typename S>
90         struct ItemKeyword<T, S, true>: public ItemKeywordBase
91         {
92                 std::string keyword;
93
94                 ItemKeyword(const std::string &kw): keyword(kw) { }
95
96                 virtual void add_to_loader(Loader &ldr) const
97                 { ldr.add(keyword, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
98         };
99
100         /**
101         Used to store types that can be created automatically.
102         */
103         struct ItemCreatorBase
104         {
105                 virtual ~ItemCreatorBase() { }
106
107                 template<typename S>
108                 bool create(Collection &coll, const std::string &name, S *&ptr)
109                 {
110                         ItemCreatorBridge<S> *creator=dynamic_cast<ItemCreatorBridge<S> *>(this);
111                         if(creator)
112                         {
113                                 ptr=creator->create(coll, name);
114                                 return true;
115                         }
116                         return false;
117                 }
118         };
119
120         template<typename S>
121         struct ItemCreatorBridge: public ItemCreatorBase
122         {
123                 virtual S *create(Collection &, const std::string &) const =0;
124         };
125
126         template<typename T, typename S, typename C>
127         struct ItemCreator: public ItemCreatorBridge<S>
128         {
129                 typedef T *(C::*fCreate)(const std::string &);
130
131                 fCreate create_func;
132
133                 ItemCreator(fCreate cf): create_func(cf) { }
134                 virtual S *create(Collection &coll, const std::string &name) const
135                 { return (dynamic_cast<C &>(coll).*create_func)(name); }
136         };
137
138 public:
139         /**
140         Loads objects into a Collection.
141         */
142         class Loader: public DataFile::Loader
143         {
144         private:
145                 Collection &coll;
146
147         public:
148                 Loader(Collection &);
149                 Collection &get_object() const { return coll; }
150         private:
151                 template<typename T, typename S, typename C>
152                 void coll_item(const std::string &n)
153                 {
154                         RefPtr<T> it=new T;
155                         load_sub(*it, dynamic_cast<C &>(coll));
156                         coll.add<S>(n, it.get());
157                         it.release();
158                 }
159
160                 template<typename T, typename S>
161                 void item(const std::string &n)
162                 {
163                         RefPtr<T> it=new T;
164                         load_sub(*it);
165                         coll.add<S>(n, it.get());
166                         it.release();
167                 }
168
169                 template<typename, typename, bool> friend class ItemKeyword;
170         };
171
172 private:
173         typedef std::map<std::string, ItemBase *> ItemMap;
174         typedef std::list<ItemKeywordBase *> ItemKeywordSeq;
175         typedef std::list<ItemCreatorBase *> ItemCreatorSeq;
176
177         ItemMap items;
178         ItemKeywordSeq keywords;
179         ItemCreatorSeq creators;
180
181         Collection(const Collection &);
182         Collection &operator=(const Collection &);
183 public:
184         Collection() { }
185         virtual ~Collection();
186
187         /**
188         Adds an object into the collection.  If a name collision occurs, an
189         exception is thrown.  The collection takes ownership of the object.
190         */
191         template<typename T>
192         void add(const std::string &name, T *d)
193         {
194                 if(items.count(name))
195                         throw KeyError("Duplicate key '"+name+"' in collection");
196
197                 items[name]=new Item<typename RemoveConst<T>::Type>(d);
198         }
199
200         /**
201         Gets an object of a specific type from the collection.
202         */
203         template<typename T>
204         T *get(const std::string &name) const
205         {
206                 typedef typename RemoveConst<T>::Type NCT;
207
208                 ItemMap::const_iterator i=items.find(name);
209                 if(i==items.end())
210                         throw KeyError("Item '"+name+"' not found in collection");
211
212                 const Item<NCT> *item=dynamic_cast<const Item<NCT> *>(i->second);
213                 if(!item)
214                         throw TypeError("Item '"+name+"' is not of correct type");
215
216                 return item->data;
217         }
218
219         /**
220         Gets an object of a specific type from the collection.  If the name is not
221         found in the collection and there is a creator for the item type, it is
222         invoked.
223         */
224         template<typename T>
225         T *get(const std::string &name)
226         {
227                 typedef typename RemoveConst<T>::Type NCT;
228
229                 ItemMap::const_iterator i=items.find(name);
230                 if(i==items.end())
231                 {
232                         for(ItemCreatorSeq::iterator j=creators.begin(); j!=creators.end(); ++j)
233                         {
234                                 NCT *d=0;
235                                 if((*j)->create(*this, name, d))
236                                 {
237                                         // We already know that the item didn't exist yet
238                                         items[name]=new Item<NCT>(d);
239                                         return d;
240                                 }
241                         }
242                         throw KeyError("Item '"+name+"' not found in collection");
243                 }
244
245                 const Item<NCT> *item=dynamic_cast<const Item<NCT> *>(i->second);
246                 if(!item)
247                         throw TypeError("Item '"+name+"' is not of correct type");
248
249                 return item->data;
250         }
251
252         /**
253         Returns a list of the names of objects of a specific type in the collection.
254         */
255         template<typename T>
256         std::list<std::string> get_names() const
257         {
258                 std::list<std::string> result;
259                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
260                         if(dynamic_cast<const Item<typename RemoveConst<T>::Type> *>(i->second))
261                                 result.push_back(i->first);
262                 return result;
263         }
264
265         /**
266         Returns a list of objects of a specific type in the collection.
267         */
268         template<typename T>
269         std::list<T *> get_list() const
270         {
271                 typedef typename RemoveConst<T>::Type NCT;
272
273                 std::list<T *> result;
274                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
275                         if(Item<NCT> *item=dynamic_cast<Item<NCT> *>(i->second))
276                                 result.push_back(item->data);
277                 return result;
278         }
279
280         /**
281         Checks whether a name exists in the collection.  Does not care about the
282         type of the object.
283         */
284         bool contains(const std::string &n) const;
285
286 protected:
287         /**
288         Adds a type that can be loaded from datafiles.
289         */
290         template<typename T>
291         void add_keyword(const std::string &keyword)
292         { add_keyword<T, T>(keyword); }
293
294         /**
295         Adds a type that can be loaded from datafiles, with different storage type.
296         */
297         template<typename T, typename S>
298         void add_keyword(const std::string &keyword)
299         { keywords.push_back(new ItemKeyword<T, S>(keyword)); }
300
301         /**
302         Adds a type that can be created automatically.
303         */
304         template<typename T, typename C>
305         void add_creator(T *(C::*func)(const std::string &))
306         { add_creator<T, T, C>(func); }
307
308         template<typename T, typename S, typename C>
309         void add_creator(T *(C::*func)(const std::string &))
310         { creators.push_back(new ItemCreator<T, S, C>(func)); }
311 };
312
313 } // namespace DataFile
314 } // namespace Msp
315
316 #endif