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 typedef typename RemoveConst<T>::Type NCT;
108 RefPtr<NCT> ptr(item);
111 add_var(name, get_type<NCT>(name), ptr);
115 // Avoid deleting the object
121 /// Gets a typed object from the collection.
123 T &get(const std::string &name) const
125 return extract<typename RemoveConst<T>::Type>(get_item(items, name));
128 /** Gets a typed object from the collection. If the name is not found,
129 automatic creation with the type's creator function (if defined) or from
130 sources (if present) is attempted. */
132 T &get(const std::string &name)
134 typedef typename RemoveConst<T>::Type NCT;
135 return extract<NCT>(get_var(name, get_type<NCT>(name)));
138 /** Finds a typed object in the collection. Returns null if the name does
139 not exist. Throws if the name exists but the object is of an incorrect
142 T *find(const std::string &name) const
144 ItemMap::const_iterator i = items.find(name);
145 return (i!=items.end() ? extract<typename RemoveConst<T>::Type>(i->second) : 0);
149 T *find(const std::string &name)
151 typedef typename RemoveConst<T>::Type NCT;
152 const Variant *var = find_var(name, get_type<NCT>(name));
153 return (var ? &extract<NCT>(*var) : 0);
157 void add_var(const std::string &, const CollectionItemTypeBase *, const Variant &);
158 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
159 const Variant *find_var(const std::string &, const CollectionItemTypeBase *);
162 T &extract(const Variant &var) const;
165 std::list<T *> extract_list(const std::vector<const Variant *> &vars) const
167 std::list<T *> result;
168 for(std::vector<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
169 result.push_back(&extract<T>(**i));
173 void gather_items(std::vector<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
176 void gather_items(std::vector<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
178 if(type || (type = get_type<T>()))
179 gather_items(vars, names, *type, include_sources);
181 gather_items(vars, names, CollectionItemType<T>(), false);
185 /// Returns a list of the names of objects of one type in the collection.
187 std::list<std::string> get_names() const
189 std::list<std::string> names;
190 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
194 /** Returns a list of the names of objects of one type in the collection or
195 available from sources. */
197 std::list<std::string> get_names()
199 std::list<std::string> names;
200 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
204 /// Returns a list of objects of one type in the collection.
206 std::list<T *> get_list() const
208 std::vector<const Variant *> vars;
209 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
210 return extract_list<T>(vars);
213 /** Returns a list of objects of one type, loading them from sources if
216 std::list<T *> get_list()
218 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
220 load_items_from_sources(*type);
222 std::vector<const Variant *> vars;
223 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
224 return extract_list<T>(vars);
228 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
231 unsigned get_status(const std::string &name) const
233 // XXX Should go through all applicable types
234 if(CollectionItemTypeBase *type = get_type<T>())
235 return get_status(name, *type);
237 ItemMap::const_iterator i = items.find(name);
238 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
242 /// Checks whether a typed object exists in the collection.
244 bool contains(const std::string &name) const
245 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
247 /** Checks whether a typed object exists in the collection or is loadable
250 bool contains(const std::string &name)
251 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
253 /// Returns the name of an item in the collection.
255 const std::string &get_name(T *d) const
257 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
259 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
260 if(i->second.check_type<RPNCT>())
261 if(i->second.value<RPNCT>().get()==d)
264 // XXX Need better exception class
265 throw std::runtime_error("Item not found in collection");
269 /** Adds a type to the collection. The returned descriptor object reference
270 can be used to define how objects of that type can be loaded. */
272 typename CollectionItemTypeChooser<T>::Type &add_type();
274 /** Returns a mutable reference to an existing type descriptor. This can be
275 used to e.g. override the creator function of a type added by a base class. */
277 typename CollectionItemTypeChooser<T>::Type &modify_type();
280 /** Returns the descriptor for a type, or null if one isn't defined. An
281 optional name can be given to prioritize matching types. */
283 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
285 /// Gets a descriptor with the same type as another descriptor.
286 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
288 /// Returns the descriptor for an item, or null if it's of an unknown type.
289 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
292 /** Adds a source for automatically loading items. Sources are consulted
293 in the order they are added. */
294 void add_source(const CollectionSource &);
297 /** Opens a raw resource, without interpreting it as object data. Null is
298 returned if no such file is found. The caller must dispose of the returned
299 object when done with it. */
300 IO::Seekable *open_raw(const std::string &) const;
303 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
305 void load_items_from_sources(const CollectionItemTypeBase &);
308 /** Sets a fallback collection, which will be consulted if an item is not
310 void set_fallback(Collection *);
312 Collection *get_fallback() const { return fallback; }
316 class Collection::ItemLoader<T, false>: public T::Loader
319 ItemLoader(T &o, Collection &):
325 class Collection::ItemLoader<T, true>: public T::Loader
328 ItemLoader(T &o, Collection &c):
329 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
334 class CollectionItemTypeBase
339 virtual ~ExtractorBase() { }
343 struct Extractor: ExtractorBase
345 virtual T &extract(const Variant &) const = 0;
349 std::vector<std::string> suffixes;
350 std::vector<ExtractorBase *> extractors;
352 CollectionItemTypeBase() { }
354 virtual ~CollectionItemTypeBase();
357 void set_keyword(const std::string &);
358 void add_suffix(const std::string &);
360 const std::string &get_keyword() const { return kwd; }
361 bool match_name(const std::string &) const;
362 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
363 virtual bool check_item_type(const Variant &) const = 0;
364 virtual void add_to_loader(Collection::Loader &) const = 0;
365 virtual bool can_create() const = 0;
366 virtual void create_item(Collection &, const std::string &) const = 0;
367 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
368 virtual void notify_item(Collection &, const std::string &, const Variant &) const = 0;
371 bool can_extract() const
373 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
374 if(dynamic_cast<Extractor<T> *>(*i))
380 T *extract(const Variant &var) const
382 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
383 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
384 return &ex->extract(var);
391 Describes a type of item that can be loaded by a Collection. These are created
392 by Collection::add_type.
395 class CollectionItemType: public CollectionItemTypeBase
400 virtual ~CreatorBase() { }
402 virtual T *create(Collection &, const std::string &) const = 0;
406 struct Creator: CreatorBase
408 typedef T *(C::*FuncPtr)(const std::string &);
412 Creator(FuncPtr f): func(f) { }
414 virtual T *create(Collection &coll, const std::string &name) const
415 { return (dynamic_cast<C &>(coll).*func)(name); }
420 virtual ~NotifyeeBase() { }
422 virtual void notify(Collection &, const std::string &, T &) const = 0;
426 struct Notifyee: NotifyeeBase
428 typedef void (C::*FuncPtr)(const std::string &, T &);
432 Notifyee(FuncPtr f): func(f) { }
434 virtual void notify(Collection &coll, const std::string &name, T &item) const
435 { (dynamic_cast<C &>(coll).*func)(name, item); }
439 struct Extractor: CollectionItemTypeBase::Extractor<B>
441 virtual B &extract(const Variant &var) const
442 { return *var.value<RefPtr<T> >(); }
446 std::vector<NotifyeeBase *> notif;
449 CollectionItemType():
453 ~CollectionItemType()
456 for(typename std::vector<NotifyeeBase *>::const_iterator i=notif.begin(); i!=notif.end(); ++i)
460 /** Sets a datafile keyword for this item type. The Collection's loader
461 will accept a statement with this keyword and a single string argument - the
463 CollectionItemType &keyword(const std::string &k)
469 /** Adds a suffix that is used to match names when looking for loadable
470 objects. There is no implied separator; a name matches if it ends with the
471 suffix. If a keyword is defined before any suffixes, then "."+keyword is
472 added as a suffix. */
473 CollectionItemType &suffix(const std::string &s)
479 /** Attaches a creator function to this item type. If an item is not found
480 in the Collection, the creator function for its type is called to create it.
481 The function must be a member of the Collection subclass containing the
482 type. It must return the created object, or null if it could not be
483 created. It's also permissible to load the item via other means and then
486 CollectionItemType &creator(T *(C::*func)(const std::string &))
489 creat = new Creator<C>(func);
493 /** Makes items of this type available through a base class. */
495 CollectionItemType &base()
497 extractors.push_back(new Extractor<B>);
502 CollectionItemType ¬ify(void (C::*func)(const std::string &, T &))
504 notif.push_back(new Notifyee<C>(func));
508 virtual bool is_same_type(const CollectionItemTypeBase &other) const
509 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
511 virtual bool check_item_type(const Variant &var) const
512 { return var.check_type<RefPtr<T> >(); }
514 virtual void add_to_loader(Collection::Loader &) const
517 virtual bool can_create() const
520 virtual void create_item(Collection &coll, const std::string &name) const
523 throw std::runtime_error("no creator");
524 T *obj = creat->create(coll, name);
529 virtual void load_item(Collection &, Parser &, const std::string &) const
531 throw std::runtime_error("this type cannot be loaded");
534 virtual void notify_item(Collection &coll, const std::string &name, const Variant &var) const
536 RefPtr<T> obj = var.value<RefPtr<T> >();
537 for(typename std::vector<NotifyeeBase *>::const_iterator i=notif.begin(); i!=notif.end(); ++i)
538 (*i)->notify(coll, name, *obj);
544 class LoadableCollectionItemType: public CollectionItemType<T>
547 virtual void add_to_loader(Collection::Loader &loader) const
548 { loader.add(this->kwd, &Collection::Loader::item<T, T>); }
550 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
552 RefPtr<T> obj = new T;
553 Collection::ItemLoader<T> ldr(*obj, coll);
555 coll.add(name, obj.get());
562 T &Collection::extract(const Variant &var) const
564 if(!var.check_type<RefPtr<T> >())
565 if(CollectionItemTypeBase *type = get_type_for_item(var))
566 if(T *item = type->extract<T>(var))
569 return *var.value<RefPtr<T> >();
573 typename CollectionItemTypeChooser<T>::Type &Collection::add_type()
575 typename CollectionItemTypeChooser<T>::Type *type = new typename CollectionItemTypeChooser<T>::Type;
576 types.push_back(type);
581 typename CollectionItemTypeChooser<T>::Type &Collection::modify_type()
583 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
584 if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
587 throw std::logic_error("type not found in collection");
591 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
593 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
594 if(dynamic_cast<CollectionItemType<T> *>(*j))
596 CollectionItemTypeBase *type = 0;
597 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
598 if((*j)->can_extract<T>())
600 if(!name.empty() && (*j)->match_name(name))
607 } // namespace DataFile