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 insert_unique(items, name, RefPtr<typename RemoveConst<T>::Type>(item));
108 /// Gets a typed object from the collection.
110 T &get(const std::string &name) const
112 return extract<T>(get_item(items, name));
115 /** Gets a typed object from the collection. If the name is not found,
116 automatic creation with the type's creator function (if defined) or from
117 sources (if present) is attempted. */
119 T &get(const std::string &name)
121 return extract<T>(get_var(name, get_type<T>()));
125 const Variant &get_var(const std::string &, const CollectionItemTypeBase *);
128 T &extract(const Variant &var) const;
131 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
133 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
135 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
136 if(i->second.check_type<RPNCT>())
139 objects->push_back(i->second.value<RPNCT>().get());
141 names->push_back(i->first);
145 if(CollectionItemTypeBase *type = get_type<T>())
147 for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
149 std::list<std::string> available_names = (*i)->get_names(*type);
150 for(std::list<std::string>::iterator j=available_names.begin(); j!=available_names.end(); ++j)
152 future_names->push_back(*j);
158 /// Returns a list of the names of objects of one type in the collection.
160 std::list<std::string> get_names() const
162 std::list<std::string> result;
163 collect_items<T>(0, &result, 0);
167 /** Returns a list of the names of objects of one type in the collection or
168 available from sources. */
170 std::list<std::string> get_names()
172 std::list<std::string> result;
173 collect_items<T>(0, &result, &result);
177 /// Returns a list of objects of one type in the collection.
179 std::list<T *> get_list() const
181 std::list<T *> result;
182 collect_items<T>(&result, 0, 0);
186 /** Returns a list of objects of one type, loading them from sources if
189 std::list<T *> get_list()
191 std::list<T *> result;
192 std::list<std::string> future;
193 collect_items<T>(&result, 0, &future);
194 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
195 result.push_back(&get<T>(*i));
201 unsigned get_status(const std::string &name) const
203 ItemMap::const_iterator i = items.find(name);
206 if(CollectionItemTypeBase *type = get_type<T>())
208 for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j)
209 if((*j)->is_loadable(*type, name))
215 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
216 if(!i->second.check_type<RPNCT>())
223 /// Checks whether a typed object exists in the collection.
225 bool contains(const std::string &name) const
226 { return get_status<T>(name)==1; }
228 /** Checks whether a typed object exists in the collection or is loadable
231 bool contains(const std::string &name)
232 { return get_status<T>(name)>0; }
234 /// Returns the name of an item in the collection.
236 const std::string &get_name(T *d) const
238 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
240 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
241 if(i->second.check_type<RPNCT>())
242 if(i->second.value<RPNCT>().get()==d)
245 // XXX Need better exception class
246 throw std::runtime_error("Item not found in collection");
250 /** Adds a type to the collection. The returned descriptor object reference
251 can be used to define how objects of that type can be loaded. */
253 CollectionItemType<T> &add_type();
255 /// Returns the descriptor for a type, or null if one isn't defined.
257 CollectionItemTypeBase *get_type() const;
259 /// Returns the descriptor for an item, or null if it's of an unknown type.
260 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
262 void add_source(CollectionSource &);
266 class Collection::ItemLoader<T, false>: public T::Loader
269 ItemLoader(T &o, Collection &):
275 class Collection::ItemLoader<T, true>: public T::Loader
278 ItemLoader(T &o, Collection &c):
279 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
284 class CollectionItemTypeBase
289 virtual ~ExtractorBase() { }
293 struct Extractor: ExtractorBase
295 virtual T &extract(const Variant &) const = 0;
299 std::vector<std::string> suffixes;
300 std::vector<ExtractorBase *> extractors;
302 CollectionItemTypeBase() { }
304 virtual ~CollectionItemTypeBase();
306 void set_keyword(const std::string &);
307 const std::string &get_keyword() const { return kwd; }
308 void add_suffix(const std::string &);
309 bool match_name(const std::string &) const;
310 virtual bool check_item_type(const Variant &) const = 0;
311 virtual void add_to_loader(Collection::Loader &) const = 0;
312 virtual bool can_create() const = 0;
313 virtual void create_item(Collection &, const std::string &) const = 0;
314 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
317 bool can_extract() const
319 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
320 if(dynamic_cast<Extractor<T> *>(*i))
326 T *extract(const Variant &var) const
328 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
329 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
330 return &ex->extract(var);
337 Describes a type of item that can be loaded by a Collection. These are created
338 by Collection::add_type.
341 class CollectionItemType: public CollectionItemTypeBase
346 virtual ~CreatorBase() { }
348 virtual T *create(Collection &, const std::string &) const = 0;
352 struct Creator: CreatorBase
354 typedef T *(C::*FuncPtr)(const std::string &);
358 Creator(FuncPtr f): func(f) { }
360 virtual T *create(Collection &coll, const std::string &name) const
361 { return (static_cast<C &>(coll).*func)(name); }
365 struct Extractor: CollectionItemTypeBase::Extractor<B>
367 virtual B &extract(const Variant &var) const
368 { return *var.value<RefPtr<T> >(); }
374 CollectionItemType():
378 ~CollectionItemType()
383 /** Sets a datafile keyword for this item type. The Collection's loader
384 will accept a statement with this keyword and a single string argument - the
386 CollectionItemType &keyword(const std::string &k)
392 /** Adds a suffix that is used to match names when looking for future
393 objects. There is no implied separator; a name matches if it ends with the
394 suffix. If a keyword is defined before any suffixes, then "."+keyword is
395 added as a suffix. */
396 CollectionItemType &suffix(const std::string &s)
402 /** Attaches a creator function to this item type. If an item is not found
403 in the Collection, the creator function for its type is called to create it.
404 The function must be a member of the Collection subclass containing the
405 type. It must return the created object, or null if it could not be
406 created. It's also permissible to load the item via other means and then
409 CollectionItemType &creator(T *(C::*func)(const std::string &))
412 creat = new Creator<C>(func);
416 /** Makes items of this type available through a base class. */
418 CollectionItemType &base()
420 extractors.push_back(new Extractor<B>);
424 virtual bool check_item_type(const Variant &var) const
425 { return var.check_type<RefPtr<T> >(); }
427 virtual void add_to_loader(Collection::Loader &loader) const
428 { loader.add(kwd, &Collection::Loader::item<T, T>); }
430 virtual bool can_create() const
433 virtual void create_item(Collection &coll, const std::string &name) const
436 throw std::runtime_error("no creator");
437 T *obj = creat->create(coll, name);
442 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
444 RefPtr<T> obj = new T;
445 Collection::ItemLoader<T> ldr(*obj, coll);
447 // Collection::add will delete the object if it fails
448 coll.add(name, obj.release());
454 T &Collection::extract(const Variant &var) const
456 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
458 if(!var.check_type<RPNCT>())
459 if(CollectionItemTypeBase *type = get_type_for_item(var))
460 if(T *item = type->extract<T>(var))
463 return *var.value<RPNCT>();
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<typename RemoveConst<T>::Type> *>(*j))
480 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
481 if((*j)->can_extract<T>())
486 } // namespace DataFile