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
46 Loads objects into a Collection. Automatically picks up keywords from
49 class Loader: public DataFile::Loader
51 template<typename T> friend class CollectionItemType;
54 template<typename T, typename S, bool = NeedsCollection<typename T::Loader>::value>
61 Collection &get_object() const { return coll; }
63 template<typename T, typename S, typename C>
64 void coll_item(const std::string &n)
67 load_sub(*it, dynamic_cast<C &>(coll));
68 coll.add<S>(n, it.get());
72 template<typename T, typename S>
73 void item(const std::string &n)
77 coll.add<S>(n, it.get());
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 RefPtr<typename RemoveConst<T>::Type> ptr(item);
106 insert_unique(items, name, ptr);
110 // Avoid deleting the object
116 /// Gets a typed object from the collection.
118 T &get(const std::string &name) const
120 typedef typename RemoveConst<T>::Type NCT;
122 return *get_item(items, name).value<RefPtr<NCT> >();
125 /** Gets a typed object from the collection. If the name is not found in
126 and a creator for the item type is defined, it is invoked. */
128 T &get(const std::string &);
130 /// Returns a list of the names of objects of one type in the collection.
132 std::list<std::string> get_names() const
134 std::list<std::string> result;
135 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
136 if(i->second.check_type<RefPtr<typename RemoveConst<T>::Type> >())
137 result.push_back(i->first);
141 /// Returns a list of objects of one type in the collection.
143 std::list<T *> get_list() const
145 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
147 std::list<T *> result;
148 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
149 if(i->second.check_type<RPNCT>())
150 result.push_back(i->second.value<RPNCT>().get());
154 /** Checks whether a name exists in the collection. Does not care about the
155 type of the object. */
156 bool contains(const std::string &n) const;
158 /// Returns the name of an item in the collection.
160 const std::string &get_name(T *d) const
162 typedef RefPtr<typename RemoveConst<T>::Type> RPNCT;
164 for(ItemMap::const_iterator i=items.begin(); i!=items.end(); ++i)
165 if(i->second.check_type<RPNCT>())
166 if(i->second.value<RPNCT>().get()==d)
169 // XXX Need better exception class
170 throw std::runtime_error("Item not found in collection");
174 /** Adds a type to the collection. The returned descriptor object reference
175 can be used to define how objects of that type can be loaded. */
177 CollectionItemType<T> &add_type();
181 template<typename T, typename S>
182 struct Collection::Loader::Add<T, S, false>
184 static void add(Loader &loader, const std::string &kwd)
185 { loader.add(kwd, &Loader::item<T, S>); }
188 template<typename T, typename S>
189 struct Collection::Loader::Add<T, S, true>
191 static void add(Loader &loader, const std::string &kwd)
192 { loader.add(kwd, &Loader::coll_item<T, S, typename T::Loader::Collection>); }
196 class CollectionItemTypeBase
204 virtual ~TagBase() { }
208 class Tag: public TagBase
217 CollectionItemTypeBase(): tag(0) { }
219 virtual ~CollectionItemTypeBase()
222 virtual void add_to_loader(Collection::Loader &) const = 0;
223 virtual bool can_create() const = 0;
224 virtual void create_item(Collection &, const std::string &) const = 0;
227 bool check_type() const
228 { return dynamic_cast<Tag<T> *>(tag); }
233 Describes a type of item that can be loaded by a Collection. These are created
234 by Collection::add_type.
237 class CollectionItemType: public CollectionItemTypeBase
245 virtual ~CreatorBase() { }
247 virtual T *create(Collection &, const std::string &) const = 0;
251 class Creator: public CreatorBase
254 typedef T *(C::*FuncPtr)(const std::string &);
260 Creator(FuncPtr f): func(f) { }
262 virtual T *create(Collection &coll, const std::string &name) const
263 { return (static_cast<C &>(coll).*func)(name); }
271 virtual ~StoreBase() { }
273 virtual void store(Collection &, const std::string &, T *) = 0;
275 virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
279 class Store: public StoreBase
282 virtual void store(Collection &coll, const std::string &name, T *obj)
283 { coll.add(name, static_cast<S *>(obj)); }
285 virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
286 { Collection::Loader::Add<T, S>::add(loader, kwd); }
293 CollectionItemType():
294 creat(0), store(new Store<T>)
295 { tag = new Tag<T>; }
297 ~CollectionItemType()
303 /** Sets a datafile keyword for this item type. The Collection's loader
304 will accept a statement with this keyword and a single string argument - the
306 CollectionItemType &keyword(const std::string &k)
312 /** Attaches a creator function to this item type. If an item is not found
313 in the Collection, the creator function for its type is called to create it.
314 The function must be a member of the Collection subclass containing the
315 type. It must return the created object, or null if it could not be
316 created. It's also permissible to load the item via other means and then
319 CollectionItemType &creator(T *(C::*func)(const std::string &))
322 creat = new Creator<C>(func);
326 /** Specifies the storage type for items of this type. It must be a base
327 class of the actual type. */
329 CollectionItemType &store_as()
334 store = new Store<S>;
338 virtual void add_to_loader(Collection::Loader &loader) const
339 { store->add_to_loader(loader, kwd); }
341 virtual bool can_create() const
344 virtual void create_item(Collection &coll, const std::string &name) const
347 throw std::runtime_error("no creator");
348 T *obj = creat->create(coll, name);
350 store->store(coll, name, obj);
356 T &Collection::get(const std::string &name)
358 typedef typename RemoveConst<T>::Type NCT;
360 if(!items.count(name))
362 for(TypeList::iterator i=types.begin(); i!=types.end(); ++i)
363 if((*i)->can_create() && (*i)->check_type<NCT>())
364 (*i)->create_item(*this, name);
367 return *get_item(items, name).value<RefPtr<NCT> >();
371 CollectionItemType<T> &Collection::add_type()
373 CollectionItemType<T> *type = new CollectionItemType<T>;
374 types.push_back(type);
378 } // namespace DataFile