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
130 return *var.value<RefPtr<typename RemoveConst<T>::Type> >();
134 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
136 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
138 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
139 if(i->second.check_type<RPNCT>())
142 objects->push_back(i->second.value<RPNCT>().get());
144 names->push_back(i->first);
148 if(CollectionItemTypeBase *type = get_type<T>())
150 for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
152 std::list<std::string> available_names = (*i)->get_names(*type);
153 for(std::list<std::string>::iterator j=available_names.begin(); j!=available_names.end(); ++j)
155 future_names->push_back(*j);
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> result;
166 collect_items<T>(0, &result, 0);
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> result;
176 collect_items<T>(0, &result, &result);
180 /// Returns a list of objects of one type in the collection.
182 std::list<T *> get_list() const
184 std::list<T *> result;
185 collect_items<T>(&result, 0, 0);
189 /** Returns a list of objects of one type, loading them from sources if
192 std::list<T *> get_list()
194 std::list<T *> result;
195 std::list<std::string> future;
196 collect_items<T>(&result, 0, &future);
197 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
198 result.push_back(&get<T>(*i));
204 unsigned get_status(const std::string &name) const
206 ItemMap::const_iterator i = items.find(name);
209 if(CollectionItemTypeBase *type = get_type<T>())
211 for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j)
212 if((*j)->is_loadable(*type, name))
218 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
219 if(!i->second.check_type<RPNCT>())
226 /// Checks whether a typed object exists in the collection.
228 bool contains(const std::string &name) const
229 { return get_status<T>(name)==1; }
231 /** Checks whether a typed object exists in the collection or is loadable
234 bool contains(const std::string &name)
235 { return get_status<T>(name)>0; }
237 /// Returns the name of an item in the collection.
239 const std::string &get_name(T *d) const
241 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
243 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
244 if(i->second.check_type<RPNCT>())
245 if(i->second.value<RPNCT>().get()==d)
248 // XXX Need better exception class
249 throw std::runtime_error("Item not found in collection");
253 /** Adds a type to the collection. The returned descriptor object reference
254 can be used to define how objects of that type can be loaded. */
256 CollectionItemType<T> &add_type();
258 /** Returns the descriptor for a type, or null if one isn't defined. */
260 CollectionItemTypeBase *get_type() 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
292 virtual ~TagBase() { }
296 class Tag: public TagBase
300 std::vector<std::string> suffixes;
303 CollectionItemTypeBase();
305 virtual ~CollectionItemTypeBase();
307 void set_keyword(const std::string &);
308 const std::string &get_keyword() const { return kwd; }
309 void add_suffix(const std::string &);
310 bool match_name(const std::string &) const;
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 check_type() const
318 { return dynamic_cast<Tag<T> *>(tag); }
323 Describes a type of item that can be loaded by a Collection. These are created
324 by Collection::add_type.
327 class CollectionItemType: public CollectionItemTypeBase
335 virtual ~CreatorBase() { }
337 virtual T *create(Collection &, const std::string &) const = 0;
341 class Creator: public CreatorBase
344 typedef T *(C::*FuncPtr)(const std::string &);
350 Creator(FuncPtr f): func(f) { }
352 virtual T *create(Collection &coll, const std::string &name) const
353 { return (static_cast<C &>(coll).*func)(name); }
361 virtual ~StoreBase() { }
363 virtual void store(Collection &, const std::string &, T *) = 0;
365 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
369 class Store: public StoreBase
372 virtual void store(Collection &coll, const std::string &name, T *obj)
373 { coll.add(name, static_cast<S *>(obj)); }
375 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
376 { loader.add(kwd, &Collection::Loader::item<T, S>); }
383 CollectionItemType():
384 creat(0), store(new Store<T>)
385 { tag = new Tag<T>; }
387 ~CollectionItemType()
393 /** Sets a datafile keyword for this item type. The Collection's loader
394 will accept a statement with this keyword and a single string argument - the
396 CollectionItemType &keyword(const std::string &k)
402 /** Adds a suffix that is used to match names when looking for future
403 objects. There is no implied separator; a name matches if it ends with the
404 suffix. If a keyword is defined before any suffixes, then "."+keyword is
405 added as a suffix. */
406 CollectionItemType &suffix(const std::string &s)
412 /** Attaches a creator function to this item type. If an item is not found
413 in the Collection, the creator function for its type is called to create it.
414 The function must be a member of the Collection subclass containing the
415 type. It must return the created object, or null if it could not be
416 created. It's also permissible to load the item via other means and then
419 CollectionItemType &creator(T *(C::*func)(const std::string &))
422 creat = new Creator<C>(func);
426 /** Specifies the storage type for items of this type. It must be a base
427 class of the actual type. */
429 CollectionItemType &store_as()
434 store = new Store<S>;
438 virtual void add_to_loader(Collection::Loader &loader) const
439 { store->add_to_loader(loader, kwd); }
441 virtual bool can_create() const
444 virtual void create_item(Collection &coll, const std::string &name) const
447 throw std::runtime_error("no creator");
448 T *obj = creat->create(coll, name);
450 store->store(coll, name, obj);
453 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
455 RefPtr<T> obj = new T;
456 Collection::ItemLoader<T> ldr(*obj, coll);
458 store->store(coll, name, obj.get());
465 CollectionItemType<T> &Collection::add_type()
467 CollectionItemType<T> *type = new CollectionItemType<T>;
468 types.push_back(type);
473 CollectionItemTypeBase *Collection::get_type() const
475 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
476 if((*j)->check_type<typename RemoveConst<T>::Type>())
481 } // namespace DataFile