1 #ifndef MSP_DATAFILE_COLLECTION_H_
2 #define MSP_DATAFILE_COLLECTION_H_
4 #include <msp/core/attributes.h>
5 #include <msp/core/maputils.h>
6 #include <msp/core/noncopyable.h>
7 #include <msp/core/refptr.h>
8 #include "collectionsource.h"
12 /* XXX This file is a big mess with too many things in it. However, the
13 dependencies between those things make it difficult to split up. */
18 class CollectionItemTypeBase;
21 class CollectionItemType;
24 class LoadableCollectionItemType;
26 template<typename T, bool = HasLoader<T>::value>
27 struct CollectionItemTypeChooser;
30 struct CollectionItemTypeChooser<T, true>
31 { typedef LoadableCollectionItemType<T> Type; };
34 struct CollectionItemTypeChooser<T, false>
35 { typedef CollectionItemType<T> Type; };
38 A collection of objects that can be loaded from a datafile. Each object is
39 identified by a name, which must be unique across the entire collection.
41 While this class can be instantiated by itself and used for storing objects,
42 loading requires that a subclass defines the supported types. See the add_type
45 Collections can have sources for loading objects on demand. Automatic loading
46 only works on a non-const Collection. See class CollectionSource for details.
48 As a last resort, a fallback collection can be designated for loading items
49 that are not present. Items retrieted from the fallback collection are shared
50 between the collections, and are only deleted when all collections in the chain
53 class Collection: private NonCopyable
57 Loads objects into a Collection. Automatically picks up keywords from
60 class Loader: public DataFile::Loader
62 template<typename T> friend class LoadableCollectionItemType;
69 Collection &get_object() const { return coll; }
71 template<typename T, typename S>
72 void item(const std::string &n)
75 ItemLoader<T> ldr(*it, coll);
77 coll.add<S>(n, it.get());
82 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
86 typedef std::map<std::string, Variant> ItemMap;
87 typedef std::vector<CollectionItemTypeBase *> TypeList;
88 typedef std::vector<const CollectionSource *> SourceList;
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)));
137 /** Finds a typed object in the collection. Returns null if the name does
138 not exist. Throws if the name exists but the object is of an incorrect
141 T *find(const std::string &name) const
143 ItemMap::const_iterator i = items.find(name);
144 return (i!=items.end() ? extract<typename RemoveConst<T>::Type>(i->second) : 0);
148 T *find(const std::string &name)
150 typedef typename RemoveConst<T>::Type NCT;
151 const Variant *var = find_var(name, get_type<NCT>(name));
152 return (var ? &extract<NCT>(*var) : 0);
156 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
157 const Variant *find_var(const std::string &, const CollectionItemTypeBase *);
160 T &extract(const Variant &var) const;
163 std::list<T *> extract_list(const std::vector<const Variant *> &vars) const
165 std::list<T *> result;
166 for(std::vector<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
167 result.push_back(&extract<T>(**i));
171 void gather_items(std::vector<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
174 void gather_items(std::vector<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
176 if(type || (type = get_type<T>()))
177 gather_items(vars, names, *type, include_sources);
179 gather_items(vars, names, CollectionItemType<T>(), false);
183 /// Returns a list of the names of objects of one type in the collection.
185 std::list<std::string> get_names() const
187 std::list<std::string> names;
188 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
192 /** Returns a list of the names of objects of one type in the collection or
193 available from sources. */
195 std::list<std::string> get_names()
197 std::list<std::string> names;
198 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
202 /// Returns a list of objects of one type in the collection.
204 std::list<T *> get_list() const
206 std::vector<const Variant *> vars;
207 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
208 return extract_list<T>(vars);
211 /** Returns a list of objects of one type, loading them from sources if
214 std::list<T *> get_list()
216 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
218 load_items_from_sources(*type);
220 std::vector<const Variant *> vars;
221 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
222 return extract_list<T>(vars);
226 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
229 unsigned get_status(const std::string &name) const
231 // XXX Should go through all applicable types
232 if(CollectionItemTypeBase *type = get_type<T>())
233 return get_status(name, *type);
235 ItemMap::const_iterator i = items.find(name);
236 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
240 /// Checks whether a typed object exists in the collection.
242 bool contains(const std::string &name) const
243 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
245 /** Checks whether a typed object exists in the collection or is loadable
248 bool contains(const std::string &name)
249 { return get_status<typename RemoveConst<T>::Type>(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 typename CollectionItemTypeChooser<T>::Type &add_type();
272 /** Returns a mutable reference to an existing type descriptor. This can be
273 used to e.g. override the creator function of a type added by a base class. */
275 typename CollectionItemTypeChooser<T>::Type &modify_type();
278 /** Returns the descriptor for a type, or null if one isn't defined. An
279 optional name can be given to prioritize matching types. */
281 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
283 /// Gets a descriptor with the same type as another descriptor.
284 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
286 /// Returns the descriptor for an item, or null if it's of an unknown type.
287 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
290 /** Adds a source for automatically loading items. Sources are consulted
291 in the order they are added. */
292 void add_source(const CollectionSource &);
295 /** Opens a raw resource, without interpreting it as object data. Null is
296 returned if no such file is found. The caller must dispose of the returned
297 object when done with it. */
298 IO::Seekable *open_raw(const std::string &) const;
301 // Deprecated. Use open_raw instead.
302 DEPRECATED IO::Seekable *open_from_sources(const std::string &n) { return open_raw(n); }
305 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
307 void load_items_from_sources(const CollectionItemTypeBase &);
310 /** Sets a fallback collection, which will be consulted if an item is not
312 void set_fallback(Collection *);
314 Collection *get_fallback() const { return fallback; }
318 class Collection::ItemLoader<T, false>: public T::Loader
321 ItemLoader(T &o, Collection &):
327 class Collection::ItemLoader<T, true>: public T::Loader
330 ItemLoader(T &o, Collection &c):
331 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
336 class CollectionItemTypeBase
341 virtual ~ExtractorBase() { }
345 struct Extractor: ExtractorBase
347 virtual T &extract(const Variant &) const = 0;
351 std::vector<std::string> suffixes;
352 std::vector<ExtractorBase *> extractors;
354 CollectionItemTypeBase() { }
356 virtual ~CollectionItemTypeBase();
359 void set_keyword(const std::string &);
360 void add_suffix(const std::string &);
362 const std::string &get_keyword() const { return kwd; }
363 bool match_name(const std::string &) const;
364 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
365 virtual bool check_item_type(const Variant &) const = 0;
366 virtual void add_to_loader(Collection::Loader &) const = 0;
367 virtual bool can_create() const = 0;
368 virtual void create_item(Collection &, const std::string &) const = 0;
369 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
372 bool can_extract() const
374 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
375 if(dynamic_cast<Extractor<T> *>(*i))
381 T *extract(const Variant &var) const
383 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
384 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
385 return &ex->extract(var);
392 Describes a type of item that can be loaded by a Collection. These are created
393 by Collection::add_type.
396 class CollectionItemType: public CollectionItemTypeBase
401 virtual ~CreatorBase() { }
403 virtual T *create(Collection &, const std::string &) const = 0;
407 struct Creator: CreatorBase
409 typedef T *(C::*FuncPtr)(const std::string &);
413 Creator(FuncPtr f): func(f) { }
415 virtual T *create(Collection &coll, const std::string &name) const
416 { return (dynamic_cast<C &>(coll).*func)(name); }
420 struct Extractor: CollectionItemTypeBase::Extractor<B>
422 virtual B &extract(const Variant &var) const
423 { return *var.value<RefPtr<T> >(); }
429 CollectionItemType():
433 ~CollectionItemType()
438 /** Sets a datafile keyword for this item type. The Collection's loader
439 will accept a statement with this keyword and a single string argument - the
441 CollectionItemType &keyword(const std::string &k)
447 /** Adds a suffix that is used to match names when looking for loadable
448 objects. There is no implied separator; a name matches if it ends with the
449 suffix. If a keyword is defined before any suffixes, then "."+keyword is
450 added as a suffix. */
451 CollectionItemType &suffix(const std::string &s)
457 /** Attaches a creator function to this item type. If an item is not found
458 in the Collection, the creator function for its type is called to create it.
459 The function must be a member of the Collection subclass containing the
460 type. It must return the created object, or null if it could not be
461 created. It's also permissible to load the item via other means and then
464 CollectionItemType &creator(T *(C::*func)(const std::string &))
467 creat = new Creator<C>(func);
471 /** Makes items of this type available through a base class. */
473 CollectionItemType &base()
475 extractors.push_back(new Extractor<B>);
479 virtual bool is_same_type(const CollectionItemTypeBase &other) const
480 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
482 virtual bool check_item_type(const Variant &var) const
483 { return var.check_type<RefPtr<T> >(); }
485 virtual void add_to_loader(Collection::Loader &) const
488 virtual bool can_create() const
491 virtual void create_item(Collection &coll, const std::string &name) const
494 throw std::runtime_error("no creator");
495 T *obj = creat->create(coll, name);
500 virtual void load_item(Collection &, Parser &, const std::string &) const
502 throw std::runtime_error("this type cannot be loaded");
508 class LoadableCollectionItemType: public CollectionItemType<T>
511 virtual void add_to_loader(Collection::Loader &loader) const
512 { loader.add(this->kwd, &Collection::Loader::item<T, T>); }
514 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
516 RefPtr<T> obj = new T;
517 Collection::ItemLoader<T> ldr(*obj, coll);
519 coll.add(name, obj.get());
526 T &Collection::extract(const Variant &var) const
528 if(!var.check_type<RefPtr<T> >())
529 if(CollectionItemTypeBase *type = get_type_for_item(var))
530 if(T *item = type->extract<T>(var))
533 return *var.value<RefPtr<T> >();
537 typename CollectionItemTypeChooser<T>::Type &Collection::add_type()
539 typename CollectionItemTypeChooser<T>::Type *type = new typename CollectionItemTypeChooser<T>::Type;
540 types.push_back(type);
545 typename CollectionItemTypeChooser<T>::Type &Collection::modify_type()
547 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
548 if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
551 throw std::logic_error("type not found in collection");
555 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
557 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
558 if(dynamic_cast<CollectionItemType<T> *>(*j))
560 CollectionItemTypeBase *type = 0;
561 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
562 if((*j)->can_extract<T>())
564 if(!name.empty() && (*j)->match_name(name))
571 } // namespace DataFile