1 #ifndef DYNAMIC_PROPERTY_MAP_RG09302004_HPP
2 #define DYNAMIC_PROPERTY_MAP_RG09302004_HPP
3 
4 // Copyright 2004-5 The Trustees of Indiana University.
5 
6 // Use, modification and distribution is subject to the Boost Software
7 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
9 
10 //  dynamic_property_map.hpp -
11 //    Support for runtime-polymorphic property maps.  This header is factored
12 //  out of Doug Gregor's routines for reading GraphML files for use in reading
13 //  GraphViz graph files.
14 
15 //  Authors: Doug Gregor
16 //           Ronald Garcia
17 //
18 
19 
20 #include <boost/config.hpp>
21 #include <boost/throw_exception.hpp>
22 #include <boost/property_map/property_map.hpp>
23 #include <boost/lexical_cast.hpp>
24 #include <boost/any.hpp>
25 #include <boost/function/function3.hpp>
26 #include <boost/type_traits/is_convertible.hpp>
27 #include <typeinfo>
28 #include <boost/mpl/bool.hpp>
29 #include <stdexcept>
30 #include <sstream>
31 #include <map>
32 #include <boost/type.hpp>
33 #include <boost/smart_ptr.hpp>
34 
35 namespace boost {
36 
37 namespace detail {
38 
39   // read_value -
40   //   A wrapper around lexical_cast, which does not behave as
41   //   desired for std::string types.
42   template<typename Value>
read_value(const std::string & value)43   inline Value read_value(const std::string& value)
44   { return boost::lexical_cast<Value>(value); }
45 
46   template<>
read_value(const std::string & value)47   inline std::string read_value<std::string>(const std::string& value)
48   { return value; }
49 
50 }
51 
52 
53 // dynamic_property_map -
54 //  This interface supports polymorphic manipulation of property maps.
55 class dynamic_property_map
56 {
57 public:
~dynamic_property_map()58   virtual ~dynamic_property_map() { }
59 
60   virtual boost::any get(const any& key) = 0;
61   virtual std::string get_string(const any& key) = 0;
62   virtual void put(const any& key, const any& value) = 0;
63   virtual const std::type_info& key() const = 0;
64   virtual const std::type_info& value() const = 0;
65 };
66 
67 
68 //////////////////////////////////////////////////////////////////////
69 // Property map exceptions
70 //////////////////////////////////////////////////////////////////////
71 
72 struct dynamic_property_exception : public std::exception {
~dynamic_property_exceptionboost::dynamic_property_exception73   virtual ~dynamic_property_exception() throw() {}
74   virtual const char* what() const throw() = 0;
75 };
76 
77 struct property_not_found : public dynamic_property_exception {
78   std::string property;
79   mutable std::string statement;
property_not_foundboost::property_not_found80   property_not_found(const std::string& property) : property(property) {}
~property_not_foundboost::property_not_found81   virtual ~property_not_found() throw() {}
82 
whatboost::property_not_found83   const char* what() const throw() {
84     if(statement.empty())
85       statement =
86         std::string("Property not found: ") + property + ".";
87 
88     return statement.c_str();
89   }
90 };
91 
92 struct dynamic_get_failure : public dynamic_property_exception {
93   std::string property;
94   mutable std::string statement;
dynamic_get_failureboost::dynamic_get_failure95   dynamic_get_failure(const std::string& property) : property(property) {}
~dynamic_get_failureboost::dynamic_get_failure96   virtual ~dynamic_get_failure() throw() {}
97 
whatboost::dynamic_get_failure98   const char* what() const throw() {
99     if(statement.empty())
100       statement =
101         std::string(
102          "dynamic property get cannot retrieve value for  property: ")
103         + property + ".";
104 
105     return statement.c_str();
106   }
107 };
108 
109 struct dynamic_const_put_error  : public dynamic_property_exception {
~dynamic_const_put_errorboost::dynamic_const_put_error110   virtual ~dynamic_const_put_error() throw() {}
111 
whatboost::dynamic_const_put_error112   const char* what() const throw() {
113     return "Attempt to put a value into a const property map: ";
114   }
115 };
116 
117 
118 namespace detail {
119 
120 // Trying to work around VC++ problem that seems to relate to having too many
121 // functions named "get"
122 template <typename PMap, typename Key>
123 typename boost::property_traits<PMap>::reference
get_wrapper_xxx(const PMap & pmap,const Key & key)124 get_wrapper_xxx(const PMap& pmap, const Key& key) {
125   using boost::get;
126   return get(pmap, key);
127 }
128 
129 //
130 // dynamic_property_map_adaptor -
131 //   property-map adaptor to support runtime polymorphism.
132 template<typename PropertyMap>
133 class dynamic_property_map_adaptor : public dynamic_property_map
134 {
135   typedef typename property_traits<PropertyMap>::key_type key_type;
136   typedef typename property_traits<PropertyMap>::value_type value_type;
137   typedef typename property_traits<PropertyMap>::category category;
138 
139   // do_put - overloaded dispatches from the put() member function.
140   //   Attempts to "put" to a property map that does not model
141   //   WritablePropertyMap result in a runtime exception.
142 
143   //   in_value must either hold an object of value_type or a string that
144   //   can be converted to value_type via iostreams.
do_put(const any & in_key,const any & in_value,mpl::bool_<true>)145   void do_put(const any& in_key, const any& in_value, mpl::bool_<true>)
146   {
147     using boost::put;
148 
149     key_type key_ = any_cast<key_type>(in_key);
150     if (in_value.type() == typeid(value_type)) {
151       put(property_map_, key_, any_cast<value_type>(in_value));
152     } else {
153       //  if in_value is an empty string, put a default constructed value_type.
154       std::string v = any_cast<std::string>(in_value);
155       if (v.empty()) {
156         put(property_map_, key_, value_type());
157       } else {
158         put(property_map_, key_, detail::read_value<value_type>(v));
159       }
160     }
161   }
162 
do_put(const any &,const any &,mpl::bool_<false>)163   void do_put(const any&, const any&, mpl::bool_<false>)
164   {
165     BOOST_THROW_EXCEPTION(dynamic_const_put_error());
166   }
167 
168 public:
dynamic_property_map_adaptor(const PropertyMap & property_map_)169   explicit dynamic_property_map_adaptor(const PropertyMap& property_map_)
170     : property_map_(property_map_) { }
171 
get(const any & key_)172   virtual boost::any get(const any& key_)
173   {
174     return get_wrapper_xxx(property_map_, any_cast<typename boost::property_traits<PropertyMap>::key_type>(key_));
175   }
176 
get_string(const any & key_)177   virtual std::string get_string(const any& key_)
178   {
179     std::ostringstream out;
180     out << get_wrapper_xxx(property_map_, any_cast<typename boost::property_traits<PropertyMap>::key_type>(key_));
181     return out.str();
182   }
183 
put(const any & in_key,const any & in_value)184   virtual void put(const any& in_key, const any& in_value)
185   {
186     do_put(in_key, in_value,
187            mpl::bool_<(is_convertible<category*,
188                                       writable_property_map_tag*>::value)>());
189   }
190 
key() const191   virtual const std::type_info& key()   const { return typeid(key_type); }
value() const192   virtual const std::type_info& value() const { return typeid(value_type); }
193 
base()194   PropertyMap&       base()       { return property_map_; }
base() const195   const PropertyMap& base() const { return property_map_; }
196 
197 private:
198   PropertyMap property_map_;
199 };
200 
201 } // namespace detail
202 
203 //
204 // dynamic_properties -
205 //   container for dynamic property maps
206 //
207 struct dynamic_properties
208 {
209   typedef std::multimap<std::string, boost::shared_ptr<dynamic_property_map> >
210     property_maps_type;
211   typedef boost::function3<boost::shared_ptr<dynamic_property_map>,
212                            const std::string&,
213                            const boost::any&,
214                            const boost::any&> generate_fn_type;
215 public:
216 
217   typedef property_maps_type::iterator iterator;
218   typedef property_maps_type::const_iterator const_iterator;
219 
dynamic_propertiesboost::dynamic_properties220   dynamic_properties() : generate_fn() { }
dynamic_propertiesboost::dynamic_properties221   dynamic_properties(const generate_fn_type& g) : generate_fn(g) {}
222 
~dynamic_propertiesboost::dynamic_properties223   ~dynamic_properties() {}
224 
225   template<typename PropertyMap>
226   dynamic_properties&
propertyboost::dynamic_properties227   property(const std::string& name, PropertyMap property_map_)
228   {
229     boost::shared_ptr<dynamic_property_map> pm(
230       boost::static_pointer_cast<dynamic_property_map>(
231         boost::make_shared<detail::dynamic_property_map_adaptor<PropertyMap> >(property_map_)));
232     property_maps.insert(property_maps_type::value_type(name, pm));
233 
234     return *this;
235   }
236 
237   template<typename PropertyMap>
238   dynamic_properties
propertyboost::dynamic_properties239   property(const std::string& name, PropertyMap property_map_) const
240   {
241     dynamic_properties result = *this;
242     result.property(name, property_map_);
243     return result;
244   }
245 
beginboost::dynamic_properties246   iterator       begin()       { return property_maps.begin(); }
beginboost::dynamic_properties247   const_iterator begin() const { return property_maps.begin(); }
endboost::dynamic_properties248   iterator       end()         { return property_maps.end(); }
endboost::dynamic_properties249   const_iterator end() const   { return property_maps.end(); }
250 
lower_boundboost::dynamic_properties251   iterator lower_bound(const std::string& name)
252   { return property_maps.lower_bound(name); }
253 
lower_boundboost::dynamic_properties254   const_iterator lower_bound(const std::string& name) const
255   { return property_maps.lower_bound(name); }
256 
257   void
insertboost::dynamic_properties258   insert(const std::string& name, boost::shared_ptr<dynamic_property_map> pm)
259   {
260     property_maps.insert(property_maps_type::value_type(name, pm));
261   }
262 
263   template<typename Key, typename Value>
264   boost::shared_ptr<dynamic_property_map>
generateboost::dynamic_properties265   generate(const std::string& name, const Key& key, const Value& value)
266   {
267     if(!generate_fn) {
268       BOOST_THROW_EXCEPTION(property_not_found(name));
269     } else {
270       return generate_fn(name,key,value);
271     }
272   }
273 
274 private:
275   property_maps_type property_maps;
276   generate_fn_type generate_fn;
277 };
278 
279 template<typename Key, typename Value>
280 bool
put(const std::string & name,dynamic_properties & dp,const Key & key,const Value & value)281 put(const std::string& name, dynamic_properties& dp, const Key& key,
282     const Value& value)
283 {
284   for (dynamic_properties::iterator i = dp.lower_bound(name);
285        i != dp.end() && i->first == name; ++i) {
286     if (i->second->key() == typeid(key)) {
287       i->second->put(key, value);
288       return true;
289     }
290   }
291 
292   boost::shared_ptr<dynamic_property_map> new_map = dp.generate(name, key, value);
293   if (new_map.get()) {
294     new_map->put(key, value);
295     dp.insert(name, new_map);
296     return true;
297   } else {
298     return false;
299   }
300 }
301 
302 template<typename Value, typename Key>
303 Value
304 get(const std::string& name, const dynamic_properties& dp, const Key& key)
305 {
306   for (dynamic_properties::const_iterator i = dp.lower_bound(name);
307        i != dp.end() && i->first == name; ++i) {
308     if (i->second->key() == typeid(key))
309       return any_cast<Value>(i->second->get(key));
310   }
311 
312   BOOST_THROW_EXCEPTION(dynamic_get_failure(name));
313 }
314 
315 template<typename Value, typename Key>
316 Value
317 get(const std::string& name, const dynamic_properties& dp, const Key& key, type<Value>)
318 {
319   for (dynamic_properties::const_iterator i = dp.lower_bound(name);
320        i != dp.end() && i->first == name; ++i) {
321     if (i->second->key() == typeid(key))
322       return any_cast<Value>(i->second->get(key));
323   }
324 
325   BOOST_THROW_EXCEPTION(dynamic_get_failure(name));
326 }
327 
328 template<typename Key>
329 std::string
get(const std::string & name,const dynamic_properties & dp,const Key & key)330 get(const std::string& name, const dynamic_properties& dp, const Key& key)
331 {
332   for (dynamic_properties::const_iterator i = dp.lower_bound(name);
333        i != dp.end() && i->first == name; ++i) {
334     if (i->second->key() == typeid(key))
335       return i->second->get_string(key);
336   }
337 
338   BOOST_THROW_EXCEPTION(dynamic_get_failure(name));
339 }
340 
341 // The easy way to ignore properties.
342 inline
343 boost::shared_ptr<boost::dynamic_property_map>
ignore_other_properties(const std::string &,const boost::any &,const boost::any &)344 ignore_other_properties(const std::string&,
345                         const boost::any&,
346                         const boost::any&) {
347   return boost::shared_ptr<boost::dynamic_property_map>();
348 }
349 
350 } // namespace boost
351 
352 #endif // DYNAMIC_PROPERTY_MAP_RG09302004_HPP
353