// ---------------------------------------------------------------------------- // Copyright (C) 2002-2005 Marcin Kalicinski // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // For more information, see www.boost.org // ---------------------------------------------------------------------------- #ifndef BOOST_PROPERTY_TREE_DETAIL_PTREE_IMPLEMENTATION_HPP_INCLUDED #define BOOST_PROPERTY_TREE_DETAIL_PTREE_IMPLEMENTATION_HPP_INCLUDED #include #include #include #include #include #include // for std::less #include // for std::auto_ptr #include #include #include #include // for boost::prior #include ////////////////////////////////////////////////////////////////////////////// // Debug macros #ifdef BOOST_PROPERTY_TREE_DEBUG // Increment instances counter #define BOOST_PROPERTY_TREE_DEBUG_INCREMENT_INSTANCES_COUNT() \ { \ typedef boost::detail::lightweight_mutex::scoped_lock lock; \ lock l(debug_mutex); \ ++debug_instances_count; \ } // Decrement instances counter #define BOOST_PROPERTY_TREE_DEBUG_DECREMENT_INSTANCES_COUNT() \ { \ typedef boost::detail::lightweight_mutex::scoped_lock lock; \ lock l(debug_mutex); \ BOOST_ASSERT(debug_instances_count > 0); \ --debug_instances_count; \ } #else // BOOST_PROPERTY_TREE_DEBUG #define BOOST_PROPERTY_TREE_DEBUG_INCREMENT_INSTANCES_COUNT() static_cast(0) #define BOOST_PROPERTY_TREE_DEBUG_DECREMENT_INSTANCES_COUNT() static_cast(0) #endif // BOOST_PROPERTY_TREE_DEBUG namespace boost { namespace property_tree { namespace detail { template struct array_to_pointer_decay { typedef T type; }; template struct array_to_pointer_decay { typedef const T *type; }; //////////////////////////////////////////////////////////////////////////// // Extractor and inserter template struct extractor { inline bool operator()(const std::basic_string &data, Type &extracted, const std::locale &loc) const { std::basic_istringstream stream(data); stream.imbue(loc); stream >> extracted >> std::ws; return stream.eof() && !stream.fail() && !stream.bad(); } }; template struct extractor > { inline bool operator()(const std::basic_string &data, std::basic_string &extracted, const std::locale &loc) const { extracted = data; return true; } }; template struct inserter { inline bool operator()(std::basic_string &data, const Type &to_insert, const std::locale &loc) const { typedef typename detail::array_to_pointer_decay::type Type2; std::basic_ostringstream stream; stream.imbue(loc); if (std::numeric_limits::is_specialized) stream.precision(std::numeric_limits::digits10 + 1); stream << to_insert; data = stream.str(); return !stream.fail() && !stream.bad(); } }; template struct inserter > { inline bool operator()(std::basic_string &data, const std::basic_string &to_insert, const std::locale &loc) const { data = to_insert; return true; } }; } /////////////////////////////////////////////////////////////////////////// // Impl template struct basic_ptree::impl { data_type m_data; container_type m_container; index_type m_index; }; //////////////////////////////////////////////////////////////////////////// // Traits template struct ptree_traits { typedef Ch char_type; typedef std::basic_string key_type; typedef std::basic_string data_type; template struct extractor: public detail::extractor { }; template struct inserter: public detail::inserter { }; inline bool operator()(const key_type &key1, const key_type &key2) const { return key1 < key2; } }; template struct iptree_traits { std::locale loc; typedef Ch char_type; typedef std::basic_string key_type; typedef std::basic_string data_type; template struct extractor: public detail::extractor { }; template struct inserter: public detail::inserter { }; inline bool operator()(Ch c1, Ch c2) const // Helper for comparing characters { return std::toupper(c1, loc) < std::toupper(c2, loc); } inline bool operator()(const key_type &key1, const key_type &key2) const { return std::lexicographical_compare(key1.begin(), key1.end(), key2.begin(), key2.end(), *this); } }; /////////////////////////////////////////////////////////////////////////// // Exceptions class ptree_error: public std::runtime_error { public: ptree_error(const std::string &what): std::runtime_error(what) { } ~ptree_error() throw() { } }; class ptree_bad_data: public ptree_error { public: ptree_bad_data(const std::string &what): ptree_error(what) { } ~ptree_bad_data() throw() { } }; class ptree_bad_path: public ptree_error { public: ptree_bad_path(const std::string &what): ptree_error(what) { } ~ptree_bad_path() throw() { } }; /////////////////////////////////////////////////////////////////////////// // Construction & destruction template basic_ptree::basic_ptree() { m_impl = new impl; BOOST_PROPERTY_TREE_DEBUG_INCREMENT_INSTANCES_COUNT(); } template basic_ptree::basic_ptree(const data_type &rhs) { std::auto_ptr tmp(new impl); tmp->m_data = rhs; m_impl = tmp.release(); BOOST_PROPERTY_TREE_DEBUG_INCREMENT_INSTANCES_COUNT(); } template basic_ptree::basic_ptree(const basic_ptree &rhs) { std::auto_ptr tmp(new impl); tmp->m_data = rhs.data(); m_impl = tmp.get(); insert(end(), rhs.begin(), rhs.end()); tmp.release(); BOOST_PROPERTY_TREE_DEBUG_INCREMENT_INSTANCES_COUNT(); } template basic_ptree::~basic_ptree() { BOOST_PROPERTY_TREE_DEBUG_DECREMENT_INSTANCES_COUNT(); delete m_impl; } /////////////////////////////////////////////////////////////////////////// // Iterator access template typename basic_ptree::iterator basic_ptree::begin() { return m_impl->m_container.begin(); } template typename basic_ptree::const_iterator basic_ptree::begin() const { return m_impl->m_container.begin(); } template typename basic_ptree::iterator basic_ptree::end() { return m_impl->m_container.end(); } template typename basic_ptree::const_iterator basic_ptree::end() const { return m_impl->m_container.end(); } template typename basic_ptree::reverse_iterator basic_ptree::rbegin() { return m_impl->m_container.rbegin(); } template typename basic_ptree::const_reverse_iterator basic_ptree::rbegin() const { return m_impl->m_container.rbegin(); } template typename basic_ptree::reverse_iterator basic_ptree::rend() { return m_impl->m_container.rend(); } template typename basic_ptree::const_reverse_iterator basic_ptree::rend() const { return m_impl->m_container.rend(); } /////////////////////////////////////////////////////////////////////////// // Data access template typename basic_ptree::size_type basic_ptree::size() const { return m_impl->m_index.size(); } template bool basic_ptree::empty() const { return m_impl->m_index.empty(); } template typename basic_ptree::data_type & basic_ptree::data() { return m_impl->m_data; } template const typename basic_ptree::data_type & basic_ptree::data() const { return m_impl->m_data; } template typename basic_ptree::value_type & basic_ptree::front() { return m_impl->m_container.front(); } template const typename basic_ptree::value_type & basic_ptree::front() const { return m_impl->m_container.front(); } template typename basic_ptree::value_type & basic_ptree::back() { return m_impl->m_container.back(); } template const typename basic_ptree::value_type & basic_ptree::back() const { return m_impl->m_container.back(); } /////////////////////////////////////////////////////////////////////////// // Operators template basic_ptree & basic_ptree::operator =(const basic_ptree &rhs) { if (&rhs != this) { clear(); data() = rhs.data(); insert(end(), rhs.begin(), rhs.end()); } return *this; } template bool basic_ptree::operator ==(const basic_ptree &rhs) const { // Data and sizes must be equal if (size() != rhs.size() || data() != rhs.data()) return false; // Keys and children must be equal Tr tr; const_iterator it = begin(); const_iterator it_rhs = rhs.begin(); const_iterator it_end = end(); for (; it != it_end; ++it, ++it_rhs) if (tr(it->first, it_rhs->first) || tr(it_rhs->first, it->first) || it->second != it_rhs->second) return false; // Equal return true; } template bool basic_ptree::operator !=(const basic_ptree &rhs) const { return !operator ==(rhs); } /////////////////////////////////////////////////////////////////////////// // Container operations template typename basic_ptree::iterator basic_ptree::find(const key_type &key) { typename index_type::iterator it = m_impl->m_index.find(key); return it == m_impl->m_index.end() ? end() : it->second; } template typename basic_ptree::const_iterator basic_ptree::find(const key_type &key) const { typename index_type::const_iterator it = m_impl->m_index.find(key); return it == m_impl->m_index.end() ? end() : it->second; } template typename basic_ptree::size_type basic_ptree::count(const key_type &key) const { return m_impl->m_index.count(key); } template void basic_ptree::clear() { m_impl->m_data = data_type(); m_impl->m_container.clear(); m_impl->m_index.clear(); } template typename basic_ptree::iterator basic_ptree::insert(iterator where, const value_type &value) { // Insert new value into container. If that throws nothing needs to be rolled back where = m_impl->m_container.insert(where, value); // Update index. If that throws we need to rollback the insert try { m_impl->m_index.insert(typename index_type::value_type(where->first, where)); } catch (...) { m_impl->m_container.erase(where); // rollback the insert throw; } return where; } template template void basic_ptree::insert(iterator where, It first, It last) { for (; first != last; ++first, ++where) where = insert(where, value_type(first->first, first->second)); } template typename basic_ptree::iterator basic_ptree::erase(iterator where) { // Remove from index typename index_type::iterator lo = m_impl->m_index.lower_bound(where->first); typename index_type::iterator hi = m_impl->m_index.upper_bound(where->first); for (; lo != hi; ++lo) if (lo->second == where) { m_impl->m_index.erase(lo); break; } // Remove from container return m_impl->m_container.erase(where); } template typename basic_ptree::size_type basic_ptree::erase(const key_type &key) { size_type count = 0; typename index_type::iterator lo = m_impl->m_index.lower_bound(key); if (lo != m_impl->m_index.end()) { typename index_type::iterator hi = m_impl->m_index.upper_bound(key); while (lo != hi) { typename index_type::iterator it = lo++; erase(it->second); ++count; } } return count; } template template typename basic_ptree::iterator basic_ptree::erase(It first, It last) { while (first != last) first = erase(first); return first; } template typename basic_ptree::iterator basic_ptree::push_front(const value_type &value) { return insert(begin(), value); } template typename basic_ptree::iterator basic_ptree::push_back(const value_type &value) { return insert(end(), value); } template void basic_ptree::pop_front() { erase(begin()); } template void basic_ptree::pop_back() { erase(boost::prior(end())); } template void basic_ptree::swap(basic_ptree &rhs) { std::swap(m_impl, rhs.m_impl); } template void basic_ptree::reverse() { m_impl->m_container.reverse(); } template template void basic_ptree::sort(SortTr tr) { m_impl->m_container.sort(tr); } /////////////////////////////////////////////////////////////////////////// // ptree operations // Get child ptree with custom separator template basic_ptree & basic_ptree::get_child(char_type separator, const key_type &path) { if (optional &> result = get_child_optional(separator, path)) return result.get(); else throw ptree_bad_path("key \"" + detail::narrow(path.c_str()) + "\" does not exist"); } // Get child ptree with custom separator template const basic_ptree & basic_ptree::get_child(char_type separator, const key_type &path) const { basic_ptree *nc_this = const_cast *>(this); return nc_this->get_child(separator, path); } // Get child ptree with custom separator template basic_ptree & basic_ptree::get_child(char_type separator, const key_type &path, basic_ptree &default_value) { if (optional &> result = get_child_optional(separator, path)) return result.get(); else return default_value; } // Get child ptree with custom separator template const basic_ptree & basic_ptree::get_child(char_type separator, const key_type &path, const basic_ptree &default_value) const { basic_ptree *nc_this = const_cast *>(this); basic_ptree &nc_default_value = const_cast &>(default_value); return nc_this->get_child(separator, path, nc_default_value); } // Get child ptree with custom separator template optional &> basic_ptree::get_child_optional(char_type separator, const key_type &path) { typename key_type::size_type n = path.find(separator); if (n != key_type::npos) { key_type head = path.substr(0, n); key_type tail = path.substr(n + 1, key_type::npos); iterator it = find(head); if (it != end()) return it->second.get_child_optional(separator, tail); else return optional &>(); } else { iterator it = find(path); if (it != end()) return it->second; else return optional &>(); } } // Get child ptree with custom separator template optional &> basic_ptree::get_child_optional(char_type separator, const key_type &path) const { basic_ptree *nc_this = const_cast *>(this); optional &> tmp = nc_this->get_child_optional(separator, path); if (tmp) return optional &>(tmp.get()); else return optional &>(); } // Get child ptree with default separator template basic_ptree & basic_ptree::get_child(const key_type &path) { return get_child(char_type('.'), path); } // Get child ptree with default separator template const basic_ptree & basic_ptree::get_child(const key_type &path) const { return get_child(char_type('.'), path); } // Get child ptree with default separator template basic_ptree & basic_ptree::get_child(const key_type &path, basic_ptree &default_value) { return get_child(char_type('.'), path, default_value); } // Get child ptree with default separator template const basic_ptree & basic_ptree::get_child(const key_type &path, const basic_ptree &default_value) const { return get_child(char_type('.'), path, default_value); } // Get child ptree with default separator template optional &> basic_ptree::get_child_optional(const key_type &path) { return get_child_optional(char_type('.'), path); } // Get child ptree with default separator template optional &> basic_ptree::get_child_optional(const key_type &path) const { return get_child_optional(char_type('.'), path); } // Put child ptree with custom separator template basic_ptree & basic_ptree::put_child(char_type separator, const key_type &path, const basic_ptree &value, bool do_not_replace) { typename key_type::size_type n = path.find(separator); if (n == key_type::npos) { if (do_not_replace) return push_back(value_type(path, value))->second; else { iterator it = find(path); if (it == end()) return push_back(value_type(path, value))->second; else { it->second = value; return it->second; } } } else { key_type head = path.substr(0, n); key_type tail = path.substr(n + 1, key_type::npos); iterator it = find(head); if (it == end()) it = push_back(value_type(head, basic_ptree())); return it->second.put_child(separator, tail, value, do_not_replace); } } // Put child ptree with default separator template basic_ptree & basic_ptree::put_child(const key_type &path, const basic_ptree &value, bool do_not_replace) { return put_child(char_type('.'), path, value, do_not_replace); } // Get value from data of ptree template template Type basic_ptree::get_own(const std::locale &loc) const { if (optional result = get_own_optional(loc)) return result.get(); else throw ptree_bad_data(std::string("conversion of data into type '") + typeid(Type).name() + "' failed"); } // Get value from data of ptree template template Type basic_ptree::get_own(const Type &default_value, const std::locale &loc) const { if (optional result = get_own_optional(loc)) return result.get(); else return default_value; } // Get value from data of ptree template template std::basic_string basic_ptree::get_own(const CharType *default_value, const std::locale &loc) const { BOOST_STATIC_ASSERT((boost::is_same::value == true)); // Character types must match return get_own(std::basic_string(default_value), loc); } // Get value from data of ptree template template optional basic_ptree::get_own_optional(const std::locale &loc) const { BOOST_STATIC_ASSERT(boost::is_pointer::value == false); // Disallow pointer types, they are unsafe Type tmp; if (typename traits_type::template extractor()(m_impl->m_data, tmp, loc)) { return optional(tmp); } else return optional(); } // Get value from data of child ptree (custom path separator) template template Type basic_ptree::get(char_type separator, const key_type &path, const std::locale &loc) const { return get_child(separator, path).get_own(loc); } // Get value from data of child ptree (custom path separator) template template Type basic_ptree::get(char_type separator, const key_type &path, const Type &default_value, const std::locale &loc) const { if (optional result = get_optional(separator, path, loc)) return *result; else return default_value; } // Get value from data of child ptree (custom path separator) template template std::basic_string basic_ptree::get(char_type separator, const key_type &path, const CharType *default_value, const std::locale &loc) const { BOOST_STATIC_ASSERT((boost::is_same::value == true)); // Character types must match return get(separator, path, std::basic_string(default_value), loc); } // Get value from data of child ptree (custom path separator) template template optional basic_ptree::get_optional(char_type separator, const key_type &path, const std::locale &loc) const { if (optional &> child = get_child_optional(separator, path)) return child.get().get_own_optional(loc); else return optional(); } // Get value from data of child ptree (default path separator) template template Type basic_ptree::get(const key_type &path, const std::locale &loc) const { return get(char_type('.'), path, loc); } // Get value from data of child ptree (default path separator) template template Type basic_ptree::get(const key_type &path, const Type &default_value, const std::locale &loc) const { return get(char_type('.'), path, default_value, loc); } // Get value from data of child ptree (default path separator) template template std::basic_string basic_ptree::get(const key_type &path, const CharType *default_value, const std::locale &loc) const { return get(char_type('.'), path, default_value, loc); } // Get value from data of child ptree (default path separator) template template optional basic_ptree::get_optional(const key_type &path, const std::locale &loc) const { return get_optional(char_type('.'), path, loc); } // Put value in data of ptree template template void basic_ptree::put_own(const Type &value, const std::locale &loc) { using namespace boost; // Make sure that no pointer other than char_type * is allowed BOOST_STATIC_ASSERT((is_pointer::value == false || is_same::type>::type>::value == true)); typename traits_type::template inserter()(m_impl->m_data, value, loc); } // Put value in data of child ptree (custom path separator) template template basic_ptree & basic_ptree::put(char_type separator, const key_type &path, const Type &value, bool do_not_replace, const std::locale &loc) { optional &> child; if (!do_not_replace && (child = get_child_optional(separator, path))) { child.get().put_own(value, loc); return *child; } else { basic_ptree &child2 = put_child(separator, path, empty_ptree >(), do_not_replace); child2.put_own(value, loc); return child2; } } // Put value in data of child ptree (default path separator) template template basic_ptree & basic_ptree::put(const key_type &path, const Type &value, bool do_not_replace, const std::locale &loc) { return put(char_type('.'), path, value, do_not_replace, loc); } //////////////////////////////////////////////////////////////////////////// // Debugging #ifdef BOOST_PROPERTY_TREE_DEBUG template typename basic_ptree::size_type basic_ptree::debug_get_instances_count() { empty_ptree >(); // Make sure empty ptree exists return debug_instances_count - 1; // Do not count empty ptree } template typename basic_ptree::size_type basic_ptree::debug_instances_count; template boost::detail::lightweight_mutex basic_ptree::debug_mutex; #endif /////////////////////////////////////////////////////////////////////////// // Free functions template inline const Ptree &empty_ptree() { static Ptree pt; return pt; } template inline void swap(basic_ptree &pt1, basic_ptree &pt2) { pt1.swap(pt2); } } } // Undefine debug macros #ifdef BOOST_PROPERTY_TREE_DEBUG # undef BOOST_PROPERTY_TREE_DEBUG_INCREMENT_INSTANCES_COUNT # undef BOOST_PROPERTY_TREE_DEBUG_DECREMENT_INSTANCES_COUNT #endif #endif