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;
58 template<typename T, typename S, bool = NeedsCollection<typename T::Loader>::value>
65 Collection &get_object() const { return coll; }
67 template<typename T, typename S, typename C>
68 void coll_item(const std::string &n)
71 load_sub(*it, dynamic_cast<C &>(coll));
72 coll.add<S>(n, it.get());
76 template<typename T, typename S>
77 void item(const std::string &n)
81 coll.add<S>(n, it.get());
87 typedef std::map<std::string, Variant> ItemMap;
88 typedef std::list<CollectionItemTypeBase *> TypeList;
93 Collection(const Collection &);
94 Collection &operator=(const Collection &);
97 virtual ~Collection();
99 /** Adds an object into the collection. The name must not pre-exist. The
100 collection takes ownership of the object. */
102 void add(const std::string &name, T *item)
105 throw std::invalid_argument("Collection::add(item)");
107 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
109 ItemMap::iterator i = items.find(name);
112 if(i->second.check_type<RPNCT>())
114 // Replace a future object placeholder
115 RPNCT &ptr = i->second.value<RPNCT>();
123 throw key_error(typeid(ItemMap));
126 items.insert(ItemMap::value_type(name, RPNCT(item)));
130 /** Adds the name of a future object to the collection. The object itself
131 will be loaded on first access. The calling subclass should be prepared to
132 create the object on request. */
134 void add_future(const std::string &name)
136 RefPtr<typename RemoveConst<T>::Type> ptr(0);
137 insert_unique(items, name, ptr);
140 void add_future(const std::string &name);
143 /// Gets a typed object from the collection.
145 T &get(const std::string &name) const
147 typedef typename RemoveConst<T>::Type NCT;
149 T *ptr = get_item(items, name).value<RefPtr<NCT> >();
151 throw key_error(typeid(ItemMap));
155 /** Gets a typed object from the collection. If the name is not found in
156 and a creator for the item type is defined, it is invoked. */
158 T &get(const std::string &);
162 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names)
164 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
166 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
167 if(i->second.check_type<RPNCT>())
169 T *ptr = i->second.value<RPNCT>().get();
173 objects->push_back(ptr);
175 names->push_back(i->first);
177 else if(future_names)
178 future_names->push_back(i->first);
183 /** Returns a list of the names of loaded objects of one type in the
186 std::list<std::string> get_names() const
188 std::list<std::string> result;
189 collect_items<T>(0, &result, 0);
193 /** Returns a list of the names of objects of one type in the collection,
194 including any future objects. */
196 std::list<std::string> get_names()
198 std::list<std::string> result;
199 collect_items<T>(0, &result, &result);
203 /// Returns a list of loaded objects of one type in the collection.
205 std::list<T *> get_list() const
207 std::list<T *> result;
208 collect_items<T>(&result, 0, 0);
212 /** Returns a list of objects of one type in the collection. Any future
213 objects of that type are loaded and returned in the list. */
215 std::list<T *> get_list()
217 std::list<T *> result;
218 std::list<std::string> future;
219 collect_items<T>(&result, 0, &future);
220 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
221 result.push_back(&get<T>(*i));
227 unsigned get_status(const std::string &name) const
229 ItemMap::const_iterator i = items.find(name);
233 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
234 if(!i->second.check_type<RPNCT>())
237 T *ptr = i->second.value<RPNCT>().get();
242 /// Checks whether a typed object exists and is loaded in the collection.
244 bool contains(const std::string &name) const
245 { return get_status<T>(name)==1; }
247 /** Checks whether a typed object exists in the collection, as either a
248 loaded or future object. */
250 bool contains(const std::string &name)
251 { return get_status<T>(name)>0; }
253 /// Returns the name of an item in the collection.
255 const std::string &get_name(T *d) const
257 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
259 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
260 if(i->second.check_type<RPNCT>())
261 if(i->second.value<RPNCT>().get()==d)
264 // XXX Need better exception class
265 throw std::runtime_error("Item not found in collection");
269 /** Adds a type to the collection. The returned descriptor object reference
270 can be used to define how objects of that type can be loaded. */
272 CollectionItemType<T> &add_type();
276 template<typename T, typename S>
277 struct Collection::Loader::Add<T, S, false>
279 static void add(Loader &loader, const std::string &kwd)
280 { loader.add(kwd, &Loader::item<T, S>); }
283 template<typename T, typename S>
284 struct Collection::Loader::Add<T, S, true>
286 static void add(Loader &loader, const std::string &kwd)
287 { loader.add(kwd, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
291 class CollectionItemTypeBase
299 virtual ~TagBase() { }
303 class Tag: public TagBase
307 std::vector<std::string> suffixes;
310 CollectionItemTypeBase();
312 virtual ~CollectionItemTypeBase();
314 void set_keyword(const std::string &);
315 void add_suffix(const std::string &);
316 virtual void add_to_loader(Collection::Loader &) const = 0;
317 virtual bool can_create() const = 0;
318 virtual void create_item(Collection &, const std::string &) const = 0;
319 bool match_name(const std::string &) const;
320 virtual Variant create_future() const = 0;
323 bool check_type() const
324 { return dynamic_cast<Tag<T> *>(tag); }
329 Describes a type of item that can be loaded by a Collection. These are created
330 by Collection::add_type.
333 class CollectionItemType: public CollectionItemTypeBase
341 virtual ~CreatorBase() { }
343 virtual T *create(Collection &, const std::string &) const = 0;
347 class Creator: public CreatorBase
350 typedef T *(C::*FuncPtr)(const std::string &);
356 Creator(FuncPtr f): func(f) { }
358 virtual T *create(Collection &coll, const std::string &name) const
359 { return (static_cast<C &>(coll).*func)(name); }
367 virtual ~StoreBase() { }
369 virtual void store(Collection &, const std::string &, T *) = 0;
370 virtual Variant create_future() const = 0;
372 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
376 class Store: public StoreBase
379 virtual void store(Collection &coll, const std::string &name, T *obj)
380 { coll.add(name, static_cast<S *>(obj)); }
382 virtual Variant create_future() const
383 { return RefPtr<S>(0); }
385 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
386 { Collection::Loader::Add<T, S>::add(loader, kwd); }
393 CollectionItemType():
394 creat(0), store(new Store<T>)
395 { tag = new Tag<T>; }
397 ~CollectionItemType()
403 /** Sets a datafile keyword for this item type. The Collection's loader
404 will accept a statement with this keyword and a single string argument - the
406 CollectionItemType &keyword(const std::string &k)
412 /** Adds a suffix that is used to match names when looking for future
413 objects. There is no implied separator; a name matches if it ends with the
414 suffix. If a keyword is defined before any suffixes, then "."+keyword is
415 added as a suffix. */
416 CollectionItemType &suffix(const std::string &s)
422 /** Attaches a creator function to this item type. If an item is not found
423 in the Collection, the creator function for its type is called to create it.
424 The function must be a member of the Collection subclass containing the
425 type. It must return the created object, or null if it could not be
426 created. It's also permissible to load the item via other means and then
429 CollectionItemType &creator(T *(C::*func)(const std::string &))
432 creat = new Creator<C>(func);
436 /** Specifies the storage type for items of this type. It must be a base
437 class of the actual type. */
439 CollectionItemType &store_as()
444 store = new Store<S>;
448 virtual void add_to_loader(Collection::Loader &loader) const
449 { store->add_to_loader(loader, kwd); }
451 virtual bool can_create() const
454 virtual void create_item(Collection &coll, const std::string &name) const
457 throw std::runtime_error("no creator");
458 T *obj = creat->create(coll, name);
460 store->store(coll, name, obj);
463 virtual Variant create_future() const
464 { return store->create_future(); }
469 T &Collection::get(const std::string &name)
471 typedef typename RemoveConst<T>::Type NCT;
473 ItemMap::iterator i = items.find(name);
476 NCT *ptr = i->second.value<RefPtr<NCT> >().get();
481 for(TypeList::iterator j=types.begin(); j!=types.end(); ++j)
482 if((*j)->can_create() && (*j)->check_type<NCT>())
483 (*j)->create_item(*this, name);
485 return *get_item(items, name).value<RefPtr<NCT> >();
489 CollectionItemType<T> &Collection::add_type()
491 CollectionItemType<T> *type = new CollectionItemType<T>;
492 types.push_back(type);
496 } // namespace DataFile