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