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.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> append_and_preserve_iter(Sequence & s,const Sequence & r,Iterator &,std::forward_iterator_tag)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> append_and_preserve_iter(Sequence & s,const Sequence & r,Iterator & it,std::random_access_iterator_tag)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> dump_sequence(const Sequence &)53 inline std::string dump_sequence(const Sequence &) 54 { 55 return "<undumpable sequence>"; 56 } dump_sequence(const std::string & s)57 inline std::string dump_sequence(const std::string &s) 58 { 59 return s; 60 } 61 #ifndef BOOST_NO_STD_WSTRING dump_sequence(const std::wstring & s)62 inline std::string dump_sequence(const std::wstring &s) 63 { 64 return narrow<std::string>(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. separator() const126 char_type separator() const { return m_separator; } 127 dump() const128 std::string dump() const { 129 return detail::dump_sequence(m_value); 130 } 131 132 /// Append a second path to this one. 133 /// @pre o's separator is the same as this one's, or o has no separators operator /=(const string_path & o)134 string_path& operator /=(const string_path &o) { 135 // If it's single, there's no separator. This allows to do 136 // p /= "piece"; 137 // even for non-default separators. 138 BOOST_ASSERT((m_separator == o.m_separator 139 || o.empty() 140 || o.single()) 141 && "Incompatible paths."); 142 if(!o.empty()) { 143 String sub; 144 if(!this->empty()) { 145 sub.push_back(m_separator); 146 } 147 sub.insert(sub.end(), o.cstart(), o.m_value.end()); 148 detail::append_and_preserve_iter(m_value, sub, m_start, 149 typename std::iterator_traits<s_iter>::iterator_category()); 150 } 151 return *this; 152 } 153 154 private: 155 typedef typename String::iterator s_iter; 156 typedef typename String::const_iterator s_c_iter; 157 String m_value; 158 char_type m_separator; 159 Translator m_tr; 160 s_iter m_start; cstart() const161 s_c_iter cstart() const { return m_start; } 162 }; 163 164 template <typename String, typename Translator> inline string_path(char_type separator)165 string_path<String, Translator>::string_path(char_type separator) 166 : m_separator(separator), m_start(m_value.begin()) 167 {} 168 169 template <typename String, typename Translator> inline string_path(const String & value,char_type separator,Translator tr)170 string_path<String, Translator>::string_path(const String &value, 171 char_type separator, 172 Translator tr) 173 : m_value(value), m_separator(separator), 174 m_tr(tr), m_start(m_value.begin()) 175 {} 176 177 template <typename String, typename Translator> inline string_path(const char_type * value,char_type separator,Translator tr)178 string_path<String, Translator>::string_path(const char_type *value, 179 char_type separator, 180 Translator tr) 181 : m_value(value), m_separator(separator), 182 m_tr(tr), m_start(m_value.begin()) 183 {} 184 185 template <typename String, typename Translator> inline string_path(const string_path & o)186 string_path<String, Translator>::string_path(const string_path &o) 187 : m_value(o.m_value), m_separator(o.m_separator), 188 m_tr(o.m_tr), m_start(m_value.begin()) 189 { 190 std::advance(m_start, std::distance(o.m_value.begin(), o.cstart())); 191 } 192 193 template <typename String, typename Translator> inline 194 string_path<String, Translator>& operator =(const string_path & o)195 string_path<String, Translator>::operator =(const string_path &o) 196 { 197 m_value = o.m_value; 198 m_separator = o.m_separator; 199 m_tr = o.m_tr; 200 m_start = m_value.begin(); 201 std::advance(m_start, std::distance(o.m_value.begin(), o.cstart())); 202 return *this; 203 } 204 205 template <typename String, typename Translator> reduce()206 typename Translator::external_type string_path<String, Translator>::reduce() 207 { 208 BOOST_ASSERT(!empty() && "Reducing empty path"); 209 210 s_iter next_sep = std::find(m_start, m_value.end(), m_separator); 211 String part(m_start, next_sep); 212 m_start = next_sep; 213 if(!empty()) { 214 // Unless we're at the end, skip the separator we found. 215 ++m_start; 216 } 217 218 if(optional<key_type> key = m_tr.get_value(part)) { 219 return *key; 220 } 221 BOOST_PROPERTY_TREE_THROW(ptree_bad_path("Path syntax error", *this)); 222 } 223 224 template <typename String, typename Translator> inline empty() const225 bool string_path<String, Translator>::empty() const 226 { 227 return m_start == m_value.end(); 228 } 229 230 template <typename String, typename Translator> inline single() const231 bool string_path<String, Translator>::single() const 232 { 233 return std::find(static_cast<s_c_iter>(m_start), 234 m_value.end(), m_separator) 235 == m_value.end(); 236 } 237 238 // By default, this is the path for strings. You can override this by 239 // specializing path_of for a more specific form of std::basic_string. 240 template <typename Ch, typename Traits, typename Alloc> 241 struct path_of< std::basic_string<Ch, Traits, Alloc> > 242 { 243 typedef std::basic_string<Ch, Traits, Alloc> _string; 244 typedef string_path< _string, id_translator<_string> > type; 245 }; 246 247 template <typename String, typename Translator> inline operator /(string_path<String,Translator> p1,const string_path<String,Translator> & p2)248 string_path<String, Translator> operator /( 249 string_path<String, Translator> p1, 250 const string_path<String, Translator> &p2) 251 { 252 p1 /= p2; 253 return p1; 254 } 255 256 // These shouldn't be necessary, but GCC won't find the one above. 257 template <typename String, typename Translator> inline operator /(string_path<String,Translator> p1,const typename String::value_type * p2)258 string_path<String, Translator> operator /( 259 string_path<String, Translator> p1, 260 const typename String::value_type *p2) 261 { 262 p1 /= p2; 263 return p1; 264 } 265 266 template <typename String, typename Translator> inline operator /(const typename String::value_type * p1,const string_path<String,Translator> & p2)267 string_path<String, Translator> operator /( 268 const typename String::value_type *p1, 269 const string_path<String, Translator> &p2) 270 { 271 string_path<String, Translator> t(p1); 272 t /= p2; 273 return t; 274 } 275 276 }} 277 278 #endif 279