+template<typename T>
+class Collection::ItemLoader<T, false>: public T::Loader
+{
+public:
+ ItemLoader(T &o, Collection &):
+ T::Loader(o)
+ { }
+};
+
+template<typename T>
+class Collection::ItemLoader<T, true>: public T::Loader
+{
+public:
+ ItemLoader(T &o, Collection &c):
+ T::Loader(o, dynamic_cast<typename T::Loader::Collection &>(c))
+ { }
+};
+
+
+class CollectionItemTypeBase
+{
+protected:
+ struct ExtractorBase
+ {
+ virtual ~ExtractorBase() { }
+ };
+
+ template<typename T>
+ struct Extractor: ExtractorBase
+ {
+ virtual T &extract(const Variant &) const = 0;
+ };
+
+ std::string kwd;
+ std::vector<std::string> suffixes;
+ std::vector<ExtractorBase *> extractors;
+
+ CollectionItemTypeBase() { }
+public:
+ virtual ~CollectionItemTypeBase();
+
+protected:
+ void set_keyword(const std::string &);
+ void add_suffix(const std::string &);
+public:
+ const std::string &get_keyword() const { return kwd; }
+ bool match_name(const std::string &) const;
+ virtual bool is_same_type(const CollectionItemTypeBase &) const = 0;
+ virtual bool check_item_type(const Variant &) const = 0;
+ virtual void add_to_loader(Collection::Loader &) const = 0;
+ virtual bool can_create() const = 0;
+ virtual void create_item(Collection &, const std::string &) const = 0;
+ virtual void load_item(Collection &, Parser &, const std::string &) const = 0;
+
+ template<typename T>
+ bool can_extract() const
+ {
+ for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
+ if(dynamic_cast<Extractor<T> *>(*i))
+ return true;
+ return false;
+ }
+
+ template<typename T>
+ T *extract(const Variant &var) const
+ {
+ for(std::vector<ExtractorBase *>::const_iterator i=extractors.begin(); i!=extractors.end(); ++i)
+ if(Extractor<T> *ex = dynamic_cast<Extractor<T> *>(*i))
+ return &ex->extract(var);
+ return 0;
+ }
+};
+
+
+/**
+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:
+ struct CreatorBase
+ {
+ virtual ~CreatorBase() { }
+
+ virtual T *create(Collection &, const std::string &) const = 0;
+ };
+
+ template<typename C>
+ struct Creator: CreatorBase
+ {
+ typedef T *(C::*FuncPtr)(const std::string &);
+
+ FuncPtr func;
+
+ Creator(FuncPtr f): func(f) { }
+
+ virtual T *create(Collection &coll, const std::string &name) const
+ { return (dynamic_cast<C &>(coll).*func)(name); }
+ };
+
+ template<typename B>
+ struct Extractor: CollectionItemTypeBase::Extractor<B>
+ {
+ virtual B &extract(const Variant &var) const
+ { return *var.value<RefPtr<T> >(); }
+ };
+
+ CreatorBase *creat;
+
+public:
+ CollectionItemType():
+ creat(0)
+ { }
+
+ ~CollectionItemType()
+ {
+ delete creat;
+ }
+
+ /** 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)
+ {
+ set_keyword(k);
+ return *this;
+ }
+
+ /** Adds a suffix that is used to match names when looking for loadable
+ objects. There is no implied separator; a name matches if it ends with the
+ suffix. If a keyword is defined before any suffixes, then "."+keyword is
+ added as a suffix. */
+ CollectionItemType &suffix(const std::string &s)
+ {
+ add_suffix(s);
+ 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;
+ }
+
+ /** Makes items of this type available through a base class. */
+ template<typename B>
+ CollectionItemType &base()
+ {
+ extractors.push_back(new Extractor<B>);
+ return *this;
+ }
+
+ virtual bool is_same_type(const CollectionItemTypeBase &other) const
+ { return dynamic_cast<const CollectionItemType<T> *>(&other); }
+
+ virtual bool check_item_type(const Variant &var) const
+ { return var.check_type<RefPtr<T> >(); }
+
+ virtual void add_to_loader(Collection::Loader &) const
+ { }
+
+ 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)
+ coll.add(name, obj);
+ }
+
+ virtual void load_item(Collection &, Parser &, const std::string &) const
+ {
+ throw std::runtime_error("this type cannot be loaded");
+ }
+};
+
+
+template<typename T>
+class LoadableCollectionItemType: public CollectionItemType<T>
+{
+public:
+ virtual void add_to_loader(Collection::Loader &loader) const
+ { loader.add(this->kwd, &Collection::Loader::item<T, T>); }
+
+ virtual void load_item(Collection &coll, Parser &parser, const std::string &name) const
+ {
+ RefPtr<T> obj = new T;
+ Collection::ItemLoader<T> ldr(*obj, coll);
+ ldr.load(parser);
+ coll.add(name, obj.get());
+ obj.release();
+ }
+};
+
+
+template<typename T>
+T &Collection::extract(const Variant &var) const
+{
+ if(!var.check_type<RefPtr<T> >())
+ if(CollectionItemTypeBase *type = get_type_for_item(var))
+ if(T *item = type->extract<T>(var))
+ return *item;
+
+ return *var.value<RefPtr<T> >();
+}
+
+template<typename T>
+typename CollectionItemTypeChooser<T>::Type &Collection::add_type()
+{
+ typename CollectionItemTypeChooser<T>::Type *type = new typename CollectionItemTypeChooser<T>::Type;
+ types.push_back(type);
+ return *type;
+}
+
+template<typename T>
+typename CollectionItemTypeChooser<T>::Type &Collection::modify_type()
+{
+ for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
+ if(CollectionItemType<T> *t = dynamic_cast<CollectionItemType<T> *>(*j))
+ return *t;
+
+ throw std::logic_error("type not found in collection");
+}
+
+template<typename T>
+CollectionItemTypeBase *Collection::get_type(const std::string &name) const
+{
+ for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
+ if(dynamic_cast<CollectionItemType<T> *>(*j))
+ return *j;
+ CollectionItemTypeBase *type = 0;
+ for(TypeList::const_iterator j=types.begin(); j!=types.end(); ++j)
+ if((*j)->can_extract<T>())
+ {
+ if(!name.empty() && (*j)->match_name(name))
+ return *j;
+ type = *j;
+ }
+ return type;
+}
+