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.
53 Loads objects into a Collection. Automatically picks up keywords from
56 class Loader: public DataFile::Loader
58 template<typename T> friend class CollectionItemType;
65 Collection &get_object() const { return coll; }
67 template<typename T, typename S>
68 void item(const std::string &n)
71 ItemLoader<T> ldr(*it, coll);
73 coll.add<S>(n, it.get());
78 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
82 typedef std::map<std::string, Variant> ItemMap;
83 typedef std::list<CollectionItemTypeBase *> TypeList;
84 typedef std::list<CollectionSource *> SourceList;
90 Collection(const Collection &);
91 Collection &operator=(const Collection &);
94 virtual ~Collection();
96 /** Adds an object into the collection. The name must not pre-exist. The
97 collection takes ownership of the object. */
99 void add(const std::string &name, T *item)
102 throw std::invalid_argument("Collection::add(item)");
104 RefPtr<typename RemoveConst<T>::Type> ptr(item);
107 insert_unique(items, name, ptr);
111 // Avoid deleting the object
117 /// Gets a typed object from the collection.
119 T &get(const std::string &name) const
121 return extract<typename RemoveConst<T>::Type>(get_item(items, name));
124 /** Gets a typed object from the collection. If the name is not found,
125 automatic creation with the type's creator function (if defined) or from
126 sources (if present) is attempted. */
128 T &get(const std::string &name)
130 typedef typename RemoveConst<T>::Type NCT;
131 return extract<NCT>(get_var(name, get_type<NCT>()));
135 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
138 T &extract(const Variant &var) const;
141 std::list<T *> extract_list(const std::list<const Variant *> &vars) const
143 std::list<T *> result;
144 for(std::list<const Variant *>::const_iterator i=vars.begin(); i!=vars.end(); ++i)
145 result.push_back(&extract<T>(**i));
149 void gather_items(std::list<const Variant *> *, std::list<std::string> *, const CollectionItemTypeBase &, bool) const;
152 void gather_items(std::list<const Variant *> *vars, std::list<std::string> *names, const CollectionItemTypeBase *type, bool include_sources) const
154 if(type || (type = get_type<T>()))
155 gather_items(vars, names, *type, include_sources);
157 gather_items(vars, names, CollectionItemType<T>(), false);
161 /// Returns a list of the names of objects of one type in the collection.
163 std::list<std::string> get_names() const
165 std::list<std::string> names;
166 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, false);
170 /** Returns a list of the names of objects of one type in the collection or
171 available from sources. */
173 std::list<std::string> get_names()
175 std::list<std::string> names;
176 gather_items<typename RemoveConst<T>::Type>(0, &names, 0, true);
180 /// Returns a list of objects of one type in the collection.
182 std::list<T *> get_list() const
184 std::list<const Variant *> vars;
185 gather_items<typename RemoveConst<T>::Type>(&vars, 0, 0, false);
186 return extract_list<T>(vars);
189 /** Returns a list of objects of one type, loading them from sources if
192 std::list<T *> get_list()
194 CollectionItemTypeBase *type = get_type<typename RemoveConst<T>::Type>();
196 load_items_from_sources(*type);
198 std::list<const Variant *> vars;
199 gather_items<typename RemoveConst<T>::Type>(&vars, 0, type, true);
200 return extract_list<T>(vars);
204 unsigned get_status(const std::string &, const CollectionItemTypeBase &) const;
207 unsigned get_status(const std::string &name) const
209 // XXX Should go through all applicable types
210 if(CollectionItemTypeBase *type = get_type<T>())
211 return get_status(name, *type);
213 ItemMap::const_iterator i = items.find(name);
214 return (i!=items.end() && i->second.check_type<RefPtr<T> >());
218 /// Checks whether a typed object exists in the collection.
220 bool contains(const std::string &name) const
221 { return get_status<typename RemoveConst<T>::Type>(name)==1; }
223 /** Checks whether a typed object exists in the collection or is loadable
226 bool contains(const std::string &name)
227 { return get_status<typename RemoveConst<T>::Type>(name)>0; }
229 /// Returns the name of an item in the collection.
231 const std::string &get_name(T *d) const
233 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
235 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
236 if(i->second.check_type<RPNCT>())
237 if(i->second.value<RPNCT>().get()==d)
240 // XXX Need better exception class
241 throw std::runtime_error("Item not found in collection");
245 /** Adds a type to the collection. The returned descriptor object reference
246 can be used to define how objects of that type can be loaded. */
248 CollectionItemType<T> &add_type();
251 /// Returns the descriptor for a type, or null if one isn't defined.
253 CollectionItemTypeBase *get_type() const;
255 /// Returns the descriptor for an item, or null if it's of an unknown type.
256 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
259 void add_source(CollectionSource &);
262 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
264 void load_items_from_sources(const CollectionItemTypeBase &);
268 class Collection::ItemLoader<T, false>: public T::Loader
271 ItemLoader(T &o, Collection &):
277 class Collection::ItemLoader<T, true>: public T::Loader
280 ItemLoader(T &o, Collection &c):
281 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
286 class CollectionItemTypeBase
291 virtual ~ExtractorBase() { }
295 struct Extractor: ExtractorBase
297 virtual T &extract(const Variant &) const = 0;
301 std::vector<std::string> suffixes;
302 std::vector<ExtractorBase *> extractors;
304 CollectionItemTypeBase() { }
306 virtual ~CollectionItemTypeBase();
308 void set_keyword(const std::string &);
309 const std::string &get_keyword() const { return kwd; }
310 void add_suffix(const std::string &);
311 bool match_name(const std::string &) const;
312 virtual bool check_item_type(const Variant &) const = 0;
313 virtual void add_to_loader(Collection::Loader &) const = 0;
314 virtual bool can_create() const = 0;
315 virtual void create_item(Collection &, const std::string &) const = 0;
316 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
319 bool can_extract() const
321 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
322 if(dynamic_cast<Extractor<T> *>(*i))
328 T *extract(const Variant &var) const
330 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
331 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
332 return &ex->extract(var);
339 Describes a type of item that can be loaded by a Collection. These are created
340 by Collection::add_type.
343 class CollectionItemType: public CollectionItemTypeBase
348 virtual ~CreatorBase() { }
350 virtual T *create(Collection &, const std::string &) const = 0;
354 struct Creator: CreatorBase
356 typedef T *(C::*FuncPtr)(const std::string &);
360 Creator(FuncPtr f): func(f) { }
362 virtual T *create(Collection &coll, const std::string &name) const
363 { return (static_cast<C &>(coll).*func)(name); }
367 struct Extractor: CollectionItemTypeBase::Extractor<B>
369 virtual B &extract(const Variant &var) const
370 { return *var.value<RefPtr<T> >(); }
376 CollectionItemType():
380 ~CollectionItemType()
385 /** Sets a datafile keyword for this item type. The Collection's loader
386 will accept a statement with this keyword and a single string argument - the
388 CollectionItemType &keyword(const std::string &k)
394 /** Adds a suffix that is used to match names when looking for future
395 objects. There is no implied separator; a name matches if it ends with the
396 suffix. If a keyword is defined before any suffixes, then "."+keyword is
397 added as a suffix. */
398 CollectionItemType &suffix(const std::string &s)
404 /** Attaches a creator function to this item type. If an item is not found
405 in the Collection, the creator function for its type is called to create it.
406 The function must be a member of the Collection subclass containing the
407 type. It must return the created object, or null if it could not be
408 created. It's also permissible to load the item via other means and then
411 CollectionItemType &creator(T *(C::*func)(const std::string &))
414 creat = new Creator<C>(func);
418 /** Makes items of this type available through a base class. */
420 CollectionItemType &base()
422 extractors.push_back(new Extractor<B>);
426 virtual bool check_item_type(const Variant &var) const
427 { return var.check_type<RefPtr<T> >(); }
429 virtual void add_to_loader(Collection::Loader &loader) const
430 { loader.add(kwd, &Collection::Loader::item<T, T>); }
432 virtual bool can_create() const
435 virtual void create_item(Collection &coll, const std::string &name) const
438 throw std::runtime_error("no creator");
439 T *obj = creat->create(coll, name);
444 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
446 RefPtr<T> obj = new T;
447 Collection::ItemLoader<T> ldr(*obj, coll);
449 coll.add(name, obj.get());
456 T &Collection::extract(const Variant &var) const
458 if(!var.check_type<RefPtr<T> >())
459 if(CollectionItemTypeBase *type = get_type_for_item(var))
460 if(T *item = type->extract<T>(var))
463 return *var.value<RefPtr<T> >();
467 CollectionItemType<T> &Collection::add_type()
469 CollectionItemType<T> *type = new CollectionItemType<T>;
470 types.push_back(type);
475 CollectionItemTypeBase *Collection::get_type() const
477 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
478 if(dynamic_cast<CollectionItemType<T> *>(*j))
480 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
481 if((*j)->can_extract<T>())
486 } // namespace DataFile