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