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
30bool ReaderMapping::s_translations_enabled = true;
31
32ReaderMapping::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
39ReaderIterator
40ReaderMapping::get_iter() const
41{
42 assert_is_array(m_doc, m_sx);
43
44 return ReaderIterator(m_doc, m_sx);
45}
46
47const sexp::Value*
48ReaderMapping::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
82bool
83ReaderMapping::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
88bool
89ReaderMapping::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
94bool
95ReaderMapping::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
100bool
101ReaderMapping::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
108bool
109ReaderMapping::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
157bool
158ReaderMapping::get(const char* key, std::vector<bool>& value) const
159{
160 value.clear();
161 GET_VALUES_MACRO("bool", is_boolean, as_bool);
162}
163
164bool
165ReaderMapping::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
172bool
173ReaderMapping::get(const char* key, std::vector<float>& value) const
174{
175 value.clear();
176 GET_VALUES_MACRO("float", is_real, as_float);
177}
178
179bool
180ReaderMapping::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
186bool
187ReaderMapping::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
195bool
196ReaderMapping::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
207bool
208ReaderMapping::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
219bool
220ReaderMapping::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