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