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>
13 Helper struct to determine whether a Loader has a Collection typedef.
16 struct NeedsCollection
18 struct Yes { char c[2]; };
19 struct No { char c; };
22 static Yes f(typename U::Collection *);
26 enum { value = (sizeof(f<T>(0))==sizeof(Yes)) };
29 class CollectionItemTypeBase;
32 class CollectionItemType;
35 A collection of objects that can be loaded from a datafile. Each object is
36 identified by a name, which must be unique across the entire collection.
38 While this class can be instantiated by itself and used for storing objects,
39 loading requires that a subclass defines the supported types. See the add_type
42 Collections also support a notion of "future objects". These are objects which
43 are known to be possible to load, but loading them is deferred to the first
44 time they are requested.
50 Loads objects into a Collection. Automatically picks up keywords from
53 class Loader: public DataFile::Loader
55 template<typename T> friend class CollectionItemType;
62 Collection &get_object() const { return coll; }
64 template<typename T, typename S>
65 void item(const std::string &n)
68 ItemLoader<T> ldr(*it, coll);
70 coll.add<S>(n, it.get());
76 template<typename T, bool = NeedsCollection<typename T::Loader>::value>
80 typedef std::map<std::string, Variant> ItemMap;
81 typedef std::list<CollectionItemTypeBase *> TypeList;
86 Collection(const Collection &);
87 Collection &operator=(const Collection &);
90 virtual ~Collection();
92 /** Adds an object into the collection. The name must not pre-exist. The
93 collection takes ownership of the object. */
95 void add(const std::string &name, T *item)
98 throw std::invalid_argument("Collection::add(item)");
100 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
102 ItemMap::iterator i = items.find(name);
105 if(i->second.check_type<RPNCT>())
107 // Replace a future object placeholder
108 RPNCT &ptr = i->second.value<RPNCT>();
116 throw key_error(typeid(ItemMap));
119 items.insert(ItemMap::value_type(name, RPNCT(item)));
123 /** Adds the name of a future object to the collection. The object itself
124 will be loaded on first access. The calling subclass should be prepared to
125 create the object on request. */
127 void add_future(const std::string &name)
129 RefPtr<typename RemoveConst<T>::Type> ptr(0);
130 insert_unique(items, name, ptr);
133 void add_future(const std::string &name);
136 /// Gets a typed object from the collection.
138 T &get(const std::string &name) const
140 typedef typename RemoveConst<T>::Type NCT;
142 T *ptr = get_item(items, name).value<RefPtr<NCT> >().get();
144 throw key_error(typeid(ItemMap));
148 /** Gets a typed object from the collection. If the name is not found in
149 and a creator for the item type is defined, it is invoked. */
151 T &get(const std::string &);
155 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
157 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
159 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
160 if(i->second.check_type<RPNCT>())
162 T *ptr = i->second.value<RPNCT>().get();
166 objects->push_back(ptr);
168 names->push_back(i->first);
170 else if(future_names)
171 future_names->push_back(i->first);
176 /** Returns a list of the names of loaded objects of one type in the
179 std::list<std::string> get_names() const
181 std::list<std::string> result;
182 collect_items<T>(0, &result, 0);
186 /** Returns a list of the names of objects of one type in the collection,
187 including any future objects. */
189 std::list<std::string> get_names()
191 std::list<std::string> result;
192 collect_items<T>(0, &result, &result);
196 /// Returns a list of loaded objects of one type in the collection.
198 std::list<T *> get_list() const
200 std::list<T *> result;
201 collect_items<T>(&result, 0, 0);
205 /** Returns a list of objects of one type in the collection. Any future
206 objects of that type are loaded and returned in the list. */
208 std::list<T *> get_list()
210 std::list<T *> result;
211 std::list<std::string> future;
212 collect_items<T>(&result, 0, &future);
213 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
214 result.push_back(&get<T>(*i));
220 unsigned get_status(const std::string &name) const
222 ItemMap::const_iterator i = items.find(name);
226 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
227 if(!i->second.check_type<RPNCT>())
230 T *ptr = i->second.value<RPNCT>().get();
235 /// Checks whether a typed object exists and is loaded in the collection.
237 bool contains(const std::string &name) const
238 { return get_status<T>(name)==1; }
240 /** Checks whether a typed object exists in the collection, as either a
241 loaded or future object. */
243 bool contains(const std::string &name)
244 { return get_status<T>(name)>0; }
246 /// Returns the name of an item in the collection.
248 const std::string &get_name(T *d) const
250 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
252 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
253 if(i->second.check_type<RPNCT>())
254 if(i->second.value<RPNCT>().get()==d)
257 // XXX Need better exception class
258 throw std::runtime_error("Item not found in collection");
262 /** Adds a type to the collection. The returned descriptor object reference
263 can be used to define how objects of that type can be loaded. */
265 CollectionItemType<T> &add_type();
269 class Collection::ItemLoader<T, false>: public T::Loader
272 ItemLoader(T &o, Collection &):
278 class Collection::ItemLoader<T, true>: public T::Loader
281 ItemLoader(T &o, Collection &c):
282 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
287 class CollectionItemTypeBase
295 virtual ~TagBase() { }
299 class Tag: public TagBase
303 std::vector<std::string> suffixes;
306 CollectionItemTypeBase();
308 virtual ~CollectionItemTypeBase();
310 void set_keyword(const std::string &);
311 void add_suffix(const std::string &);
312 virtual void add_to_loader(Collection::Loader &) const = 0;
313 virtual bool can_create() const = 0;
314 virtual void create_item(Collection &, const std::string &) const = 0;
315 bool match_name(const std::string &) const;
316 virtual Variant create_future() const = 0;
319 bool check_type() const
320 { return dynamic_cast<Tag<T> *>(tag); }
325 Describes a type of item that can be loaded by a Collection. These are created
326 by Collection::add_type.
329 class CollectionItemType: public CollectionItemTypeBase
337 virtual ~CreatorBase() { }
339 virtual T *create(Collection &, const std::string &) const = 0;
343 class Creator: public CreatorBase
346 typedef T *(C::*FuncPtr)(const std::string &);
352 Creator(FuncPtr f): func(f) { }
354 virtual T *create(Collection &coll, const std::string &name) const
355 { return (static_cast<C &>(coll).*func)(name); }
363 virtual ~StoreBase() { }
365 virtual void store(Collection &, const std::string &, T *) = 0;
366 virtual Variant create_future() const = 0;
368 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
372 class Store: public StoreBase
375 virtual void store(Collection &coll, const std::string &name, T *obj)
376 { coll.add(name, static_cast<S *>(obj)); }
378 virtual Variant create_future() const
379 { return RefPtr<S>(0); }
381 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
382 { loader.add(kwd, &Collection::Loader::item<T, S>); }
389 CollectionItemType():
390 creat(0), store(new Store<T>)
391 { tag = new Tag<T>; }
393 ~CollectionItemType()
399 /** Sets a datafile keyword for this item type. The Collection's loader
400 will accept a statement with this keyword and a single string argument - the
402 CollectionItemType &keyword(const std::string &k)
408 /** Adds a suffix that is used to match names when looking for future
409 objects. There is no implied separator; a name matches if it ends with the
410 suffix. If a keyword is defined before any suffixes, then "."+keyword is
411 added as a suffix. */
412 CollectionItemType &suffix(const std::string &s)
418 /** Attaches a creator function to this item type. If an item is not found
419 in the Collection, the creator function for its type is called to create it.
420 The function must be a member of the Collection subclass containing the
421 type. It must return the created object, or null if it could not be
422 created. It's also permissible to load the item via other means and then
425 CollectionItemType &creator(T *(C::*func)(const std::string &))
428 creat = new Creator<C>(func);
432 /** Specifies the storage type for items of this type. It must be a base
433 class of the actual type. */
435 CollectionItemType &store_as()
440 store = new Store<S>;
444 virtual void add_to_loader(Collection::Loader &loader) const
445 { store->add_to_loader(loader, kwd); }
447 virtual bool can_create() const
450 virtual void create_item(Collection &coll, const std::string &name) const
453 throw std::runtime_error("no creator");
454 T *obj = creat->create(coll, name);
456 store->store(coll, name, obj);
459 virtual Variant create_future() const
460 { return store->create_future(); }
465 T &Collection::get(const std::string &name)
467 typedef typename RemoveConst<T>::Type NCT;
469 ItemMap::iterator i = items.find(name);
472 NCT *ptr = i->second.value<RefPtr<NCT> >().get();
477 for(TypeList::iterator j=types.begin(); j!=types.end(); ++j)
478 if((*j)->can_create() && (*j)->check_type<NCT>())
479 (*j)->create_item(*this, name);
481 return *get_item(items, name).value<RefPtr<NCT> >();
485 CollectionItemType<T> &Collection::add_type()
487 CollectionItemType<T> *type = new CollectionItemType<T>;
488 types.push_back(type);
492 } // namespace DataFile