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