1 #ifndef MSP_DATAFILE_COLLECTION_H_
2 #define MSP_DATAFILE_COLLECTION_H_
4 #include <msp/core/maputils.h>
5 #include <msp/core/meta.h>
6 #include <msp/core/refptr.h>
13 Helper struct to determine whether a Loader has a Collection typedef.
16 struct NeedsCollection
18 struct Yes { char c[2]; };
19 struct No { char c; };
22 static Yes f(typename U::Collection *);
26 enum { value = (sizeof(f<T>(0))==sizeof(Yes)) };
29 class CollectionItemTypeBase;
32 class CollectionItemType;
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.
42 Loads objects into a Collection.
44 class Loader: public DataFile::Loader
46 template<typename T> friend class CollectionItemType;
49 template<typename T, typename S, bool = NeedsCollection<T>::value >
56 Collection &get_object() const { return coll; }
58 template<typename T, typename S, typename C>
59 void coll_item(const std::string &n)
62 load_sub(*it, dynamic_cast<C &>(coll));
63 coll.add<S>(n, it.get());
67 template<typename T, typename S>
68 void item(const std::string &n)
72 coll.add<S>(n, it.get());
76 template<typename, typename, bool> friend class ItemKeyword;
80 typedef std::map<std::string, Variant> ItemMap;
81 typedef std::list<CollectionItemTypeBase *> TypeList;
86 Collection(const Collection &);
87 Collection &operator=(const Collection &);
90 virtual ~Collection();
93 Adds an object into the collection. If a name collision occurs, an
94 exception is thrown. The collection takes ownership of the object.
97 void add(const std::string &name, T *item)
100 throw std::invalid_argument("Collection::add(item)");
102 RefPtr<typename RemoveConst<T>::Type> ptr(item);
105 insert_unique(items, name, ptr);
109 // Avoid deleting the object
116 Gets an object of a specific type from the collection.
119 T &get(const std::string &name) const
121 typedef typename RemoveConst<T>::Type NCT;
123 return *get_item(items, name).value<RefPtr<NCT> >();
127 Gets an object of a specific type from the collection. If the name is not
128 found in the collection and there is a creator for the item type, it is
132 T &get(const std::string &);
135 Returns a list of the names of objects of a specific type in the collection.
138 std::list<std::string> get_names() const
140 std::list<std::string> result;
141 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
142 if(i->second.check_type<RefPtr<typename RemoveConst<T>::Type> >())
143 result.push_back(i->first);
148 Returns a list of objects of a specific type in the collection.
151 std::list<T *> get_list() const
153 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
155 std::list<T *> result;
156 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
157 if(i->second.check_type<RPNCT>())
158 result.push_back(i->second.value<RPNCT>().get());
163 Checks whether a name exists in the collection. Does not care about the
166 bool contains(const std::string &n) const;
169 Returns the name of an item in the collection.
172 const std::string &get_name(T *d) const
174 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
176 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
177 if(i->second.check_type<RPNCT>())
178 if(i->second.value<RPNCT>().get()==d)
181 // XXX Need better exception class
182 throw std::runtime_error("Item not found in collection");
187 CollectionItemType<T> &add_type();
191 template<typename T, typename S>
192 struct Collection::Loader::Add<T, S, false>
194 static void add(Loader &loader, const std::string &kwd)
195 { loader.add(kwd, &Loader::item<T, S>); }
198 template<typename T, typename S>
199 struct Collection::Loader::Add<T, S, true>
201 static void add(Loader &loader, const std::string &kwd)
202 { loader.add(kwd, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
206 class CollectionItemTypeBase
214 virtual ~TagBase() { }
218 class Tag: public TagBase
227 CollectionItemTypeBase(): tag(0) { }
229 virtual ~CollectionItemTypeBase()
232 virtual void add_to_loader(Collection::Loader &) const = 0;
233 virtual bool can_create() const = 0;
234 virtual void create_item(Collection &, const std::string &) const = 0;
237 bool check_type() const
238 { return dynamic_cast<Tag<T> *>(tag); }
243 class CollectionItemType: public CollectionItemTypeBase
251 virtual ~CreatorBase() { }
253 virtual T *create(Collection &, const std::string &) const = 0;
257 class Creator: public CreatorBase
260 typedef T *(C::*FuncPtr)(const std::string &);
266 Creator(FuncPtr f): func(f) { }
268 virtual T *create(Collection &coll, const std::string &name) const
269 { return (static_cast<C &>(coll).*func)(name); }
277 virtual ~StoreBase() { }
279 virtual void store(Collection &, const std::string &, T *) = 0;
281 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
285 class Store: public StoreBase
288 virtual void store(Collection &coll, const std::string &name, T *obj)
289 { coll.add(name, static_cast<S *>(obj)); }
291 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
292 { Collection::Loader::Add<T, S>::add(loader, kwd); }
295 CreatorBase *creator;
299 CollectionItemType():
300 creator(0), store(new Store<T>)
301 { tag = new Tag<T>; }
303 ~CollectionItemType()
309 CollectionItemType &keyword(const std::string &k)
316 CollectionItemType &create(T *(C::*func)(const std::string &))
319 creator = new Creator<C>(func);
324 CollectionItemType &store_as()
329 store = new Store<S>;
333 virtual void add_to_loader(Collection::Loader &loader) const
334 { store->add_to_loader(loader, kwd); }
336 virtual bool can_create() const
337 { return creator!=0; }
339 virtual void create_item(Collection &coll, const std::string &name) const
342 throw std::runtime_error("no creator");
343 T *obj = creator->create(coll, name);
344 store->store(coll, name, obj);
350 T &Collection::get(const std::string &name)
352 typedef typename RemoveConst<T>::Type NCT;
354 if(!items.count(name))
356 for(TypeList::iterator i=types.begin(); i!=types.end(); ++i)
357 if((*i)->can_create() && (*i)->check_type<NCT>())
358 (*i)->create_item(*this, name);
361 return *get_item(items, name).value<RefPtr<NCT> >();
365 CollectionItemType<T> &Collection::add_type()
367 CollectionItemType<T> *type = new CollectionItemType<T>;
368 types.push_back(type);
372 } // namespace DataFile