]> git.sesse.net Git - casparcg/blob - common/ptree.h
Remove most of boost::lexical_cast.
[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/iterator/iterator_facade.hpp>
32
33 #include <map>
34
35 namespace caspar {
36
37 struct ptree_exception : virtual user_error { };
38
39 static std::string to_xpath(std::string path)
40 {
41         path.insert(path.begin(), '/');
42         boost::replace_all(path, "<xmlattr>.", "@");
43         boost::replace_all(path, ".", "/");
44         return path;
45 }
46
47 template <typename T, typename Ptree>
48 T ptree_get(const Ptree& ptree, const typename Ptree::key_type& path)
49 {
50         try
51         {
52                 return ptree.template get<T>(path);
53         }
54         catch (boost::property_tree::ptree_bad_path&)
55         {
56                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("No such element: " + to_xpath(u8(path))));
57         }
58         catch (const boost::property_tree::ptree_bad_data& e)
59         {
60                 CASPAR_SCOPED_CONTEXT_MSG(to_xpath(u8(path)))
61                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info(e.what()));
62         }
63 }
64
65 template <typename T, typename Ptree>
66 T ptree_get_value(const Ptree& ptree)
67 {
68         try
69         {
70                 return ptree.template get_value<T>();
71         }
72         catch (const boost::property_tree::ptree_bad_data& e)
73         {
74                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info(e.what()));
75         }
76 }
77
78 template <typename Ptree>
79 const Ptree& ptree_get_child(const Ptree& ptree, const typename Ptree::key_type& path)
80 {
81         try
82         {
83                 return ptree.get_child(path);
84         }
85         catch (boost::property_tree::ptree_bad_path&)
86         {
87                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("No such element: " + to_xpath(u8(path))));
88         }
89 }
90
91 template <typename Ptree>
92 class scope_aware_ptree_child_range
93 {
94         const Ptree&                                    child_;
95         spl::shared_ptr<scoped_context> ctx_;
96
97         typedef std::pair<const typename Ptree::key_type, Ptree> type;
98 public:
99         class scoped_const_iterator : public boost::iterator_facade<scoped_const_iterator, type const, boost::forward_traversal_tag>
100         {
101                 spl::shared_ptr<scoped_context> ctx_;
102                 typename Ptree::const_iterator  wrapped_;
103         public:
104                 scoped_const_iterator(spl::shared_ptr<scoped_context> ctx, typename Ptree::const_iterator it)
105                         : ctx_(std::move(ctx))
106                         , wrapped_(std::move(it))
107                 {
108                 }
109
110                 void increment()
111                 {
112                         ++wrapped_;
113                 }
114
115                 bool equal(const scoped_const_iterator& other) const
116                 {
117                         return wrapped_ == other.wrapped_;
118                 }
119
120                 const type& dereference() const
121                 {
122                         return *wrapped_;
123                 }
124         };
125
126         typedef scoped_const_iterator iterator;
127         typedef scoped_const_iterator const_iterator;
128
129         scope_aware_ptree_child_range(const Ptree& parent, const typename Ptree::key_type& path)
130                 : child_(ptree_get_child(parent, path))
131                 , ctx_(spl::make_shared<scoped_context>(to_xpath(u8(path))))
132         {
133         }
134
135         scoped_const_iterator begin() const
136         {
137                 return scoped_const_iterator(ctx_, child_.begin());
138         }
139
140         scoped_const_iterator end() const
141         {
142                 return scoped_const_iterator(ctx_, child_.end());
143         }
144 };
145
146 template <typename Key>
147 struct iterate_children_tag
148 {
149         Key val;
150
151         iterate_children_tag(Key val_)
152                 : val(std::move(val_))
153         {
154         }
155 };
156
157 typedef iterate_children_tag<std::wstring> witerate_children;
158 typedef iterate_children_tag<std::string> iterate_children;
159
160 template <typename Ptree>
161 scope_aware_ptree_child_range<Ptree> operator|(const Ptree& ptree, iterate_children_tag<typename Ptree::key_type> path)
162 {
163         return scope_aware_ptree_child_range<Ptree>(ptree, path.val);
164 }
165
166 template<typename Ptree>
167 struct basic_scoped_element_translator
168 {
169         mutable std::shared_ptr<scoped_context>                 ctx;
170         mutable std::map<typename Ptree::key_type, int> by_name;
171
172         typedef const std::pair<const typename Ptree::key_type, Ptree>& result_type;
173
174         result_type operator()(result_type pair) const
175         {
176                 if (!ctx) // Lazy
177                         ctx.reset(new scoped_context);
178
179                 ctx->replace_msg("/" + u8(pair.first) + "[" + std::to_string(++by_name[pair.first]) + "]");
180                 return pair;
181         }
182 };
183
184 template <typename Ptree> struct element_context_iteration_tag { };
185 static element_context_iteration_tag<typename boost::property_tree::wptree> welement_context_iteration;
186 static element_context_iteration_tag<typename boost::property_tree::ptree> element_context_iteration;
187
188 template <typename Range, typename Ptree>
189 auto operator|(const Range& rng, element_context_iteration_tag<Ptree> tag) -> decltype(rng | boost::adaptors::transformed(basic_scoped_element_translator<Ptree>()))
190 {
191         return rng | boost::adaptors::transformed(basic_scoped_element_translator<Ptree>());
192 }
193
194 template <typename Key, typename Ptree, typename Str>
195 void ptree_verify_element_name(const std::pair<const Key, Ptree>& elem, const Str& expected)
196 {
197         if (elem.first != expected)
198                 CASPAR_THROW_EXCEPTION(ptree_exception() << msg_info("Expected element named " + u8(expected) + ". Was " + u8(elem.first)));
199 }
200
201 }