Chris@16: #ifndef DYNAMIC_PROPERTY_MAP_RG09302004_HPP Chris@16: #define DYNAMIC_PROPERTY_MAP_RG09302004_HPP Chris@16: Chris@16: // Copyright 2004-5 The Trustees of Indiana University. Chris@16: Chris@16: // Use, modification and distribution is subject to the Boost Software Chris@16: // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // dynamic_property_map.hpp - Chris@16: // Support for runtime-polymorphic property maps. This header is factored Chris@16: // out of Doug Gregor's routines for reading GraphML files for use in reading Chris@16: // GraphViz graph files. Chris@16: Chris@16: // Authors: Doug Gregor Chris@16: // Ronald Garcia Chris@16: // Chris@16: Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: // read_value - Chris@16: // A wrapper around lexical_cast, which does not behave as Chris@16: // desired for std::string types. Chris@16: template Chris@16: inline Value read_value(const std::string& value) Chris@16: { return boost::lexical_cast(value); } Chris@16: Chris@16: template<> Chris@16: inline std::string read_value(const std::string& value) Chris@16: { return value; } Chris@16: Chris@16: } Chris@16: Chris@16: Chris@16: // dynamic_property_map - Chris@16: // This interface supports polymorphic manipulation of property maps. Chris@16: class dynamic_property_map Chris@16: { Chris@16: public: Chris@16: virtual ~dynamic_property_map() { } Chris@16: Chris@16: virtual boost::any get(const any& key) = 0; Chris@16: virtual std::string get_string(const any& key) = 0; Chris@16: virtual void put(const any& key, const any& value) = 0; Chris@16: virtual const std::type_info& key() const = 0; Chris@16: virtual const std::type_info& value() const = 0; Chris@16: }; Chris@16: Chris@16: Chris@16: ////////////////////////////////////////////////////////////////////// Chris@16: // Property map exceptions Chris@16: ////////////////////////////////////////////////////////////////////// Chris@16: Chris@16: struct dynamic_property_exception : public std::exception { Chris@16: virtual ~dynamic_property_exception() throw() {} Chris@16: virtual const char* what() const throw() = 0; Chris@16: }; Chris@16: Chris@16: struct property_not_found : public dynamic_property_exception { Chris@16: std::string property; Chris@16: mutable std::string statement; Chris@16: property_not_found(const std::string& property) : property(property) {} Chris@16: virtual ~property_not_found() throw() {} Chris@16: Chris@16: const char* what() const throw() { Chris@16: if(statement.empty()) Chris@16: statement = Chris@16: std::string("Property not found: ") + property + "."; Chris@16: Chris@16: return statement.c_str(); Chris@16: } Chris@16: }; Chris@16: Chris@16: struct dynamic_get_failure : public dynamic_property_exception { Chris@16: std::string property; Chris@16: mutable std::string statement; Chris@16: dynamic_get_failure(const std::string& property) : property(property) {} Chris@16: virtual ~dynamic_get_failure() throw() {} Chris@16: Chris@16: const char* what() const throw() { Chris@16: if(statement.empty()) Chris@16: statement = Chris@16: std::string( Chris@16: "dynamic property get cannot retrieve value for property: ") Chris@16: + property + "."; Chris@16: Chris@16: return statement.c_str(); Chris@16: } Chris@16: }; Chris@16: Chris@16: struct dynamic_const_put_error : public dynamic_property_exception { Chris@16: virtual ~dynamic_const_put_error() throw() {} Chris@16: Chris@16: const char* what() const throw() { Chris@16: return "Attempt to put a value into a const property map: "; Chris@16: } Chris@16: }; Chris@16: Chris@16: Chris@16: namespace detail { Chris@16: Chris@16: // Trying to work around VC++ problem that seems to relate to having too many Chris@16: // functions named "get" Chris@16: template Chris@16: typename boost::property_traits::reference Chris@16: get_wrapper_xxx(const PMap& pmap, const Key& key) { Chris@16: using boost::get; Chris@16: return get(pmap, key); Chris@16: } Chris@16: Chris@16: // Chris@16: // dynamic_property_map_adaptor - Chris@16: // property-map adaptor to support runtime polymorphism. Chris@16: template Chris@16: class dynamic_property_map_adaptor : public dynamic_property_map Chris@16: { Chris@16: typedef typename property_traits::key_type key_type; Chris@16: typedef typename property_traits::value_type value_type; Chris@16: typedef typename property_traits::category category; Chris@16: Chris@16: // do_put - overloaded dispatches from the put() member function. Chris@16: // Attempts to "put" to a property map that does not model Chris@16: // WritablePropertyMap result in a runtime exception. Chris@16: Chris@16: // in_value must either hold an object of value_type or a string that Chris@16: // can be converted to value_type via iostreams. Chris@16: void do_put(const any& in_key, const any& in_value, mpl::bool_) Chris@16: { Chris@16: using boost::put; Chris@16: Chris@101: key_type key_ = any_cast(in_key); Chris@16: if (in_value.type() == typeid(value_type)) { Chris@101: put(property_map_, key_, any_cast(in_value)); Chris@16: } else { Chris@16: // if in_value is an empty string, put a default constructed value_type. Chris@16: std::string v = any_cast(in_value); Chris@16: if (v.empty()) { Chris@101: put(property_map_, key_, value_type()); Chris@16: } else { Chris@101: put(property_map_, key_, detail::read_value(v)); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: void do_put(const any&, const any&, mpl::bool_) Chris@16: { Chris@16: BOOST_THROW_EXCEPTION(dynamic_const_put_error()); Chris@16: } Chris@16: Chris@16: public: Chris@16: explicit dynamic_property_map_adaptor(const PropertyMap& property_map_) Chris@16: : property_map_(property_map_) { } Chris@16: Chris@101: virtual boost::any get(const any& key_) Chris@16: { Chris@101: return get_wrapper_xxx(property_map_, any_cast::key_type>(key_)); Chris@16: } Chris@16: Chris@101: virtual std::string get_string(const any& key_) Chris@16: { Chris@16: std::ostringstream out; Chris@101: out << get_wrapper_xxx(property_map_, any_cast::key_type>(key_)); Chris@16: return out.str(); Chris@16: } Chris@16: Chris@16: virtual void put(const any& in_key, const any& in_value) Chris@16: { Chris@16: do_put(in_key, in_value, Chris@16: mpl::bool_<(is_convertible::value)>()); Chris@16: } Chris@16: Chris@16: virtual const std::type_info& key() const { return typeid(key_type); } Chris@16: virtual const std::type_info& value() const { return typeid(value_type); } Chris@16: Chris@16: PropertyMap& base() { return property_map_; } Chris@16: const PropertyMap& base() const { return property_map_; } Chris@16: Chris@16: private: Chris@16: PropertyMap property_map_; Chris@16: }; Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: // Chris@16: // dynamic_properties - Chris@16: // container for dynamic property maps Chris@16: // Chris@16: struct dynamic_properties Chris@16: { Chris@16: typedef std::multimap > Chris@16: property_maps_type; Chris@16: typedef boost::function3, Chris@16: const std::string&, Chris@16: const boost::any&, Chris@16: const boost::any&> generate_fn_type; Chris@16: public: Chris@16: Chris@16: typedef property_maps_type::iterator iterator; Chris@16: typedef property_maps_type::const_iterator const_iterator; Chris@16: Chris@16: dynamic_properties() : generate_fn() { } Chris@16: dynamic_properties(const generate_fn_type& g) : generate_fn(g) {} Chris@16: Chris@16: ~dynamic_properties() {} Chris@16: Chris@16: template Chris@16: dynamic_properties& Chris@16: property(const std::string& name, PropertyMap property_map_) Chris@16: { Chris@16: boost::shared_ptr pm( Chris@16: boost::static_pointer_cast( Chris@16: boost::make_shared >(property_map_))); Chris@16: property_maps.insert(property_maps_type::value_type(name, pm)); Chris@16: Chris@16: return *this; Chris@16: } Chris@16: Chris@16: template Chris@16: dynamic_properties Chris@16: property(const std::string& name, PropertyMap property_map_) const Chris@16: { Chris@16: dynamic_properties result = *this; Chris@16: result.property(name, property_map_); Chris@16: return result; Chris@16: } Chris@16: Chris@16: iterator begin() { return property_maps.begin(); } Chris@16: const_iterator begin() const { return property_maps.begin(); } Chris@16: iterator end() { return property_maps.end(); } Chris@16: const_iterator end() const { return property_maps.end(); } Chris@16: Chris@16: iterator lower_bound(const std::string& name) Chris@16: { return property_maps.lower_bound(name); } Chris@16: Chris@16: const_iterator lower_bound(const std::string& name) const Chris@16: { return property_maps.lower_bound(name); } Chris@16: Chris@16: void Chris@16: insert(const std::string& name, boost::shared_ptr pm) Chris@16: { Chris@16: property_maps.insert(property_maps_type::value_type(name, pm)); Chris@16: } Chris@16: Chris@16: template Chris@16: boost::shared_ptr Chris@16: generate(const std::string& name, const Key& key, const Value& value) Chris@16: { Chris@16: if(!generate_fn) { Chris@16: BOOST_THROW_EXCEPTION(property_not_found(name)); Chris@16: } else { Chris@16: return generate_fn(name,key,value); Chris@16: } Chris@16: } Chris@16: Chris@16: private: Chris@16: property_maps_type property_maps; Chris@16: generate_fn_type generate_fn; Chris@16: }; Chris@16: Chris@16: template Chris@16: bool Chris@16: put(const std::string& name, dynamic_properties& dp, const Key& key, Chris@16: const Value& value) Chris@16: { Chris@16: for (dynamic_properties::iterator i = dp.lower_bound(name); Chris@16: i != dp.end() && i->first == name; ++i) { Chris@16: if (i->second->key() == typeid(key)) { Chris@16: i->second->put(key, value); Chris@16: return true; Chris@16: } Chris@16: } Chris@16: Chris@16: boost::shared_ptr new_map = dp.generate(name, key, value); Chris@16: if (new_map.get()) { Chris@16: new_map->put(key, value); Chris@16: dp.insert(name, new_map); Chris@16: return true; Chris@16: } else { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: Value Chris@16: get(const std::string& name, const dynamic_properties& dp, const Key& key) Chris@16: { Chris@16: for (dynamic_properties::const_iterator i = dp.lower_bound(name); Chris@16: i != dp.end() && i->first == name; ++i) { Chris@16: if (i->second->key() == typeid(key)) Chris@16: return any_cast(i->second->get(key)); Chris@16: } Chris@16: Chris@16: BOOST_THROW_EXCEPTION(dynamic_get_failure(name)); Chris@16: } Chris@16: Chris@16: template Chris@16: Value Chris@16: get(const std::string& name, const dynamic_properties& dp, const Key& key, type) Chris@16: { Chris@16: for (dynamic_properties::const_iterator i = dp.lower_bound(name); Chris@16: i != dp.end() && i->first == name; ++i) { Chris@16: if (i->second->key() == typeid(key)) Chris@16: return any_cast(i->second->get(key)); Chris@16: } Chris@16: Chris@16: BOOST_THROW_EXCEPTION(dynamic_get_failure(name)); Chris@16: } Chris@16: Chris@16: template Chris@16: std::string Chris@16: get(const std::string& name, const dynamic_properties& dp, const Key& key) Chris@16: { Chris@16: for (dynamic_properties::const_iterator i = dp.lower_bound(name); Chris@16: i != dp.end() && i->first == name; ++i) { Chris@16: if (i->second->key() == typeid(key)) Chris@16: return i->second->get_string(key); Chris@16: } Chris@16: Chris@16: BOOST_THROW_EXCEPTION(dynamic_get_failure(name)); Chris@16: } Chris@16: Chris@16: // The easy way to ignore properties. Chris@16: inline Chris@101: boost::shared_ptr Chris@16: ignore_other_properties(const std::string&, Chris@16: const boost::any&, Chris@16: const boost::any&) { Chris@16: return boost::shared_ptr(); Chris@16: } Chris@16: Chris@16: } // namespace boost Chris@16: Chris@16: #endif // DYNAMIC_PROPERTY_MAP_RG09302004_HPP