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 A collection of objects that can be loaded from a datafile. Each object is
24 identified by a name, which must be unique across the entire collection.
26 While this class can be instantiated by itself and used for storing objects,
27 loading requires that a subclass defines the supported types. See the add_type
30 Collections can have sources for loading objects on demand. Automatic loading
31 only works on a non-const Collection. See class CollectionSource for details.
33 As a last resort, a fallback collection can be designated for loading items
34 that are not present. Items retrieted from the fallback collection are shared
35 between the collections, and are only deleted when all collections in the chain
42 Loads objects into a Collection. Automatically picks up keywords from
45 class Loader: public DataFile::Loader
47 template<typename T> friend class CollectionItemType;
54 Collection &get_object() const { return coll; }
56 template<typename T, typename S>
57 void item(const std::string &n)
60 ItemLoader<T> ldr(*it, coll);
62 coll.add<S>(n, it.get());
67 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
71 typedef std::map<std::string, Variant> ItemMap;
72 typedef std::vector<CollectionItemTypeBase *> TypeList;
73 typedef std::vector<const CollectionSource *> SourceList;
80 Collection(const Collection &);
81 Collection &operator=(const Collection &);
84 virtual ~Collection();
86 /** Adds an object into the collection. The name must not pre-exist. The
87 collection takes ownership of the object. */
89 void add(const std::string &name, T *item)
92 throw std::invalid_argument("Collection::add(item)");
94 RefPtr<typename RemoveConst<T>::Type> ptr(item);
97 insert_unique(items, name, ptr);
101 // Avoid deleting the object
107 /// Gets a typed object from the collection.
109 T &get(const std::string &name) const
111 return extract<typename RemoveConst<T>::Type>(get_item(items, name));
114 /** Gets a typed object from the collection. If the name is not found,
115 automatic creation with the type's creator function (if defined) or from
116 sources (if present) is attempted. */
118 T &get(const std::string &name)
120 typedef typename RemoveConst<T>::Type NCT;
121 return extract<NCT>(get_var(name, get_type<NCT>(name)));
125 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
128 T &extract(const Variant &var) const;
131 std::list<T *> extract_list(const std::vector<const Variant *> &vars) const
133 std::list<T *> result;
134 for(std::vector<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
135 result.push_back(&extract<T>(**i));
139 void gather_items(std::vector<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
142 void gather_items(std::vector<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
144 if(type || (type = get_type<T>()))
145 gather_items(vars, names, *type, include_sources);
147 gather_items(vars, names, CollectionItemType<T>(), false);
151 /// Returns a list of the names of objects of one type in the collection.
153 std::list<std::string> get_names() const
155 std::list<std::string> names;
156 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
160 /** Returns a list of the names of objects of one type in the collection or
161 available from sources. */
163 std::list<std::string> get_names()
165 std::list<std::string> names;
166 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
170 /// Returns a list of objects of one type in the collection.
172 std::list<T *> get_list() const
174 std::vector<const Variant *> vars;
175 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
176 return extract_list<T>(vars);
179 /** Returns a list of objects of one type, loading them from sources if
182 std::list<T *> get_list()
184 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
186 load_items_from_sources(*type);
188 std::vector<const Variant *> vars;
189 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
190 return extract_list<T>(vars);
194 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
197 unsigned get_status(const std::string &name) const
199 // XXX Should go through all applicable types
200 if(CollectionItemTypeBase *type = get_type<T>())
201 return get_status(name, *type);
203 ItemMap::const_iterator i = items.find(name);
204 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
208 /// Checks whether a typed object exists in the collection.
210 bool contains(const std::string &name) const
211 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
213 /** Checks whether a typed object exists in the collection or is loadable
216 bool contains(const std::string &name)
217 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
219 /// Returns the name of an item in the collection.
221 const std::string &get_name(T *d) const
223 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
225 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
226 if(i->second.check_type<RPNCT>())
227 if(i->second.value<RPNCT>().get()==d)
230 // XXX Need better exception class
231 throw std::runtime_error("Item not found in collection");
235 /** Adds a type to the collection. The returned descriptor object reference
236 can be used to define how objects of that type can be loaded. */
238 CollectionItemType<T> &add_type();
240 /** Returns a mutable reference to an existing type descriptor. This can be
241 used to e.g. override the creator function of a type added by a base class. */
243 CollectionItemType<T> &modify_type();
246 /** Returns the descriptor for a type, or null if one isn't defined. An
247 optional name can be given to prioritize matching types. */
249 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
251 /// Gets a descriptor with the same type as another descriptor.
252 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
254 /// Returns the descriptor for an item, or null if it's of an unknown type.
255 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
258 /** Adds a source for automatically loading items. Sources are consulted
259 in the order they are added. */
260 void add_source(const CollectionSource &);
263 /** Opens a raw resource, without interpreting it as object data. Null is
264 returned if no such file is found. The caller must dispose of the returned
265 object when done with it. */
266 IO::Seekable *open_raw(const std::string &) const;
269 // Deprecated. Use open_raw instead.
270 DEPRECATED IO::Seekable *open_from_sources(const std::string &n) { return open_raw(n); }
273 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
275 void load_items_from_sources(const CollectionItemTypeBase &);
278 /** Sets a fallback collection, which will be consulted if an item is not
280 void set_fallback(Collection *);
282 Collection *get_fallback() const { return fallback; }
286 class Collection::ItemLoader<T, false>: public T::Loader
289 ItemLoader(T &o, Collection &):
295 class Collection::ItemLoader<T, true>: public T::Loader
298 ItemLoader(T &o, Collection &c):
299 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
304 class CollectionItemTypeBase
309 virtual ~ExtractorBase() { }
313 struct Extractor: ExtractorBase
315 virtual T &extract(const Variant &) const = 0;
319 std::vector<std::string> suffixes;
320 std::vector<ExtractorBase *> extractors;
322 CollectionItemTypeBase() { }
324 virtual ~CollectionItemTypeBase();
326 void set_keyword(const std::string &);
327 const std::string &get_keyword() const { return kwd; }
328 void add_suffix(const std::string &);
329 bool match_name(const std::string &) const;
330 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
331 virtual bool check_item_type(const Variant &) const = 0;
332 virtual void add_to_loader(Collection::Loader &) const = 0;
333 virtual bool can_create() const = 0;
334 virtual void create_item(Collection &, const std::string &) const = 0;
335 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
338 bool can_extract() const
340 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
341 if(dynamic_cast<Extractor<T> *>(*i))
347 T *extract(const Variant &var) const
349 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
350 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
351 return &ex->extract(var);
358 Describes a type of item that can be loaded by a Collection. These are created
359 by Collection::add_type.
362 class CollectionItemType: public CollectionItemTypeBase
367 virtual ~CreatorBase() { }
369 virtual T *create(Collection &, const std::string &) const = 0;
373 struct Creator: CreatorBase
375 typedef T *(C::*FuncPtr)(const std::string &);
379 Creator(FuncPtr f): func(f) { }
381 virtual T *create(Collection &coll, const std::string &name) const
382 { return (dynamic_cast<C &>(coll).*func)(name); }
386 struct Extractor: CollectionItemTypeBase::Extractor<B>
388 virtual B &extract(const Variant &var) const
389 { return *var.value<RefPtr<T> >(); }
395 CollectionItemType():
399 ~CollectionItemType()
404 /** Sets a datafile keyword for this item type. The Collection's loader
405 will accept a statement with this keyword and a single string argument - the
407 CollectionItemType &keyword(const std::string &k)
413 /** Adds a suffix that is used to match names when looking for loadable
414 objects. There is no implied separator; a name matches if it ends with the
415 suffix. If a keyword is defined before any suffixes, then "."+keyword is
416 added as a suffix. */
417 CollectionItemType &suffix(const std::string &s)
423 /** Attaches a creator function to this item type. If an item is not found
424 in the Collection, the creator function for its type is called to create it.
425 The function must be a member of the Collection subclass containing the
426 type. It must return the created object, or null if it could not be
427 created. It's also permissible to load the item via other means and then
430 CollectionItemType &creator(T *(C::*func)(const std::string &))
433 creat = new Creator<C>(func);
437 /** Makes items of this type available through a base class. */
439 CollectionItemType &base()
441 extractors.push_back(new Extractor<B>);
445 virtual bool is_same_type(const CollectionItemTypeBase &other) const
446 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
448 virtual bool check_item_type(const Variant &var) const
449 { return var.check_type<RefPtr<T> >(); }
451 virtual void add_to_loader(Collection::Loader &loader) const
452 { loader.add(kwd, &Collection::Loader::item<T, T>); }
454 virtual bool can_create() const
457 virtual void create_item(Collection &coll, const std::string &name) const
460 throw std::runtime_error("no creator");
461 T *obj = creat->create(coll, name);
466 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
468 RefPtr<T> obj = new T;
469 Collection::ItemLoader<T> ldr(*obj, coll);
471 coll.add(name, obj.get());
478 T &Collection::extract(const Variant &var) const
480 if(!var.check_type<RefPtr<T> >())
481 if(CollectionItemTypeBase *type = get_type_for_item(var))
482 if(T *item = type->extract<T>(var))
485 return *var.value<RefPtr<T> >();
489 CollectionItemType<T> &Collection::add_type()
491 CollectionItemType<T> *type = new CollectionItemType<T>;
492 types.push_back(type);
497 CollectionItemType<T> &Collection::modify_type()
499 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
500 if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
503 throw std::logic_error("type not found in collection");
507 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
509 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
510 if(dynamic_cast<CollectionItemType<T> *>(*j))
512 CollectionItemTypeBase *type = 0;
513 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
514 if((*j)->can_extract<T>())
516 if(!name.empty() && (*j)->match_name(name))
523 } // namespace DataFile