1 #ifndef MSP_DATAFILE_COLLECTION_H_
2 #define MSP_DATAFILE_COLLECTION_H_
4 #include <msp/core/maputils.h>
5 #include <msp/core/meta.h>
6 #include <msp/core/refptr.h>
7 #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. */
17 Helper struct to determine whether a Loader has a Collection typedef.
20 struct NeedsCollection
22 struct Yes { char c[2]; };
23 struct No { char c; };
26 static Yes f(typename U::Collection *);
30 enum { value = (sizeof(f<T>(0))==sizeof(Yes)) };
33 class CollectionItemTypeBase;
36 class CollectionItemType;
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 A fallback collection can be designated as another way of loading items that
50 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
58 Loads objects into a Collection. Automatically picks up keywords from
61 class Loader: public DataFile::Loader
63 template<typename T> friend class CollectionItemType;
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::list<CollectionItemTypeBase *> TypeList;
89 typedef std::list<CollectionSource *> SourceList;
96 Collection(const Collection &);
97 Collection &operator=(const Collection &);
100 virtual ~Collection();
102 /** Adds an object into the collection. The name must not pre-exist. The
103 collection takes ownership of the object. */
105 void add(const std::string &name, T *item)
108 throw std::invalid_argument("Collection::add(item)");
110 RefPtr<typename RemoveConst<T>::Type> ptr(item);
113 insert_unique(items, name, ptr);
117 // Avoid deleting the object
123 /// Gets a typed object from the collection.
125 T &get(const std::string &name) const
127 return extract<typename RemoveConst<T>::Type>(get_item(items, name));
130 /** Gets a typed object from the collection. If the name is not found,
131 automatic creation with the type's creator function (if defined) or from
132 sources (if present) is attempted. */
134 T &get(const std::string &name)
136 typedef typename RemoveConst<T>::Type NCT;
137 return extract<NCT>(get_var(name, get_type<NCT>()));
141 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
144 T &extract(const Variant &var) const;
147 std::list<T *> extract_list(const std::list<const Variant *> &vars) const
149 std::list<T *> result;
150 for(std::list<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
151 result.push_back(&extract<T>(**i));
155 void gather_items(std::list<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
158 void gather_items(std::list<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
160 if(type || (type = get_type<T>()))
161 gather_items(vars, names, *type, include_sources);
163 gather_items(vars, names, CollectionItemType<T>(), false);
167 /// Returns a list of the names of objects of one type in the collection.
169 std::list<std::string> get_names() const
171 std::list<std::string> names;
172 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
176 /** Returns a list of the names of objects of one type in the collection or
177 available from sources. */
179 std::list<std::string> get_names()
181 std::list<std::string> names;
182 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
186 /// Returns a list of objects of one type in the collection.
188 std::list<T *> get_list() const
190 std::list<const Variant *> vars;
191 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
192 return extract_list<T>(vars);
195 /** Returns a list of objects of one type, loading them from sources if
198 std::list<T *> get_list()
200 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
202 load_items_from_sources(*type);
204 std::list<const Variant *> vars;
205 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
206 return extract_list<T>(vars);
210 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
213 unsigned get_status(const std::string &name) const
215 // XXX Should go through all applicable types
216 if(CollectionItemTypeBase *type = get_type<T>())
217 return get_status(name, *type);
219 ItemMap::const_iterator i = items.find(name);
220 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
224 /// Checks whether a typed object exists in the collection.
226 bool contains(const std::string &name) const
227 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
229 /** Checks whether a typed object exists in the collection or is loadable
232 bool contains(const std::string &name)
233 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
235 /// Returns the name of an item in the collection.
237 const std::string &get_name(T *d) const
239 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
241 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
242 if(i->second.check_type<RPNCT>())
243 if(i->second.value<RPNCT>().get()==d)
246 // XXX Need better exception class
247 throw std::runtime_error("Item not found in collection");
251 /** Adds a type to the collection. The returned descriptor object reference
252 can be used to define how objects of that type can be loaded. */
254 CollectionItemType<T> &add_type();
257 /// Returns the descriptor for a type, or null if one isn't defined.
259 CollectionItemTypeBase *get_type() const;
261 /// Returns the descriptor for an item, or null if it's of an unknown type.
262 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
265 void add_source(CollectionSource &);
267 IO::Seekable *open_from_sources(const std::string &);
270 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
272 void load_items_from_sources(const CollectionItemTypeBase &);
275 /** Sets a fallback collection, which will be consulted if an item is not
277 void set_fallback(Collection *);
281 class Collection::ItemLoader<T, false>: public T::Loader
284 ItemLoader(T &o, Collection &):
290 class Collection::ItemLoader<T, true>: public T::Loader
293 ItemLoader(T &o, Collection &c):
294 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
299 class CollectionItemTypeBase
304 virtual ~ExtractorBase() { }
308 struct Extractor: ExtractorBase
310 virtual T &extract(const Variant &) const = 0;
314 std::vector<std::string> suffixes;
315 std::vector<ExtractorBase *> extractors;
317 CollectionItemTypeBase() { }
319 virtual ~CollectionItemTypeBase();
321 void set_keyword(const std::string &);
322 const std::string &get_keyword() const { return kwd; }
323 void add_suffix(const std::string &);
324 bool match_name(const std::string &) const;
325 virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
326 virtual bool check_item_type(const Variant &) const = 0;
327 virtual void add_to_loader(Collection::Loader &) const = 0;
328 virtual bool can_create() const = 0;
329 virtual void create_item(Collection &, const std::string &) const = 0;
330 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
333 bool can_extract() const
335 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
336 if(dynamic_cast<Extractor<T> *>(*i))
342 T *extract(const Variant &var) const
344 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
345 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
346 return &ex->extract(var);
353 Describes a type of item that can be loaded by a Collection. These are created
354 by Collection::add_type.
357 class CollectionItemType: public CollectionItemTypeBase
362 virtual ~CreatorBase() { }
364 virtual T *create(Collection &, const std::string &) const = 0;
368 struct Creator: CreatorBase
370 typedef T *(C::*FuncPtr)(const std::string &);
374 Creator(FuncPtr f): func(f) { }
376 virtual T *create(Collection &coll, const std::string &name) const
377 { return (static_cast<C &>(coll).*func)(name); }
381 struct Extractor: CollectionItemTypeBase::Extractor<B>
383 virtual B &extract(const Variant &var) const
384 { return *var.value<RefPtr<T> >(); }
390 CollectionItemType():
394 ~CollectionItemType()
399 /** Sets a datafile keyword for this item type. The Collection's loader
400 will accept a statement with this keyword and a single string argument - the
402 CollectionItemType &keyword(const std::string &k)
408 /** Adds a suffix that is used to match names when looking for future
409 objects. There is no implied separator; a name matches if it ends with the
410 suffix. If a keyword is defined before any suffixes, then "."+keyword is
411 added as a suffix. */
412 CollectionItemType &suffix(const std::string &s)
418 /** Attaches a creator function to this item type. If an item is not found
419 in the Collection, the creator function for its type is called to create it.
420 The function must be a member of the Collection subclass containing the
421 type. It must return the created object, or null if it could not be
422 created. It's also permissible to load the item via other means and then
425 CollectionItemType &creator(T *(C::*func)(const std::string &))
428 creat = new Creator<C>(func);
432 /** Makes items of this type available through a base class. */
434 CollectionItemType &base()
436 extractors.push_back(new Extractor<B>);
440 virtual bool is_same_type(const CollectionItemTypeBase &other) const
441 { return dynamic_cast<const CollectionItemType<T> *>(&other); }
443 virtual bool check_item_type(const Variant &var) const
444 { return var.check_type<RefPtr<T> >(); }
446 virtual void add_to_loader(Collection::Loader &loader) const
447 { loader.add(kwd, &Collection::Loader::item<T, T>); }
449 virtual bool can_create() const
452 virtual void create_item(Collection &coll, const std::string &name) const
455 throw std::runtime_error("no creator");
456 T *obj = creat->create(coll, name);
461 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
463 RefPtr<T> obj = new T;
464 Collection::ItemLoader<T> ldr(*obj, coll);
466 coll.add(name, obj.get());
473 T &Collection::extract(const Variant &var) const
475 if(!var.check_type<RefPtr<T> >())
476 if(CollectionItemTypeBase *type = get_type_for_item(var))
477 if(T *item = type->extract<T>(var))
480 return *var.value<RefPtr<T> >();
484 CollectionItemType<T> &Collection::add_type()
486 CollectionItemType<T> *type = new CollectionItemType<T>;
487 types.push_back(type);
492 CollectionItemTypeBase *Collection::get_type() const
494 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
495 if(dynamic_cast<CollectionItemType<T> *>(*j))
497 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
498 if((*j)->can_extract<T>())
503 } // namespace DataFile