| 1 | // ---------------------------------------------------------------------------- |
| 2 | // Copyright (C) 2009 Sebastian Redl |
| 3 | // |
| 4 | // Distributed under the Boost Software License, Version 1.0. |
| 5 | // (See accompanying file LICENSE_1_0.txt or copy at |
| 6 | // http://www.boost.org/LICENSE_1_0.txt) |
| 7 | // |
| 8 | // For more information, see www.boost.org |
| 9 | // ---------------------------------------------------------------------------- |
| 10 | |
| 11 | #ifndef BOOST_PROPERTY_TREE_STRING_PATH_HPP_INCLUDED |
| 12 | #define BOOST_PROPERTY_TREE_STRING_PATH_HPP_INCLUDED |
| 13 | |
| 14 | #include <boost/property_tree/ptree_fwd.hpp> |
| 15 | #include <boost/property_tree/id_translator.hpp> |
| 16 | #include <boost/property_tree/exceptions.hpp> |
| 17 | #include <boost/property_tree/detail/ptree_utils.hpp> |
| 18 | |
| 19 | #include <boost/static_assert.hpp> |
| 20 | #include <boost/assert.hpp> |
| 21 | #include <boost/type_traits/is_same.hpp> |
| 22 | #include <boost/optional/optional.hpp> |
| 23 | #include <boost/throw_exception.hpp> |
| 24 | #include <algorithm> |
| 25 | #include <string> |
| 26 | #include <iterator> |
| 27 | |
| 28 | namespace boost { namespace property_tree |
| 29 | { |
| 30 | namespace detail |
| 31 | { |
| 32 | template <typename Sequence, typename Iterator> |
| 33 | void append_and_preserve_iter(Sequence &s, const Sequence &r, |
| 34 | Iterator &, std::forward_iterator_tag) |
| 35 | { |
| 36 | // Here we boldly assume that anything that is not random-access |
| 37 | // preserves validity. This is valid for the STL sequences. |
| 38 | s.insert(s.end(), r.begin(), r.end()); |
| 39 | } |
| 40 | template <typename Sequence, typename Iterator> |
| 41 | void append_and_preserve_iter(Sequence &s, const Sequence &r, |
| 42 | Iterator &it, |
| 43 | std::random_access_iterator_tag) |
| 44 | { |
| 45 | // Convert the iterator to an index, and later back. |
| 46 | typename std::iterator_traits<Iterator>::difference_type idx = |
| 47 | it - s.begin(); |
| 48 | s.insert(s.end(), r.begin(), r.end()); |
| 49 | it = s.begin() + idx; |
| 50 | } |
| 51 | |
| 52 | template <typename Sequence> |
| 53 | inline std::string dump_sequence(const Sequence &) |
| 54 | { |
| 55 | return "<undumpable sequence>" ; |
| 56 | } |
| 57 | inline std::string dump_sequence(const std::string &s) |
| 58 | { |
| 59 | return s; |
| 60 | } |
| 61 | #ifndef BOOST_NO_STD_WSTRING |
| 62 | inline std::string dump_sequence(const std::wstring &s) |
| 63 | { |
| 64 | return narrow<std::string>(text: s.c_str()); |
| 65 | } |
| 66 | #endif |
| 67 | } |
| 68 | |
| 69 | /// Default path class. A path is a sequence of values. Groups of values |
| 70 | /// are separated by the separator value, which defaults to '.' cast to |
| 71 | /// the sequence's value type. The group of values is then passed to the |
| 72 | /// translator to get a key. |
| 73 | /// |
| 74 | /// If instantiated with std::string and id_translator\<std::string\>, |
| 75 | /// it accepts paths of the form "one.two.three.four". |
| 76 | /// |
| 77 | /// @tparam String Any Sequence. If the sequence does not support random- |
| 78 | /// access iteration, concatenation of paths assumes that |
| 79 | /// insertions at the end preserve iterator validity. |
| 80 | /// @tparam Translator A translator with internal_type == String. |
| 81 | template <typename String, typename Translator> |
| 82 | class string_path |
| 83 | { |
| 84 | BOOST_STATIC_ASSERT((is_same<String, |
| 85 | typename Translator::internal_type>::value)); |
| 86 | public: |
| 87 | typedef typename Translator::external_type key_type; |
| 88 | typedef typename String::value_type char_type; |
| 89 | |
| 90 | /// Create an empty path. |
| 91 | explicit string_path(char_type separator = char_type('.')); |
| 92 | /// Create a path by parsing the given string. |
| 93 | /// @param value A sequence, possibly with separators, that describes |
| 94 | /// the path, e.g. "one.two.three". |
| 95 | /// @param separator The separator used in parsing. Defaults to '.'. |
| 96 | /// @param tr The translator used by this path to convert the individual |
| 97 | /// parts to keys. |
| 98 | string_path(const String &value, char_type separator = char_type('.'), |
| 99 | Translator tr = Translator()); |
| 100 | /// Create a path by parsing the given string. |
| 101 | /// @param value A zero-terminated array of values. Only use if zero- |
| 102 | /// termination makes sense for your type, and your |
| 103 | /// sequence supports construction from it. Intended for |
| 104 | /// string literals. |
| 105 | /// @param separator The separator used in parsing. Defaults to '.'. |
| 106 | /// @param tr The translator used by this path to convert the individual |
| 107 | /// parts to keys. |
| 108 | string_path(const char_type *value, |
| 109 | char_type separator = char_type('.'), |
| 110 | Translator tr = Translator()); |
| 111 | |
| 112 | // Default copying doesn't do the right thing with the iterator |
| 113 | string_path(const string_path &o); |
| 114 | string_path& operator =(const string_path &o); |
| 115 | |
| 116 | /// Take a single element off the path at the front and return it. |
| 117 | key_type reduce(); |
| 118 | |
| 119 | /// Test if the path is empty. |
| 120 | bool empty() const; |
| 121 | |
| 122 | /// Test if the path contains a single element, i.e. no separators. |
| 123 | bool single() const; |
| 124 | |
| 125 | /// Get the separator used by this path. |
| 126 | char_type separator() const { return m_separator; } |
| 127 | |
| 128 | std::string dump() const { |
| 129 | return detail::dump_sequence(m_value); |
| 130 | } |
| 131 | |
| 132 | /// Concatenates two path components |
| 133 | friend string_path operator /(string_path p1, const string_path &p2) |
| 134 | { |
| 135 | p1 /= p2; |
| 136 | return p1; |
| 137 | } |
| 138 | |
| 139 | /// Append a second path to this one. |
| 140 | /// @pre o's separator is the same as this one's, or o has no separators |
| 141 | string_path& operator /=(const string_path &o) { |
| 142 | // If it's single, there's no separator. This allows to do |
| 143 | // p /= "piece"; |
| 144 | // even for non-default separators. |
| 145 | BOOST_ASSERT((m_separator == o.m_separator |
| 146 | || o.empty() |
| 147 | || o.single()) |
| 148 | && "Incompatible paths." ); |
| 149 | if(!o.empty()) { |
| 150 | String sub; |
| 151 | if(!this->empty()) { |
| 152 | sub.push_back(m_separator); |
| 153 | } |
| 154 | sub.insert(sub.end(), o.cstart(), o.m_value.end()); |
| 155 | detail::append_and_preserve_iter(m_value, sub, m_start, |
| 156 | typename std::iterator_traits<s_iter>::iterator_category()); |
| 157 | } |
| 158 | return *this; |
| 159 | } |
| 160 | |
| 161 | private: |
| 162 | typedef typename String::iterator s_iter; |
| 163 | typedef typename String::const_iterator s_c_iter; |
| 164 | String m_value; |
| 165 | char_type m_separator; |
| 166 | Translator m_tr; |
| 167 | s_iter m_start; |
| 168 | s_c_iter cstart() const { return m_start; } |
| 169 | }; |
| 170 | |
| 171 | template <typename String, typename Translator> inline |
| 172 | string_path<String, Translator>::string_path(char_type separator) |
| 173 | : m_separator(separator), m_start(m_value.begin()) |
| 174 | {} |
| 175 | |
| 176 | template <typename String, typename Translator> inline |
| 177 | string_path<String, Translator>::string_path(const String &value, |
| 178 | char_type separator, |
| 179 | Translator tr) |
| 180 | : m_value(value), m_separator(separator), |
| 181 | m_tr(tr), m_start(m_value.begin()) |
| 182 | {} |
| 183 | |
| 184 | template <typename String, typename Translator> inline |
| 185 | string_path<String, Translator>::string_path(const char_type *value, |
| 186 | char_type separator, |
| 187 | Translator tr) |
| 188 | : m_value(value), m_separator(separator), |
| 189 | m_tr(tr), m_start(m_value.begin()) |
| 190 | {} |
| 191 | |
| 192 | template <typename String, typename Translator> inline |
| 193 | string_path<String, Translator>::string_path(const string_path &o) |
| 194 | : m_value(o.m_value), m_separator(o.m_separator), |
| 195 | m_tr(o.m_tr), m_start(m_value.begin()) |
| 196 | { |
| 197 | std::advance(m_start, std::distance(o.m_value.begin(), o.cstart())); |
| 198 | } |
| 199 | |
| 200 | template <typename String, typename Translator> inline |
| 201 | string_path<String, Translator>& |
| 202 | string_path<String, Translator>::operator =(const string_path &o) |
| 203 | { |
| 204 | m_value = o.m_value; |
| 205 | m_separator = o.m_separator; |
| 206 | m_tr = o.m_tr; |
| 207 | m_start = m_value.begin(); |
| 208 | std::advance(m_start, std::distance(o.m_value.begin(), o.cstart())); |
| 209 | return *this; |
| 210 | } |
| 211 | |
| 212 | template <typename String, typename Translator> |
| 213 | typename Translator::external_type string_path<String, Translator>::reduce() |
| 214 | { |
| 215 | BOOST_ASSERT(!empty() && "Reducing empty path" ); |
| 216 | |
| 217 | s_iter next_sep = std::find(m_start, m_value.end(), m_separator); |
| 218 | String part(m_start, next_sep); |
| 219 | m_start = next_sep; |
| 220 | if(!empty()) { |
| 221 | // Unless we're at the end, skip the separator we found. |
| 222 | ++m_start; |
| 223 | } |
| 224 | |
| 225 | if(optional<key_type> key = m_tr.get_value(part)) { |
| 226 | return *key; |
| 227 | } |
| 228 | BOOST_PROPERTY_TREE_THROW(ptree_bad_path("Path syntax error" , *this)); |
| 229 | } |
| 230 | |
| 231 | template <typename String, typename Translator> inline |
| 232 | bool string_path<String, Translator>::empty() const |
| 233 | { |
| 234 | return m_start == m_value.end(); |
| 235 | } |
| 236 | |
| 237 | template <typename String, typename Translator> inline |
| 238 | bool string_path<String, Translator>::single() const |
| 239 | { |
| 240 | return std::find(static_cast<s_c_iter>(m_start), |
| 241 | m_value.end(), m_separator) |
| 242 | == m_value.end(); |
| 243 | } |
| 244 | |
| 245 | // By default, this is the path for strings. You can override this by |
| 246 | // specializing path_of for a more specific form of std::basic_string. |
| 247 | template <typename Ch, typename Traits, typename Alloc> |
| 248 | struct path_of< std::basic_string<Ch, Traits, Alloc> > |
| 249 | { |
| 250 | typedef std::basic_string<Ch, Traits, Alloc> _string; |
| 251 | typedef string_path< _string, id_translator<_string> > type; |
| 252 | }; |
| 253 | }} |
| 254 | |
| 255 | #endif |
| 256 | |