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 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
143 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
145 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
146 if(i->second.check_type<RPNCT>())
149 objects->push_back(i->second.value<RPNCT>().get());
151 names->push_back(i->first);
155 if(CollectionItemTypeBase *type = get_type<T>())
157 for(SourceList::const_iterator i=sources.begin(); i!=sources.end(); ++i)
159 std::list<std::string> available_names = (*i)->get_names(*type);
160 for(std::list<std::string>::iterator j=available_names.begin(); j!=available_names.end(); ++j)
162 future_names->push_back(*j);
168 /// Returns a list of the names of objects of one type in the collection.
170 std::list<std::string> get_names() const
172 std::list<std::string> result;
173 collect_items<T>(0, &result, 0);
177 /** Returns a list of the names of objects of one type in the collection or
178 available from sources. */
180 std::list<std::string> get_names()
182 std::list<std::string> result;
183 collect_items<T>(0, &result, &result);
187 /// Returns a list of objects of one type in the collection.
189 std::list<T *> get_list() const
191 std::list<T *> result;
192 collect_items<T>(&result, 0, 0);
196 /** Returns a list of objects of one type, loading them from sources if
199 std::list<T *> get_list()
201 std::list<T *> result;
202 std::list<std::string> future;
203 collect_items<T>(&result, 0, &future);
204 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
205 result.push_back(&get<T>(*i));
211 unsigned get_status(const std::string &name) const
213 ItemMap::const_iterator i = items.find(name);
216 if(CollectionItemTypeBase *type = get_type<T>())
218 for(SourceList::const_iterator j=sources.begin(); j!=sources.end(); ++j)
219 if((*j)->is_loadable(*type, name))
225 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
226 if(!i->second.check_type<RPNCT>())
233 /// Checks whether a typed object exists in the collection.
235 bool contains(const std::string &name) const
236 { return get_status<T>(name)==1; }
238 /** Checks whether a typed object exists in the collection or is loadable
241 bool contains(const std::string &name)
242 { return get_status<T>(name)>0; }
244 /// Returns the name of an item in the collection.
246 const std::string &get_name(T *d) const
248 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
250 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
251 if(i->second.check_type<RPNCT>())
252 if(i->second.value<RPNCT>().get()==d)
255 // XXX Need better exception class
256 throw std::runtime_error("Item not found in collection");
260 /** Adds a type to the collection. The returned descriptor object reference
261 can be used to define how objects of that type can be loaded. */
263 CollectionItemType<T> &add_type();
265 /// Returns the descriptor for a type, or null if one isn't defined.
267 CollectionItemTypeBase *get_type() const;
269 /// Returns the descriptor for an item, or null if it's of an unknown type.
270 CollectionItemTypeBase *get_type_for_item(const Variant &) const;
272 void add_source(CollectionSource &);
276 class Collection::ItemLoader<T, false>: public T::Loader
279 ItemLoader(T &o, Collection &):
285 class Collection::ItemLoader<T, true>: public T::Loader
288 ItemLoader(T &o, Collection &c):
289 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
294 class CollectionItemTypeBase
299 virtual ~ExtractorBase() { }
303 struct Extractor: ExtractorBase
305 virtual T &extract(const Variant &) const = 0;
309 std::vector<std::string> suffixes;
310 std::vector<ExtractorBase *> extractors;
312 CollectionItemTypeBase() { }
314 virtual ~CollectionItemTypeBase();
316 void set_keyword(const std::string &);
317 const std::string &get_keyword() const { return kwd; }
318 void add_suffix(const std::string &);
319 bool match_name(const std::string &) const;
320 virtual bool check_item_type(const Variant &) const = 0;
321 virtual void add_to_loader(Collection::Loader &) const = 0;
322 virtual bool can_create() const = 0;
323 virtual void create_item(Collection &, const std::string &) const = 0;
324 virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
327 bool can_extract() const
329 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
330 if(dynamic_cast<Extractor<T> *>(*i))
336 T *extract(const Variant &var) const
338 for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
339 if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
340 return &ex->extract(var);
347 Describes a type of item that can be loaded by a Collection. These are created
348 by Collection::add_type.
351 class CollectionItemType: public CollectionItemTypeBase
356 virtual ~CreatorBase() { }
358 virtual T *create(Collection &, const std::string &) const = 0;
362 struct Creator: CreatorBase
364 typedef T *(C::*FuncPtr)(const std::string &);
368 Creator(FuncPtr f): func(f) { }
370 virtual T *create(Collection &coll, const std::string &name) const
371 { return (static_cast<C &>(coll).*func)(name); }
375 struct Extractor: CollectionItemTypeBase::Extractor<B>
377 virtual B &extract(const Variant &var) const
378 { return *var.value<RefPtr<T> >(); }
384 CollectionItemType():
388 ~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 /** Makes items of this type available through a base class. */
428 CollectionItemType &base()
430 extractors.push_back(new Extractor<B>);
434 virtual bool check_item_type(const Variant &var) const
435 { return var.check_type<RefPtr<T> >(); }
437 virtual void add_to_loader(Collection::Loader &loader) const
438 { loader.add(kwd, &Collection::Loader::item<T, T>); }
440 virtual bool can_create() const
443 virtual void create_item(Collection &coll, const std::string &name) const
446 throw std::runtime_error("no creator");
447 T *obj = creat->create(coll, name);
452 virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
454 RefPtr<T> obj = new T;
455 Collection::ItemLoader<T> ldr(*obj, coll);
457 coll.add(name, obj.get());
464 T &Collection::extract(const Variant &var) const
466 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
468 if(!var.check_type<RPNCT>())
469 if(CollectionItemTypeBase *type = get_type_for_item(var))
470 if(T *item = type->extract<T>(var))
473 return *var.value<RPNCT>();
477 CollectionItemType<T> &Collection::add_type()
479 CollectionItemType<T> *type = new CollectionItemType<T>;
480 types.push_back(type);
485 CollectionItemTypeBase *Collection::get_type() const
487 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
488 if(dynamic_cast<CollectionItemType<typename RemoveConst<T>::Type> *>(*j))
490 for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
491 if((*j)->can_extract<T>())
496 } // namespace DataFile