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
310 CollectionItemTypeBase(): tag(0) { }
312 virtual ~CollectionItemTypeBase()
315 virtual void add_to_loader(Collection::Loader &) const = 0;
316 virtual bool can_create() const = 0;
317 virtual void create_item(Collection &, const std::string &) const = 0;
320 bool check_type() const
321 { return dynamic_cast<Tag<T> *>(tag); }
326 Describes a type of item that can be loaded by a Collection. These are created
327 by Collection::add_type.
330 class CollectionItemType: public CollectionItemTypeBase
338 virtual ~CreatorBase() { }
340 virtual T *create(Collection &, const std::string &) const = 0;
344 class Creator: public CreatorBase
347 typedef T *(C::*FuncPtr)(const std::string &);
353 Creator(FuncPtr f): func(f) { }
355 virtual T *create(Collection &coll, const std::string &name) const
356 { return (static_cast<C &>(coll).*func)(name); }
364 virtual ~StoreBase() { }
366 virtual void store(Collection &, const std::string &, T *) = 0;
368 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
372 class Store: public StoreBase
375 virtual void store(Collection &coll, const std::string &name, T *obj)
376 { coll.add(name, static_cast<S *>(obj)); }
378 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
379 { Collection::Loader::Add<T, S>::add(loader, kwd); }
386 CollectionItemType():
387 creat(0), store(new Store<T>)
388 { tag = new Tag<T>; }
390 ~CollectionItemType()
396 /** Sets a datafile keyword for this item type. The Collection's loader
397 will accept a statement with this keyword and a single string argument - the
399 CollectionItemType &keyword(const std::string &k)
405 /** Attaches a creator function to this item type. If an item is not found
406 in the Collection, the creator function for its type is called to create it.
407 The function must be a member of the Collection subclass containing the
408 type. It must return the created object, or null if it could not be
409 created. It's also permissible to load the item via other means and then
412 CollectionItemType &creator(T *(C::*func)(const std::string &))
415 creat = new Creator<C>(func);
419 /** Specifies the storage type for items of this type. It must be a base
420 class of the actual type. */
422 CollectionItemType &store_as()
427 store = new Store<S>;
431 virtual void add_to_loader(Collection::Loader &loader) const
432 { store->add_to_loader(loader, kwd); }
434 virtual bool can_create() const
437 virtual void create_item(Collection &coll, const std::string &name) const
440 throw std::runtime_error("no creator");
441 T *obj = creat->create(coll, name);
443 store->store(coll, name, obj);
449 T &Collection::get(const std::string &name)
451 typedef typename RemoveConst<T>::Type NCT;
453 ItemMap::iterator i = items.find(name);
456 NCT *ptr = i->second.value<RefPtr<NCT> >().get();
461 for(TypeList::iterator j=types.begin(); j!=types.end(); ++j)
462 if((*j)->can_create() && (*j)->check_type<NCT>())
463 (*j)->create_item(*this, name);
465 return *get_item(items, name).value<RefPtr<NCT> >();
469 CollectionItemType<T> &Collection::add_type()
471 CollectionItemType<T> *type = new CollectionItemType<T>;
472 types.push_back(type);
476 } // namespace DataFile