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
289 virtual ~TagBase() { }
297 std::vector<std::string> suffixes;
300 CollectionItemTypeBase();
302 virtual ~CollectionItemTypeBase();
304 void set_keyword(const std::string &);
305 const std::string &get_keyword() const { return kwd; }
306 void add_suffix(const std::string &);
307 bool match_name(const std::string &) const;
308 virtual void add_to_loader(Collection::Loader &) const = 0;
309 virtual bool can_create() const = 0;
310 virtual void create_item(Collection &, const std::string &) const = 0;
311 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
314 bool check_type() const
315 { return dynamic_cast<Tag<T> *>(tag); }
320 Describes a type of item that can be loaded by a Collection. These are created
321 by Collection::add_type.
324 class CollectionItemType: public CollectionItemTypeBase
329 virtual ~CreatorBase() { }
331 virtual T *create(Collection &, const std::string &) const = 0;
335 struct Creator: CreatorBase
337 typedef T *(C::*FuncPtr)(const std::string &);
341 Creator(FuncPtr f): func(f) { }
343 virtual T *create(Collection &coll, const std::string &name) const
344 { return (static_cast<C &>(coll).*func)(name); }
349 virtual ~StoreBase() { }
351 virtual void store(Collection &, const std::string &, T *) = 0;
353 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
357 struct Store: StoreBase
359 virtual void store(Collection &coll, const std::string &name, T *obj)
360 { coll.add(name, static_cast<S *>(obj)); }
362 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
363 { loader.add(kwd, &Collection::Loader::item<T, S>); }
370 CollectionItemType():
371 creat(0), store(new Store<T>)
372 { tag = new Tag<T>; }
374 ~CollectionItemType()
380 /** Sets a datafile keyword for this item type. The Collection's loader
381 will accept a statement with this keyword and a single string argument - the
383 CollectionItemType &keyword(const std::string &k)
389 /** Adds a suffix that is used to match names when looking for future
390 objects. There is no implied separator; a name matches if it ends with the
391 suffix. If a keyword is defined before any suffixes, then "."+keyword is
392 added as a suffix. */
393 CollectionItemType &suffix(const std::string &s)
399 /** Attaches a creator function to this item type. If an item is not found
400 in the Collection, the creator function for its type is called to create it.
401 The function must be a member of the Collection subclass containing the
402 type. It must return the created object, or null if it could not be
403 created. It's also permissible to load the item via other means and then
406 CollectionItemType &creator(T *(C::*func)(const std::string &))
409 creat = new Creator<C>(func);
413 /** Specifies the storage type for items of this type. It must be a base
414 class of the actual type. */
416 CollectionItemType &store_as()
421 store = new Store<S>;
425 virtual void add_to_loader(Collection::Loader &loader) const
426 { store->add_to_loader(loader, kwd); }
428 virtual bool can_create() const
431 virtual void create_item(Collection &coll, const std::string &name) const
434 throw std::runtime_error("no creator");
435 T *obj = creat->create(coll, name);
437 store->store(coll, name, obj);
440 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
442 RefPtr<T> obj = new T;
443 Collection::ItemLoader<T> ldr(*obj, coll);
445 store->store(coll, name, obj.get());
452 CollectionItemType<T> &Collection::add_type()
454 CollectionItemType<T> *type = new CollectionItemType<T>;
455 types.push_back(type);
460 CollectionItemTypeBase *Collection::get_type() const
462 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
463 if((*j)->check_type<typename RemoveConst<T>::Type>())
468 } // namespace DataFile