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/refptr.h>
7 #include "collectionsource.h"
11 /* XXX This file is a big mess with too many things in it. However, the
12 dependencies between those things make it difficult to split up. */
17 class CollectionItemTypeBase;
20 class CollectionItemType;
23 class LoadableCollectionItemType;
25 template<typename T, bool = HasLoader<T>::value>
26 struct CollectionItemTypeChooser;
29 struct CollectionItemTypeChooser<T, true>
30 { typedef LoadableCollectionItemType<T> Type; };
33 struct CollectionItemTypeChooser<T, false>
34 { typedef CollectionItemType<T> Type; };
37 A collection of objects that can be loaded from a datafile. Each object is
38 identified by a name, which must be unique across the entire collection.
40 While this class can be instantiated by itself and used for storing objects,
41 loading requires that a subclass defines the supported types. See the add_type
44 Collections can have sources for loading objects on demand. Automatic loading
45 only works on a non-const Collection. See class CollectionSource for details.
47 As a last resort, a fallback collection can be designated for loading items
48 that are not present. Items retrieted from the fallback collection are shared
49 between the collections, and are only deleted when all collections in the chain
56 Loads objects into a Collection. Automatically picks up keywords from
59 class Loader: public DataFile::Loader
61 template<typename T> friend class LoadableCollectionItemType;
68 Collection &get_object() const { return coll; }
70 template<typename T, typename S>
71 void item(const std::string &n)
74 ItemLoader<T> ldr(*it, coll);
76 coll.add<S>(n, it.get());
81 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
85 typedef std::map<std::string, Variant> ItemMap;
86 typedef std::vector<CollectionItemTypeBase *> TypeList;
87 typedef std::vector<const CollectionSource *> SourceList;
94 Collection(const Collection &);
95 Collection &operator=(const Collection &);
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 RefPtr<typename RemoveConst<T>::Type> ptr(item);
111 insert_unique(items, 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)));
139 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
142 T &extract(const Variant &var) const;
145 std::list<T *> extract_list(const std::vector<const Variant *> &vars) const
147 std::list<T *> result;
148 for(std::vector<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
149 result.push_back(&extract<T>(**i));
153 void gather_items(std::vector<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
156 void gather_items(std::vector<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
158 if(type || (type = get_type<T>()))
159 gather_items(vars, names, *type, include_sources);
161 gather_items(vars, names, CollectionItemType<T>(), false);
165 /// Returns a list of the names of objects of one type in the collection.
167 std::list<std::string> get_names() const
169 std::list<std::string> names;
170 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
174 /** Returns a list of the names of objects of one type in the collection or
175 available from sources. */
177 std::list<std::string> get_names()
179 std::list<std::string> names;
180 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
184 /// Returns a list of objects of one type in the collection.
186 std::list<T *> get_list() const
188 std::vector<const Variant *> vars;
189 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
190 return extract_list<T>(vars);
193 /** Returns a list of objects of one type, loading them from sources if
196 std::list<T *> get_list()
198 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
200 load_items_from_sources(*type);
202 std::vector<const Variant *> vars;
203 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
204 return extract_list<T>(vars);
208 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
211 unsigned get_status(const std::string &name) const
213 // XXX Should go through all applicable types
214 if(CollectionItemTypeBase *type = get_type<T>())
215 return get_status(name, *type);
217 ItemMap::const_iterator i = items.find(name);
218 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
222 /// Checks whether a typed object exists in the collection.
224 bool contains(const std::string &name) const
225 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
227 /** Checks whether a typed object exists in the collection or is loadable
230 bool contains(const std::string &name)
231 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
233 /// Returns the name of an item in the collection.
235 const std::string &get_name(T *d) const
237 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
239 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
240 if(i->second.check_type<RPNCT>())
241 if(i->second.value<RPNCT>().get()==d)
244 // XXX Need better exception class
245 throw std::runtime_error("Item not found in collection");
249 /** Adds a type to the collection. The returned descriptor object reference
250 can be used to define how objects of that type can be loaded. */
252 typename CollectionItemTypeChooser<T>::Type &add_type();
254 /** Returns a mutable reference to an existing type descriptor. This can be
255 used to e.g. override the creator function of a type added by a base class. */
257 typename CollectionItemTypeChooser<T>::Type &modify_type();
260 /** Returns the descriptor for a type, or null if one isn't defined. An
261 optional name can be given to prioritize matching types. */
263 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
265 /// Gets a descriptor with the same type as another descriptor.
266 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
268 /// Returns the descriptor for an item, or null if it's of an unknown type.
269 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
272 /** Adds a source for automatically loading items. Sources are consulted
273 in the order they are added. */
274 void add_source(const CollectionSource &);
277 /** Opens a raw resource, without interpreting it as object data. Null is
278 returned if no such file is found. The caller must dispose of the returned
279 object when done with it. */
280 IO::Seekable *open_raw(const std::string &) const;
283 // Deprecated. Use open_raw instead.
284 DEPRECATED IO::Seekable *open_from_sources(const std::string &n) { return open_raw(n); }
287 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
289 void load_items_from_sources(const CollectionItemTypeBase &);
292 /** Sets a fallback collection, which will be consulted if an item is not
294 void set_fallback(Collection *);
296 Collection *get_fallback() const { return fallback; }
300 class Collection::ItemLoader<T, false>: public T::Loader
303 ItemLoader(T &o, Collection &):
309 class Collection::ItemLoader<T, true>: public T::Loader
312 ItemLoader(T &o, Collection &c):
313 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
318 class CollectionItemTypeBase
323 virtual ~ExtractorBase() { }
327 struct Extractor: ExtractorBase
329 virtual T &extract(const Variant &) const = 0;
333 std::vector<std::string> suffixes;
334 std::vector<ExtractorBase *> extractors;
336 CollectionItemTypeBase() { }
338 virtual ~CollectionItemTypeBase();
341 void set_keyword(const std::string &);
342 void add_suffix(const std::string &);
344 const std::string &get_keyword() const { return kwd; }
345 bool match_name(const std::string &) const;
346 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
347 virtual bool check_item_type(const Variant &) const = 0;
348 virtual void add_to_loader(Collection::Loader &) const = 0;
349 virtual bool can_create() const = 0;
350 virtual void create_item(Collection &, const std::string &) const = 0;
351 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
354 bool can_extract() const
356 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
357 if(dynamic_cast<Extractor<T> *>(*i))
363 T *extract(const Variant &var) const
365 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
366 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
367 return &ex->extract(var);
374 Describes a type of item that can be loaded by a Collection. These are created
375 by Collection::add_type.
378 class CollectionItemType: public CollectionItemTypeBase
383 virtual ~CreatorBase() { }
385 virtual T *create(Collection &, const std::string &) const = 0;
389 struct Creator: CreatorBase
391 typedef T *(C::*FuncPtr)(const std::string &);
395 Creator(FuncPtr f): func(f) { }
397 virtual T *create(Collection &coll, const std::string &name) const
398 { return (dynamic_cast<C &>(coll).*func)(name); }
402 struct Extractor: CollectionItemTypeBase::Extractor<B>
404 virtual B &extract(const Variant &var) const
405 { return *var.value<RefPtr<T> >(); }
411 CollectionItemType():
415 ~CollectionItemType()
420 /** Sets a datafile keyword for this item type. The Collection's loader
421 will accept a statement with this keyword and a single string argument - the
423 CollectionItemType &keyword(const std::string &k)
429 /** Adds a suffix that is used to match names when looking for loadable
430 objects. There is no implied separator; a name matches if it ends with the
431 suffix. If a keyword is defined before any suffixes, then "."+keyword is
432 added as a suffix. */
433 CollectionItemType &suffix(const std::string &s)
439 /** Attaches a creator function to this item type. If an item is not found
440 in the Collection, the creator function for its type is called to create it.
441 The function must be a member of the Collection subclass containing the
442 type. It must return the created object, or null if it could not be
443 created. It's also permissible to load the item via other means and then
446 CollectionItemType &creator(T *(C::*func)(const std::string &))
449 creat = new Creator<C>(func);
453 /** Makes items of this type available through a base class. */
455 CollectionItemType &base()
457 extractors.push_back(new Extractor<B>);
461 virtual bool is_same_type(const CollectionItemTypeBase &other) const
462 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
464 virtual bool check_item_type(const Variant &var) const
465 { return var.check_type<RefPtr<T> >(); }
467 virtual void add_to_loader(Collection::Loader &) const
470 virtual bool can_create() const
473 virtual void create_item(Collection &coll, const std::string &name) const
476 throw std::runtime_error("no creator");
477 T *obj = creat->create(coll, name);
482 virtual void load_item(Collection &, Parser &, const std::string &) const
484 throw std::runtime_error("this type cannot be loaded");
490 class LoadableCollectionItemType: public CollectionItemType<T>
493 virtual void add_to_loader(Collection::Loader &loader) const
494 { loader.add(this->kwd, &Collection::Loader::item<T, T>); }
496 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
498 RefPtr<T> obj = new T;
499 Collection::ItemLoader<T> ldr(*obj, coll);
501 coll.add(name, obj.get());
508 T &Collection::extract(const Variant &var) const
510 if(!var.check_type<RefPtr<T> >())
511 if(CollectionItemTypeBase *type = get_type_for_item(var))
512 if(T *item = type->extract<T>(var))
515 return *var.value<RefPtr<T> >();
519 typename CollectionItemTypeChooser<T>::Type &Collection::add_type()
521 typename CollectionItemTypeChooser<T>::Type *type = new typename CollectionItemTypeChooser<T>::Type;
522 types.push_back(type);
527 typename CollectionItemTypeChooser<T>::Type &Collection::modify_type()
529 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
530 if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
533 throw std::logic_error("type not found in collection");
537 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
539 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
540 if(dynamic_cast<CollectionItemType<T> *>(*j))
542 CollectionItemTypeBase *type = 0;
543 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
544 if((*j)->can_extract<T>())
546 if(!name.empty() && (*j)->match_name(name))
553 } // namespace DataFile