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