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
42 Collections also support a notion of "future objects". These are objects which
43 are known to be possible to load, but loading them is deferred to the first
44 time they are requested.
50 Loads objects into a Collection. Automatically picks up keywords from
53 class Loader: public DataFile::Loader
55 template<typename T> friend class CollectionItemType;
58 template<typename T, typename S, bool = NeedsCollection<typename T::Loader>::value>
65 Collection &get_object() const { return coll; }
67 template<typename T, typename S, typename C>
68 void coll_item(const std::string &n)
71 load_sub(*it, dynamic_cast<C &>(coll));
72 coll.add<S>(n, it.get());
76 template<typename T, typename S>
77 void item(const std::string &n)
81 coll.add<S>(n, it.get());
87 typedef std::map<std::string, Variant> ItemMap;
88 typedef std::list<CollectionItemTypeBase *> TypeList;
93 Collection(const Collection &);
94 Collection &operator=(const Collection &);
97 virtual ~Collection();
99 /** Adds an object into the collection. The name must not pre-exist. The
100 collection takes ownership of the object. */
102 void add(const std::string &name, T *item)
105 throw std::invalid_argument("Collection::add(item)");
107 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
109 ItemMap::iterator i = items.find(name);
112 if(i->second.check_type<RPNCT>())
114 // Replace a future object placeholder
115 RPNCT &ptr = i->second.value<RPNCT>();
123 throw key_error(typeid(ItemMap));
126 items.insert(ItemMap::value_type(name, RPNCT(item)));
130 /** Adds the name of a future object to the collection. The object itself
131 will be loaded on first access. The calling subclass should be prepared to
132 create the object on request. */
134 void add_future(const std::string &name)
136 RefPtr<typename RemoveConst<T>::Type> ptr(0);
137 insert_unique(items, name, ptr);
141 /// Gets a typed object from the collection.
143 T &get(const std::string &name) const
145 typedef typename RemoveConst<T>::Type NCT;
147 T *ptr = get_item(items, name).value<RefPtr<NCT> >();
149 throw key_error(typeid(ItemMap));
153 /** Gets a typed object from the collection. If the name is not found in
154 and a creator for the item type is defined, it is invoked. */
156 T &get(const std::string &);
160 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names)
162 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
164 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
165 if(i->second.check_type<RPNCT>())
167 T *ptr = i->second.value<RPNCT>().get();
171 objects->push_back(ptr);
173 names->push_back(i->first);
175 else if(future_names)
176 future_names->push_back(i->first);
181 /** Returns a list of the names of loaded objects of one type in the
184 std::list<std::string> get_names() const
186 std::list<std::string> result;
187 collect_items<T>(0, &result, 0);
191 /** Returns a list of the names of objects of one type in the collection,
192 including any future objects. */
194 std::list<std::string> get_names()
196 std::list<std::string> result;
197 collect_items<T>(0, &result, &result);
201 /// Returns a list of loaded objects of one type in the collection.
203 std::list<T *> get_list() const
205 std::list<T *> result;
206 collect_items<T>(&result, 0, 0);
210 /** Returns a list of objects of one type in the collection. Any future
211 objects of that type are loaded and returned in the list. */
213 std::list<T *> get_list()
215 std::list<T *> result;
216 std::list<std::string> future;
217 collect_items<T>(&result, 0, &future);
218 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
219 result.push_back(&get<T>(*i));
225 unsigned get_status(const std::string &name) const
227 ItemMap::const_iterator i = items.find(name);
231 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
232 if(!i->second.check_type<RPNCT>())
235 T *ptr = i->second.value<RPNCT>().get();
240 /// Checks whether a typed object exists and is loaded in the collection.
242 bool contains(const std::string &name) const
243 { return get_status<T>(name)==1; }
245 /** Checks whether a typed object exists in the collection, as either a
246 loaded or future object. */
248 bool contains(const std::string &name)
249 { return get_status<T>(name)>0; }
251 /// Returns the name of an item in the collection.
253 const std::string &get_name(T *d) const
255 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
257 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
258 if(i->second.check_type<RPNCT>())
259 if(i->second.value<RPNCT>().get()==d)
262 // XXX Need better exception class
263 throw std::runtime_error("Item not found in collection");
267 /** Adds a type to the collection. The returned descriptor object reference
268 can be used to define how objects of that type can be loaded. */
270 CollectionItemType<T> &add_type();
274 template<typename T, typename S>
275 struct Collection::Loader::Add<T, S, false>
277 static void add(Loader &loader, const std::string &kwd)
278 { loader.add(kwd, &Loader::item<T, S>); }
281 template<typename T, typename S>
282 struct Collection::Loader::Add<T, S, true>
284 static void add(Loader &loader, const std::string &kwd)
285 { loader.add(kwd, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
289 class CollectionItemTypeBase
297 virtual ~TagBase() { }
301 class Tag: public TagBase
307 CollectionItemTypeBase();
309 virtual ~CollectionItemTypeBase();
311 virtual void add_to_loader(Collection::Loader &) const = 0;
312 virtual bool can_create() const = 0;
313 virtual void create_item(Collection &, const std::string &) const = 0;
316 bool check_type() const
317 { return dynamic_cast<Tag<T> *>(tag); }
322 Describes a type of item that can be loaded by a Collection. These are created
323 by Collection::add_type.
326 class CollectionItemType: public CollectionItemTypeBase
334 virtual ~CreatorBase() { }
336 virtual T *create(Collection &, const std::string &) const = 0;
340 class Creator: public CreatorBase
343 typedef T *(C::*FuncPtr)(const std::string &);
349 Creator(FuncPtr f): func(f) { }
351 virtual T *create(Collection &coll, const std::string &name) const
352 { return (static_cast<C &>(coll).*func)(name); }
360 virtual ~StoreBase() { }
362 virtual void store(Collection &, const std::string &, T *) = 0;
364 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
368 class Store: public StoreBase
371 virtual void store(Collection &coll, const std::string &name, T *obj)
372 { coll.add(name, static_cast<S *>(obj)); }
374 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
375 { Collection::Loader::Add<T, S>::add(loader, kwd); }
382 CollectionItemType():
383 creat(0), store(new Store<T>)
384 { tag = new Tag<T>; }
386 ~CollectionItemType()
392 /** Sets a datafile keyword for this item type. The Collection's loader
393 will accept a statement with this keyword and a single string argument - the
395 CollectionItemType &keyword(const std::string &k)
401 /** Attaches a creator function to this item type. If an item is not found
402 in the Collection, the creator function for its type is called to create it.
403 The function must be a member of the Collection subclass containing the
404 type. It must return the created object, or null if it could not be
405 created. It's also permissible to load the item via other means and then
408 CollectionItemType &creator(T *(C::*func)(const std::string &))
411 creat = new Creator<C>(func);
415 /** Specifies the storage type for items of this type. It must be a base
416 class of the actual type. */
418 CollectionItemType &store_as()
423 store = new Store<S>;
427 virtual void add_to_loader(Collection::Loader &loader) const
428 { store->add_to_loader(loader, kwd); }
430 virtual bool can_create() const
433 virtual void create_item(Collection &coll, const std::string &name) const
436 throw std::runtime_error("no creator");
437 T *obj = creat->create(coll, name);
439 store->store(coll, name, obj);
445 T &Collection::get(const std::string &name)
447 typedef typename RemoveConst<T>::Type NCT;
449 ItemMap::iterator i = items.find(name);
452 NCT *ptr = i->second.value<RefPtr<NCT> >().get();
457 for(TypeList::iterator j=types.begin(); j!=types.end(); ++j)
458 if((*j)->can_create() && (*j)->check_type<NCT>())
459 (*j)->create_item(*this, name);
461 return *get_item(items, name).value<RefPtr<NCT> >();
465 CollectionItemType<T> &Collection::add_type()
467 CollectionItemType<T> *type = new CollectionItemType<T>;
468 types.push_back(type);
472 } // namespace DataFile