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 /** Adds the name of a future object, guessing its type. If a type matching
134 the name can't be found, nothing is done. */
135 void add_future(const std::string &name);
137 /** Adds the name of a future object, using a keyword to determine its type.
138 The keyword must be known to the collection. */
139 void add_future_with_keyword(const std::string &name, const std::string &);
142 /// Gets a typed object from the collection.
144 T &get(const std::string &name) const
146 typedef typename RemoveConst<T>::Type NCT;
148 T *ptr = get_item(items, name).value<RefPtr<NCT> >().get();
150 throw key_error(typeid(ItemMap));
154 /** Gets a typed object from the collection. If the name is not found in
155 and a creator for the item type is defined, it is invoked. */
157 T &get(const std::string &);
161 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
163 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
165 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
166 if(i->second.check_type<RPNCT>())
168 T *ptr = i->second.value<RPNCT>().get();
172 objects->push_back(ptr);
174 names->push_back(i->first);
176 else if(future_names)
177 future_names->push_back(i->first);
182 /** Returns a list of the names of loaded objects of one type in the
185 std::list<std::string> get_names() const
187 std::list<std::string> result;
188 collect_items<T>(0, &result, 0);
192 /** Returns a list of the names of objects of one type in the collection,
193 including any future objects. */
195 std::list<std::string> get_names()
197 std::list<std::string> result;
198 collect_items<T>(0, &result, &result);
202 /// Returns a list of loaded objects of one type in the collection.
204 std::list<T *> get_list() const
206 std::list<T *> result;
207 collect_items<T>(&result, 0, 0);
211 /** Returns a list of objects of one type in the collection. Any future
212 objects of that type are loaded and returned in the list. */
214 std::list<T *> get_list()
216 std::list<T *> result;
217 std::list<std::string> future;
218 collect_items<T>(&result, 0, &future);
219 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
220 result.push_back(&get<T>(*i));
226 unsigned get_status(const std::string &name) const
228 ItemMap::const_iterator i = items.find(name);
232 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
233 if(!i->second.check_type<RPNCT>())
236 T *ptr = i->second.value<RPNCT>().get();
241 /// Checks whether a typed object exists and is loaded in the collection.
243 bool contains(const std::string &name) const
244 { return get_status<T>(name)==1; }
246 /** Checks whether a typed object exists in the collection, as either a
247 loaded or future object. */
249 bool contains(const std::string &name)
250 { return get_status<T>(name)>0; }
252 /// Returns the name of an item in the collection.
254 const std::string &get_name(T *d) const
256 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
258 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
259 if(i->second.check_type<RPNCT>())
260 if(i->second.value<RPNCT>().get()==d)
263 // XXX Need better exception class
264 throw std::runtime_error("Item not found in collection");
268 /** Adds a type to the collection. The returned descriptor object reference
269 can be used to define how objects of that type can be loaded. */
271 CollectionItemType<T> &add_type();
275 class Collection::ItemLoader<T, false>: public T::Loader
278 ItemLoader(T &o, Collection &):
284 class Collection::ItemLoader<T, true>: public T::Loader
287 ItemLoader(T &o, Collection &c):
288 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
293 class CollectionItemTypeBase
301 virtual ~TagBase() { }
305 class Tag: public TagBase
309 std::vector<std::string> suffixes;
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 void add_to_loader(Collection::Loader &) const = 0;
321 virtual bool can_create() const = 0;
322 virtual void create_item(Collection &, const std::string &) const = 0;
323 virtual Variant create_future() const = 0;
326 bool check_type() const
327 { return dynamic_cast<Tag<T> *>(tag); }
332 Describes a type of item that can be loaded by a Collection. These are created
333 by Collection::add_type.
336 class CollectionItemType: public CollectionItemTypeBase
344 virtual ~CreatorBase() { }
346 virtual T *create(Collection &, const std::string &) const = 0;
350 class Creator: public CreatorBase
353 typedef T *(C::*FuncPtr)(const std::string &);
359 Creator(FuncPtr f): func(f) { }
361 virtual T *create(Collection &coll, const std::string &name) const
362 { return (static_cast<C &>(coll).*func)(name); }
370 virtual ~StoreBase() { }
372 virtual void store(Collection &, const std::string &, T *) = 0;
373 virtual Variant create_future() const = 0;
375 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
379 class Store: public StoreBase
382 virtual void store(Collection &coll, const std::string &name, T *obj)
383 { coll.add(name, static_cast<S *>(obj)); }
385 virtual Variant create_future() const
386 { return RefPtr<S>(0); }
388 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
389 { loader.add(kwd, &Collection::Loader::item<T, S>); }
396 CollectionItemType():
397 creat(0), store(new Store<T>)
398 { tag = new Tag<T>; }
400 ~CollectionItemType()
406 /** Sets a datafile keyword for this item type. The Collection's loader
407 will accept a statement with this keyword and a single string argument - the
409 CollectionItemType &keyword(const std::string &k)
415 /** Adds a suffix that is used to match names when looking for future
416 objects. There is no implied separator; a name matches if it ends with the
417 suffix. If a keyword is defined before any suffixes, then "."+keyword is
418 added as a suffix. */
419 CollectionItemType &suffix(const std::string &s)
425 /** Attaches a creator function to this item type. If an item is not found
426 in the Collection, the creator function for its type is called to create it.
427 The function must be a member of the Collection subclass containing the
428 type. It must return the created object, or null if it could not be
429 created. It's also permissible to load the item via other means and then
432 CollectionItemType &creator(T *(C::*func)(const std::string &))
435 creat = new Creator<C>(func);
439 /** Specifies the storage type for items of this type. It must be a base
440 class of the actual type. */
442 CollectionItemType &store_as()
447 store = new Store<S>;
451 virtual void add_to_loader(Collection::Loader &loader) const
452 { store->add_to_loader(loader, kwd); }
454 virtual bool can_create() const
457 virtual void create_item(Collection &coll, const std::string &name) const
460 throw std::runtime_error("no creator");
461 T *obj = creat->create(coll, name);
463 store->store(coll, name, obj);
466 virtual Variant create_future() const
467 { return store->create_future(); }
472 T &Collection::get(const std::string &name)
474 typedef typename RemoveConst<T>::Type NCT;
476 ItemMap::iterator i = items.find(name);
479 NCT *ptr = i->second.value<RefPtr<NCT> >().get();
484 for(TypeList::iterator j=types.begin(); j!=types.end(); ++j)
485 if((*j)->can_create() && (*j)->check_type<NCT>())
486 (*j)->create_item(*this, name);
488 NCT *ptr = get_item(items, name).value<RefPtr<NCT> >().get();
490 throw key_error(typeid(ItemMap));
495 CollectionItemType<T> &Collection::add_type()
497 CollectionItemType<T> *type = new CollectionItemType<T>;
498 types.push_back(type);
502 } // namespace DataFile