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>
7 #include "collectionsource.h"
10 /* XXX This file is a big mess with too many things in it. However, the
11 dependencies between those things make it difficult to split up. */
17 Helper struct to determine whether a Loader has a Collection typedef.
20 struct NeedsCollection
22 struct Yes { char c[2]; };
23 struct No { char c; };
26 static Yes f(typename U::Collection *);
30 enum { value = (sizeof(f<T>(0))==sizeof(Yes)) };
33 class CollectionItemTypeBase;
36 class CollectionItemType;
39 A collection of objects that can be loaded from a datafile. Each object is
40 identified by a name, which must be unique across the entire collection.
42 While this class can be instantiated by itself and used for storing objects,
43 loading requires that a subclass defines the supported types. See the add_type
46 Collections can have sources for loading objects on demand. Automatic loading
47 only works on a non-const Collection. See class CollectionSource for details.
53 Loads objects into a Collection. Automatically picks up keywords from
56 class Loader: public DataFile::Loader
58 template<typename T> friend class CollectionItemType;
65 Collection &get_object() const { return coll; }
67 template<typename T, typename S>
68 void item(const std::string &n)
71 ItemLoader<T> ldr(*it, coll);
73 coll.add<S>(n, it.get());
79 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
83 typedef std::map<std::string, Variant> ItemMap;
84 typedef std::list<CollectionItemTypeBase *> TypeList;
85 typedef std::list<CollectionSource *> SourceList;
91 Collection(const Collection &);
92 Collection &operator=(const Collection &);
95 virtual ~Collection();
97 /** Adds an object into the collection. The name must not pre-exist. The
98 collection takes ownership of the object. */
100 void add(const std::string &name, T *item)
103 throw std::invalid_argument("Collection::add(item)");
105 insert_unique(items, name, RefPtr<typename RemoveConst<T>::Type>(item));
108 /// Gets a typed object from the collection.
110 T &get(const std::string &name) const
112 return *get_item(items, name).value<RefPtr<typename RemoveConst<T>::Type> >();
115 /** Gets a typed object from the collection. If the name is not found,
116 automatic creation with the type's creator function (if defined) or from
117 sources (if present) is attempted. */
119 T &get(const std::string &);
123 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
125 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
127 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
128 if(i->second.check_type<RPNCT>())
131 objects->push_back(i->second.value<RPNCT>().get());
133 names->push_back(i->first);
137 if(CollectionItemTypeBase *type = get_type<T>())
139 for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
141 std::list<std::string> available_names = (*i)->get_names(*type);
142 for(std::list<std::string>::iterator j=available_names.begin(); j!=available_names.end(); ++j)
144 future_names->push_back(*j);
150 /// Returns a list of the names of objects of one type in the collection.
152 std::list<std::string> get_names() const
154 std::list<std::string> result;
155 collect_items<T>(0, &result, 0);
159 /** Returns a list of the names of objects of one type in the collection or
160 available from sources. */
162 std::list<std::string> get_names()
164 std::list<std::string> result;
165 collect_items<T>(0, &result, &result);
169 /// Returns a list of objects of one type in the collection.
171 std::list<T *> get_list() const
173 std::list<T *> result;
174 collect_items<T>(&result, 0, 0);
178 /** Returns a list of objects of one type, loading them from sources if
181 std::list<T *> get_list()
183 std::list<T *> result;
184 std::list<std::string> future;
185 collect_items<T>(&result, 0, &future);
186 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
187 result.push_back(&get<T>(*i));
193 unsigned get_status(const std::string &name) const
195 ItemMap::const_iterator i = items.find(name);
198 if(CollectionItemTypeBase *type = get_type<T>())
200 for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j)
201 if((*j)->is_loadable(*type, name))
207 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
208 if(!i->second.check_type<RPNCT>())
215 /// Checks whether a typed object exists in the collection.
217 bool contains(const std::string &name) const
218 { return get_status<T>(name)==1; }
220 /** Checks whether a typed object exists in the collection or is loadable
223 bool contains(const std::string &name)
224 { return get_status<T>(name)>0; }
226 /// Returns the name of an item in the collection.
228 const std::string &get_name(T *d) const
230 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
232 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
233 if(i->second.check_type<RPNCT>())
234 if(i->second.value<RPNCT>().get()==d)
237 // XXX Need better exception class
238 throw std::runtime_error("Item not found in collection");
242 /** Adds a type to the collection. The returned descriptor object reference
243 can be used to define how objects of that type can be loaded. */
245 CollectionItemType<T> &add_type();
247 /** Returns the descriptor for a type, or null if one isn't defined. */
249 CollectionItemTypeBase *get_type() const;
251 void add_source(CollectionSource &);
255 class Collection::ItemLoader<T, false>: public T::Loader
258 ItemLoader(T &o, Collection &):
264 class Collection::ItemLoader<T, true>: public T::Loader
267 ItemLoader(T &o, Collection &c):
268 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
273 class CollectionItemTypeBase
281 virtual ~TagBase() { }
285 class Tag: public TagBase
289 std::vector<std::string> suffixes;
292 CollectionItemTypeBase();
294 virtual ~CollectionItemTypeBase();
296 void set_keyword(const std::string &);
297 const std::string &get_keyword() const { return kwd; }
298 void add_suffix(const std::string &);
299 bool match_name(const std::string &) const;
300 virtual void add_to_loader(Collection::Loader &) const = 0;
301 virtual bool can_create() const = 0;
302 virtual void create_item(Collection &, const std::string &) const = 0;
303 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
306 bool check_type() const
307 { return dynamic_cast<Tag<T> *>(tag); }
312 Describes a type of item that can be loaded by a Collection. These are created
313 by Collection::add_type.
316 class CollectionItemType: public CollectionItemTypeBase
324 virtual ~CreatorBase() { }
326 virtual T *create(Collection &, const std::string &) const = 0;
330 class Creator: public CreatorBase
333 typedef T *(C::*FuncPtr)(const std::string &);
339 Creator(FuncPtr f): func(f) { }
341 virtual T *create(Collection &coll, const std::string &name) const
342 { return (static_cast<C &>(coll).*func)(name); }
350 virtual ~StoreBase() { }
352 virtual void store(Collection &, const std::string &, T *) = 0;
354 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
358 class Store: public StoreBase
361 virtual void store(Collection &coll, const std::string &name, T *obj)
362 { coll.add(name, static_cast<S *>(obj)); }
364 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
365 { loader.add(kwd, &Collection::Loader::item<T, S>); }
372 CollectionItemType():
373 creat(0), store(new Store<T>)
374 { tag = new Tag<T>; }
376 ~CollectionItemType()
382 /** Sets a datafile keyword for this item type. The Collection's loader
383 will accept a statement with this keyword and a single string argument - the
385 CollectionItemType &keyword(const std::string &k)
391 /** Adds a suffix that is used to match names when looking for future
392 objects. There is no implied separator; a name matches if it ends with the
393 suffix. If a keyword is defined before any suffixes, then "."+keyword is
394 added as a suffix. */
395 CollectionItemType &suffix(const std::string &s)
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);
442 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
444 RefPtr<T> obj = new T;
445 Collection::ItemLoader<T> ldr(*obj, coll);
447 store->store(coll, name, obj.get());
454 T &Collection::get(const std::string &name)
456 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
458 ItemMap::iterator i = items.find(name);
460 return *i->second.value<RPNCT>();
462 if(CollectionItemTypeBase *type = get_type<T>())
465 if(type->can_create())
467 type->create_item(*this, name);
468 loaded = items.count(name);
470 for(SourceList::iterator j=sources.begin(); (!loaded && j!=sources.end()); ++j)
472 (*j)->load(*this, *type, name);
473 loaded = items.count(name);
477 return *get_item(items, name).value<RPNCT>();
481 CollectionItemType<T> &Collection::add_type()
483 CollectionItemType<T> *type = new CollectionItemType<T>;
484 types.push_back(type);
489 CollectionItemTypeBase *Collection::get_type() const
491 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
492 if((*j)->check_type<typename RemoveConst<T>::Type>())
497 } // namespace DataFile