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: public Sfinae
23 static Yes f(typename U::Collection *);
27 enum { value = Evaluate<sizeof(f<T>(0))>::value };
30 class CollectionItemTypeBase;
33 class CollectionItemType;
36 A collection of objects that can be loaded from a datafile. Each object is
37 identified by a name, which must be unique across the entire collection.
39 While this class can be instantiated by itself and used for storing objects,
40 loading requires that a subclass defines the supported types. See the add_type
43 Collections can have sources for loading objects on demand. Automatic loading
44 only works on a non-const Collection. See class CollectionSource for details.
46 As a last resort, a fallback collection can be designated for loading items
47 that are not present. Items retrieted from the fallback collection are shared
48 between the collections, and are only deleted when all collections in the chain
55 Loads objects into a Collection. Automatically picks up keywords from
58 class Loader: public DataFile::Loader
60 template<typename T> friend class CollectionItemType;
67 Collection &get_object() const { return coll; }
69 template<typename T, typename S>
70 void item(const std::string &n)
73 ItemLoader<T> ldr(*it, coll);
75 coll.add<S>(n, it.get());
80 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
84 typedef std::map<std::string, Variant> ItemMap;
85 typedef std::list<CollectionItemTypeBase *> TypeList;
86 typedef std::list<const CollectionSource *> SourceList;
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 RefPtr<typename RemoveConst<T>::Type> ptr(item);
110 insert_unique(items, name, ptr);
114 // Avoid deleting the object
120 /// Gets a typed object from the collection.
122 T &get(const std::string &name) const
124 return extract<typename RemoveConst<T>::Type>(get_item(items, name));
127 /** Gets a typed object from the collection. If the name is not found,
128 automatic creation with the type's creator function (if defined) or from
129 sources (if present) is attempted. */
131 T &get(const std::string &name)
133 typedef typename RemoveConst<T>::Type NCT;
134 return extract<NCT>(get_var(name, get_type<NCT>(name)));
138 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
141 T &extract(const Variant &var) const;
144 std::list<T *> extract_list(const std::list<const Variant *> &vars) const
146 std::list<T *> result;
147 for(std::list<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
148 result.push_back(&extract<T>(**i));
152 void gather_items(std::list<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
155 void gather_items(std::list<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
157 if(type || (type = get_type<T>()))
158 gather_items(vars, names, *type, include_sources);
160 gather_items(vars, names, CollectionItemType<T>(), false);
164 /// Returns a list of the names of objects of one type in the collection.
166 std::list<std::string> get_names() const
168 std::list<std::string> names;
169 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
173 /** Returns a list of the names of objects of one type in the collection or
174 available from sources. */
176 std::list<std::string> get_names()
178 std::list<std::string> names;
179 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
183 /// Returns a list of objects of one type in the collection.
185 std::list<T *> get_list() const
187 std::list<const Variant *> vars;
188 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
189 return extract_list<T>(vars);
192 /** Returns a list of objects of one type, loading them from sources if
195 std::list<T *> get_list()
197 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
199 load_items_from_sources(*type);
201 std::list<const Variant *> vars;
202 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
203 return extract_list<T>(vars);
207 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
210 unsigned get_status(const std::string &name) const
212 // XXX Should go through all applicable types
213 if(CollectionItemTypeBase *type = get_type<T>())
214 return get_status(name, *type);
216 ItemMap::const_iterator i = items.find(name);
217 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
221 /// Checks whether a typed object exists in the collection.
223 bool contains(const std::string &name) const
224 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
226 /** Checks whether a typed object exists in the collection or is loadable
229 bool contains(const std::string &name)
230 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
232 /// Returns the name of an item in the collection.
234 const std::string &get_name(T *d) const
236 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
238 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
239 if(i->second.check_type<RPNCT>())
240 if(i->second.value<RPNCT>().get()==d)
243 // XXX Need better exception class
244 throw std::runtime_error("Item not found in collection");
248 /** Adds a type to the collection. The returned descriptor object reference
249 can be used to define how objects of that type can be loaded. */
251 CollectionItemType<T> &add_type();
254 /** Returns the descriptor for a type, or null if one isn't defined. An
255 optional name can be given to prioritize matching types. */
257 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
259 /// Gets a descriptor with the same type as another descriptor.
260 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
262 /// Returns the descriptor for an item, or null if it's of an unknown type.
263 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
266 /** Adds a source for automatically loading items. Sources are consulted
267 in the order they are added. */
268 void add_source(const CollectionSource &);
271 /** Opens a raw resource, without interpreting it as object data. Null is
272 returned if no such file is found. The caller must dispose of the returned
273 object when done with it. */
274 IO::Seekable *open_raw(const std::string &) const;
277 IO::Seekable *open_from_sources(const std::string &n) { return open_raw(n); }
280 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
282 void load_items_from_sources(const CollectionItemTypeBase &);
285 /** Sets a fallback collection, which will be consulted if an item is not
287 void set_fallback(Collection *);
289 Collection *get_fallback() const { return fallback; }
293 class Collection::ItemLoader<T, false>: public T::Loader
296 ItemLoader(T &o, Collection &):
302 class Collection::ItemLoader<T, true>: public T::Loader
305 ItemLoader(T &o, Collection &c):
306 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
311 class CollectionItemTypeBase
316 virtual ~ExtractorBase() { }
320 struct Extractor: ExtractorBase
322 virtual T &extract(const Variant &) const = 0;
326 std::vector<std::string> suffixes;
327 std::vector<ExtractorBase *> extractors;
329 CollectionItemTypeBase() { }
331 virtual ~CollectionItemTypeBase();
333 void set_keyword(const std::string &);
334 const std::string &get_keyword() const { return kwd; }
335 void add_suffix(const std::string &);
336 bool match_name(const std::string &) const;
337 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
338 virtual bool check_item_type(const Variant &) const = 0;
339 virtual void add_to_loader(Collection::Loader &) const = 0;
340 virtual bool can_create() const = 0;
341 virtual void create_item(Collection &, const std::string &) const = 0;
342 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
345 bool can_extract() const
347 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
348 if(dynamic_cast<Extractor<T> *>(*i))
354 T *extract(const Variant &var) const
356 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
357 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
358 return &ex->extract(var);
365 Describes a type of item that can be loaded by a Collection. These are created
366 by Collection::add_type.
369 class CollectionItemType: public CollectionItemTypeBase
374 virtual ~CreatorBase() { }
376 virtual T *create(Collection &, const std::string &) const = 0;
380 struct Creator: CreatorBase
382 typedef T *(C::*FuncPtr)(const std::string &);
386 Creator(FuncPtr f): func(f) { }
388 virtual T *create(Collection &coll, const std::string &name) const
389 { return (dynamic_cast<C &>(coll).*func)(name); }
393 struct Extractor: CollectionItemTypeBase::Extractor<B>
395 virtual B &extract(const Variant &var) const
396 { return *var.value<RefPtr<T> >(); }
402 CollectionItemType():
406 ~CollectionItemType()
411 /** Sets a datafile keyword for this item type. The Collection's loader
412 will accept a statement with this keyword and a single string argument - the
414 CollectionItemType &keyword(const std::string &k)
420 /** Adds a suffix that is used to match names when looking for future
421 objects. There is no implied separator; a name matches if it ends with the
422 suffix. If a keyword is defined before any suffixes, then "."+keyword is
423 added as a suffix. */
424 CollectionItemType &suffix(const std::string &s)
430 /** Attaches a creator function to this item type. If an item is not found
431 in the Collection, the creator function for its type is called to create it.
432 The function must be a member of the Collection subclass containing the
433 type. It must return the created object, or null if it could not be
434 created. It's also permissible to load the item via other means and then
437 CollectionItemType &creator(T *(C::*func)(const std::string &))
440 creat = new Creator<C>(func);
444 /** Makes items of this type available through a base class. */
446 CollectionItemType &base()
448 extractors.push_back(new Extractor<B>);
452 virtual bool is_same_type(const CollectionItemTypeBase &other) const
453 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
455 virtual bool check_item_type(const Variant &var) const
456 { return var.check_type<RefPtr<T> >(); }
458 virtual void add_to_loader(Collection::Loader &loader) const
459 { loader.add(kwd, &Collection::Loader::item<T, T>); }
461 virtual bool can_create() const
464 virtual void create_item(Collection &coll, const std::string &name) const
467 throw std::runtime_error("no creator");
468 T *obj = creat->create(coll, name);
473 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
475 RefPtr<T> obj = new T;
476 Collection::ItemLoader<T> ldr(*obj, coll);
478 coll.add(name, obj.get());
485 T &Collection::extract(const Variant &var) const
487 if(!var.check_type<RefPtr<T> >())
488 if(CollectionItemTypeBase *type = get_type_for_item(var))
489 if(T *item = type->extract<T>(var))
492 return *var.value<RefPtr<T> >();
496 CollectionItemType<T> &Collection::add_type()
498 CollectionItemType<T> *type = new CollectionItemType<T>;
499 types.push_back(type);
504 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
506 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
507 if(dynamic_cast<CollectionItemType<T> *>(*j))
509 CollectionItemTypeBase *type = 0;
510 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
511 if((*j)->can_extract<T>())
513 if(!name.empty() && (*j)->match_name(name))
520 } // namespace DataFile