From: Helge Norberg Date: Wed, 2 Dec 2015 16:44:25 +0000 (+0100) Subject: Created abstraction for property tree get(), get_child() and get_value() to be able... X-Git-Tag: 2.1.0_Beta1~154 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=5745b88f98fd06fdc4ee936d4ce4f3ab9c5de749;p=casparcg Created abstraction for property tree get(), get_child() and get_value() to be able to throw our own exception with callstack and context_info to easier debug XML errors. Also integrated ptree iteration with context_info to automatically provide the xpath where iteration is at --- diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index d39d10a7d..6c5057ebe 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -81,6 +81,7 @@ set(HEADERS param.h polling_filesystem_monitor.h prec_timer.h + ptree.h scope_exit.h semaphore.h stdafx.h diff --git a/common/ptree.h b/common/ptree.h new file mode 100644 index 000000000..a45e838b3 --- /dev/null +++ b/common/ptree.h @@ -0,0 +1,202 @@ +/* +* Copyright (c) 2011 Sveriges Television AB +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Helge Norberg, helge.norberg@svt.se +*/ + +#pragma once + +#include "except.h" + +#include "memory.h" + +#include +#include +#include +#include +#include + +#include + +namespace caspar { + +struct ptree_exception : virtual user_error { }; + +static std::string to_xpath(std::string path) +{ + path.insert(path.begin(), '/'); + boost::replace_all(path, ".", "@"); + boost::replace_all(path, ".", "/"); + return path; +} + +template +T ptree_get(const Ptree& ptree, const typename Ptree::key_type& path) +{ + try + { + return ptree.get(path); + } + catch (boost::property_tree::ptree_bad_path&) + { + CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("No such element: " + to_xpath(u8(path)))); + } + catch (const boost::property_tree::ptree_bad_data& e) + { + CASPAR_SCOPED_CONTEXT_MSG(to_xpath(u8(path))) + CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info(e.what())); + } +} + +template +T ptree_get_value(const Ptree& ptree) +{ + try + { + return ptree.get_value(); + } + catch (const boost::property_tree::ptree_bad_data& e) + { + CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info(e.what())); + } +} + +template +const Ptree& ptree_get_child(const Ptree& ptree, const typename Ptree::key_type& path) +{ + try + { + return ptree.get_child(path); + } + catch (boost::property_tree::ptree_bad_path&) + { + CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("No such element: " + to_xpath(u8(path)))); + } +} + +template +class scope_aware_ptree_child_range +{ + const Ptree& child_; + spl::shared_ptr ctx_; + + typedef std::pair type; +public: + class scoped_const_iterator : public boost::iterator_facade + { + spl::shared_ptr ctx_; + typename Ptree::const_iterator wrapped_; + public: + scoped_const_iterator(spl::shared_ptr ctx, typename Ptree::const_iterator it) + : ctx_(std::move(ctx)) + , wrapped_(std::move(it)) + { + } + + void increment() + { + ++wrapped_; + } + + bool equal(const scoped_const_iterator& other) const + { + return wrapped_ == other.wrapped_; + } + + const type& dereference() const + { + return *wrapped_; + } + }; + + typedef scoped_const_iterator iterator; + typedef scoped_const_iterator const_iterator; + + scope_aware_ptree_child_range(const Ptree& parent, const typename Ptree::key_type& path) + : child_(ptree_get_child(parent, path)) + , ctx_(spl::make_shared(to_xpath(u8(path)))) + { + } + + scoped_const_iterator begin() const + { + return scoped_const_iterator(ctx_, child_.begin()); + } + + scoped_const_iterator end() const + { + return scoped_const_iterator(ctx_, child_.end()); + } +}; + +template +struct iterate_children_tag +{ + Key val; + + iterate_children_tag(Key val_) + : val(std::move(val_)) + { + } +}; + +typedef iterate_children_tag witerate_children; +typedef iterate_children_tag iterate_children; + +template +scope_aware_ptree_child_range operator|(const Ptree& ptree, iterate_children_tag path) +{ + return scope_aware_ptree_child_range(ptree, path.val); +} + +template +struct basic_scoped_element_translator +{ + mutable std::shared_ptr ctx; + mutable std::map by_name; + + typedef const std::pair& result_type; + + result_type operator()(result_type pair) const + { + if (!ctx) // Lazy + ctx.reset(new scoped_context); + + ctx->replace_msg("/" + u8(pair.first) + "[" + boost::lexical_cast(++by_name[pair.first]) + "]"); + return pair; + } +}; + +template struct element_context_iteration_tag { }; +static element_context_iteration_tag welement_context_iteration; +static element_context_iteration_tag element_context_iteration; + +template +auto operator|(const Range& rng, element_context_iteration_tag tag) -> decltype(rng | boost::adaptors::transformed(basic_scoped_element_translator())) +{ + return rng | boost::adaptors::transformed(basic_scoped_element_translator()); +} + +template +void ptree_verify_element_name(const std::pair& elem, const Str& expected) +{ + if (elem.first != expected) + CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("Expected element named " + u8(expected))); +} + +}