+
+/**
+Describes a type of item that can be loaded by a Collection. These are created
+by Collection::add_type.
+*/
+template<typename T>
+class CollectionItemType: public CollectionItemTypeBase
+{
+private:
+ class CreatorBase
+ {
+ protected:
+ CreatorBase() { }
+ public:
+ virtual ~CreatorBase() { }
+
+ virtual T *create(Collection &, const std::string &) const = 0;
+ };
+
+ template<typename C>
+ class Creator: public CreatorBase
+ {
+ public:
+ typedef T *(C::*FuncPtr)(const std::string &);
+
+ private:
+ FuncPtr func;
+
+ public:
+ Creator(FuncPtr f): func(f) { }
+
+ virtual T *create(Collection &coll, const std::string &name) const
+ { return (static_cast<C &>(coll).*func)(name); }
+ };
+
+ class StoreBase
+ {
+ protected:
+ StoreBase() { }
+ public:
+ virtual ~StoreBase() { }
+
+ virtual void store(Collection &, const std::string &, T *) = 0;
+
+ virtual void add_to_loader(Collection::Loader &, const std::string &) = 0;
+ };
+
+ template<typename S>
+ class Store: public StoreBase
+ {
+ public:
+ virtual void store(Collection &coll, const std::string &name, T *obj)
+ { coll.add(name, static_cast<S *>(obj)); }
+
+ virtual void add_to_loader(Collection::Loader &loader, const std::string &kwd)
+ { Collection::Loader::Add<T, S>::add(loader, kwd); }
+ };
+
+ CreatorBase *creat;
+ StoreBase *store;
+
+public:
+ CollectionItemType():
+ creat(0), store(new Store<T>)
+ { tag = new Tag<T>; }
+
+ ~CollectionItemType()
+ {
+ delete creat;
+ delete store;
+ }
+
+ /** Sets a datafile keyword for this item type. The Collection's loader
+ will accept a statement with this keyword and a single string argument - the
+ item's name. */
+ CollectionItemType &keyword(const std::string &k)
+ {
+ kwd = k;
+ return *this;
+ }
+
+ /** Attaches a creator function to this item type. If an item is not found
+ in the Collection, the creator function for its type is called to create it.
+ The function must be a member of the Collection subclass containing the
+ type. It must return the created object, or null if it could not be
+ created. It's also permissible to load the item via other means and then
+ return null. */
+ template<typename C>
+ CollectionItemType &creator(T *(C::*func)(const std::string &))
+ {
+ delete creat;
+ creat = new Creator<C>(func);
+ return *this;
+ }
+
+ /** Specifies the storage type for items of this type. It must be a base
+ class of the actual type. */
+ template<typename S>
+ CollectionItemType &store_as()
+ {
+ delete tag;
+ tag = new Tag<S>;
+ delete store;
+ store = new Store<S>;
+ return *this;
+ }
+
+ virtual void add_to_loader(Collection::Loader &loader) const
+ { store->add_to_loader(loader, kwd); }
+
+ virtual bool can_create() const
+ { return creat!=0; }
+
+ virtual void create_item(Collection &coll, const std::string &name) const
+ {
+ if(!creat)
+ throw std::runtime_error("no creator");
+ T *obj = creat->create(coll, name);
+ if(obj)
+ store->store(coll, name, obj);
+ }
+};
+
+
+template<typename T>
+T &Collection::get(const std::string &name)
+{
+ typedef typename RemoveConst<T>::Type NCT;
+
+ if(!items.count(name))
+ {
+ for(TypeList::iterator i=types.begin(); i!=types.end(); ++i)
+ if((*i)->can_create() && (*i)->check_type<NCT>())
+ (*i)->create_item(*this, name);
+ }
+
+ return *get_item(items, name).value<RefPtr<NCT> >();
+}
+
+template<typename T>
+CollectionItemType<T> &Collection::add_type()
+{
+ CollectionItemType<T> *type = new CollectionItemType<T>;
+ types.push_back(type);
+ return *type;
+}
+