Branch data Line data Source code
1 : : /*
2 : : * The MIT License (MIT)
3 : : *
4 : : * Copyright (c) 2024 Yaroslav Riabtsev <yaroslav.riabtsev@rwth-aachen.de>
5 : : *
6 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
7 : : * of this software and associated documentation files (the "Software"), to deal
8 : : * in the Software without restriction, including without limitation the rights
9 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 : : * copies of the Software, and to permit persons to whom the Software is
11 : : * furnished to do so, subject to the following conditions:
12 : : *
13 : : * The above copyright notice and this permission notice shall be included
14 : : * in all copies or substantial portions of the Software.
15 : : *
16 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : : * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 : : * SOFTWARE.
23 : : */
24 : :
25 : : #include "json.hpp"
26 : : #include "reference.hpp"
27 : :
28 : : #include <algorithm>
29 : : #include <ranges>
30 : : #include <sstream>
31 : :
32 : 112 : std::string json_lib::json_type_to_string(const json_type type) {
33 [ + + + + : 112 : switch (type) {
+ + + + ]
34 : 16 : case json_type::object_json:
35 [ + - ]: 32 : return "JSON-Object";
36 : 16 : case json_type::array_json:
37 [ + - ]: 32 : return "JSON-Array";
38 : 16 : case json_type::string_json:
39 [ + - ]: 32 : return "JSON-String";
40 : 15 : case json_type::real_json:
41 [ + - ]: 30 : return "JSON-Real";
42 : 17 : case json_type::integer_json:
43 [ + - ]: 34 : return "JSON-Integer";
44 : 16 : case json_type::boolean_json:
45 [ + - ]: 32 : return "JSON-Boolean";
46 : 15 : case json_type::null_json:
47 [ + - ]: 30 : return "JSON-Null";
48 : 1 : default:
49 [ + - ]: 2 : return "Unknown JSON-Type";
50 : : }
51 : : }
52 : :
53 : 52 : std::invalid_argument json_lib::throw_message(
54 : : const std::shared_ptr<const json>& obj1,
55 : : const std::shared_ptr<const json>& obj2, const std::source_location location
56 : : ) {
57 [ + - ]: 52 : std::ostringstream oss;
58 : : oss << "[Json-Error] Attempting to evaluate a "
59 [ + - + - ]: 52 : << json_type_to_string(obj1->type()) << " by a "
60 [ + - + - : 104 : << json_type_to_string(obj2->type()) << ". ";
+ - + - +
- + - +
- ]
61 : : #ifndef NDEBUG
62 [ + - ]: 52 : oss << "Values: ";
63 : : try {
64 [ + + + - ]: 52 : oss << obj1->to_string();
65 [ - + ]: 1 : } catch (const std::runtime_error& e) {
66 [ + - + - : 1 : oss << "{ " << e.what() << " }";
+ - ]
67 : 1 : }
68 [ + - ]: 52 : oss << " and ";
69 : : try {
70 [ + + + - ]: 52 : oss << obj2->to_string();
71 [ - + ]: 1 : } catch (const std::runtime_error& e) {
72 [ + - + - : 1 : oss << "{ " << e.what() << " }";
+ - ]
73 : 1 : }
74 [ + - ]: 52 : oss << ". ";
75 [ + - + - : 52 : oss << "In file: " << location.file_name() << '(' << location.line() << ':'
+ - + - +
- ]
76 [ + - ]: 52 : << location.column() << ") in function `" << location.function_name()
77 [ + - + - : 52 : << "`.";
+ - ]
78 : : #endif
79 [ + - + - ]: 104 : return std::invalid_argument(oss.str());
80 : 52 : }
81 : :
82 : 14 : json_lib::json_boolean::json_boolean(const bool value)
83 : 14 : : value(value) {
84 : 14 : _type = (json_type::boolean_json);
85 : 14 : }
86 : :
87 : 2535 : json_lib::json_integer::json_integer(const int value)
88 : 2535 : : value(value) {
89 : 2535 : _type = (json_type::integer_json);
90 : 2535 : }
91 : :
92 : 10 : json_lib::json_real::json_real(const float value)
93 : 10 : : value(value) {
94 : 10 : _type = (json_type::real_json);
95 [ + - ]: 10 : const std::string result = std::to_string(value);
96 : 10 : size_t last = result.size() - 1;
97 [ + + ]: 58 : while (result[last] == '0') {
98 : 48 : --last;
99 : : }
100 [ + + ]: 10 : if (result[last] == '.') {
101 : 8 : ++last;
102 : : }
103 [ + - ]: 10 : str_value = result.substr(0, last + 1);
104 : 10 : }
105 : :
106 : 816 : json_lib::json_real::json_real(const std::string& str_value)
107 [ + - ]: 816 : : str_value(str_value) {
108 : 816 : _type = (json_type::real_json);
109 : : size_t pos;
110 : : try {
111 [ + + ]: 816 : value = std::stof(str_value, &pos);
112 [ + + ]: 811 : if (pos != str_value.length()) {
113 [ + - ]: 2 : throw std::invalid_argument("Invalid characters in float string");
114 : : }
115 [ - + + ]: 7 : } catch (const std::invalid_argument&) {
116 : 5 : throw;
117 : 7 : } catch (const std::out_of_range&) {
118 : 2 : throw;
119 : 2 : }
120 : 823 : }
121 : :
122 : 7573 : json_lib::json_string::json_string(std::string value)
123 : 7573 : : value(std::move(value)) {
124 : 7573 : _type = (json_type::string_json);
125 : 7573 : }
126 : :
127 : 97 : json_lib::json_array::json_array(const std::vector<std::shared_ptr<json>>& arr)
128 [ + - ]: 97 : : list(arr) {
129 : 97 : _type = (json_type::array_json);
130 : 97 : }
131 : :
132 : 2281 : json_lib::json_object::json_object(
133 : : const std::vector<std::pair<std::string, std::shared_ptr<json>>>& obj
134 : 2281 : )
135 [ + - ]: 2281 : : data(obj) {
136 : 2281 : _type = (json_type::object_json);
137 [ + + ]: 14279 : for (size_t i = 0; i < obj.size(); ++i) {
138 [ + - + + ]: 11999 : if (indexes.contains(obj[i].first)) {
139 : : throw std::invalid_argument(
140 [ + - + - ]: 2 : "key `" + obj[i].first + "` is already set"
141 [ + - ]: 3 : );
142 : : }
143 [ + - ]: 11998 : indexes[obj[i].first] = i;
144 : : }
145 : 2284 : }
146 : :
147 : 35 : void json_lib::json_array::touch() {
148 [ + + ]: 35 : if (touched) {
149 : 2 : looped = true;
150 : 2 : return;
151 : : }
152 : 33 : touched = true;
153 [ + + ]: 123 : for (auto& child : list) {
154 [ + - + + ]: 90 : if (child->type() == json_type::reference_json) {
155 : : const auto ref
156 : : = std::dynamic_pointer_cast<reference_lib::json_reference>(child
157 : 6 : );
158 [ + - + - ]: 6 : ref->set_parent(shared_from_this());
159 [ + - ]: 6 : child = ref->value();
160 : 6 : }
161 [ + - ]: 90 : child->touch();
162 : : }
163 : 33 : touched = false;
164 : : }
165 : :
166 : 18 : void json_lib::json_object::touch() {
167 [ + + ]: 18 : if (touched) {
168 : 4 : looped = true;
169 : 4 : return;
170 : : }
171 : 14 : touched = true;
172 [ + - + - : 40 : for (auto& child : data | std::views::values) {
+ - + + ]
173 [ + - + + ]: 26 : if (child->type() == json_type::reference_json) {
174 : : const auto ref
175 : : = std::dynamic_pointer_cast<reference_lib::json_reference>(child
176 : 9 : );
177 [ + - + - ]: 9 : ref->set_parent(shared_from_this());
178 [ + - ]: 9 : child = ref->value();
179 : 9 : }
180 [ + - ]: 26 : child->touch();
181 : : }
182 : 14 : touched = false;
183 : : }
184 : :
185 : 48 : void json_lib::json::set_root(const std::shared_ptr<json>&) { }
186 : :
187 : 1 : void json_lib::json_array::set_root(const std::shared_ptr<json>& item) {
188 [ - + ]: 1 : if (touched) {
189 : 0 : looped = true;
190 : 0 : return;
191 : : }
192 : 1 : touched = true;
193 [ + + ]: 8 : for (auto& child : list) {
194 [ + - ]: 7 : child->set_root(item);
195 [ + - + + ]: 7 : if (child->type() == json_type::reference_json) {
196 : : const auto ref
197 : : = std::dynamic_pointer_cast<reference_lib::json_reference>(child
198 : 4 : );
199 [ + - ]: 4 : child = ref->value();
200 : 4 : }
201 : : }
202 : 1 : touched = false;
203 : : }
204 : :
205 : 0 : void json_lib::json_object::set_root(const std::shared_ptr<json>& item) {
206 [ # # ]: 0 : if (touched) {
207 : 0 : looped = true;
208 : 0 : return;
209 : : }
210 : 0 : touched = true;
211 [ # # # # : 0 : for (auto& child : data | std::views::values) {
# # # # ]
212 [ # # ]: 0 : child->set_root(item);
213 [ # # # # ]: 0 : if (child->type() == json_type::reference_json) {
214 : : const auto ref
215 : : = std::dynamic_pointer_cast<reference_lib::json_reference>(child
216 : 0 : );
217 [ # # ]: 0 : child = ref->value();
218 : 0 : }
219 : : }
220 : 0 : touched = false;
221 : : }
222 : :
223 : 29 : bool json_lib::json::empty() const { return true; }
224 : :
225 : 2 : bool json_lib::json_array::empty() const { return size() == 0; }
226 : :
227 : 1 : bool json_lib::json_object::empty() const { return size() == 0; }
228 : :
229 : 29 : bool json_lib::json::compact() const { return true; }
230 : :
231 : 9 : bool json_lib::json_array::compact() const {
232 : 9 : return std::ranges::all_of(list, [](const auto& element) {
233 [ + - + + ]: 25 : return element->compact() && element->empty();
234 : 9 : });
235 : : }
236 : :
237 : 11 : bool json_lib::json_object::compact() const {
238 : 11 : return size() == 0
239 [ + + + + : 18 : || (size() == 1 && data.begin()->second->compact()
+ - + - ]
240 [ + - + + ]: 18 : && data.begin()->second->empty());
241 : : }
242 : :
243 : 360 : std::string json_lib::json::to_string() const {
244 : 360 : return formatted_string(false);
245 : : }
246 : :
247 : 376 : std::string json_lib::json::formatted_string(const bool pretty) const {
248 : 376 : return indented_string(0, pretty);
249 : : }
250 : :
251 : 22 : std::string json_lib::json::indented_string(size_t, bool) const {
252 [ + - ]: 44 : return "null";
253 : : }
254 : :
255 : 50 : std::string json_lib::json_boolean::indented_string(size_t, bool) const {
256 [ + + + - ]: 100 : return value ? "true" : "false";
257 : : }
258 : :
259 : 1731 : std::string json_lib::json_integer::indented_string(size_t, bool) const {
260 : 1731 : return std::to_string(value);
261 : : }
262 : :
263 : 508 : std::string json_lib::json_real::indented_string(size_t, bool) const {
264 : 508 : return str_value;
265 : : }
266 : :
267 : 5077 : std::string json_lib::json_string::indented_string(size_t, bool) const {
268 [ + - ]: 5077 : std::ostringstream escaped;
269 [ + - ]: 5077 : escaped << "\"";
270 [ + + ]: 201045 : for (const char ch : value) {
271 [ + + + + : 195968 : switch (ch) {
+ + + + ]
272 : 8 : case '\"':
273 [ + - ]: 8 : escaped << "\\\"";
274 : 8 : break;
275 : 8 : case '\\':
276 [ + - ]: 8 : escaped << "\\\\";
277 : 8 : break;
278 : 4 : case '\b':
279 [ + - ]: 4 : escaped << "\\b";
280 : 4 : break;
281 : 4 : case '\f':
282 [ + - ]: 4 : escaped << "\\f";
283 : 4 : break;
284 : 6 : case '\n':
285 [ + - ]: 6 : escaped << "\\n";
286 : 6 : break;
287 : 4 : case '\r':
288 [ + - ]: 4 : escaped << "\\r";
289 : 4 : break;
290 : 4 : case '\t':
291 [ + - ]: 4 : escaped << "\\t";
292 : 4 : break;
293 : 195930 : default:
294 : : // if (ch >= 0x20 && ch <= 0x7E) {
295 [ + - ]: 195930 : escaped << ch;
296 : : // } else {
297 : : // escaped << "\\u" << std::hex << std::setw(4)
298 : : // << std::setfill('0')
299 : : // << (static_cast<int>(static_cast<unsigned
300 : : // char>(ch)));
301 : : // }
302 : 195930 : break;
303 : : }
304 : : }
305 : :
306 [ + - ]: 5077 : escaped << "\"";
307 [ + - ]: 10154 : return escaped.str();
308 : 5077 : }
309 : :
310 : 612 : std::string json_lib::json_array::format_item(
311 : : const std::shared_ptr<json>& item, const size_t nested_level,
312 : : const bool pretty
313 : : ) {
314 : 612 : return item->indented_string(nested_level, pretty);
315 : : }
316 : :
317 : 42 : std::string json_lib::json_array::indented_string(
318 : : const size_t indent_level, const bool pretty
319 : : ) const {
320 [ + + ]: 42 : if (looped) {
321 [ + - ]: 2 : throw std::runtime_error("object is looped");
322 : : }
323 : : return '['
324 [ + - ]: 80 : + format_container(
325 [ + + + - : 40 : list, format_item, indent_level, pretty && !compact()
+ + + - ]
326 : : )
327 [ + - ]: 80 : + "]";
328 : : }
329 : :
330 : 8022 : std::string json_lib::json_object::format_item(
331 : : const std::pair<std::string, std::shared_ptr<json>>& item,
332 : : const size_t nested_level, const bool pretty
333 : : ) {
334 [ + - ]: 16044 : return "\"" + item.first
335 [ + - + - : 24066 : + "\": " + item.second->indented_string(nested_level, pretty);
+ - ]
336 : : }
337 : :
338 : 1536 : std::string json_lib::json_object::indented_string(
339 : : const size_t indent_level, const bool pretty
340 : : ) const {
341 [ + + ]: 1536 : if (looped) {
342 [ + - ]: 3 : throw std::runtime_error("object is looped");
343 : : }
344 : : return '{'
345 [ + - ]: 3066 : + format_container(
346 [ + + + - : 1533 : data, format_item, indent_level, pretty && !compact()
+ + + - ]
347 : : )
348 [ + - ]: 3066 : + "}";
349 : : }
350 : :
351 : : std::shared_ptr<json_lib::json>
352 : 52 : json_lib::json::by(const std::shared_ptr<json>& item) const {
353 [ + - + - ]: 52 : throw throw_message(shared_from_this(), item);
354 : : }
355 : :
356 : 52 : int json_lib::json_integer::as_index() const { return value; }
357 : :
358 : : std::shared_ptr<json_lib::json>
359 : 8 : json_lib::json_integer::by(const std::shared_ptr<json>& item) const {
360 [ + + + - : 8 : if (enable_symmetric_indexing && item->type() == json_type::array_json) {
+ + ]
361 : 1 : const auto array = std::dynamic_pointer_cast<json_array>(item);
362 [ + - ]: 1 : return array->at(as_index());
363 : 1 : }
364 : 7 : return json::by(item);
365 : : }
366 : :
367 : 60 : std::string json_lib::json_string::as_key() const { return value; }
368 : :
369 : : std::shared_ptr<json_lib::json>
370 : 9 : json_lib::json_string::by(const std::shared_ptr<json>& item) const {
371 [ + + + - : 9 : if (enable_symmetric_indexing && item->type() == json_type::object_json) {
+ + ]
372 : 1 : const auto object = std::dynamic_pointer_cast<json_object>(item);
373 [ + - + - ]: 2 : return object->at(as_key());
374 : 1 : }
375 : 8 : return json::by(item);
376 : : }
377 : :
378 : 66 : size_t json_lib::json_array::size() const { return list.size(); }
379 : :
380 : 53 : std::shared_ptr<json_lib::json> json_lib::json_array::at(const int index
381 : : ) const {
382 : 53 : auto absolute_index = static_cast<size_t>(index);
383 [ + + + - ]: 53 : if (enable_negative_indexing && index < 0) {
384 : 5 : absolute_index += size();
385 : : }
386 [ + + ]: 53 : if (absolute_index < size()) {
387 : 48 : return list.at(absolute_index);
388 : : }
389 [ + - ]: 5 : throw std::out_of_range("index out of range");
390 : : }
391 : :
392 : : std::shared_ptr<json_lib::json>
393 : 45 : json_lib::json_array::by(const std::shared_ptr<json>& item) const {
394 [ + + ]: 45 : if (item->type() == json_type::integer_json) {
395 : 38 : const auto number = std::dynamic_pointer_cast<json_integer>(item);
396 [ + + ]: 38 : return at(number->as_index());
397 : 38 : }
398 : 7 : return json::by(item);
399 : : }
400 : :
401 : 27 : size_t json_lib::json_object::size() const { return data.size(); }
402 : :
403 : 1 : std::vector<std::string> json_lib::json_object::get_keys() {
404 [ + - ]: 1 : if (keys.size() != size()) {
405 [ + - ]: 1 : auto result = std::views::keys(indexes);
406 [ + - + - : 3 : keys = { result.begin(), result.end() };
+ - ]
407 : : }
408 : 1 : return keys;
409 : : }
410 : :
411 : 64 : std::shared_ptr<json_lib::json> json_lib::json_object::at(const std::string& key
412 : : ) const {
413 [ + + ]: 64 : if (indexes.contains(key)) {
414 : 60 : return data.at(indexes.at(key)).second;
415 : : }
416 [ + - ]: 4 : throw std::out_of_range("key not found");
417 : : }
418 : :
419 : : std::shared_ptr<json_lib::json>
420 : 65 : json_lib::json_object::by(const std::shared_ptr<json>& item) const {
421 [ + + ]: 65 : if (item->type() == json_type::string_json) {
422 : 57 : const auto string = std::dynamic_pointer_cast<json_string>(item);
423 [ + - + + ]: 59 : return at(string->as_key());
424 : 57 : }
425 : 8 : return json::by(item);
426 : : }
|