1 | // SuperTux |
2 | // Copyright (C) 2015 Ingo Ruhnke <grumbel@gmail.com> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "util/reader_mapping.hpp" |
18 | |
19 | #include <boost/ref.hpp> |
20 | #include <boost/utility/typed_in_place_factory.hpp> |
21 | #include <sexp/io.hpp> |
22 | #include <sstream> |
23 | #include <stdexcept> |
24 | |
25 | #include "util/gettext.hpp" |
26 | #include "util/reader_collection.hpp" |
27 | #include "util/reader_document.hpp" |
28 | #include "util/reader_error.hpp" |
29 | |
30 | bool ReaderMapping::s_translations_enabled = true; |
31 | |
32 | ReaderMapping::ReaderMapping(const ReaderDocument& doc, const sexp::Value& sx) : |
33 | m_doc(doc), |
34 | m_sx(sx), |
35 | m_arr([this]() -> decltype(m_arr){ assert_is_array(m_doc, m_sx); return m_sx.as_array();}()) |
36 | { |
37 | } |
38 | |
39 | ReaderIterator |
40 | ReaderMapping::get_iter() const |
41 | { |
42 | assert_is_array(m_doc, m_sx); |
43 | |
44 | return ReaderIterator(m_doc, m_sx); |
45 | } |
46 | |
47 | const sexp::Value* |
48 | ReaderMapping::get_item(const char* key) const |
49 | { |
50 | for (size_t i = 1; i < m_arr.size(); ++i) |
51 | { |
52 | auto const& pair = m_arr[i]; |
53 | |
54 | // size should be >=2 not >=1, but we have to allow smaller once |
55 | // due to get_iter(), e.g. (particles-snow) |
56 | assert_array_size_ge(m_doc, pair, 1); |
57 | |
58 | assert_is_symbol(m_doc, pair.as_array()[0]); |
59 | |
60 | if (pair.as_array()[0].as_string() == key) |
61 | { |
62 | return &pair; |
63 | } |
64 | } |
65 | return nullptr; |
66 | } |
67 | |
68 | #define GET_VALUE_MACRO(type, checker, getter) \ |
69 | auto const sx = get_item(key); \ |
70 | if (!sx) { \ |
71 | if (default_value) { \ |
72 | value = *default_value; \ |
73 | } \ |
74 | return false; \ |
75 | } else { \ |
76 | assert_array_size_eq(m_doc, *sx, 2); \ |
77 | assert_##checker(m_doc, sx->as_array()[1]); \ |
78 | value = sx->as_array()[1].getter(); \ |
79 | return true; \ |
80 | } |
81 | |
82 | bool |
83 | ReaderMapping::get(const char* key, bool& value, const boost::optional<bool>& default_value) const |
84 | { |
85 | GET_VALUE_MACRO("bool" , is_boolean, as_bool); |
86 | } |
87 | |
88 | bool |
89 | ReaderMapping::get(const char* key, int& value, const boost::optional<int>& default_value) const |
90 | { |
91 | GET_VALUE_MACRO("int" , is_integer, as_int); |
92 | } |
93 | |
94 | bool |
95 | ReaderMapping::get(const char* key, uint32_t& value, const boost::optional<uint32_t>& default_value) const |
96 | { |
97 | GET_VALUE_MACRO("uint32_t" , is_integer, as_int); |
98 | } |
99 | |
100 | bool |
101 | ReaderMapping::get(const char* key, float& value, const boost::optional<float>& default_value) const |
102 | { |
103 | GET_VALUE_MACRO("float" , is_real, as_float); |
104 | } |
105 | |
106 | #undef GET_VALUE_MACRO |
107 | |
108 | bool |
109 | ReaderMapping::get(const char* key, std::string& value, const boost::optional<const char*>& default_value) const |
110 | { |
111 | auto const sx = get_item(key); |
112 | if (!sx) { |
113 | if (default_value) { |
114 | value = *default_value; |
115 | } |
116 | return false; |
117 | } else { |
118 | assert_array_size_eq(m_doc, *sx, 2); |
119 | |
120 | auto const& item = sx->as_array(); |
121 | |
122 | if (item[1].is_string()) { |
123 | value = item[1].as_string(); |
124 | return true; |
125 | } else if (item[1].is_array() && |
126 | item[1].as_array().size() == 2 && |
127 | item[1].as_array()[0].is_symbol() && |
128 | item[1].as_array()[0].as_string() == "_" && |
129 | item[1].as_array()[1].is_string()) { |
130 | if (s_translations_enabled) { |
131 | value = _(item[1].as_array()[1].as_string()); |
132 | } else { |
133 | value = item[1].as_array()[1].as_string(); |
134 | } |
135 | return true; |
136 | } else { |
137 | raise_exception(m_doc, item[1], "expected string" ); |
138 | } |
139 | } |
140 | } |
141 | |
142 | #define GET_VALUES_MACRO(type, checker, getter) \ |
143 | auto const sx = get_item(key); \ |
144 | if (!sx) { \ |
145 | return false; \ |
146 | } else { \ |
147 | assert_is_array(m_doc, *sx); \ |
148 | auto const& item = sx->as_array(); \ |
149 | for (size_t i = 1; i < item.size(); ++i) \ |
150 | { \ |
151 | assert_##checker(m_doc, item[i]); \ |
152 | value.emplace_back(item[i].getter()); \ |
153 | } \ |
154 | return true; \ |
155 | } |
156 | |
157 | bool |
158 | ReaderMapping::get(const char* key, std::vector<bool>& value) const |
159 | { |
160 | value.clear(); |
161 | GET_VALUES_MACRO("bool" , is_boolean, as_bool); |
162 | } |
163 | |
164 | bool |
165 | ReaderMapping::get(const char* key, std::vector<int>& value) const |
166 | { |
167 | value.clear(); |
168 | GET_VALUES_MACRO("int" , is_integer, as_int); |
169 | } |
170 | |
171 | |
172 | bool |
173 | ReaderMapping::get(const char* key, std::vector<float>& value) const |
174 | { |
175 | value.clear(); |
176 | GET_VALUES_MACRO("float" , is_real, as_float); |
177 | } |
178 | |
179 | bool |
180 | ReaderMapping::get(const char* key, std::vector<std::string>& value) const |
181 | { |
182 | value.clear(); |
183 | GET_VALUES_MACRO("string" , is_string, as_string); |
184 | } |
185 | |
186 | bool |
187 | ReaderMapping::get(const char* key, std::vector<unsigned int>& value) const |
188 | { |
189 | value.clear(); |
190 | GET_VALUES_MACRO("unsigned int" , is_integer, as_int); |
191 | } |
192 | |
193 | #undef GET_VALUES_MACRO |
194 | |
195 | bool |
196 | ReaderMapping::get(const char* key, boost::optional<ReaderMapping>& value) const |
197 | { |
198 | auto const sx = get_item(key); |
199 | if (sx) { |
200 | value = boost::in_place<ReaderMapping>(boost::ref(m_doc), boost::ref(*sx)); |
201 | return true; |
202 | } else { |
203 | return false; |
204 | } |
205 | } |
206 | |
207 | bool |
208 | ReaderMapping::get(const char* key, boost::optional<ReaderCollection>& value) const |
209 | { |
210 | auto const sx = get_item(key); |
211 | if (sx) { |
212 | value = boost::in_place<ReaderCollection>(boost::ref(m_doc), boost::ref(*sx)); |
213 | return true; |
214 | } else { |
215 | return false; |
216 | } |
217 | } |
218 | |
219 | bool |
220 | ReaderMapping::get(const char* key, sexp::Value& value) const |
221 | { |
222 | auto const sx = get_item(key); |
223 | if (!sx) { |
224 | return false; |
225 | } else { |
226 | assert_array_size_eq(m_doc, *sx, 2); |
227 | value = sx->as_array()[1]; |
228 | return true; |
229 | } |
230 | } |
231 | |
232 | /* EOF */ |
233 | |