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.
46 Other classes are available to provide refined ways of loading objects from
47 files. See DirectoryCollection and PackCollection.
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;
89 Collection(const Collection &);
90 Collection &operator=(const Collection &);
93 virtual ~Collection();
95 /** Adds an object into the collection. The name must not pre-exist. The
96 collection takes ownership of the object. */
98 void add(const std::string &name, T *item)
101 throw std::invalid_argument("Collection::add(item)");
103 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
105 ItemMap::iterator i = items.find(name);
108 if(i->second.check_type<RPNCT>())
110 // Replace a future object placeholder
111 RPNCT &ptr = i->second.value<RPNCT>();
119 throw key_error(typeid(ItemMap));
122 items.insert(ItemMap::value_type(name, RPNCT(item)));
126 /** Adds the name of a future object to the collection. The object itself
127 will be loaded on first access. The calling subclass should be prepared to
128 create the object on request. */
130 void add_future(const std::string &name)
132 RefPtr<typename RemoveConst<T>::Type> ptr(0);
133 insert_unique(items, name, ptr);
136 /** Adds the name of a future object, guessing its type. If a type matching
137 the name can't be found, nothing is done. */
138 void add_future(const std::string &name);
140 /** Adds the name of a future object, using a keyword to determine its type.
141 The keyword must be known to the collection. */
142 void add_future_with_keyword(const std::string &name, const std::string &);
145 /// Gets a typed object from the collection.
147 T &get(const std::string &name) const
149 typedef typename RemoveConst<T>::Type NCT;
151 T *ptr = get_item(items, name).value<RefPtr<NCT> >().get();
153 throw key_error(typeid(ItemMap));
157 /** Gets a typed object from the collection. If the name is not found in
158 and a creator for the item type is defined, it is invoked. */
160 T &get(const std::string &);
164 void collect_items(std::list<T *> *objects, std::list<std::string> *names, std::list<std::string> *future_names) const
166 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
168 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
169 if(i->second.check_type<RPNCT>())
171 T *ptr = i->second.value<RPNCT>().get();
175 objects->push_back(ptr);
177 names->push_back(i->first);
179 else if(future_names)
180 future_names->push_back(i->first);
185 /** Returns a list of the names of loaded objects of one type in the
188 std::list<std::string> get_names() const
190 std::list<std::string> result;
191 collect_items<T>(0, &result, 0);
195 /** Returns a list of the names of objects of one type in the collection,
196 including any future objects. */
198 std::list<std::string> get_names()
200 std::list<std::string> result;
201 collect_items<T>(0, &result, &result);
205 /// Returns a list of loaded objects of one type in the collection.
207 std::list<T *> get_list() const
209 std::list<T *> result;
210 collect_items<T>(&result, 0, 0);
214 /** Returns a list of objects of one type in the collection. Any future
215 objects of that type are loaded and returned in the list. */
217 std::list<T *> get_list()
219 std::list<T *> result;
220 std::list<std::string> future;
221 collect_items<T>(&result, 0, &future);
222 for(std::list<std::string>::iterator i=future.begin(); i!=future.end(); ++i)
223 result.push_back(&get<T>(*i));
229 unsigned get_status(const std::string &name) const
231 ItemMap::const_iterator i = items.find(name);
235 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
236 if(!i->second.check_type<RPNCT>())
239 T *ptr = i->second.value<RPNCT>().get();
244 /// Checks whether a typed object exists and is loaded in the collection.
246 bool contains(const std::string &name) const
247 { return get_status<T>(name)==1; }
249 /** Checks whether a typed object exists in the collection, as either a
250 loaded or future object. */
252 bool contains(const std::string &name)
253 { return get_status<T>(name)>0; }
255 /// Returns the name of an item in the collection.
257 const std::string &get_name(T *d) const
259 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
261 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
262 if(i->second.check_type<RPNCT>())
263 if(i->second.value<RPNCT>().get()==d)
266 // XXX Need better exception class
267 throw std::runtime_error("Item not found in collection");
271 /** Adds a type to the collection. The returned descriptor object reference
272 can be used to define how objects of that type can be loaded. */
274 CollectionItemType<T> &add_type();
278 class Collection::ItemLoader<T, false>: public T::Loader
281 ItemLoader(T &o, Collection &):
287 class Collection::ItemLoader<T, true>: public T::Loader
290 ItemLoader(T &o, Collection &c):
291 T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
296 class CollectionItemTypeBase
304 virtual ~TagBase() { }
308 class Tag: public TagBase
312 std::vector<std::string> suffixes;
315 CollectionItemTypeBase();
317 virtual ~CollectionItemTypeBase();
319 void set_keyword(const std::string &);
320 const std::string &get_keyword() const { return kwd; }
321 void add_suffix(const std::string &);
322 bool match_name(const std::string &) const;
323 virtual void add_to_loader(Collection::Loader &) const = 0;
324 virtual bool can_create() const = 0;
325 virtual void create_item(Collection &, const std::string &) const = 0;
326 virtual Variant create_future() const = 0;
329 bool check_type() const
330 { return dynamic_cast<Tag<T> *>(tag); }
335 Describes a type of item that can be loaded by a Collection. These are created
336 by Collection::add_type.
339 class CollectionItemType: public CollectionItemTypeBase
347 virtual ~CreatorBase() { }
349 virtual T *create(Collection &, const std::string &) const = 0;
353 class Creator: public CreatorBase
356 typedef T *(C::*FuncPtr)(const std::string &);
362 Creator(FuncPtr f): func(f) { }
364 virtual T *create(Collection &coll, const std::string &name) const
365 { return (static_cast<C &>(coll).*func)(name); }
373 virtual ~StoreBase() { }
375 virtual void store(Collection &, const std::string &, T *) = 0;
376 virtual Variant create_future() const = 0;
378 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
382 class Store: public StoreBase
385 virtual void store(Collection &coll, const std::string &name, T *obj)
386 { coll.add(name, static_cast<S *>(obj)); }
388 virtual Variant create_future() const
389 { return RefPtr<S>(0); }
391 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
392 { loader.add(kwd, &Collection::Loader::item<T, S>); }
399 CollectionItemType():
400 creat(0), store(new Store<T>)
401 { tag = new Tag<T>; }
403 ~CollectionItemType()
409 /** Sets a datafile keyword for this item type. The Collection's loader
410 will accept a statement with this keyword and a single string argument - the
412 CollectionItemType &keyword(const std::string &k)
418 /** Adds a suffix that is used to match names when looking for future
419 objects. There is no implied separator; a name matches if it ends with the
420 suffix. If a keyword is defined before any suffixes, then "."+keyword is
421 added as a suffix. */
422 CollectionItemType &suffix(const std::string &s)
428 /** Attaches a creator function to this item type. If an item is not found
429 in the Collection, the creator function for its type is called to create it.
430 The function must be a member of the Collection subclass containing the
431 type. It must return the created object, or null if it could not be
432 created. It's also permissible to load the item via other means and then
435 CollectionItemType &creator(T *(C::*func)(const std::string &))
438 creat = new Creator<C>(func);
442 /** Specifies the storage type for items of this type. It must be a base
443 class of the actual type. */
445 CollectionItemType &store_as()
450 store = new Store<S>;
454 virtual void add_to_loader(Collection::Loader &loader) const
455 { store->add_to_loader(loader, kwd); }
457 virtual bool can_create() const
460 virtual void create_item(Collection &coll, const std::string &name) const
463 throw std::runtime_error("no creator");
464 T *obj = creat->create(coll, name);
466 store->store(coll, name, obj);
469 virtual Variant create_future() const
470 { return store->create_future(); }
475 T &Collection::get(const std::string &name)
477 typedef typename RemoveConst<T>::Type NCT;
479 ItemMap::iterator i = items.find(name);
482 NCT *ptr = i->second.value<RefPtr<NCT> >().get();
487 for(TypeList::iterator j=types.begin(); j!=types.end(); ++j)
488 if((*j)->can_create() && (*j)->check_type<NCT>())
489 (*j)->create_item(*this, name);
491 NCT *ptr = get_item(items, name).value<RefPtr<NCT> >().get();
493 throw key_error(typeid(ItemMap));
498 CollectionItemType<T> &Collection::add_type()
500 CollectionItemType<T> *type = new CollectionItemType<T>;
501 types.push_back(type);
505 } // namespace DataFile