Merge lp:~widelands-dev/widelands/json_writer into lp:widelands
- json_writer
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 8969 |
Proposed branch: | lp:~widelands-dev/widelands/json_writer |
Merge into: | lp:widelands |
Diff against target: |
982 lines (+461/-289) 8 files modified
src/website/CMakeLists.txt (+4/-0) src/website/json/CMakeLists.txt (+10/-0) src/website/json/json.cc (+138/-0) src/website/json/json.h (+96/-0) src/website/json/value.cc (+56/-0) src/website/json/value.h (+67/-0) src/website/map_info.cc (+12/-39) src/website/map_object_info.cc (+78/-250) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands/json_writer |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Klaus Halfmann | code review | Approve | |
kaputtnik (community) | testing | Approve | |
Review via email: mp+357908@code.launchpad.net |
Commit message
Refactor website binaries to use a real tree data structure for writing JSON
Description of the change
Getting rid of some spaghetti code. Only affects the website, not Widelands itself.
bunnybot (widelandsofficial) wrote : | # |
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4178. State: passed. Details: https:/
Appveyor build 3976. State: success. Details: https:/
kaputtnik (franku) wrote : | # |
LGTM :-)
Successfully tested locally on the website: Upload a map, create Encyclopedia
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4395. State: failed. Details: https:/
Appveyor build 4186. State: success. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 4401. State: passed. Details: https:/
Appveyor build 4192. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
Anything I can test for this?
GunChleoc (gunchleoc) wrote : | # |
It has already been tested, what's missing is a code review.
Klaus Halfmann (klaus-halfmann) wrote : | # |
OK, for the Reeview:
This branch substitutes a streaming JSON aproach with a Tree Object model.
Streaming is normally better as of resource usage,
but in this case this does not matter.
If kaputtnik likes it his way, its all fine with me.
(Solange nix kaputt geht ;-)
Please explain which website tools are actually affected.
kaputtnik (franku) wrote : | # |
This affects uploading a map onto the website (wl_map_info) and creating the encyclopedia (wl_map_
GunChleoc (gunchleoc) wrote : | # |
The problem with the streaming was that it's very hacky and hard to read. So, we have sacrificed some efficiency and gained a lot of readability/code stability.
Thanks for the review!
@bunnybot merge
Preview Diff
1 | === modified file 'src/website/CMakeLists.txt' |
2 | --- src/website/CMakeLists.txt 2018-08-14 20:42:18 +0000 |
3 | +++ src/website/CMakeLists.txt 2019-01-13 09:00:00 +0000 |
4 | @@ -1,3 +1,5 @@ |
5 | +add_subdirectory(json) |
6 | + |
7 | wl_library(website_common |
8 | SRCS |
9 | website_common.cc |
10 | @@ -23,6 +25,7 @@ |
11 | graphic_surface |
12 | io_fileread |
13 | io_filesystem |
14 | + json |
15 | logic |
16 | map_io_map_loader |
17 | website_common |
18 | @@ -38,6 +41,7 @@ |
19 | graphic |
20 | io_fileread |
21 | io_filesystem |
22 | + json |
23 | logic |
24 | logic_tribe_basic_info |
25 | website_common |
26 | |
27 | === added directory 'src/website/json' |
28 | === added file 'src/website/json/CMakeLists.txt' |
29 | --- src/website/json/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
30 | +++ src/website/json/CMakeLists.txt 2019-01-13 09:00:00 +0000 |
31 | @@ -0,0 +1,10 @@ |
32 | +wl_library(json |
33 | + SRCS |
34 | + json.cc |
35 | + json.h |
36 | + value.cc |
37 | + value.h |
38 | + DEPENDS |
39 | + io_fileread |
40 | + io_filesystem |
41 | +) |
42 | |
43 | === added file 'src/website/json/json.cc' |
44 | --- src/website/json/json.cc 1970-01-01 00:00:00 +0000 |
45 | +++ src/website/json/json.cc 2019-01-13 09:00:00 +0000 |
46 | @@ -0,0 +1,138 @@ |
47 | +/* |
48 | + * Copyright (C) 2018 by the Widelands Development Team |
49 | + * |
50 | + * This program is free software; you can redistribute it and/or |
51 | + * modify it under the terms of the GNU General Public License |
52 | + * as published by the Free Software Foundation; either version 2 |
53 | + * of the License, or (at your option) any later version. |
54 | + * |
55 | + * This program is distributed in the hope that it will be useful, |
56 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
57 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
58 | + * GNU General Public License for more details. |
59 | + * |
60 | + * You should have received a copy of the GNU General Public License |
61 | + * along with this program; if not, write to the Free Software |
62 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
63 | + * |
64 | + */ |
65 | + |
66 | +#include "website/json/json.h" |
67 | + |
68 | +#include <memory> |
69 | + |
70 | +#include "io/filewrite.h" |
71 | + |
72 | +// ########################## JSON Element ############################# |
73 | + |
74 | +namespace JSON { |
75 | +const std::string JSON::Element::tab_ = " "; |
76 | + |
77 | +JSON::Object* Element::add_object(const std::string& key) { |
78 | + children_.push_back(std::unique_ptr<JSON::Object>(new JSON::Object(key, level_ + 1))); |
79 | + return dynamic_cast<JSON::Object*>(children_.back().get()); |
80 | +} |
81 | + |
82 | +JSON::Array* Element::add_array(const std::string& key) { |
83 | + children_.push_back(std::unique_ptr<JSON::Array>(new JSON::Array(key, level_ + 1))); |
84 | + return dynamic_cast<JSON::Array*>(children_.back().get()); |
85 | +} |
86 | + |
87 | +void Element::add_bool(const std::string& key, bool value) { |
88 | + values_.push_back(std::make_pair(key, std::unique_ptr<JSON::Value>(new JSON::Boolean(value)))); |
89 | +} |
90 | + |
91 | +void Element::add_double(const std::string& key, double value) { |
92 | + values_.push_back(std::make_pair(key, std::unique_ptr<JSON::Value>(new JSON::Double(value)))); |
93 | +} |
94 | +void Element::add_int(const std::string& key, int value) { |
95 | + values_.push_back(std::make_pair(key, std::unique_ptr<JSON::Value>(new JSON::Int(value)))); |
96 | +} |
97 | +void Element::add_empty(const std::string& key) { |
98 | + values_.push_back(std::make_pair(key, std::unique_ptr<JSON::Value>(new JSON::Empty()))); |
99 | +} |
100 | +void Element::add_string(const std::string& key, const std::string& value) { |
101 | + values_.push_back(std::make_pair(key, std::unique_ptr<JSON::Value>(new JSON::String(value)))); |
102 | +} |
103 | + |
104 | +void Element::write_to_file(FileSystem& fs, const std::string& filename) const { |
105 | + FileWrite file_writer; |
106 | + file_writer.text(as_string()); |
107 | + file_writer.write(fs, filename); |
108 | +} |
109 | + |
110 | +std::string Element::as_string() const { |
111 | + return "{\n" + children_as_string() + "}\n"; |
112 | +} |
113 | + |
114 | +std::string Element::values_as_string(const std::string& tabs) const { |
115 | + std::string result = ""; |
116 | + if (!values_.empty()) { |
117 | + for (size_t i = 0; i < values_.size() - 1; ++i) { |
118 | + const auto& element = values_.at(i); |
119 | + const std::string element_as_string = element.second->as_string(); |
120 | + result += |
121 | + tabs + tab_ + key_to_string(element.first, element_as_string.empty()) + element_as_string + ",\n"; |
122 | + } |
123 | + const auto& element = values_.at(values_.size() - 1); |
124 | + const std::string element_as_string = element.second->as_string(); |
125 | + result += tabs + tab_ + key_to_string(element.first, element_as_string.empty()) + element_as_string + |
126 | + (children_.empty() ? "\n" : ",\n"); |
127 | + } |
128 | + return result; |
129 | +} |
130 | + |
131 | +std::string Element::children_as_string() const { |
132 | + std::string result = ""; |
133 | + if (!children_.empty()) { |
134 | + for (size_t i = 0; i < children_.size() - 1; ++i) { |
135 | + result += children_.at(i)->as_string() + ",\n"; |
136 | + } |
137 | + result += children_.at(children_.size() - 1)->as_string() + "\n"; |
138 | + } |
139 | + return result; |
140 | +} |
141 | + |
142 | +std::string Element::key_to_string(const std::string& value, bool value_is_empty) { |
143 | + return "\"" + value + (value_is_empty ? "\"" : "\": "); |
144 | +} |
145 | + |
146 | +// ########################## JSON Object ############################# |
147 | + |
148 | +Object::Object(const std::string& key, int level) : JSON::Element(key, level) { |
149 | +} |
150 | + |
151 | +std::string Object::as_string() const { |
152 | + std::string result = ""; |
153 | + std::string tabs = ""; |
154 | + for (int i = 0; i < level_; ++i) { |
155 | + tabs += tab_; |
156 | + } |
157 | + |
158 | + result += tabs + (key_.empty() ? "" : key_to_string(key_)) + "{\n"; |
159 | + result += values_as_string(tabs); |
160 | + result += children_as_string(); |
161 | + result += tabs + "}"; |
162 | + return result; |
163 | +} |
164 | + |
165 | +// ########################## JSON Array ############################# |
166 | + |
167 | +Array::Array(const std::string& key, int level) : JSON::Element(key, level) { |
168 | +} |
169 | + |
170 | +std::string Array::as_string() const { |
171 | + std::string result = ""; |
172 | + std::string tabs = ""; |
173 | + for (int i = 0; i < level_; ++i) { |
174 | + tabs += tab_; |
175 | + } |
176 | + |
177 | + result += tabs + key_to_string(key_) + "[\n"; |
178 | + result += values_as_string(tabs); |
179 | + result += children_as_string(); |
180 | + result += tabs + "]"; |
181 | + return result; |
182 | +} |
183 | + |
184 | +} // namespace JSON |
185 | |
186 | === added file 'src/website/json/json.h' |
187 | --- src/website/json/json.h 1970-01-01 00:00:00 +0000 |
188 | +++ src/website/json/json.h 2019-01-13 09:00:00 +0000 |
189 | @@ -0,0 +1,96 @@ |
190 | +/* |
191 | + * Copyright (C) 2018 by the Widelands Development Team |
192 | + * |
193 | + * This program is free software; you can redistribute it and/or |
194 | + * modify it under the terms of the GNU General Public License |
195 | + * as published by the Free Software Foundation; either version 2 |
196 | + * of the License, or (at your option) any later version. |
197 | + * |
198 | + * This program is distributed in the hope that it will be useful, |
199 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
200 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
201 | + * GNU General Public License for more details. |
202 | + * |
203 | + * You should have received a copy of the GNU General Public License |
204 | + * along with this program; if not, write to the Free Software |
205 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
206 | + * |
207 | + */ |
208 | + |
209 | +#ifndef WL_WEBSITE_JSON_JSON_H |
210 | +#define WL_WEBSITE_JSON_JSON_H |
211 | + |
212 | +#include <cassert> |
213 | +#include <memory> |
214 | +#include <string> |
215 | +#include <vector> |
216 | + |
217 | +#include "io/filesystem/filesystem.h" |
218 | +#include "website/json/value.h" |
219 | + |
220 | +namespace JSON { |
221 | + |
222 | +class Array; |
223 | +class Object; |
224 | + |
225 | +class Element { |
226 | +protected: |
227 | + // Constructor for child node |
228 | + explicit Element(const std::string& key, int level) : key_(key), level_(level) { |
229 | + } |
230 | + |
231 | +public: |
232 | + // Constructor for root node |
233 | + explicit Element() : JSON::Element("", 0) { |
234 | + } |
235 | + |
236 | + JSON::Object* add_object(const std::string& key = ""); |
237 | + JSON::Array* add_array(const std::string& key); |
238 | + void add_bool(const std::string& key, bool value); |
239 | + void add_double(const std::string& key, double value); |
240 | + void add_int(const std::string& key, int value); |
241 | + void add_empty(const std::string& key); |
242 | + void add_string(const std::string& key, const std::string& value); |
243 | + |
244 | + void write_to_file(FileSystem& fs, const std::string& filename) const; |
245 | + |
246 | + virtual std::string as_string() const; |
247 | + |
248 | +protected: |
249 | + static const std::string tab_; |
250 | + |
251 | + std::string values_as_string(const std::string& tabs) const; |
252 | + std::string children_as_string() const; |
253 | + static std::string key_to_string(const std::string& value, bool value_is_empty = false); |
254 | + |
255 | + std::string key_; |
256 | + const int level_; |
257 | + std::vector<std::unique_ptr<JSON::Element>> children_; |
258 | + std::vector<std::pair<std::string, std::unique_ptr<JSON::Value>>> values_; |
259 | +}; |
260 | + |
261 | +class Object : public Element { |
262 | + friend class JSON::Element; |
263 | + |
264 | +protected: |
265 | + // Constructor for child node |
266 | + explicit Object(const std::string& key, int level); |
267 | + |
268 | +public: |
269 | + // Constructor for root node |
270 | + explicit Object() : JSON::Element("", 0) { |
271 | + } |
272 | + std::string as_string() const override; |
273 | +}; |
274 | + |
275 | +class Array : public Element { |
276 | + friend class JSON::Element; |
277 | +protected: |
278 | + // Constructor for child node |
279 | + explicit Array(const std::string& key, int level); |
280 | + |
281 | +public: |
282 | + std::string as_string() const override; |
283 | +}; |
284 | +} // namespace JSON |
285 | +#endif // end of include guard: WL_WEBSITE_JSON_JSON_H |
286 | |
287 | === added file 'src/website/json/value.cc' |
288 | --- src/website/json/value.cc 1970-01-01 00:00:00 +0000 |
289 | +++ src/website/json/value.cc 2019-01-13 09:00:00 +0000 |
290 | @@ -0,0 +1,56 @@ |
291 | +/* |
292 | + * Copyright (C) 2018 by the Widelands Development Team |
293 | + * |
294 | + * This program is free software; you can redistribute it and/or |
295 | + * modify it under the terms of the GNU General Public License |
296 | + * as published by the Free Software Foundation; either version 2 |
297 | + * of the License, or (at your option) any later version. |
298 | + * |
299 | + * This program is distributed in the hope that it will be useful, |
300 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
301 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
302 | + * GNU General Public License for more details. |
303 | + * |
304 | + * You should have received a copy of the GNU General Public License |
305 | + * along with this program; if not, write to the Free Software |
306 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
307 | + * |
308 | + */ |
309 | + |
310 | +#include "website/json/value.h" |
311 | + |
312 | +#include <sstream> |
313 | + |
314 | +namespace JSON { |
315 | + |
316 | +Boolean::Boolean(bool value) : bool_value(value) { |
317 | +} |
318 | +std::string Boolean::as_string() const { |
319 | + return bool_value ? "true" : "false"; |
320 | +} |
321 | + |
322 | +Double::Double(double value) : double_value(value) { |
323 | +} |
324 | +std::string Double::as_string() const { |
325 | + std::ostringstream strs; |
326 | + strs << double_value; |
327 | + return strs.str(); |
328 | +} |
329 | + |
330 | +Int::Int(int value) : int_value(value) {} |
331 | +std::string Int::as_string() const { |
332 | + std::ostringstream strs; |
333 | + strs << int_value; |
334 | + return strs.str(); |
335 | +} |
336 | + |
337 | +std::string Empty::as_string() const { |
338 | + return ""; |
339 | +} |
340 | + |
341 | +String::String(std::string value) : string_value(value) {} |
342 | +std::string String::as_string() const { |
343 | + return "\"" + string_value + "\""; |
344 | +} |
345 | + |
346 | +} // namespace JSON |
347 | |
348 | === added file 'src/website/json/value.h' |
349 | --- src/website/json/value.h 1970-01-01 00:00:00 +0000 |
350 | +++ src/website/json/value.h 2019-01-13 09:00:00 +0000 |
351 | @@ -0,0 +1,67 @@ |
352 | +/* |
353 | + * Copyright (C) 2018 by the Widelands Development Team |
354 | + * |
355 | + * This program is free software; you can redistribute it and/or |
356 | + * modify it under the terms of the GNU General Public License |
357 | + * as published by the Free Software Foundation; either version 2 |
358 | + * of the License, or (at your option) any later version. |
359 | + * |
360 | + * This program is distributed in the hope that it will be useful, |
361 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
362 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
363 | + * GNU General Public License for more details. |
364 | + * |
365 | + * You should have received a copy of the GNU General Public License |
366 | + * along with this program; if not, write to the Free Software |
367 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
368 | + * |
369 | + */ |
370 | + |
371 | +#ifndef WL_WEBSITE_JSON_VALUE_H |
372 | +#define WL_WEBSITE_JSON_VALUE_H |
373 | + |
374 | +#include <string> |
375 | + |
376 | +namespace JSON { |
377 | + |
378 | +/// Value types for JSON |
379 | +struct Value { |
380 | + Value() = default; |
381 | + virtual std::string as_string() const = 0; |
382 | +}; |
383 | + |
384 | +struct Boolean : Value { |
385 | + explicit Boolean(bool value); |
386 | + std::string as_string() const override; |
387 | +private: |
388 | + const bool bool_value; |
389 | +}; |
390 | + |
391 | +struct Double : Value { |
392 | + explicit Double(double value); |
393 | + std::string as_string() const override; |
394 | +private: |
395 | + const double double_value; |
396 | +}; |
397 | + |
398 | +struct Empty : Value { |
399 | + Empty() = default; |
400 | + std::string as_string() const override; |
401 | +}; |
402 | + |
403 | +struct Int : Value { |
404 | + explicit Int(int value); |
405 | + std::string as_string() const override; |
406 | +private: |
407 | + const int int_value; |
408 | +}; |
409 | + |
410 | +struct String : Value { |
411 | + explicit String(std::string value); |
412 | + std::string as_string() const override; |
413 | +private: |
414 | + const std::string string_value; |
415 | +}; |
416 | + |
417 | +} // namespace JSON |
418 | +#endif // end of include guard: WL_WEBSITE_JSON_VALUE_H |
419 | |
420 | === modified file 'src/website/map_info.cc' |
421 | --- src/website/map_info.cc 2018-12-13 07:24:01 +0000 |
422 | +++ src/website/map_info.cc 2019-01-13 09:00:00 +0000 |
423 | @@ -36,6 +36,7 @@ |
424 | #include "logic/editor_game_base.h" |
425 | #include "logic/map.h" |
426 | #include "map_io/widelands_map_loader.h" |
427 | +#include "website/json/json.h" |
428 | #include "website/website_common.h" |
429 | |
430 | using namespace Widelands; |
431 | @@ -83,48 +84,20 @@ |
432 | |
433 | // Write JSON. |
434 | { |
435 | - FileWrite fw; |
436 | - |
437 | - const auto write_string = [&fw](const std::string& s) { fw.data(s.c_str(), s.size()); }; |
438 | - const auto write_key_value = [&write_string]( |
439 | - const std::string& key, const std::string& quoted_value) { |
440 | - write_string((boost::format("\"%s\": %s") % key % quoted_value).str()); |
441 | - }; |
442 | - const auto write_key_value_string = [&write_key_value]( |
443 | - const std::string& key, const std::string& value) { |
444 | - std::string quoted_value = value; |
445 | - boost::replace_all(quoted_value, "\"", "\\\""); |
446 | - write_key_value(key, "\"" + value + "\""); |
447 | - }; |
448 | - const auto write_key_value_int = [&write_key_value]( |
449 | - const std::string& key, const int value) { |
450 | - write_key_value(key, boost::lexical_cast<std::string>(value)); |
451 | - }; |
452 | - write_string("{\n "); |
453 | - write_key_value_string("name", map->get_name()); |
454 | - write_string(",\n "); |
455 | - write_key_value_string("author", map->get_author()); |
456 | - write_string(",\n "); |
457 | - write_key_value_string("description", map->get_description()); |
458 | - write_string(",\n "); |
459 | - write_key_value_string("hint", map->get_hint()); |
460 | - write_string(",\n "); |
461 | - write_key_value_int("width", map->get_width()); |
462 | - write_string(",\n "); |
463 | - write_key_value_int("height", map->get_height()); |
464 | - write_string(",\n "); |
465 | - write_key_value_int("nr_players", map->get_nrplayers()); |
466 | - write_string(",\n "); |
467 | + std::unique_ptr<JSON::Object> json(new JSON::Object()); |
468 | + json->add_string("name", map->get_name()); |
469 | + json->add_string("author", map->get_author()); |
470 | + json->add_string("description", map->get_description()); |
471 | + json->add_string("hint", map->get_hint()); |
472 | + json->add_int("width", map->get_width()); |
473 | + json->add_int("height", map->get_height()); |
474 | + json->add_int("nr_players", map->get_nrplayers()); |
475 | |
476 | const std::string world_name = |
477 | static_cast<Widelands::WidelandsMapLoader*>(ml.get())->old_world_name(); |
478 | - write_key_value_string("world_name", world_name); |
479 | - write_string(",\n "); |
480 | - write_key_value_string("minimap", map_path + ".png"); |
481 | - write_string("\n"); |
482 | - |
483 | - write_string("}\n"); |
484 | - fw.write(*in_out_filesystem, (map_file + ".json").c_str()); |
485 | + json->add_string("world_name", world_name); |
486 | + json->add_string("minimap", map_path + ".png"); |
487 | + json->write_to_file(*in_out_filesystem, (map_file + ".json").c_str()); |
488 | } |
489 | egbase.cleanup_objects(); |
490 | } catch (std::exception& e) { |
491 | |
492 | === modified file 'src/website/map_object_info.cc' |
493 | --- src/website/map_object_info.cc 2018-08-14 20:42:18 +0000 |
494 | +++ src/website/map_object_info.cc 2019-01-13 09:00:00 +0000 |
495 | @@ -35,6 +35,7 @@ |
496 | #include "logic/map_objects/tribes/tribe_basic_info.h" |
497 | #include "logic/map_objects/tribes/tribes.h" |
498 | #include "logic/map_objects/world/world.h" |
499 | +#include "website/json/json.h" |
500 | #include "website/website_common.h" |
501 | |
502 | using namespace Widelands; |
503 | @@ -43,95 +44,12 @@ |
504 | |
505 | /* |
506 | ========================================================== |
507 | - SPECIALIZED FILEWRITE |
508 | - ========================================================== |
509 | - */ |
510 | - |
511 | -// Defines some convenience writing functions for the JSON format |
512 | -class JSONFileWrite : public FileWrite { |
513 | -public: |
514 | - JSONFileWrite() : FileWrite(), level_(0) { |
515 | - } |
516 | - |
517 | - void write_string(const std::string& s, bool use_indent = false) { |
518 | - std::string writeme = s; |
519 | - if (use_indent) { |
520 | - for (int i = 0; i < level_; ++i) { |
521 | - writeme = (boost::format(" %s") % writeme).str(); |
522 | - } |
523 | - } |
524 | - data(writeme.c_str(), writeme.size()); |
525 | - } |
526 | - void write_key(const std::string& key) { |
527 | - write_string((boost::format("\"%s\":\n") % key).str(), true); |
528 | - } |
529 | - void write_value_string(const std::string& quoted_value) { |
530 | - write_string((boost::format("\"%s\"") % quoted_value).str(), true); |
531 | - } |
532 | - void write_key_value(const std::string& key, const std::string& quoted_value) { |
533 | - write_string((boost::format("\"%s\": %s") % key % quoted_value).str(), true); |
534 | - } |
535 | - void write_key_value_string(const std::string& key, const std::string& value) { |
536 | - std::string quoted_value = value; |
537 | - boost::replace_all(quoted_value, "\"", "\\\""); |
538 | - write_key_value(key, "\"" + value + "\""); |
539 | - } |
540 | - void write_key_value_int(const std::string& key, const int value) { |
541 | - write_key_value(key, boost::lexical_cast<std::string>(value)); |
542 | - } |
543 | - void open_brace() { |
544 | - write_string("{\n", true); |
545 | - ++level_; |
546 | - } |
547 | - // JSON hates a final comma. This defaults to having NO comma. |
548 | - void close_brace(bool precede_newline = false, int current = 0, int total = 0) { |
549 | - --level_; |
550 | - if (precede_newline) { |
551 | - write_string("\n"); |
552 | - } |
553 | - if (current < total - 1) { |
554 | - write_string("},\n", true); |
555 | - } else { |
556 | - write_string("}", true); |
557 | - } |
558 | - } |
559 | - void open_array(const std::string& name) { |
560 | - write_string((boost::format("\"%s\":[\n") % name).str(), true); |
561 | - ++level_; |
562 | - } |
563 | - // JSON hates a final comma. This defaults to having NO comma. |
564 | - void close_array(int current = 0, int total = 0) { |
565 | - --level_; |
566 | - write_string("\n"); |
567 | - if (current < total - 1) { |
568 | - write_string("],\n", true); |
569 | - } else { |
570 | - write_string("]\n", true); |
571 | - } |
572 | - } |
573 | - // JSON hates a final comma. This defaults to having a comma. |
574 | - void close_element(int current = -2, int total = 0) { |
575 | - if (current < total - 1) { |
576 | - write_string(",\n"); |
577 | - } |
578 | - } |
579 | - |
580 | -private: |
581 | - int level_; |
582 | -}; |
583 | - |
584 | -/* |
585 | - ========================================================== |
586 | BUILDINGS |
587 | ========================================================== |
588 | */ |
589 | |
590 | void write_buildings(const TribeDescr& tribe, EditorGameBase& egbase, FileSystem* out_filesystem) { |
591 | - |
592 | log("\n==================\nWriting buildings:\n==================\n"); |
593 | - JSONFileWrite fw; |
594 | - fw.open_brace(); // Main |
595 | - fw.open_array("buildings"); // Buildings |
596 | |
597 | // We don't want any partially finished buildings |
598 | std::vector<const BuildingDescr*> buildings; |
599 | @@ -143,144 +61,92 @@ |
600 | } |
601 | } |
602 | |
603 | - // Now write |
604 | + std::unique_ptr<JSON::Element> json(new JSON::Element()); |
605 | + JSON::Array* json_buildings_array = json->add_array("buildings"); |
606 | for (size_t i = 0; i < buildings.size(); ++i) { |
607 | const BuildingDescr& building = *buildings[i]; |
608 | log(" %s", building.name().c_str()); |
609 | - fw.open_brace(); // Building |
610 | - |
611 | - fw.write_key_value_string("name", building.name()); |
612 | - fw.close_element(); |
613 | - fw.write_key_value_string("descname", building.descname()); |
614 | - fw.close_element(); |
615 | - fw.write_key_value_string("icon", building.representative_image_filename()); |
616 | - fw.close_element(); |
617 | - |
618 | - // Conditional stuff in between, so we won't run into trouble with the commas. |
619 | + |
620 | + JSON::Object* json_building = json_buildings_array->add_object(); |
621 | + json_building->add_string("name", building.name()); |
622 | + json_building->add_string("descname", building.descname()); |
623 | + json_building->add_string("icon", building.representative_image_filename()); |
624 | |
625 | // Buildcost |
626 | if (building.is_buildable()) { |
627 | - fw.open_array("buildcost"); // Buildcost |
628 | - size_t buildcost_counter = 0; |
629 | + JSON::Array* json_builcost_array = json_building->add_array("buildcost"); |
630 | for (WareAmount buildcost : building.buildcost()) { |
631 | const WareDescr& ware = *tribe.get_ware_descr(buildcost.first); |
632 | - fw.open_brace(); // Buildcost |
633 | - fw.write_key_value_string("name", ware.name()); |
634 | - fw.close_element(); |
635 | - fw.write_key_value_int("amount", buildcost.second); |
636 | - fw.close_brace(true, buildcost_counter, building.buildcost().size()); // Buildcost |
637 | - ++buildcost_counter; |
638 | + JSON::Object* json_builcost = json_builcost_array->add_object(); |
639 | + json_builcost->add_string("name", ware.name()); |
640 | + json_builcost->add_int("amount", buildcost.second); |
641 | } |
642 | - fw.close_array(1, 5); // Buildcost - we need a comma |
643 | } |
644 | |
645 | if (building.is_enhanced()) { |
646 | - fw.write_key_value_string( |
647 | + json_building->add_string( |
648 | "enhanced", tribe.get_building_descr(building.enhanced_from())->name()); |
649 | - fw.close_element(); |
650 | } |
651 | |
652 | if (building.enhancement() != INVALID_INDEX) { |
653 | - fw.write_key_value_string( |
654 | + json_building->add_string( |
655 | "enhancement", tribe.get_building_descr(building.enhancement())->name()); |
656 | - fw.close_element(); |
657 | } |
658 | |
659 | if (upcast(ProductionSiteDescr const, productionsite, &building)) { |
660 | - // Produces |
661 | + // Produces wares |
662 | if (productionsite->output_ware_types().size() > 0) { |
663 | - fw.open_array("produced_wares"); // Produces |
664 | - size_t produces_counter = 0; |
665 | + JSON::Array* json_wares_array = json_building->add_array("produced_wares"); |
666 | for (DescriptionIndex ware_index : productionsite->output_ware_types()) { |
667 | - fw.write_value_string(tribe.get_ware_descr(ware_index)->name()); |
668 | - fw.close_element(produces_counter, productionsite->output_ware_types().size()); |
669 | - ++produces_counter; |
670 | + json_wares_array->add_empty(tribe.get_ware_descr(ware_index)->name()); |
671 | } |
672 | - fw.close_array(1, 5); // Produces - we need a comma |
673 | } |
674 | + // Produces workers |
675 | if (productionsite->output_worker_types().size() > 0) { |
676 | - fw.open_array("produced_workers"); // Produces |
677 | - size_t produces_counter = 0; |
678 | + JSON::Array* json_workers_array = json_building->add_array("produced_workers"); |
679 | for (DescriptionIndex worker_index : productionsite->output_worker_types()) { |
680 | - fw.write_value_string(tribe.get_worker_descr(worker_index)->name()); |
681 | - fw.close_element(produces_counter, productionsite->output_worker_types().size()); |
682 | - ++produces_counter; |
683 | + json_workers_array->add_empty(tribe.get_worker_descr(worker_index)->name()); |
684 | } |
685 | - fw.close_array(1, 5); // Produces - we need a comma |
686 | } |
687 | |
688 | // Consumes |
689 | if (productionsite->input_wares().size() > 0) { |
690 | - fw.open_array("stored_wares"); // Consumes |
691 | - size_t consumes_counter = 0; |
692 | + JSON::Array* json_wares_array = json_building->add_array("stored_wares"); |
693 | for (WareAmount input : productionsite->input_wares()) { |
694 | const WareDescr& ware = *tribe.get_ware_descr(input.first); |
695 | - fw.open_brace(); // Input |
696 | - fw.write_key_value_string("name", ware.name()); |
697 | - fw.close_element(); |
698 | - fw.write_key_value_int("amount", input.second); |
699 | - fw.close_brace( |
700 | - true, consumes_counter, productionsite->input_wares().size()); // Input |
701 | - ++consumes_counter; |
702 | + JSON::Object* json_input = json_wares_array->add_object(); |
703 | + json_input->add_string("name", ware.name()); |
704 | + json_input->add_int("amount", input.second); |
705 | } |
706 | - fw.close_array(1, 5); // Consumes - we need a comma |
707 | } |
708 | |
709 | - fw.open_array("workers"); // Workers |
710 | - size_t worker_counter = 0; |
711 | + // Workers |
712 | + JSON::Array* json_workers_array = json_building->add_array("workers"); |
713 | for (WareAmount input : productionsite->working_positions()) { |
714 | const WorkerDescr& worker = *tribe.get_worker_descr(input.first); |
715 | - fw.open_brace(); // Worker |
716 | - fw.write_key_value_string("name", worker.name()); |
717 | - fw.close_element(); |
718 | - fw.write_key_value_int("amount", input.second); |
719 | - fw.close_brace( |
720 | - true, worker_counter, productionsite->working_positions().size()); // Worker |
721 | - ++worker_counter; |
722 | + JSON::Object* json_input = json_workers_array->add_object(); |
723 | + json_input->add_string("name", worker.name()); |
724 | + json_input->add_int("amount", input.second); |
725 | } |
726 | - fw.close_array(1, 5); // Workers - we need a comma |
727 | } else if (upcast(MilitarySiteDescr const, militarysite, &building)) { |
728 | - fw.write_key_value_int("conquers", militarysite->get_conquers()); |
729 | - fw.close_element(); |
730 | - fw.write_key_value_int("max_soldiers", militarysite->get_max_number_of_soldiers()); |
731 | - fw.close_element(); |
732 | - fw.write_key_value_int("heal_per_second", militarysite->get_heal_per_second()); |
733 | - fw.close_element(); |
734 | + json_building->add_int("conquers", militarysite->get_conquers()); |
735 | + json_building->add_int("max_soldiers", militarysite->get_max_number_of_soldiers()); |
736 | + json_building->add_int("heal_per_second", militarysite->get_heal_per_second()); |
737 | } |
738 | |
739 | - switch (building.type()) { |
740 | - case MapObjectType::PRODUCTIONSITE: |
741 | - fw.write_key_value_string("type", "productionsite"); |
742 | - break; |
743 | - case MapObjectType::WAREHOUSE: |
744 | - fw.write_key_value_string("type", "warehouse"); |
745 | - break; |
746 | - case MapObjectType::MARKET: |
747 | - fw.write_key_value_string("type", "market"); |
748 | - break; |
749 | - case MapObjectType::MILITARYSITE: |
750 | - fw.write_key_value_string("type", "militarysite"); |
751 | - break; |
752 | - case MapObjectType::TRAININGSITE: |
753 | - fw.write_key_value_string("type", "trainingsite"); |
754 | - break; |
755 | - default: |
756 | - NEVER_HERE(); |
757 | - } |
758 | - fw.close_element(); |
759 | + json_building->add_string("type", to_string(building.type())); |
760 | |
761 | // Size |
762 | if (building.type() == MapObjectType::WAREHOUSE && !building.is_buildable() && |
763 | !building.is_enhanced()) { |
764 | - fw.write_key_value_string("size", "headquarters"); |
765 | + json_building->add_string("size", "headquarters"); |
766 | } else if (building.get_ismine()) { |
767 | - fw.write_key_value_string("size", "mine"); |
768 | + json_building->add_string("size", "mine"); |
769 | } else if (building.get_isport()) { |
770 | - fw.write_key_value_string("size", "port"); |
771 | + json_building->add_string("size", "port"); |
772 | } else { |
773 | - fw.write_key_value_string("size", BaseImmovable::size_to_string(building.get_size())); |
774 | + json_building->add_string("size", BaseImmovable::size_to_string(building.get_size())); |
775 | } |
776 | - fw.close_element(); |
777 | |
778 | // Helptext |
779 | try { |
780 | @@ -290,16 +156,13 @@ |
781 | cr->push_arg(building.helptext_script()); |
782 | cr->resume(); |
783 | const std::string help_text = cr->pop_string(); |
784 | - fw.write_key_value_string("helptext", help_text); |
785 | + json_building->add_string("helptext", help_text); |
786 | } catch (LuaError& err) { |
787 | - fw.write_key_value_string("helptext", err.what()); |
788 | + json_building->add_string("helptext", err.what()); |
789 | } |
790 | - |
791 | - fw.close_brace(true, i, buildings.size()); // Building |
792 | } |
793 | - fw.close_array(); // Buildings |
794 | - fw.close_brace(); // Main |
795 | - fw.write(*out_filesystem, (boost::format("%s_buildings.json") % tribe.name()).str().c_str()); |
796 | + |
797 | + json->write_to_file(*out_filesystem, (boost::format("%s_buildings.json") % tribe.name()).str().c_str()); |
798 | log("\n"); |
799 | } |
800 | |
801 | @@ -311,22 +174,16 @@ |
802 | |
803 | void write_wares(const TribeDescr& tribe, EditorGameBase& egbase, FileSystem* out_filesystem) { |
804 | log("\n===============\nWriting wares:\n===============\n"); |
805 | - JSONFileWrite fw; |
806 | - fw.open_brace(); // Main |
807 | |
808 | - fw.open_array("wares"); // Wares |
809 | - size_t counter = 0; |
810 | - const size_t no_of_wares = tribe.wares().size(); |
811 | + std::unique_ptr<JSON::Element> json(new JSON::Element()); |
812 | + JSON::Array* json_wares_array = json->add_array("wares"); |
813 | for (DescriptionIndex ware_index : tribe.wares()) { |
814 | const WareDescr& ware = *tribe.get_ware_descr(ware_index); |
815 | log(" %s", ware.name().c_str()); |
816 | - fw.open_brace(); |
817 | - fw.write_key_value_string("name", ware.name()); |
818 | - fw.close_element(); |
819 | - fw.write_key_value_string("descname", ware.descname()); |
820 | - fw.close_element(); |
821 | - fw.write_key_value_string("icon", ware.icon_filename()); |
822 | - fw.close_element(); |
823 | + JSON::Object* json_ware = json_wares_array->add_object(); |
824 | + json_ware->add_string("name", ware.name()); |
825 | + json_ware->add_string("descname", ware.descname()); |
826 | + json_ware->add_string("icon", ware.icon_filename()); |
827 | |
828 | // Helptext |
829 | try { |
830 | @@ -337,17 +194,13 @@ |
831 | cr->push_arg(ware.helptext_script()); |
832 | cr->resume(); |
833 | const std::string help_text = cr->pop_string(); |
834 | - fw.write_key_value_string("helptext", help_text); |
835 | + json_ware->add_string("helptext", help_text); |
836 | } catch (LuaError& err) { |
837 | - fw.write_key_value_string("helptext", err.what()); |
838 | + json_ware->add_string("helptext", err.what()); |
839 | } |
840 | - fw.close_brace(true, counter, no_of_wares); // Ware |
841 | - ++counter; |
842 | } |
843 | - fw.close_array(); // Wares |
844 | |
845 | - fw.close_brace(); // Main |
846 | - fw.write(*out_filesystem, (boost::format("%s_wares.json") % tribe.name()).str().c_str()); |
847 | + json->write_to_file(*out_filesystem, (boost::format("%s_wares.json") % tribe.name()).str().c_str()); |
848 | log("\n"); |
849 | } |
850 | |
851 | @@ -359,22 +212,16 @@ |
852 | |
853 | void write_workers(const TribeDescr& tribe, EditorGameBase& egbase, FileSystem* out_filesystem) { |
854 | log("\n================\nWriting workers:\n================\n"); |
855 | - JSONFileWrite fw; |
856 | - fw.open_brace(); // Main |
857 | |
858 | - fw.open_array("workers"); // Workers |
859 | - size_t counter = 0; |
860 | - const size_t no_of_workers = tribe.workers().size(); |
861 | + std::unique_ptr<JSON::Element> json(new JSON::Element()); |
862 | + JSON::Array* json_workers_array = json->add_array("workers"); |
863 | for (DescriptionIndex worker_index : tribe.workers()) { |
864 | const WorkerDescr& worker = *tribe.get_worker_descr(worker_index); |
865 | log(" %s", worker.name().c_str()); |
866 | - fw.open_brace(); |
867 | - fw.write_key_value_string("name", worker.name()); |
868 | - fw.close_element(); |
869 | - fw.write_key_value_string("descname", worker.descname()); |
870 | - fw.close_element(); |
871 | - fw.write_key_value_string("icon", worker.icon_filename()); |
872 | - fw.close_element(); |
873 | + JSON::Object* json_worker = json_workers_array->add_object(); |
874 | + json_worker->add_string("name", worker.name()); |
875 | + json_worker->add_string("descname", worker.descname()); |
876 | + json_worker->add_string("icon", worker.icon_filename()); |
877 | |
878 | // Helptext |
879 | try { |
880 | @@ -384,28 +231,20 @@ |
881 | cr->push_arg(worker.helptext_script()); |
882 | cr->resume(); |
883 | const std::string help_text = cr->pop_string(); |
884 | - fw.write_key_value_string("helptext", help_text); |
885 | + json_worker->add_string("helptext", help_text); |
886 | } catch (LuaError& err) { |
887 | - fw.write_key_value_string("helptext", err.what()); |
888 | + json_worker->add_string("helptext", err.what()); |
889 | } |
890 | |
891 | if (worker.becomes() != INVALID_INDEX) { |
892 | - fw.close_element(); |
893 | const WorkerDescr& becomes = *tribe.get_worker_descr(worker.becomes()); |
894 | - fw.write_key("becomes"); |
895 | - fw.open_brace(); |
896 | - fw.write_key_value_string("name", becomes.name()); |
897 | - fw.close_element(); |
898 | - fw.write_key_value_int("experience", worker.get_needed_experience()); |
899 | - fw.close_brace(true); |
900 | + JSON::Object* json_becomes = json_worker->add_object("becomes"); |
901 | + json_becomes->add_string("name", becomes.name()); |
902 | + json_becomes->add_int("experience", worker.get_needed_experience()); |
903 | } |
904 | - fw.close_brace(true, counter, no_of_workers); // Worker |
905 | - ++counter; |
906 | } |
907 | - fw.close_array(); // Workers |
908 | |
909 | - fw.close_brace(); // Main |
910 | - fw.write(*out_filesystem, (boost::format("%s_workers.json") % tribe.name()).str().c_str()); |
911 | + json->write_to_file(*out_filesystem, (boost::format("%s_workers.json") % tribe.name()).str().c_str()); |
912 | log("\n"); |
913 | } |
914 | |
915 | @@ -415,22 +254,17 @@ |
916 | ========================================================== |
917 | */ |
918 | |
919 | -void add_tribe_info(const Widelands::TribeBasicInfo& tribe_info, JSONFileWrite* fw) { |
920 | - fw->write_key_value_string("name", tribe_info.name); |
921 | - fw->close_element(); |
922 | - fw->write_key_value_string("descname", tribe_info.descname); |
923 | - fw->close_element(); |
924 | - fw->write_key_value_string("author", tribe_info.author); |
925 | - fw->close_element(); |
926 | - fw->write_key_value_string("tooltip", tribe_info.tooltip); |
927 | - fw->close_element(); |
928 | - fw->write_key_value_string("icon", tribe_info.icon); |
929 | +void add_tribe_info(const Widelands::TribeBasicInfo& tribe_info, JSON::Element* json_tribe) { |
930 | + json_tribe->add_string("name", tribe_info.name); |
931 | + json_tribe->add_string("descname", tribe_info.descname); |
932 | + json_tribe->add_string("author", tribe_info.author); |
933 | + json_tribe->add_string("tooltip", tribe_info.tooltip); |
934 | + json_tribe->add_string("icon", tribe_info.icon); |
935 | } |
936 | |
937 | void write_tribes(EditorGameBase& egbase, FileSystem* out_filesystem) { |
938 | - JSONFileWrite fw; |
939 | - fw.open_brace(); // Main |
940 | - fw.open_array("tribes"); // Tribes |
941 | + std::unique_ptr<JSON::Element> json(new JSON::Element()); |
942 | + JSON::Array* json_tribes_array = json->add_array("tribes"); |
943 | |
944 | /// Tribes |
945 | egbase.mutable_tribes()->postload(); // Make sure that all values have been set. |
946 | @@ -442,28 +276,22 @@ |
947 | log("\n\n=========================\nWriting tribe: %s\n=========================\n", |
948 | tribe_info.name.c_str()); |
949 | |
950 | - fw.open_brace(); // TribeDescr |
951 | - add_tribe_info(tribe_info, &fw); |
952 | - fw.close_brace(true, tribe_index, tribeinfos.size()); // TribeDescr |
953 | + // Main file |
954 | + JSON::Object* json_tribe = json_tribes_array->add_object(); |
955 | + add_tribe_info(tribe_info, json_tribe); |
956 | |
957 | // These go in separate files |
958 | - |
959 | - JSONFileWrite fw_tribe; |
960 | - fw_tribe.open_brace(); // TribeDescr |
961 | - add_tribe_info(tribe_info, &fw_tribe); |
962 | - fw_tribe.close_brace(true); // TribeDescr |
963 | - fw_tribe.write( |
964 | - *out_filesystem, (boost::format("tribe_%s.json") % tribe_info.name).str().c_str()); |
965 | + std::unique_ptr<JSON::Object> json_tribe_for_file(new JSON::Object()); |
966 | + add_tribe_info(tribe_info, json_tribe_for_file.get()); |
967 | + json_tribe_for_file->write_to_file(*out_filesystem, (boost::format("tribe_%s.json") % tribe_info.name).str().c_str()); |
968 | |
969 | const TribeDescr& tribe = *tribes.get_tribe_descr(tribes.tribe_index(tribe_info.name)); |
970 | - |
971 | write_buildings(tribe, egbase, out_filesystem); |
972 | write_wares(tribe, egbase, out_filesystem); |
973 | write_workers(tribe, egbase, out_filesystem); |
974 | } |
975 | - fw.close_array(); // Tribes |
976 | - fw.close_brace(); // Main |
977 | - fw.write(*out_filesystem, "tribes.json"); |
978 | + |
979 | + json->write_to_file(*out_filesystem, "tribes.json"); |
980 | } |
981 | |
982 | } // namespace |
Continuous integration builds have changed state:
Travis build 4173. State: failed. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 447150251. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ json_writer- 3971.
Appveyor build 3971. State: success. Details: https:/