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());
79 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
83 typedef std::map<std::string, Variant> ItemMap;
84 typedef std::list<CollectionItemTypeBase *> TypeList;
85 typedef std::list<CollectionSource *> SourceList;
91 Collection(const Collection &);
92 Collection &operator=(const Collection &);
95 virtual ~Collection();
97 /** Adds an object into the collection. The name must not pre-exist. The
98 collection takes ownership of the object. */
100 void add(const std::string &name, T *item)
103 throw std::invalid_argument("Collection::add(item)");
105 RefPtr<typename RemoveConst<T>::Type> ptr(item);
108 insert_unique(items, name, ptr);
112 // Avoid deleting the object
118 /// Gets a typed object from the collection.
120 T &get(const std::string &name) const
122 return extract<T>(get_item(items, name));
125 /** Gets a typed object from the collection. If the name is not found,
126 automatic creation with the type's creator function (if defined) or from
127 sources (if present) is attempted. */
129 T &get(const std::string &name)
131 return extract<T>(get_var(name, get_type<T>()));
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();
250 /// Returns the descriptor for a type, or null if one isn't defined.
252 CollectionItemTypeBase *get_type() 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;
257 void add_source(CollectionSource &);
259 void gather_names_from_sources(std::list<std::string> &, const CollectionItemTypeBase &) const;
261 void load_items_from_sources(const CollectionItemTypeBase &);
265 class Collection::ItemLoader<T, false>: public T::Loader
268 ItemLoader(T &o, Collection &):
274 class Collection::ItemLoader<T, true>: public T::Loader
277 ItemLoader(T &o, Collection &c):
278 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
283 class CollectionItemTypeBase
288 virtual ~ExtractorBase() { }
292 struct Extractor: ExtractorBase
294 virtual T &extract(const Variant &) const = 0;
298 std::vector<std::string> suffixes;
299 std::vector<ExtractorBase *> extractors;
301 CollectionItemTypeBase() { }
303 virtual ~CollectionItemTypeBase();
305 void set_keyword(const std::string &);
306 const std::string &get_keyword() const { return kwd; }
307 void add_suffix(const std::string &);
308 bool match_name(const std::string &) const;
309 virtual bool check_item_type(const Variant &) const = 0;
310 virtual void add_to_loader(Collection::Loader &) const = 0;
311 virtual bool can_create() const = 0;
312 virtual void create_item(Collection &, const std::string &) const = 0;
313 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
316 bool can_extract() const
318 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
319 if(dynamic_cast<Extractor<T> *>(*i))
325 T *extract(const Variant &var) const
327 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
328 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
329 return &ex->extract(var);
336 Describes a type of item that can be loaded by a Collection. These are created
337 by Collection::add_type.
340 class CollectionItemType: public CollectionItemTypeBase
345 virtual ~CreatorBase() { }
347 virtual T *create(Collection &, const std::string &) const = 0;
351 struct Creator: CreatorBase
353 typedef T *(C::*FuncPtr)(const std::string &);
357 Creator(FuncPtr f): func(f) { }
359 virtual T *create(Collection &coll, const std::string &name) const
360 { return (static_cast<C &>(coll).*func)(name); }
364 struct Extractor: CollectionItemTypeBase::Extractor<B>
366 virtual B &extract(const Variant &var) const
367 { return *var.value<RefPtr<T> >(); }
373 CollectionItemType():
377 ~CollectionItemType()
382 /** Sets a datafile keyword for this item type. The Collection's loader
383 will accept a statement with this keyword and a single string argument - the
385 CollectionItemType &keyword(const std::string &k)
391 /** Adds a suffix that is used to match names when looking for future
392 objects. There is no implied separator; a name matches if it ends with the
393 suffix. If a keyword is defined before any suffixes, then "."+keyword is
394 added as a suffix. */
395 CollectionItemType &suffix(const std::string &s)
401 /** Attaches a creator function to this item type. If an item is not found
402 in the Collection, the creator function for its type is called to create it.
403 The function must be a member of the Collection subclass containing the
404 type. It must return the created object, or null if it could not be
405 created. It's also permissible to load the item via other means and then
408 CollectionItemType &creator(T *(C::*func)(const std::string &))
411 creat = new Creator<C>(func);
415 /** Makes items of this type available through a base class. */
417 CollectionItemType &base()
419 extractors.push_back(new Extractor<B>);
423 virtual bool check_item_type(const Variant &var) const
424 { return var.check_type<RefPtr<T> >(); }
426 virtual void add_to_loader(Collection::Loader &loader) const
427 { loader.add(kwd, &Collection::Loader::item<T, T>); }
429 virtual bool can_create() const
432 virtual void create_item(Collection &coll, const std::string &name) const
435 throw std::runtime_error("no creator");
436 T *obj = creat->create(coll, name);
441 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
443 RefPtr<T> obj = new T;
444 Collection::ItemLoader<T> ldr(*obj, coll);
446 coll.add(name, obj.get());
453 T &Collection::extract(const Variant &var) const
455 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
457 if(!var.check_type<RPNCT>())
458 if(CollectionItemTypeBase *type = get_type_for_item(var))
459 if(T *item = type->extract<T>(var))
462 return *var.value<RPNCT>();
466 CollectionItemType<T> &Collection::add_type()
468 CollectionItemType<T> *type = new CollectionItemType<T>;
469 types.push_back(type);
474 CollectionItemTypeBase *Collection::get_type() const
476 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
477 if(dynamic_cast<CollectionItemType<typename RemoveConst<T>::Type> *>(*j))
479 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
480 if((*j)->can_extract<T>())
485 } // namespace DataFile