]> git.sesse.net Git - casparcg/blob - common/ptree.h
[CHANGELOG] Updated
[casparcg] / common / ptree.h
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #pragma once
23
24 #include "except.h"
25
26 #include "memory.h"
27
28 #include <boost/property_tree/ptree.hpp>
29 #include <boost/algorithm/string/replace.hpp>
30 #include <boost/range/adaptor/transformed.hpp>
31 #include <boost/lexical_cast.hpp>
32 #include <boost/iterator/iterator_facade.hpp>
33
34 #include <map>
35
36 namespace caspar {
37
38 struct ptree_exception : virtual user_error { };
39
40 static std::string to_xpath(std::string path)
41 {
42         path.insert(path.begin(), '/');
43         boost::replace_all(path, "<xmlattr>.", "@");
44         boost::replace_all(path, ".", "/");
45         return path;
46 }
47
48 template <typename T, typename Ptree>
49 T ptree_get(const Ptree& ptree, const typename Ptree::key_type& path)
50 {
51         try
52         {
53                 return ptree.template get<T>(path);
54         }
55         catch (boost::property_tree::ptree_bad_path&)
56         {
57                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("No such element: " + to_xpath(u8(path))));
58         }
59         catch (const boost::property_tree::ptree_bad_data& e)
60         {
61                 CASPAR_SCOPED_CONTEXT_MSG(to_xpath(u8(path)))
62                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info(e.what()));
63         }
64 }
65
66 template <typename T, typename Ptree>
67 T ptree_get_value(const Ptree& ptree)
68 {
69         try
70         {
71                 return ptree.template get_value<T>();
72         }
73         catch (const boost::property_tree::ptree_bad_data& e)
74         {
75                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info(e.what()));
76         }
77 }
78
79 template <typename Ptree>
80 const Ptree& ptree_get_child(const Ptree& ptree, const typename Ptree::key_type& path)
81 {
82         try
83         {
84                 return ptree.get_child(path);
85         }
86         catch (boost::property_tree::ptree_bad_path&)
87         {
88                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("No such element: " + to_xpath(u8(path))));
89         }
90 }
91
92 template <typename Ptree>
93 class scope_aware_ptree_child_range
94 {
95         const Ptree&                                    child_;
96         spl::shared_ptr<scoped_context> ctx_;
97
98         typedef std::pair<const typename Ptree::key_type, Ptree> type;
99 public:
100         class scoped_const_iterator : public boost::iterator_facade<scoped_const_iterator, type const, boost::forward_traversal_tag>
101         {
102                 spl::shared_ptr<scoped_context> ctx_;
103                 typename Ptree::const_iterator  wrapped_;
104         public:
105                 scoped_const_iterator(spl::shared_ptr<scoped_context> ctx, typename Ptree::const_iterator it)
106                         : ctx_(std::move(ctx))
107                         , wrapped_(std::move(it))
108                 {
109                 }
110
111                 void increment()
112                 {
113                         ++wrapped_;
114                 }
115
116                 bool equal(const scoped_const_iterator& other) const
117                 {
118                         return wrapped_ == other.wrapped_;
119                 }
120
121                 const type& dereference() const
122                 {
123                         return *wrapped_;
124                 }
125         };
126
127         typedef scoped_const_iterator iterator;
128         typedef scoped_const_iterator const_iterator;
129
130         scope_aware_ptree_child_range(const Ptree& parent, const typename Ptree::key_type& path)
131                 : child_(ptree_get_child(parent, path))
132                 , ctx_(spl::make_shared<scoped_context>(to_xpath(u8(path))))
133         {
134         }
135
136         scoped_const_iterator begin() const
137         {
138                 return scoped_const_iterator(ctx_, child_.begin());
139         }
140
141         scoped_const_iterator end() const
142         {
143                 return scoped_const_iterator(ctx_, child_.end());
144         }
145 };
146
147 template <typename Key>
148 struct iterate_children_tag
149 {
150         Key val;
151
152         iterate_children_tag(Key val_)
153                 : val(std::move(val_))
154         {
155         }
156 };
157
158 typedef iterate_children_tag<std::wstring> witerate_children;
159 typedef iterate_children_tag<std::string> iterate_children;
160
161 template <typename Ptree>
162 scope_aware_ptree_child_range<Ptree> operator|(const Ptree& ptree, iterate_children_tag<typename Ptree::key_type> path)
163 {
164         return scope_aware_ptree_child_range<Ptree>(ptree, path.val);
165 }
166
167 template<typename Ptree>
168 struct basic_scoped_element_translator
169 {
170         mutable std::shared_ptr<scoped_context>                 ctx;
171         mutable std::map<typename Ptree::key_type, int> by_name;
172
173         typedef const std::pair<const typename Ptree::key_type, Ptree>& result_type;
174
175         result_type operator()(result_type pair) const
176         {
177                 if (!ctx) // Lazy
178                         ctx.reset(new scoped_context);
179
180                 ctx->replace_msg("/" + u8(pair.first) + "[" + boost::lexical_cast<std::string>(++by_name[pair.first]) + "]");
181                 return pair;
182         }
183 };
184
185 template <typename Ptree> struct element_context_iteration_tag { };
186 static element_context_iteration_tag<typename boost::property_tree::wptree> welement_context_iteration;
187 static element_context_iteration_tag<typename boost::property_tree::ptree> element_context_iteration;
188
189 template <typename Range, typename Ptree>
190 auto operator|(const Range& rng, element_context_iteration_tag<Ptree> tag) -> decltype(rng | boost::adaptors::transformed(basic_scoped_element_translator<Ptree>()))
191 {
192         return rng | boost::adaptors::transformed(basic_scoped_element_translator<Ptree>());
193 }
194
195 template <typename Key, typename Ptree, typename Str>
196 void ptree_verify_element_name(const std::pair<const Key, Ptree>& elem, const Str& expected)
197 {
198         if (elem.first != expected)
199                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("Expected element named " + u8(expected) + ". Was " + u8(elem.first)));
200 }
201
202 }