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.
38 While this class can be instantiated by itself and used for storing objects,
39 loading requires that a subclass defines the supported types. See the add_type
46 Loads objects into a Collection. Automatically picks up keywords from
49 class Loader: public DataFile::Loader
51 template<typename T> friend class CollectionItemType;
54 template<typename T, typename S, bool = NeedsCollection<typename T::Loader>::value>
61 Collection &get_object() const { return coll; }
63 template<typename T, typename S, typename C>
64 void coll_item(const std::string &n)
67 load_sub(*it, dynamic_cast<C &>(coll));
68 coll.add<S>(n, it.get());
72 template<typename T, typename S>
73 void item(const std::string &n)
77 coll.add<S>(n, it.get());
83 typedef std::map<std::string, Variant> ItemMap;
84 typedef std::list<CollectionItemTypeBase *> TypeList;
89 Collection(const Collection &);
90 Collection &operator=(const Collection &);
93 virtual ~Collection();
95 /** Adds an object into the collection. The name must not pre-exist. The
96 collection takes ownership of the object. */
98 void add(const std::string &name, T *item)
101 throw std::invalid_argument("Collection::add(item)");
103 RefPtr<typename RemoveConst<T>::Type> ptr(item);
106 insert_unique(items, name, ptr);
110 // Avoid deleting the object
116 /// Gets a typed object from the collection.
118 T &get(const std::string &name) const
120 typedef typename RemoveConst<T>::Type NCT;
122 return *get_item(items, name).value<RefPtr<NCT> >();
125 /** Gets a typed object from the collection. If the name is not found in
126 and a creator for the item type is defined, it is invoked. */
128 T &get(const std::string &);
130 /// Returns a list of the names of objects of one type in the collection.
132 std::list<std::string> get_names() const
134 std::list<std::string> result;
135 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
136 if(i->second.check_type<RefPtr<typename RemoveConst<T>::Type> >())
137 result.push_back(i->first);
141 /// Returns a list of objects of one type in the collection.
143 std::list<T *> get_list() const
145 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
147 std::list<T *> result;
148 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
149 if(i->second.check_type<RPNCT>())
150 result.push_back(i->second.value<RPNCT>().get());
154 /// Checks whether a typed object exists in the collection.
156 bool contains(const std::string &name) const
158 ItemMap::const_iterator i = items.find(name);
162 return i->second.check_type<typename RemoveConst<T>::Type>();
165 /// Returns the name of an item in the collection.
167 const std::string &get_name(T *d) const
169 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
171 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
172 if(i->second.check_type<RPNCT>())
173 if(i->second.value<RPNCT>().get()==d)
176 // XXX Need better exception class
177 throw std::runtime_error("Item not found in collection");
181 /** Adds a type to the collection. The returned descriptor object reference
182 can be used to define how objects of that type can be loaded. */
184 CollectionItemType<T> &add_type();
188 template<typename T, typename S>
189 struct Collection::Loader::Add<T, S, false>
191 static void add(Loader &loader, const std::string &kwd)
192 { loader.add(kwd, &Loader::item<T, S>); }
195 template<typename T, typename S>
196 struct Collection::Loader::Add<T, S, true>
198 static void add(Loader &loader, const std::string &kwd)
199 { loader.add(kwd, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
203 class CollectionItemTypeBase
211 virtual ~TagBase() { }
215 class Tag: public TagBase
224 CollectionItemTypeBase(): tag(0) { }
226 virtual ~CollectionItemTypeBase()
229 virtual void add_to_loader(Collection::Loader &) const = 0;
230 virtual bool can_create() const = 0;
231 virtual void create_item(Collection &, const std::string &) const = 0;
234 bool check_type() const
235 { return dynamic_cast<Tag<T> *>(tag); }
240 Describes a type of item that can be loaded by a Collection. These are created
241 by Collection::add_type.
244 class CollectionItemType: public CollectionItemTypeBase
252 virtual ~CreatorBase() { }
254 virtual T *create(Collection &, const std::string &) const = 0;
258 class Creator: public CreatorBase
261 typedef T *(C::*FuncPtr)(const std::string &);
267 Creator(FuncPtr f): func(f) { }
269 virtual T *create(Collection &coll, const std::string &name) const
270 { return (static_cast<C &>(coll).*func)(name); }
278 virtual ~StoreBase() { }
280 virtual void store(Collection &, const std::string &, T *) = 0;
282 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
286 class Store: public StoreBase
289 virtual void store(Collection &coll, const std::string &name, T *obj)
290 { coll.add(name, static_cast<S *>(obj)); }
292 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
293 { Collection::Loader::Add<T, S>::add(loader, kwd); }
300 CollectionItemType():
301 creat(0), store(new Store<T>)
302 { tag = new Tag<T>; }
304 ~CollectionItemType()
310 /** Sets a datafile keyword for this item type. The Collection's loader
311 will accept a statement with this keyword and a single string argument - the
313 CollectionItemType &keyword(const std::string &k)
319 /** Attaches a creator function to this item type. If an item is not found
320 in the Collection, the creator function for its type is called to create it.
321 The function must be a member of the Collection subclass containing the
322 type. It must return the created object, or null if it could not be
323 created. It's also permissible to load the item via other means and then
326 CollectionItemType &creator(T *(C::*func)(const std::string &))
329 creat = new Creator<C>(func);
333 /** Specifies the storage type for items of this type. It must be a base
334 class of the actual type. */
336 CollectionItemType &store_as()
341 store = new Store<S>;
345 virtual void add_to_loader(Collection::Loader &loader) const
346 { store->add_to_loader(loader, kwd); }
348 virtual bool can_create() const
351 virtual void create_item(Collection &coll, const std::string &name) const
354 throw std::runtime_error("no creator");
355 T *obj = creat->create(coll, name);
357 store->store(coll, name, obj);
363 T &Collection::get(const std::string &name)
365 typedef typename RemoveConst<T>::Type NCT;
367 if(!items.count(name))
369 for(TypeList::iterator i=types.begin(); i!=types.end(); ++i)
370 if((*i)->can_create() && (*i)->check_type<NCT>())
371 (*i)->create_item(*this, name);
374 return *get_item(items, name).value<RefPtr<NCT> >();
378 CollectionItemType<T> &Collection::add_type()
380 CollectionItemType<T> *type = new CollectionItemType<T>;
381 types.push_back(type);
385 } // namespace DataFile