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)));
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 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
158 const Variant *find_var(const std::string &, const CollectionItemTypeBase *);
161 T &extract(const Variant &var) const;
164 std::list<T *> extract_list(const std::vector<const Variant *> &vars) const
166 std::list<T *> result;
167 for(std::vector<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
168 result.push_back(&extract<T>(**i));
172 void gather_items(std::vector<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
175 void gather_items(std::vector<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
177 if(type || (type = get_type<T>()))
178 gather_items(vars, names, *type, include_sources);
180 gather_items(vars, names, CollectionItemType<T>(), false);
184 /// Returns a list of the names of objects of one type in the collection.
186 std::list<std::string> get_names() const
188 std::list<std::string> names;
189 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
193 /** Returns a list of the names of objects of one type in the collection or
194 available from sources. */
196 std::list<std::string> get_names()
198 std::list<std::string> names;
199 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
203 /// Returns a list of objects of one type in the collection.
205 std::list<T *> get_list() const
207 std::vector<const Variant *> vars;
208 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
209 return extract_list<T>(vars);
212 /** Returns a list of objects of one type, loading them from sources if
215 std::list<T *> get_list()
217 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
219 load_items_from_sources(*type);
221 std::vector<const Variant *> vars;
222 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
223 return extract_list<T>(vars);
227 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
230 unsigned get_status(const std::string &name) const
232 // XXX Should go through all applicable types
233 if(CollectionItemTypeBase *type = get_type<T>())
234 return get_status(name, *type);
236 ItemMap::const_iterator i = items.find(name);
237 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
241 /// Checks whether a typed object exists in the collection.
243 bool contains(const std::string &name) const
244 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
246 /** Checks whether a typed object exists in the collection or is loadable
249 bool contains(const std::string &name)
250 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
252 /// Returns the name of an item in the collection.
254 const std::string &get_name(T *d) const
256 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
258 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
259 if(i->second.check_type<RPNCT>())
260 if(i->second.value<RPNCT>().get()==d)
263 // XXX Need better exception class
264 throw std::runtime_error("Item not found in collection");
268 /** Adds a type to the collection. The returned descriptor object reference
269 can be used to define how objects of that type can be loaded. */
271 typename CollectionItemTypeChooser<T>::Type &add_type();
273 /** Returns a mutable reference to an existing type descriptor. This can be
274 used to e.g. override the creator function of a type added by a base class. */
276 typename CollectionItemTypeChooser<T>::Type &modify_type();
279 /** Returns the descriptor for a type, or null if one isn't defined. An
280 optional name can be given to prioritize matching types. */
282 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
284 /// Gets a descriptor with the same type as another descriptor.
285 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
287 /// Returns the descriptor for an item, or null if it's of an unknown type.
288 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
291 /** Adds a source for automatically loading items. Sources are consulted
292 in the order they are added. */
293 void add_source(const CollectionSource &);
296 /** Opens a raw resource, without interpreting it as object data. Null is
297 returned if no such file is found. The caller must dispose of the returned
298 object when done with it. */
299 IO::Seekable *open_raw(const std::string &) const;
302 // Deprecated. Use open_raw instead.
303 DEPRECATED IO::Seekable *open_from_sources(const std::string &n) { return open_raw(n); }
306 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
308 void load_items_from_sources(const CollectionItemTypeBase &);
311 /** Sets a fallback collection, which will be consulted if an item is not
313 void set_fallback(Collection *);
315 Collection *get_fallback() const { return fallback; }
319 class Collection::ItemLoader<T, false>: public T::Loader
322 ItemLoader(T &o, Collection &):
328 class Collection::ItemLoader<T, true>: public T::Loader
331 ItemLoader(T &o, Collection &c):
332 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
337 class CollectionItemTypeBase
342 virtual ~ExtractorBase() { }
346 struct Extractor: ExtractorBase
348 virtual T &extract(const Variant &) const = 0;
352 std::vector<std::string> suffixes;
353 std::vector<ExtractorBase *> extractors;
355 CollectionItemTypeBase() { }
357 virtual ~CollectionItemTypeBase();
360 void set_keyword(const std::string &);
361 void add_suffix(const std::string &);
363 const std::string &get_keyword() const { return kwd; }
364 bool match_name(const std::string &) const;
365 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
366 virtual bool check_item_type(const Variant &) const = 0;
367 virtual void add_to_loader(Collection::Loader &) const = 0;
368 virtual bool can_create() const = 0;
369 virtual void create_item(Collection &, const std::string &) const = 0;
370 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
373 bool can_extract() const
375 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
376 if(dynamic_cast<Extractor<T> *>(*i))
382 T *extract(const Variant &var) const
384 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
385 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
386 return &ex->extract(var);
393 Describes a type of item that can be loaded by a Collection. These are created
394 by Collection::add_type.
397 class CollectionItemType: public CollectionItemTypeBase
402 virtual ~CreatorBase() { }
404 virtual T *create(Collection &, const std::string &) const = 0;
408 struct Creator: CreatorBase
410 typedef T *(C::*FuncPtr)(const std::string &);
414 Creator(FuncPtr f): func(f) { }
416 virtual T *create(Collection &coll, const std::string &name) const
417 { return (dynamic_cast<C &>(coll).*func)(name); }
421 struct Extractor: CollectionItemTypeBase::Extractor<B>
423 virtual B &extract(const Variant &var) const
424 { return *var.value<RefPtr<T> >(); }
430 CollectionItemType():
434 ~CollectionItemType()
439 /** Sets a datafile keyword for this item type. The Collection's loader
440 will accept a statement with this keyword and a single string argument - the
442 CollectionItemType &keyword(const std::string &k)
448 /** Adds a suffix that is used to match names when looking for loadable
449 objects. There is no implied separator; a name matches if it ends with the
450 suffix. If a keyword is defined before any suffixes, then "."+keyword is
451 added as a suffix. */
452 CollectionItemType &suffix(const std::string &s)
458 /** Attaches a creator function to this item type. If an item is not found
459 in the Collection, the creator function for its type is called to create it.
460 The function must be a member of the Collection subclass containing the
461 type. It must return the created object, or null if it could not be
462 created. It's also permissible to load the item via other means and then
465 CollectionItemType &creator(T *(C::*func)(const std::string &))
468 creat = new Creator<C>(func);
472 /** Makes items of this type available through a base class. */
474 CollectionItemType &base()
476 extractors.push_back(new Extractor<B>);
480 virtual bool is_same_type(const CollectionItemTypeBase &other) const
481 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
483 virtual bool check_item_type(const Variant &var) const
484 { return var.check_type<RefPtr<T> >(); }
486 virtual void add_to_loader(Collection::Loader &) const
489 virtual bool can_create() const
492 virtual void create_item(Collection &coll, const std::string &name) const
495 throw std::runtime_error("no creator");
496 T *obj = creat->create(coll, name);
501 virtual void load_item(Collection &, Parser &, const std::string &) const
503 throw std::runtime_error("this type cannot be loaded");
509 class LoadableCollectionItemType: public CollectionItemType<T>
512 virtual void add_to_loader(Collection::Loader &loader) const
513 { loader.add(this->kwd, &Collection::Loader::item<T, T>); }
515 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
517 RefPtr<T> obj = new T;
518 Collection::ItemLoader<T> ldr(*obj, coll);
520 coll.add(name, obj.get());
527 T &Collection::extract(const Variant &var) const
529 if(!var.check_type<RefPtr<T> >())
530 if(CollectionItemTypeBase *type = get_type_for_item(var))
531 if(T *item = type->extract<T>(var))
534 return *var.value<RefPtr<T> >();
538 typename CollectionItemTypeChooser<T>::Type &Collection::add_type()
540 typename CollectionItemTypeChooser<T>::Type *type = new typename CollectionItemTypeChooser<T>::Type;
541 types.push_back(type);
546 typename CollectionItemTypeChooser<T>::Type &Collection::modify_type()
548 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
549 if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
552 throw std::logic_error("type not found in collection");
556 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
558 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
559 if(dynamic_cast<CollectionItemType<T> *>(*j))
561 CollectionItemTypeBase *type = 0;
562 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
563 if((*j)->can_extract<T>())
565 if(!name.empty() && (*j)->match_name(name))
572 } // namespace DataFile