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