1 #ifndef MSP_DATAFILE_COLLECTION_H_
2 #define MSP_DATAFILE_COLLECTION_H_
4 #include <msp/core/maputils.h>
5 #include <msp/core/refptr.h>
6 #include "collectionsource.h"
10 /* XXX This file is a big mess with too many things in it. However, the
11 dependencies between those things make it difficult to split up. */
16 class CollectionItemTypeBase;
19 class CollectionItemType;
22 A collection of objects that can be loaded from a datafile. Each object is
23 identified by a name, which must be unique across the entire collection.
25 While this class can be instantiated by itself and used for storing objects,
26 loading requires that a subclass defines the supported types. See the add_type
29 Collections can have sources for loading objects on demand. Automatic loading
30 only works on a non-const Collection. See class CollectionSource for details.
32 As a last resort, a fallback collection can be designated for loading items
33 that are not present. Items retrieted from the fallback collection are shared
34 between the collections, and are only deleted when all collections in the chain
41 Loads objects into a Collection. Automatically picks up keywords from
44 class Loader: public DataFile::Loader
46 template<typename T> friend class CollectionItemType;
53 Collection &get_object() const { return coll; }
55 template<typename T, typename S>
56 void item(const std::string &n)
59 ItemLoader<T> ldr(*it, coll);
61 coll.add<S>(n, it.get());
66 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
70 typedef std::map<std::string, Variant> ItemMap;
71 typedef std::list<CollectionItemTypeBase *> TypeList;
72 typedef std::list<const CollectionSource *> SourceList;
79 Collection(const Collection &);
80 Collection &operator=(const Collection &);
83 virtual ~Collection();
85 /** Adds an object into the collection. The name must not pre-exist. The
86 collection takes ownership of the object. */
88 void add(const std::string &name, T *item)
91 throw std::invalid_argument("Collection::add(item)");
93 RefPtr<typename RemoveConst<T>::Type> ptr(item);
96 insert_unique(items, name, ptr);
100 // Avoid deleting the object
106 /// Gets a typed object from the collection.
108 T &get(const std::string &name) const
110 return extract<typename RemoveConst<T>::Type>(get_item(items, name));
113 /** Gets a typed object from the collection. If the name is not found,
114 automatic creation with the type's creator function (if defined) or from
115 sources (if present) is attempted. */
117 T &get(const std::string &name)
119 typedef typename RemoveConst<T>::Type NCT;
120 return extract<NCT>(get_var(name, get_type<NCT>(name)));
124 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
127 T &extract(const Variant &var) const;
130 std::list<T *> extract_list(const std::list<const Variant *> &vars) const
132 std::list<T *> result;
133 for(std::list<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
134 result.push_back(&extract<T>(**i));
138 void gather_items(std::list<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
141 void gather_items(std::list<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
143 if(type || (type = get_type<T>()))
144 gather_items(vars, names, *type, include_sources);
146 gather_items(vars, names, CollectionItemType<T>(), false);
150 /// Returns a list of the names of objects of one type in the collection.
152 std::list<std::string> get_names() const
154 std::list<std::string> names;
155 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
159 /** Returns a list of the names of objects of one type in the collection or
160 available from sources. */
162 std::list<std::string> get_names()
164 std::list<std::string> names;
165 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
169 /// Returns a list of objects of one type in the collection.
171 std::list<T *> get_list() const
173 std::list<const Variant *> vars;
174 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
175 return extract_list<T>(vars);
178 /** Returns a list of objects of one type, loading them from sources if
181 std::list<T *> get_list()
183 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
185 load_items_from_sources(*type);
187 std::list<const Variant *> vars;
188 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
189 return extract_list<T>(vars);
193 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
196 unsigned get_status(const std::string &name) const
198 // XXX Should go through all applicable types
199 if(CollectionItemTypeBase *type = get_type<T>())
200 return get_status(name, *type);
202 ItemMap::const_iterator i = items.find(name);
203 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
207 /// Checks whether a typed object exists in the collection.
209 bool contains(const std::string &name) const
210 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
212 /** Checks whether a typed object exists in the collection or is loadable
215 bool contains(const std::string &name)
216 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
218 /// Returns the name of an item in the collection.
220 const std::string &get_name(T *d) const
222 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
224 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
225 if(i->second.check_type<RPNCT>())
226 if(i->second.value<RPNCT>().get()==d)
229 // XXX Need better exception class
230 throw std::runtime_error("Item not found in collection");
234 /** Adds a type to the collection. The returned descriptor object reference
235 can be used to define how objects of that type can be loaded. */
237 CollectionItemType<T> &add_type();
240 /** Returns the descriptor for a type, or null if one isn't defined. An
241 optional name can be given to prioritize matching types. */
243 CollectionItemTypeBase *get_type(const std::string & = std::string()) const;
245 /// Gets a descriptor with the same type as another descriptor.
246 CollectionItemTypeBase *get_type(const CollectionItemTypeBase &) const;
248 /// Returns the descriptor for an item, or null if it's of an unknown type.
249 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
252 /** Adds a source for automatically loading items. Sources are consulted
253 in the order they are added. */
254 void add_source(const CollectionSource &);
257 /** Opens a raw resource, without interpreting it as object data. Null is
258 returned if no such file is found. The caller must dispose of the returned
259 object when done with it. */
260 IO::Seekable *open_raw(const std::string &) const;
263 IO::Seekable *open_from_sources(const std::string &n) { return open_raw(n); }
266 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
268 void load_items_from_sources(const CollectionItemTypeBase &);
271 /** Sets a fallback collection, which will be consulted if an item is not
273 void set_fallback(Collection *);
275 Collection *get_fallback() const { return fallback; }
279 class Collection::ItemLoader<T, false>: public T::Loader
282 ItemLoader(T &o, Collection &):
288 class Collection::ItemLoader<T, true>: public T::Loader
291 ItemLoader(T &o, Collection &c):
292 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
297 class CollectionItemTypeBase
302 virtual ~ExtractorBase() { }
306 struct Extractor: ExtractorBase
308 virtual T &extract(const Variant &) const = 0;
312 std::vector<std::string> suffixes;
313 std::vector<ExtractorBase *> extractors;
315 CollectionItemTypeBase() { }
317 virtual ~CollectionItemTypeBase();
319 void set_keyword(const std::string &);
320 const std::string &get_keyword() const { return kwd; }
321 void add_suffix(const std::string &);
322 bool match_name(const std::string &) const;
323 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
324 virtual bool check_item_type(const Variant &) const = 0;
325 virtual void add_to_loader(Collection::Loader &) const = 0;
326 virtual bool can_create() const = 0;
327 virtual void create_item(Collection &, const std::string &) const = 0;
328 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
331 bool can_extract() const
333 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
334 if(dynamic_cast<Extractor<T> *>(*i))
340 T *extract(const Variant &var) const
342 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
343 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
344 return &ex->extract(var);
351 Describes a type of item that can be loaded by a Collection. These are created
352 by Collection::add_type.
355 class CollectionItemType: public CollectionItemTypeBase
360 virtual ~CreatorBase() { }
362 virtual T *create(Collection &, const std::string &) const = 0;
366 struct Creator: CreatorBase
368 typedef T *(C::*FuncPtr)(const std::string &);
372 Creator(FuncPtr f): func(f) { }
374 virtual T *create(Collection &coll, const std::string &name) const
375 { return (dynamic_cast<C &>(coll).*func)(name); }
379 struct Extractor: CollectionItemTypeBase::Extractor<B>
381 virtual B &extract(const Variant &var) const
382 { return *var.value<RefPtr<T> >(); }
388 CollectionItemType():
392 ~CollectionItemType()
397 /** Sets a datafile keyword for this item type. The Collection's loader
398 will accept a statement with this keyword and a single string argument - the
400 CollectionItemType &keyword(const std::string &k)
406 /** Adds a suffix that is used to match names when looking for future
407 objects. There is no implied separator; a name matches if it ends with the
408 suffix. If a keyword is defined before any suffixes, then "."+keyword is
409 added as a suffix. */
410 CollectionItemType &suffix(const std::string &s)
416 /** Attaches a creator function to this item type. If an item is not found
417 in the Collection, the creator function for its type is called to create it.
418 The function must be a member of the Collection subclass containing the
419 type. It must return the created object, or null if it could not be
420 created. It's also permissible to load the item via other means and then
423 CollectionItemType &creator(T *(C::*func)(const std::string &))
426 creat = new Creator<C>(func);
430 /** Makes items of this type available through a base class. */
432 CollectionItemType &base()
434 extractors.push_back(new Extractor<B>);
438 virtual bool is_same_type(const CollectionItemTypeBase &other) const
439 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
441 virtual bool check_item_type(const Variant &var) const
442 { return var.check_type<RefPtr<T> >(); }
444 virtual void add_to_loader(Collection::Loader &loader) const
445 { loader.add(kwd, &Collection::Loader::item<T, T>); }
447 virtual bool can_create() const
450 virtual void create_item(Collection &coll, const std::string &name) const
453 throw std::runtime_error("no creator");
454 T *obj = creat->create(coll, name);
459 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
461 RefPtr<T> obj = new T;
462 Collection::ItemLoader<T> ldr(*obj, coll);
464 coll.add(name, obj.get());
471 T &Collection::extract(const Variant &var) const
473 if(!var.check_type<RefPtr<T> >())
474 if(CollectionItemTypeBase *type = get_type_for_item(var))
475 if(T *item = type->extract<T>(var))
478 return *var.value<RefPtr<T> >();
482 CollectionItemType<T> &Collection::add_type()
484 CollectionItemType<T> *type = new CollectionItemType<T>;
485 types.push_back(type);
490 CollectionItemTypeBase *Collection::get_type(const std::string &name) const
492 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
493 if(dynamic_cast<CollectionItemType<T> *>(*j))
495 CollectionItemTypeBase *type = 0;
496 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
497 if((*j)->can_extract<T>())
499 if(!name.empty() && (*j)->match_name(name))
506 } // namespace DataFile