1 #ifndef MSP_DATAFILE_COLLECTION_H_
2 #define MSP_DATAFILE_COLLECTION_H_
5 #include <msp/core/attributes.h>
6 #include <msp/core/maputils.h>
7 #include <msp/core/noncopyable.h>
8 #include <msp/core/refptr.h>
9 #include "collectionsource.h"
13 /* XXX This file is a big mess with too many things in it. However, the
14 dependencies between those things make it difficult to split up. */
19 class CollectionItemTypeBase;
22 class CollectionItemType;
25 class LoadableCollectionItemType;
27 template<typename T, bool = HasLoader<T>::value>
28 struct CollectionItemTypeChooser;
31 struct CollectionItemTypeChooser<T, true>
32 { typedef LoadableCollectionItemType<T> Type; };
35 struct CollectionItemTypeChooser<T, false>
36 { typedef CollectionItemType<T> Type; };
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.
49 As a last resort, a fallback collection can be designated for loading items
50 that are not present. Items retrieted from the fallback collection are shared
51 between the collections, and are only deleted when all collections in the chain
54 class Collection: private NonCopyable
58 Loads objects into a Collection. Automatically picks up keywords from
61 class Loader: public DataFile::Loader
63 template<typename T> friend class LoadableCollectionItemType;
70 Collection &get_object() const { return coll; }
72 template<typename T, typename S>
73 void item(const std::string &n)
76 ItemLoader<T> ldr(*it, coll);
78 coll.add<S>(n, it.get());
83 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
87 typedef std::map<std::string, Variant> ItemMap;
88 typedef std::vector<CollectionItemTypeBase *> TypeList;
89 typedef std::vector<const CollectionSource *> SourceList;
98 virtual ~Collection();
100 /** Adds an object into the collection. The name must not pre-exist. The
101 collection takes ownership of the object. */
103 void add(const std::string &name, T *item)
106 throw std::invalid_argument("Collection::add(item)");
108 typedef typename std::remove_cv<T>::type NCT;
109 RefPtr<NCT> ptr(item);
112 add_var(name, get_type<NCT>(name), ptr);
116 // Avoid deleting the object
122 /// Gets a typed object from the collection.
124 T &get(const std::string &name) const
126 return extract<typename std::remove_cv<T>::type>(get_item(items, name));
129 /** Gets a typed object from the collection. If the name is not found,
130 automatic creation with the type's creator function (if defined) or from
131 sources (if present) is attempted. */
133 T &get(const std::string &name)
135 typedef typename std::remove_cv<T>::type NCT;
136 return extract<NCT>(get_var(name, get_type<NCT>(name)));
139 /** Finds a typed object in the collection. Returns null if the name does
140 not exist. Throws if the name exists but the object is of an incorrect
143 T *find(const std::string &name) const
145 ItemMap::const_iterator i = items.find(name);
146 return (i!=items.end() ? extract<typename std::remove_cv<T>::type>(i->second) : 0);
150 T *find(const std::string &name)
152 typedef typename std::remove_cv<T>::type NCT;
153 const Variant *var = find_var(name, get_type<NCT>(name));
154 return (var ? &extract<NCT>(*var) : 0);
158 void add_var(const std::string &, const CollectionItemTypeBase *, const Variant &);
159 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
160 const Variant *find_var(const std::string &, const CollectionItemTypeBase *);
163 T &extract(const Variant &var) const;
166 std::list<T *> extract_list(const std::vector<const Variant *> &vars) const
168 std::list<T *> result;
169 for(std::vector<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
170 result.push_back(&extract<T>(**i));
174 void gather_items(std::vector<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
177 void gather_items(std::vector<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
179 if(type || (type = get_type<T>()))
180 gather_items(vars, names, *type, include_sources);
182 gather_items(vars, names, CollectionItemType<T>(), false);
186 /// Returns a list of the names of objects of one type in the collection.
188 std::list<std::string> get_names() const
190 std::list<std::string> names;
191 gather_items<typename std::remove_cv<T>::type>(0, &names, 0, false);
195 /** Returns a list of the names of objects of one type in the collection or
196 available from sources. */
198 std::list<std::string> get_names()
200 std::list<std::string> names;
201 gather_items<typename std::remove_cv<T>::type>(0, &names, 0, true);
205 /// Returns a list of objects of one type in the collection.
207 std::list<T *> get_list() const
209 std::vector<const Variant *> vars;
210 gather_items<typename std::remove_cv<T>::type>(&vars, 0, 0, false);
211 return extract_list<T>(vars);
214 /** Returns a list of objects of one type, loading them from sources if
217 std::list<T *> get_list()
219 CollectionItemTypeBase *type = get_type<typename std::remove_cv<T>::type>();
221 load_items_from_sources(*type);
223 std::vector<const Variant *> vars;
224 gather_items<typename std::remove_cv<T>::type>(&vars, 0, type, true);
225 return extract_list<T>(vars);
229 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
232 unsigned get_status(const std::string &name) const
234 // XXX Should go through all applicable types
235 if(CollectionItemTypeBase *type = get_type<T>())
236 return get_status(name, *type);
238 ItemMap::const_iterator i = items.find(name);
239 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
243 /// Checks whether a typed object exists in the collection.
245 bool contains(const std::string &name) const
246 { return get_status<typename std::remove_cv<T>::type>(name)==1; }
248 /** Checks whether a typed object exists in the collection or is loadable
251 bool contains(const std::string &name)
252 { return get_status<typename std::remove_cv<T>::type>(name)>0; }
254 /// Returns the name of an item in the collection.
256 const std::string &get_name(T *d) const
258 typedef RefPtr<typename std::remove_cv<T>::type> RPNCT;
260 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
261 if(i->second.check_type<RPNCT>())
262 if(i->second.value<RPNCT>().get()==d)
265 // XXX Need better exception class
266 throw std::runtime_error("Item not found in collection");
270 /** Adds a type to the collection. The returned descriptor object reference
271 can be used to define how objects of that type can be loaded. */
273 typename CollectionItemTypeChooser<T>::Type &add_type();
275 /** Returns a mutable reference to an existing type descriptor. This can be
276 used to e.g. override the creator function of a type added by a base class. */
278 typename CollectionItemTypeChooser<T>::Type &modify_type();
281 /** Returns the descriptor for a type, or null if one isn't defined. An
282 optional name can be given to prioritize matching types. */
284 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
286 /// Gets a descriptor with the same type as another descriptor.
287 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
289 /// Returns the descriptor for an item, or null if it's of an unknown type.
290 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
293 /** Adds a source for automatically loading items. Sources are consulted
294 in the order they are added. */
295 void add_source(const CollectionSource &);
298 /** Opens a raw resource, without interpreting it as object data. Null is
299 returned if no such file is found. The caller must dispose of the returned
300 object when done with it. */
301 IO::Seekable *open_raw(const std::string &) const;
304 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
306 void load_items_from_sources(const CollectionItemTypeBase &);
309 /** Sets a fallback collection, which will be consulted if an item is not
311 void set_fallback(Collection *);
313 Collection *get_fallback() const { return fallback; }
317 class Collection::ItemLoader<T, false>: public T::Loader
320 ItemLoader(T &o, Collection &):
326 class Collection::ItemLoader<T, true>: public T::Loader
329 ItemLoader(T &o, Collection &c):
330 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
335 class CollectionItemTypeBase
340 virtual ~ExtractorBase() { }
344 struct Extractor: ExtractorBase
346 virtual T &extract(const Variant &) const = 0;
350 std::vector<std::string> suffixes;
351 std::vector<ExtractorBase *> extractors;
353 CollectionItemTypeBase() { }
355 virtual ~CollectionItemTypeBase();
358 void set_keyword(const std::string &);
359 void add_suffix(const std::string &);
361 const std::string &get_keyword() const { return kwd; }
362 bool match_name(const std::string &) const;
363 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
364 virtual bool check_item_type(const Variant &) const = 0;
365 virtual void add_to_loader(Collection::Loader &) const = 0;
366 virtual bool can_create() const = 0;
367 virtual void create_item(Collection &, const std::string &) const = 0;
368 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
369 virtual void notify_item(Collection &, const std::string &, const Variant &) 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); }
421 virtual ~NotifyeeBase() { }
423 virtual void notify(Collection &, const std::string &, T &) const = 0;
427 struct Notifyee: NotifyeeBase
429 typedef void (C::*FuncPtr)(const std::string &, T &);
433 Notifyee(FuncPtr f): func(f) { }
435 virtual void notify(Collection &coll, const std::string &name, T &item) const
436 { (dynamic_cast<C &>(coll).*func)(name, item); }
440 struct Extractor: CollectionItemTypeBase::Extractor<B>
442 virtual B &extract(const Variant &var) const
443 { return *var.value<RefPtr<T> >(); }
447 std::vector<NotifyeeBase *> notif;
450 CollectionItemType():
454 ~CollectionItemType()
457 for(typename std::vector<NotifyeeBase *>::const_iterator i=notif.begin(); i!=notif.end(); ++i)
461 /** Sets a datafile keyword for this item type. The Collection's loader
462 will accept a statement with this keyword and a single string argument - the
464 CollectionItemType &keyword(const std::string &k)
470 /** Adds a suffix that is used to match names when looking for loadable
471 objects. There is no implied separator; a name matches if it ends with the
472 suffix. If a keyword is defined before any suffixes, then "."+keyword is
473 added as a suffix. */
474 CollectionItemType &suffix(const std::string &s)
480 /** Attaches a creator function to this item type. If an item is not found
481 in the Collection, the creator function for its type is called to create it.
482 The function must be a member of the Collection subclass containing the
483 type. It must return the created object, or null if it could not be
484 created. It's also permissible to load the item via other means and then
487 CollectionItemType &creator(T *(C::*func)(const std::string &))
490 creat = new Creator<C>(func);
494 /** Makes items of this type available through a base class. */
496 CollectionItemType &base()
498 extractors.push_back(new Extractor<B>);
503 CollectionItemType ¬ify(void (C::*func)(const std::string &, T &))
505 notif.push_back(new Notifyee<C>(func));
509 virtual bool is_same_type(const CollectionItemTypeBase &other) const
510 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
512 virtual bool check_item_type(const Variant &var) const
513 { return var.check_type<RefPtr<T> >(); }
515 virtual void add_to_loader(Collection::Loader &) const
518 virtual bool can_create() const
521 virtual void create_item(Collection &coll, const std::string &name) const
524 throw std::runtime_error("no creator");
525 T *obj = creat->create(coll, name);
530 virtual void load_item(Collection &, Parser &, const std::string &) const
532 throw std::runtime_error("this type cannot be loaded");
535 virtual void notify_item(Collection &coll, const std::string &name, const Variant &var) const
537 RefPtr<T> obj = var.value<RefPtr<T> >();
538 for(typename std::vector<NotifyeeBase *>::const_iterator i=notif.begin(); i!=notif.end(); ++i)
539 (*i)->notify(coll, name, *obj);
545 class LoadableCollectionItemType: public CollectionItemType<T>
548 virtual void add_to_loader(Collection::Loader &loader) const
549 { loader.add(this->kwd, &Collection::Loader::item<T, T>); }
551 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
553 RefPtr<T> obj = new T;
554 Collection::ItemLoader<T> ldr(*obj, coll);
556 coll.add(name, obj.get());
563 T &Collection::extract(const Variant &var) const
565 if(!var.check_type<RefPtr<T> >())
566 if(CollectionItemTypeBase *type = get_type_for_item(var))
567 if(T *item = type->extract<T>(var))
570 return *var.value<RefPtr<T> >();
574 typename CollectionItemTypeChooser<T>::Type &Collection::add_type()
576 typename CollectionItemTypeChooser<T>::Type *type = new typename CollectionItemTypeChooser<T>::Type;
577 types.push_back(type);
582 typename CollectionItemTypeChooser<T>::Type &Collection::modify_type()
584 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
585 if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
588 throw std::logic_error("type not found in collection");
592 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
594 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
595 if(dynamic_cast<CollectionItemType<T> *>(*j))
597 CollectionItemTypeBase *type = 0;
598 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
599 if((*j)->can_extract<T>())
601 if(!name.empty() && (*j)->match_name(name))
608 } // namespace DataFile