]> git.tdb.fi Git - libs/datafile.git/blob - source/collection.h
Add missing virtual destructor to Collection::ItemKeywordBase
[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 ~ItemKeywordBase() { }
76                 virtual void add_to_loader(Loader &) const { };
77         };
78
79         template<typename T, typename S, bool need_coll=NeedsCollection<typename T::Loader>::result>
80         struct ItemKeyword: public ItemKeywordBase
81         {
82                 std::string keyword;
83
84                 ItemKeyword(const std::string &kw): keyword(kw) { }
85
86                 void add_to_loader(Loader &ldr) const
87                 { ldr.add(keyword, &Loader::item<T, S>); }
88         };
89
90         template<typename T, typename S>
91         struct ItemKeyword<T, S, true>: public ItemKeywordBase
92         {
93                 std::string keyword;
94
95                 ItemKeyword(const std::string &kw): keyword(kw) { }
96
97                 virtual void add_to_loader(Loader &ldr) const
98                 { ldr.add(keyword, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
99         };
100
101         /**
102         Used to store types that can be created automatically.
103         */
104         struct ItemCreatorBase
105         {
106                 virtual ~ItemCreatorBase() { }
107
108                 template<typename S>
109                 bool create(Collection &coll, const std::string &name, S *&ptr)
110                 {
111                         ItemCreatorBridge<S> *creator=dynamic_cast<ItemCreatorBridge<S> *>(this);
112                         if(creator)
113                         {
114                                 ptr=creator->create(coll, name);
115                                 return true;
116                         }
117                         return false;
118                 }
119         };
120
121         template<typename S>
122         struct ItemCreatorBridge: public ItemCreatorBase
123         {
124                 virtual S *create(Collection &, const std::string &) const =0;
125         };
126
127         template<typename T, typename S, typename C>
128         struct ItemCreator: public ItemCreatorBridge<S>
129         {
130                 typedef T *(C::*fCreate)(const std::string &);
131
132                 fCreate create_func;
133
134                 ItemCreator(fCreate cf): create_func(cf) { }
135                 virtual S *create(Collection &coll, const std::string &name) const
136                 { return (dynamic_cast<C &>(coll).*create_func)(name); }
137         };
138
139 public:
140         /**
141         Loads objects into a Collection.
142         */
143         class Loader: public DataFile::Loader
144         {
145         private:
146                 Collection &coll;
147
148         public:
149                 Loader(Collection &);
150                 Collection &get_object() const { return coll; }
151         private:
152                 template<typename T, typename S, typename C>
153                 void coll_item(const std::string &n)
154                 {
155                         RefPtr<T> it=new T;
156                         load_sub(*it, dynamic_cast<C &>(coll));
157                         coll.add<S>(n, it.get());
158                         it.release();
159                 }
160
161                 template<typename T, typename S>
162                 void item(const std::string &n)
163                 {
164                         RefPtr<T> it=new T;
165                         load_sub(*it);
166                         coll.add<S>(n, it.get());
167                         it.release();
168                 }
169
170                 template<typename, typename, bool> friend class ItemKeyword;
171         };
172
173 private:
174         typedef std::map<std::string, ItemBase *> ItemMap;
175         typedef std::list<ItemKeywordBase *> ItemKeywordSeq;
176         typedef std::list<ItemCreatorBase *> ItemCreatorSeq;
177
178         ItemMap items;
179         ItemKeywordSeq keywords;
180         ItemCreatorSeq creators;
181
182         Collection(const Collection &);
183         Collection &operator=(const Collection &);
184 public:
185         Collection() { }
186         virtual ~Collection();
187
188         /**
189         Adds an object into the collection.  If a name collision occurs, an
190         exception is thrown.  The collection takes ownership of the object.
191         */
192         template<typename T>
193         void add(const std::string &name, T *d)
194         {
195                 if(items.count(name))
196                         throw KeyError("Duplicate key in collection", name);
197
198                 items[name]=new Item<typename RemoveConst<T>::Type>(d);
199         }
200
201         /**
202         Gets an object of a specific type from the collection.
203         */
204         template<typename T>
205         T *get(const std::string &name) const
206         {
207                 typedef typename RemoveConst<T>::Type NCT;
208
209                 ItemMap::const_iterator i=items.find(name);
210                 if(i==items.end())
211                         throw KeyError("Item not found in collection", name);
212
213                 const Item<NCT> *item=dynamic_cast<const Item<NCT> *>(i->second);
214                 if(!item)
215                         throw TypeError("Type mismatch on item '"+name+"'");
216
217                 return item->data;
218         }
219
220         /**
221         Gets an object of a specific type from the collection.  If the name is not
222         found in the collection and there is a creator for the item type, it is
223         invoked.
224         */
225         template<typename T>
226         T *get(const std::string &name)
227         {
228                 typedef typename RemoveConst<T>::Type NCT;
229
230                 ItemMap::const_iterator i=items.find(name);
231                 if(i==items.end())
232                 {
233                         for(ItemCreatorSeq::iterator j=creators.begin(); j!=creators.end(); ++j)
234                         {
235                                 NCT *d=0;
236                                 if((*j)->create(*this, name, d))
237                                 {
238                                         // We already know that the item didn't exist yet
239                                         items[name]=new Item<NCT>(d);
240                                         return d;
241                                 }
242                         }
243                         throw KeyError("Item not found in collection", name);
244                 }
245
246                 const Item<NCT> *item=dynamic_cast<const Item<NCT> *>(i->second);
247                 if(!item)
248                         throw TypeError("Type mismatch on item '"+name+"'");
249
250                 return item->data;
251         }
252
253         /**
254         Returns a list of the names of objects of a specific type in the collection.
255         */
256         template<typename T>
257         std::list<std::string> get_names() const
258         {
259                 std::list<std::string> result;
260                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
261                         if(dynamic_cast<const Item<typename RemoveConst<T>::Type> *>(i->second))
262                                 result.push_back(i->first);
263                 return result;
264         }
265
266         /**
267         Returns a list of objects of a specific type in the collection.
268         */
269         template<typename T>
270         std::list<T *> get_list() const
271         {
272                 typedef typename RemoveConst<T>::Type NCT;
273
274                 std::list<T *> result;
275                 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
276                         if(Item<NCT> *item=dynamic_cast<Item<NCT> *>(i->second))
277                                 result.push_back(item->data);
278                 return result;
279         }
280
281         /**
282         Checks whether a name exists in the collection.  Does not care about the
283         type of the object.
284         */
285         bool contains(const std::string &n) const;
286
287 protected:
288         /**
289         Adds a type that can be loaded from datafiles.
290         */
291         template<typename T>
292         void add_keyword(const std::string &keyword)
293         { add_keyword<T, T>(keyword); }
294
295         /**
296         Adds a type that can be loaded from datafiles, with different storage type.
297         */
298         template<typename T, typename S>
299         void add_keyword(const std::string &keyword)
300         { keywords.push_back(new ItemKeyword<T, S>(keyword)); }
301
302         /**
303         Adds a type that can be created automatically.
304         */
305         template<typename T, typename C>
306         void add_creator(T *(C::*func)(const std::string &))
307         { add_creator<T, T, C>(func); }
308
309         template<typename T, typename S, typename C>
310         void add_creator(T *(C::*func)(const std::string &))
311         { creators.push_back(new ItemCreator<T, S, C>(func)); }
312 };
313
314 } // namespace DataFile
315 } // namespace Msp
316
317 #endif