]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | #ifndef BOOST_PROPERTY_MAP_DYNAMIC_PROPERTY_MAP_HPP |
2 | #define BOOST_PROPERTY_MAP_DYNAMIC_PROPERTY_MAP_HPP | |
7c673cae FG |
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> | |
43 | inline Value read_value(const std::string& value) | |
44 | { return boost::lexical_cast<Value>(value); } | |
45 | ||
46 | template<> | |
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: | |
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 { | |
73 | 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; | |
80 | property_not_found(const std::string& property) : property(property) {} | |
81 | virtual ~property_not_found() throw() {} | |
82 | ||
83 | 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; | |
95 | dynamic_get_failure(const std::string& property) : property(property) {} | |
96 | virtual ~dynamic_get_failure() throw() {} | |
97 | ||
98 | 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 { | |
110 | virtual ~dynamic_const_put_error() throw() {} | |
111 | ||
112 | 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 | |
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. | |
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 | ||
163 | void do_put(const any&, const any&, mpl::bool_<false>) | |
164 | { | |
165 | BOOST_THROW_EXCEPTION(dynamic_const_put_error()); | |
166 | } | |
167 | ||
168 | public: | |
169 | explicit dynamic_property_map_adaptor(const PropertyMap& property_map_) | |
170 | : property_map_(property_map_) { } | |
171 | ||
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 | ||
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 | ||
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 | ||
191 | virtual const std::type_info& key() const { return typeid(key_type); } | |
192 | virtual const std::type_info& value() const { return typeid(value_type); } | |
193 | ||
194 | PropertyMap& base() { return property_map_; } | |
195 | 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 | ||
220 | dynamic_properties() : generate_fn() { } | |
221 | dynamic_properties(const generate_fn_type& g) : generate_fn(g) {} | |
222 | ||
223 | ~dynamic_properties() {} | |
224 | ||
225 | template<typename PropertyMap> | |
226 | dynamic_properties& | |
227 | 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 | |
239 | 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 | ||
246 | iterator begin() { return property_maps.begin(); } | |
247 | const_iterator begin() const { return property_maps.begin(); } | |
248 | iterator end() { return property_maps.end(); } | |
249 | const_iterator end() const { return property_maps.end(); } | |
250 | ||
251 | iterator lower_bound(const std::string& name) | |
252 | { return property_maps.lower_bound(name); } | |
253 | ||
254 | const_iterator lower_bound(const std::string& name) const | |
255 | { return property_maps.lower_bound(name); } | |
256 | ||
257 | void | |
258 | 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> | |
265 | 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 | |
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 | |
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> | |
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 | ||
f67539c2 | 352 | #endif // BOOST_PROPERTY_MAP_DYNAMIC_PROPERTY_MAP_HPP |