Branch data Line data Source code
1 : : // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config.h>
16 : :
17 : : #include <cc/data.h>
18 : :
19 : : #include <cassert>
20 : : #include <climits>
21 : : #include <map>
22 : : #include <cstdio>
23 : : #include <iostream>
24 : : #include <string>
25 : : #include <sstream>
26 : :
27 : : #include <boost/algorithm/string.hpp> // for iequals
28 : :
29 : : #include <cmath>
30 : :
31 : : using namespace std;
32 : :
33 : : namespace {
34 : : const char* WHITESPACE = " \b\f\n\r\t";
35 : : } // end anonymous namespace
36 : :
37 : : namespace isc {
38 : : namespace data {
39 : :
40 : : std::string
41 : 253 : Element::str() const {
42 : 506 : std::stringstream ss;
43 [ + - ]: 253 : toJSON(ss);
44 : 253 : return (ss.str());
45 : : }
46 : :
47 : : std::string
48 : 60 : Element::toWire() const {
49 : 120 : std::stringstream ss;
50 [ + - ]: 60 : toJSON(ss);
51 : 60 : return (ss.str());
52 : : }
53 : :
54 : : void
55 : 15 : Element::toWire(std::ostream& ss) const {
56 : 15 : toJSON(ss);
57 : 15 : }
58 : :
59 : : bool
60 : 5 : Element::getValue(long int&) {
61 : 5 : return (false);
62 : : }
63 : :
64 : : bool
65 : 5 : Element::getValue(double&) {
66 : 5 : return (false);
67 : : }
68 : :
69 : : bool
70 : 5 : Element::getValue(bool&) {
71 : 5 : return (false);
72 : : }
73 : :
74 : : bool
75 : 5 : Element::getValue(std::string&) {
76 : 5 : return (false);
77 : : }
78 : :
79 : : bool
80 : 5 : Element::getValue(std::vector<ConstElementPtr>&) {
81 : 5 : return (false);
82 : : }
83 : :
84 : : bool
85 : 5 : Element::getValue(std::map<std::string, ConstElementPtr>&) {
86 : 5 : return (false);
87 : : }
88 : :
89 : : bool
90 : 1 : Element::setValue(const long int) {
91 : 1 : return (false);
92 : : }
93 : :
94 : : bool
95 : 1 : Element::setValue(const double) {
96 : 1 : return (false);
97 : : }
98 : :
99 : : bool
100 : 2 : Element::setValue(const bool) {
101 : 2 : return (false);
102 : : }
103 : :
104 : : bool
105 : 2 : Element::setValue(const std::string&) {
106 : 2 : return (false);
107 : : }
108 : :
109 : : bool
110 : 2 : Element::setValue(const std::vector<ConstElementPtr>&) {
111 : 2 : return (false);
112 : : }
113 : :
114 : : bool
115 : 2 : Element::setValue(const std::map<std::string, ConstElementPtr>&) {
116 : 2 : return (false);
117 : : }
118 : :
119 : : ConstElementPtr
120 : 1 : Element::get(const int) const {
121 [ + - ][ + - ]: 2 : isc_throw(TypeError, "get(int) called on a non-list Element");
122 : : }
123 : :
124 : : void
125 : 1 : Element::set(const size_t, ConstElementPtr) {
126 [ + - ][ + - ]: 2 : isc_throw(TypeError, "set(int, element) called on a non-list Element");
127 : : }
128 : :
129 : : void
130 : 1 : Element::add(ConstElementPtr) {
131 [ + - ][ + - ]: 2 : isc_throw(TypeError, "add() called on a non-list Element");
132 : : }
133 : :
134 : : void
135 : 1 : Element::remove(const int) {
136 [ + - ][ + - ]: 2 : isc_throw(TypeError, "remove(int) called on a non-list Element");
137 : : }
138 : :
139 : : size_t
140 : 1 : Element::size() const {
141 [ + - ][ + - ]: 2 : isc_throw(TypeError, "size() called on a non-list Element");
142 : : }
143 : :
144 : : ConstElementPtr
145 : 1 : Element::get(const std::string&) const {
146 [ + - ][ + - ]: 2 : isc_throw(TypeError, "get(string) called on a non-map Element");
147 : : }
148 : :
149 : : void
150 : 1 : Element::set(const std::string&, ConstElementPtr) {
151 [ + - ][ + - ]: 2 : isc_throw(TypeError, "set(name, element) called on a non-map Element");
152 : : }
153 : :
154 : : void
155 : 1 : Element::remove(const std::string&) {
156 [ + - ][ + - ]: 2 : isc_throw(TypeError, "remove(string) called on a non-map Element");
157 : : }
158 : :
159 : : bool
160 : 1 : Element::contains(const std::string&) const {
161 [ + - ][ + - ]: 2 : isc_throw(TypeError, "contains(string) called on a non-map Element");
162 : : }
163 : :
164 : : ConstElementPtr
165 : 1 : Element::find(const std::string&) const {
166 [ + - ][ + - ]: 2 : isc_throw(TypeError, "find(string) called on a non-map Element");
167 : : }
168 : :
169 : : bool
170 : 1 : Element::find(const std::string&, ConstElementPtr) const {
171 : 1 : return (false);
172 : : }
173 : :
174 : : namespace {
175 : : inline void
176 : 35 : throwJSONError(const std::string& error, const std::string& file, int line,
177 : : int pos)
178 : : {
179 : 70 : std::stringstream ss;
180 [ + - ][ + - ]: 35 : ss << error << " in " + file + ":" << line << ":" << pos;
[ + - ][ + - ]
[ + - ][ + - ]
181 [ + - ][ + - ]: 105 : isc_throw(JSONError, ss.str());
[ + - ]
182 : : }
183 : : }
184 : :
185 : : std::ostream&
186 : 71 : operator<<(std::ostream &out, const Element& e) {
187 [ + - ]: 71 : return (out << e.str());
188 : : }
189 : :
190 : : bool
191 : 280 : operator==(const Element& a, const Element& b) {
192 : 280 : return (a.equals(b));
193 : : }
194 : :
195 : 33 : bool operator!=(const Element& a, const Element& b) {
196 : 33 : return (!a.equals(b));
197 : : };
198 : :
199 : : //
200 : : // factory functions
201 : : //
202 : : ElementPtr
203 : 34 : Element::create() {
204 : 68 : return (ElementPtr(new NullElement()));
205 : : }
206 : :
207 : : ElementPtr
208 : 1761 : Element::create(const long int i) {
209 : 3522 : return (ElementPtr(new IntElement(i)));
210 : : }
211 : :
212 : : ElementPtr
213 : 77 : Element::create(const double d) {
214 : 154 : return (ElementPtr(new DoubleElement(d)));
215 : : }
216 : :
217 : : ElementPtr
218 : 9629 : Element::create(const std::string& s) {
219 [ + - ]: 19258 : return (ElementPtr(new StringElement(s)));
220 : : }
221 : :
222 : : ElementPtr
223 : 2836 : Element::create(const bool b) {
224 : 5672 : return (ElementPtr(new BoolElement(b)));
225 : : }
226 : :
227 : : ElementPtr
228 : 3382 : Element::createList() {
229 : 6764 : return (ElementPtr(new ListElement()));
230 : : }
231 : :
232 : : ElementPtr
233 : 5660 : Element::createMap() {
234 : 11320 : return (ElementPtr(new MapElement()));
235 : : }
236 : :
237 : :
238 : : //
239 : : // helper functions for fromJSON factory
240 : : //
241 : : namespace {
242 : : bool
243 : 198662 : char_in(const char c, const char *chars) {
244 [ + + ]: 887424 : for (size_t i = 0; i < strlen(chars); ++i) {
245 [ + + ]: 688762 : if (chars[i] == c) {
246 : : return (true);
247 : : }
248 : : }
249 : : return (false);
250 : : }
251 : :
252 : : void
253 : 29117 : skip_chars(std::istream &in, const char *chars, int& line, int& pos) {
254 : 29117 : char c = in.peek();
255 [ + + ][ - + ]: 37856 : while (char_in(c, chars) && c != EOF) {
[ + + ]
256 [ + + ]: 8739 : if (c == '\n') {
257 : 878 : ++line;
258 : 878 : pos = 1;
259 : : } else {
260 : 7861 : ++pos;
261 : : }
262 : 8739 : in.get();
263 : 8739 : c = in.peek();
264 : : }
265 : 29117 : }
266 : :
267 : : // skip on the input stream to one of the characters in chars
268 : : // if another character is found this function returns false
269 : : // unless that character is specified in the optional may_skip
270 : : //
271 : : // the character found is left on the stream
272 : : void
273 : 32205 : skip_to(std::istream &in, const std::string& file, int& line,
274 : : int& pos, const char* chars, const char* may_skip="")
275 : : {
276 : 32205 : char c = in.get();
277 : 32205 : ++pos;
278 [ + + ]: 37999 : while (c != EOF) {
279 [ + + ]: 37994 : if (c == '\n') {
280 : 611 : pos = 1;
281 : 611 : ++line;
282 : : }
283 [ + + ]: 37994 : if (char_in(c, may_skip)) {
284 : 5794 : c = in.get();
285 : 5794 : ++pos;
286 [ + + ]: 32200 : } else if (char_in(c, chars)) {
287 [ + + ]: 90612 : while(char_in(in.peek(), may_skip)) {
288 [ + + ]: 58415 : if (in.peek() == '\n') {
289 : 3976 : pos = 1;
290 : 3976 : ++line;
291 : : }
292 : 58415 : in.get();
293 : 58415 : ++pos;
294 : : }
295 : 32197 : in.putback(c);
296 : 32197 : --pos;
297 : 32197 : return;
298 : : } else {
299 [ + - ][ + - ]: 3 : throwJSONError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
[ + - ][ + - ]
300 : : }
301 : : }
302 [ + - ][ + - ]: 5 : throwJSONError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
303 : : }
304 : :
305 : : // TODO: Should we check for all other official escapes here (and
306 : : // error on the rest)?
307 : : std::string
308 : 22548 : str_from_stringstream(std::istream &in, const std::string& file, const int line,
309 : 0 : int& pos) throw (JSONError)
310 : : {
311 : 22548 : char c = 0;
312 [ + - ][ + - ]: 45096 : std::stringstream ss;
[ + - ]
313 [ + - ]: 22548 : c = in.get();
314 : 22548 : ++pos;
315 [ + + ]: 22548 : if (c == '"') {
316 [ + - ]: 22542 : c = in.get();
317 : 22542 : ++pos;
318 : : } else {
319 [ + - ]: 6 : throwJSONError("String expected", file, line, pos);
320 : : }
321 : :
322 [ + + ]: 251914 : while (c != EOF && c != '"') {
323 [ + + ]: 229372 : if (c == '\\') {
324 : : // see the spec for allowed escape characters
325 [ + - ][ - + : 10 : switch (in.peek()) {
+ + + + +
- + ]
326 : : case '"':
327 : : c = '"';
328 : : break;
329 : : case '/':
330 : 0 : c = '/';
331 : 0 : break;
332 : : case '\\':
333 : 1 : c = '\\';
334 : 1 : break;
335 : : case 'b':
336 : 1 : c = '\b';
337 : 1 : break;
338 : : case 'f':
339 : 1 : c = '\f';
340 : 1 : break;
341 : : case 'n':
342 : 1 : c = '\n';
343 : 1 : break;
344 : : case 'r':
345 : 1 : c = '\r';
346 : 1 : break;
347 : : case 't':
348 : 1 : c = '\t';
349 : 1 : break;
350 : : default:
351 [ # # ]: 0 : throwJSONError("Bad escape", file, line, pos);
352 : : }
353 : : // drop the escaped char
354 [ + - ]: 10 : in.get();
355 : 10 : ++pos;
356 : : }
357 [ + - ]: 229372 : ss << c;
358 [ + - ]: 229372 : c = in.get();
359 : 229372 : ++pos;
360 : : }
361 [ + + ]: 22542 : if (c == EOF) {
362 [ + - ]: 3 : throwJSONError("Unterminated string", file, line, pos);
363 : : }
364 : 22539 : return (ss.str());
365 : : }
366 : :
367 : : std::string
368 : 2869 : word_from_stringstream(std::istream &in, int& pos) {
369 : 5738 : std::stringstream ss;
370 [ + - ][ + + ]: 16683 : while (isalpha(in.peek())) {
371 [ + - ][ + - ]: 13814 : ss << (char) in.get();
372 : : }
373 : 2869 : pos += ss.str().size();
374 : 2869 : return (ss.str());
375 : : }
376 : :
377 : : static std::string
378 : 1472 : number_from_stringstream(std::istream &in, int& pos) {
379 : 2944 : std::stringstream ss;
380 [ + - ][ + + ]: 7957 : while (isdigit(in.peek()) || in.peek() == '+' || in.peek() == '-' ||
[ + - ][ + + ]
[ + - ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
381 [ + - ][ + - ]: 4520 : in.peek() == '.' || in.peek() == 'e' || in.peek() == 'E') {
[ + - ]
382 [ + - ][ + - ]: 1965 : ss << (char) in.get();
383 : : }
384 : 1472 : pos += ss.str().size();
385 : 1472 : return (ss.str());
386 : : }
387 : :
388 : : // Should we change from IntElement and DoubleElement to NumberElement
389 : : // that can also hold an e value? (and have specific getters if the
390 : : // value is larger than an int can handle)
391 : : ElementPtr
392 : 1472 : from_stringstream_number(std::istream &in, int &pos) {
393 : 1472 : long int i = 0;
394 : 1472 : double d = 0.0;
395 : 1472 : bool is_double = false;
396 : : char *endptr;
397 : :
398 : 2944 : std::string number = number_from_stringstream(in, pos);
399 : :
400 : 1472 : i = strtol(number.c_str(), &endptr, 10);
401 [ + + ]: 1472 : if (*endptr != '\0') {
402 : 79 : d = strtod(number.c_str(), &endptr);
403 : 79 : is_double = true;
404 [ - + ]: 79 : if (*endptr != '\0') {
405 [ # # ][ # # ]: 0 : isc_throw(JSONError, std::string("Bad number: ") + number);
[ # # ][ # # ]
[ # # ]
406 : : } else {
407 [ + + ][ + + ]: 79 : if (d == HUGE_VAL || d == -HUGE_VAL) {
408 [ + - ][ + - ]: 8 : isc_throw(JSONError, std::string("Number overflow: ") + number);
[ + - ][ + - ]
[ + - ]
409 : : }
410 : : }
411 : : } else {
412 [ + + ]: 1393 : if (i == LONG_MAX || i == LONG_MIN) {
413 [ + - ][ + - ]: 2 : isc_throw(JSONError, std::string("Number overflow: ") + number);
[ + - ][ + - ]
[ + - ]
414 : : }
415 : : }
416 : :
417 [ + + ]: 1467 : if (is_double) {
418 [ + - ]: 75 : return (Element::create(d));
419 : : } else {
420 [ + - ]: 1392 : return (Element::create(i));
421 : : }
422 : : }
423 : :
424 : : ElementPtr
425 : 2835 : from_stringstream_bool(std::istream &in, const std::string& file,
426 : : const int line, int& pos)
427 : : {
428 : 5670 : const std::string word = word_from_stringstream(in, pos);
429 [ + - ][ + + ]: 2835 : if (boost::iequals(word, "True")) {
430 [ + - ]: 492 : return (Element::create(true));
431 [ + + ]: 4686 : } else if (boost::iequals(word, "False")) {
432 [ + - ]: 2341 : return (Element::create(false));
433 : : } else {
434 [ + - ][ + - ]: 2 : throwJSONError(std::string("Bad boolean value: ") + word, file, line, pos);
435 : : // above is a throw shortcurt, return empty is never reached
436 : : return (ElementPtr());
437 : : }
438 : : }
439 : :
440 : : ElementPtr
441 : 34 : from_stringstream_null(std::istream &in, const std::string& file,
442 : : const int line, int& pos)
443 : : {
444 : 68 : const std::string word = word_from_stringstream(in, pos);
445 [ + - ][ + + ]: 34 : if (boost::iequals(word, "null")) {
446 [ + - ]: 32 : return (Element::create());
447 : : } else {
448 [ + - ][ + - ]: 2 : throwJSONError(std::string("Bad null value: ") + word, file, line, pos);
449 : : return (ElementPtr());
450 : : }
451 : : }
452 : :
453 : : ElementPtr
454 : 0 : from_stringstream_string(std::istream& in, const std::string& file, int& line,
455 : : int& pos)
456 : : {
457 [ + + ][ + - ]: 8089 : return (Element::create(str_from_stringstream(in, file, line, pos)));
[ # # ]
458 : : }
459 : :
460 : : ElementPtr
461 : 2322 : from_stringstream_list(std::istream &in, const std::string& file, int& line,
462 : : int& pos)
463 : : {
464 : 2322 : char c = 0;
465 : 2322 : ElementPtr list = Element::createList();
466 : : ConstElementPtr cur_list_element;
467 : :
468 [ + - ]: 2322 : skip_chars(in, WHITESPACE, line, pos);
469 [ + + ]: 6551 : while (c != EOF && c != ']') {
470 [ + - ][ + + ]: 4235 : if (in.peek() != ']') {
471 [ + + ]: 3314 : cur_list_element = Element::fromJSON(in, file, line, pos);
472 [ + - ]: 6620 : list->add(cur_list_element);
473 [ + + ]: 3310 : skip_to(in, file, line, pos, ",]", WHITESPACE);
474 : : }
475 [ + - ]: 4229 : c = in.get();
476 : 4229 : pos++;
477 : : }
478 : 2316 : return (list);
479 : : }
480 : :
481 : : ElementPtr
482 : 5081 : from_stringstream_map(std::istream &in, const std::string& file, int& line,
483 : : int& pos)
484 : : {
485 : 5081 : ElementPtr map = Element::createMap();
486 [ + - ]: 5081 : skip_chars(in, WHITESPACE, line, pos);
487 [ + - ]: 5081 : char c = in.peek();
488 [ + + ]: 5081 : if (c == EOF) {
489 [ + - ]: 2 : throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
490 [ + + ]: 5079 : } else if (c == '}') {
491 : : // empty map, skip closing curly
492 [ + - ]: 420 : c = in.get();
493 : : } else {
494 [ + + ]: 19100 : while (c != EOF && c != '}') {
495 [ + + ]: 28910 : std::string key = str_from_stringstream(in, file, line, pos);
496 : :
497 [ + + ]: 14451 : skip_to(in, file, line, pos, ":", WHITESPACE);
498 : : // skip the :
499 [ + - ]: 14448 : in.get();
500 : 14448 : pos++;
501 : :
502 [ + + ]: 14448 : ConstElementPtr value = Element::fromJSON(in, file, line, pos);
503 [ + - ]: 28888 : map->set(key, value);
504 : :
505 [ + + ]: 14444 : skip_to(in, file, line, pos, ",}", WHITESPACE);
506 [ + - ]: 14441 : c = in.get();
507 : 14441 : pos++;
508 : : }
509 : : }
510 : 5061 : return (map);
511 : : }
512 : : }
513 : :
514 : : std::string
515 : 24 : Element::typeToName(Element::types type) {
516 [ + + + + : 24 : switch (type) {
+ + + +
+ ]
517 : : case Element::integer:
518 : 3 : return (std::string("integer"));
519 : : case Element::real:
520 : 3 : return (std::string("real"));
521 : : case Element::boolean:
522 : 3 : return (std::string("boolean"));
523 : : case Element::string:
524 : 4 : return (std::string("string"));
525 : : case Element::list:
526 : 5 : return (std::string("list"));
527 : : case Element::map:
528 : 3 : return (std::string("map"));
529 : : case Element::null:
530 : 1 : return (std::string("null"));
531 : : case Element::any:
532 : 1 : return (std::string("any"));
533 : : default:
534 : 24 : return (std::string("unknown"));
535 : : }
536 : : }
537 : :
538 : : Element::types
539 : 4971 : Element::nameToType(const std::string& type_name) {
540 [ + + ]: 4971 : if (type_name == "integer") {
541 : : return (Element::integer);
542 [ + + ]: 3668 : } else if (type_name == "real") {
543 : : return (Element::real);
544 [ + + ]: 3609 : } else if (type_name == "boolean") {
545 : : return (Element::boolean);
546 [ + + ]: 2984 : } else if (type_name == "string") {
547 : : return (Element::string);
548 [ + + ]: 1385 : } else if (type_name == "list") {
549 : : return (Element::list);
550 [ + + ]: 686 : } else if (type_name == "map") {
551 : : return (Element::map);
552 [ + + ]: 17 : } else if (type_name == "named_set") {
553 : : return (Element::map);
554 [ + + ]: 13 : } else if (type_name == "null") {
555 : : return (Element::null);
556 [ + + ]: 12 : } else if (type_name == "any") {
557 : : return (Element::any);
558 : : } else {
559 [ + - ][ + - ]: 4976 : isc_throw(TypeError, type_name + " is not a valid type name");
[ + - ]
560 : : }
561 : : }
562 : :
563 : : ElementPtr
564 : 0 : Element::fromJSON(std::istream& in) throw(JSONError) {
565 : 50 : int line = 1, pos = 1;
566 [ + - ][ + - ]: 50 : return (fromJSON(in, "<istream>", line, pos));
[ # # ]
567 : : }
568 : :
569 : : ElementPtr
570 : 99 : Element::fromJSON(std::istream& in, const std::string& file_name)
571 : 0 : throw(JSONError)
572 : : {
573 : 99 : int line = 1, pos = 1;
574 [ + - ][ # # ]: 99 : return (fromJSON(in, file_name, line, pos));
575 : : }
576 : :
577 : : ElementPtr
578 : 19846 : Element::fromJSON(std::istream &in, const std::string& file, int& line,
579 : 0 : int& pos) throw(JSONError)
580 : : {
581 : 19846 : char c = 0;
582 : : ElementPtr element;
583 : 19846 : bool el_read = false;
584 [ + - ]: 19846 : skip_chars(in, WHITESPACE, line, pos);
585 [ + + ][ + + ]: 39647 : while (c != EOF && !el_read) {
[ + + ]
586 [ + - ]: 19846 : c = in.get();
587 : 19846 : pos++;
588 [ + + + + : 19846 : switch(c) {
+ + + + ]
589 : : case '1':
590 : : case '2':
591 : : case '3':
592 : : case '4':
593 : : case '5':
594 : : case '6':
595 : : case '7':
596 : : case '8':
597 : : case '9':
598 : : case '0':
599 : : case '-':
600 : : case '+':
601 : : case '.':
602 [ + - ]: 1472 : in.putback(c);
603 [ + + ][ + - ]: 1472 : element = from_stringstream_number(in, pos);
604 : : el_read = true;
605 : : break;
606 : : case 't':
607 : : case 'T':
608 : : case 'f':
609 : : case 'F':
610 [ + - ]: 2835 : in.putback(c);
611 [ + + ][ + - ]: 2835 : element = from_stringstream_bool(in, file, line, pos);
612 : : el_read = true;
613 : : break;
614 : : case 'n':
615 : : case 'N':
616 [ + - ]: 34 : in.putback(c);
617 [ + + ][ + - ]: 34 : element = from_stringstream_null(in, file, line, pos);
618 : : el_read = true;
619 : : break;
620 : : case '"':
621 [ + - ]: 8089 : in.putback('"');
622 [ + - ]: 8088 : element = from_stringstream_string(in, file, line, pos);
623 : : el_read = true;
624 : : break;
625 : : case '[':
626 [ + + ][ + - ]: 2322 : element = from_stringstream_list(in, file, line, pos);
627 : : el_read = true;
628 : : break;
629 : : case '{':
630 [ + + ][ + - ]: 5081 : element = from_stringstream_map(in, file, line, pos);
631 : : el_read = true;
632 : : break;
633 : : case EOF:
634 : : break;
635 : : default:
636 [ + - ][ + - ]: 19810 : throwJSONError(std::string("error: unexpected character ") + c, file, line, pos);
637 : : break;
638 : : }
639 : : }
640 [ + + ]: 19801 : if (el_read) {
641 : 19797 : return (element);
642 : : } else {
643 [ + - ][ + - ]: 8 : isc_throw(JSONError, "nothing read");
[ + - ]
644 : : }
645 : : }
646 : :
647 : : ElementPtr
648 : 1909 : Element::fromJSON(const std::string &in) {
649 : 3818 : std::stringstream ss;
650 [ + - ]: 1909 : ss << in;
651 : 1909 : int line = 1, pos = 1;
652 [ + - ][ + + ]: 1909 : ElementPtr result(fromJSON(ss, "<string>", line, pos));
653 [ + - ]: 1868 : skip_chars(ss, WHITESPACE, line, pos);
654 : : // ss must now be at end
655 [ + - ][ + + ]: 1868 : if (ss.peek() != EOF) {
656 [ + - ][ + - ]: 3 : throwJSONError("Extra data", "<string>", line, pos);
657 : : }
658 : 1865 : return result;
659 : : }
660 : :
661 : : // to JSON format
662 : :
663 : : void
664 : 116 : IntElement::toJSON(std::ostream& ss) const {
665 : 116 : ss << intValue();
666 : 116 : }
667 : :
668 : : void
669 : 38 : DoubleElement::toJSON(std::ostream& ss) const {
670 : 38 : ss << doubleValue();
671 : 38 : }
672 : :
673 : : void
674 : 50 : BoolElement::toJSON(std::ostream& ss) const {
675 [ + + ]: 50 : if (boolValue()) {
676 : 14 : ss << "true";
677 : : } else {
678 : 36 : ss << "false";
679 : : }
680 : 50 : }
681 : :
682 : : void
683 : 9 : NullElement::toJSON(std::ostream& ss) const {
684 : 9 : ss << "null";
685 : 9 : }
686 : :
687 : : void
688 : 401 : StringElement::toJSON(std::ostream& ss) const {
689 : 401 : ss << "\"";
690 : : char c;
691 : 401 : const std::string& str = stringValue();
692 [ + + ]: 5494 : for (size_t i = 0; i < str.size(); ++i) {
693 : 5093 : c = str[i];
694 : : // Escape characters as defined in JSON spec
695 : : // Note that we do not escape forward slash; this
696 : : // is allowed, but not mandatory.
697 [ + + + + : 5093 : switch (c) {
+ + + + ]
698 : : case '"':
699 [ + - ][ + - ]: 3 : ss << '\\' << c;
700 : : break;
701 : : case '\\':
702 [ + - ][ + - ]: 1 : ss << '\\' << c;
703 : : break;
704 : : case '\b':
705 [ + - ][ + - ]: 1 : ss << '\\' << 'b';
706 : : break;
707 : : case '\f':
708 [ + - ][ + - ]: 1 : ss << '\\' << 'f';
709 : : break;
710 : : case '\n':
711 [ + - ][ + - ]: 1 : ss << '\\' << 'n';
712 : : break;
713 : : case '\r':
714 [ + - ][ + - ]: 1 : ss << '\\' << 'r';
715 : : break;
716 : : case '\t':
717 [ + - ][ + - ]: 1 : ss << '\\' << 't';
718 : : break;
719 : : default:
720 [ + - ]: 5084 : ss << c;
721 : : }
722 : : }
723 [ + - ]: 401 : ss << "\"";
724 : 401 : }
725 : :
726 : : void
727 : 164 : ListElement::toJSON(std::ostream& ss) const {
728 : 164 : ss << "[ ";
729 : :
730 : 164 : const std::vector<ConstElementPtr>& v = listValue();
731 [ + + ]: 406 : for (std::vector<ConstElementPtr>::const_iterator it = v.begin();
732 : 406 : it != v.end(); ++it) {
733 [ + + ]: 242 : if (it != v.begin()) {
734 : 89 : ss << ", ";
735 : : }
736 : 242 : (*it)->toJSON(ss);
737 : : }
738 : 164 : ss << " ]";
739 : 164 : }
740 : :
741 : : void
742 : 296 : MapElement::toJSON(std::ostream& ss) const {
743 : 296 : ss << "{ ";
744 : :
745 : 296 : const std::map<std::string, ConstElementPtr>& m = mapValue();
746 [ + + ]: 796 : for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
747 : 796 : it != m.end(); ++it) {
748 [ + + ]: 500 : if (it != m.begin()) {
749 : 225 : ss << ", ";
750 : : }
751 : 500 : ss << "\"" << (*it).first << "\": ";
752 [ + + ]: 500 : if ((*it).second) {
753 : 497 : (*it).second->toJSON(ss);
754 : : } else {
755 : 3 : ss << "None";
756 : : }
757 : : }
758 : 296 : ss << " }";
759 : 296 : }
760 : :
761 : : // throws when one of the types in the path (except the one
762 : : // we're looking for) is not a MapElement
763 : : // returns 0 if it could simply not be found
764 : : // should that also be an exception?
765 : : ConstElementPtr
766 : 258 : MapElement::find(const std::string& id) const {
767 : 258 : const size_t sep = id.find('/');
768 [ + + ]: 258 : if (sep == std::string::npos) {
769 : 240 : return (get(id));
770 : : } else {
771 [ + - ]: 18 : ConstElementPtr ce = get(id.substr(0, sep));
772 [ + + ]: 18 : if (ce) {
773 : : // ignore trailing slash
774 [ + + ]: 6 : if (sep + 1 != id.size()) {
775 [ + - ][ + + ]: 5 : return (ce->find(id.substr(sep + 1)));
776 : : } else {
777 : : return (ce);
778 : : }
779 : : } else {
780 : : return (ElementPtr());
781 : : }
782 : : }
783 : : }
784 : :
785 : : ElementPtr
786 : 1 : Element::fromWire(const std::string& s) {
787 : 2 : std::stringstream ss;
788 [ + - ]: 1 : ss << s;
789 : 1 : int line = 0, pos = 0;
790 [ + - ][ + - ]: 2 : return (fromJSON(ss, "<wire>", line, pos));
791 : : }
792 : :
793 : : ElementPtr
794 : 25 : Element::fromWire(std::stringstream& in, int) {
795 : : //
796 : : // Check protocol version
797 : : //
798 : : //for (int i = 0 ; i < 4 ; ++i) {
799 : : // const unsigned char version_byte = get_byte(in);
800 : : // if (PROTOCOL_VERSION[i] != version_byte) {
801 : : // throw DecodeError("Protocol version incorrect");
802 : : // }
803 : : //}
804 : : //length -= 4;
805 : 25 : int line = 0, pos = 0;
806 [ + - ]: 25 : return (fromJSON(in, "<wire>", line, pos));
807 : : }
808 : :
809 : : void
810 : 15428 : MapElement::set(const std::string& key, ConstElementPtr value) {
811 : 15428 : m[key] = value;
812 : 15428 : }
813 : :
814 : : bool
815 : 2 : MapElement::find(const std::string& id, ConstElementPtr t) const {
816 : : try {
817 [ + + ]: 2 : ConstElementPtr p = find(id);
818 [ + - ]: 1 : if (p) {
819 [ + - ]: 1 : t = p;
820 : 1 : return (true);
821 : : }
822 : 1 : } catch (const TypeError&) {
823 : : // ignore
824 : : }
825 : : return (false);
826 : : }
827 : :
828 : : bool
829 : 88 : IntElement::equals(const Element& other) const {
830 : : return (other.getType() == Element::integer) &&
831 [ + + ][ + + ]: 88 : (i == other.intValue());
832 : : }
833 : :
834 : : bool
835 : 7 : DoubleElement::equals(const Element& other) const {
836 : : return (other.getType() == Element::real) &&
837 [ + + ][ - + ]: 7 : (d == other.doubleValue());
838 : : }
839 : :
840 : : bool
841 : 9 : BoolElement::equals(const Element& other) const {
842 : : return (other.getType() == Element::boolean) &&
843 [ + + ][ + + ]: 9 : (b == other.boolValue());
844 : : }
845 : :
846 : : bool
847 : 1 : NullElement::equals(const Element& other) const {
848 : 1 : return (other.getType() == Element::null);
849 : : }
850 : :
851 : : bool
852 : 443 : StringElement::equals(const Element& other) const {
853 : : return (other.getType() == Element::string) &&
854 [ + + ][ + + ]: 882 : (s == other.stringValue());
[ + + ]
855 : : }
856 : :
857 : : bool
858 : 327 : ListElement::equals(const Element& other) const {
859 [ + + ]: 327 : if (other.getType() == Element::list) {
860 : 324 : const size_t s = size();
861 [ + + ]: 324 : if (s != other.size()) {
862 : : return (false);
863 : : }
864 [ + + ]: 783 : for (size_t i = 0; i < s; ++i) {
865 [ + - ]: 912 : if (!get(i)->equals(*other.get(i))) {
[ + - + + ]
866 : : return (false);
867 : : }
868 : : }
869 : : return (true);
870 : : } else {
871 : : return (false);
872 : : }
873 : : }
874 : :
875 : : bool
876 : 159 : MapElement::equals(const Element& other) const {
877 [ + + ]: 159 : if (other.getType() == Element::map) {
878 : 156 : const std::map<std::string, ConstElementPtr>& m = mapValue();
879 [ + + ]: 329 : for (std::map<std::string, ConstElementPtr>::const_iterator it =
880 : 156 : m.begin();
881 : 329 : it != m.end() ; ++it) {
882 [ + + ]: 207 : if (other.contains((*it).first)) {
883 [ + - ]: 410 : if (!get((*it).first)->equals(*other.get((*it).first))) {
[ + - + + ]
884 : : return (false);
885 : : }
886 : : } else {
887 : : return (false);
888 : : }
889 : : }
890 : : // quickly walk through the other map too, to see if there's
891 : : // anything in there that we don't have. We don't need to
892 : : // compare those elements; if one of them is missing we
893 : : // differ (and if it's not missing the loop above has checked
894 : : // it)
895 : : std::map<std::string, ConstElementPtr>::const_iterator it;
896 [ + + ]: 621 : for (it = other.mapValue().begin();
897 : 292 : it != other.mapValue().end();
898 : : ++it) {
899 [ + - ]: 170 : if (!contains((*it).first)) {
900 : : return (false);
901 : : }
902 : : }
903 : : return (true);
904 : : } else {
905 : : return (false);
906 : : }
907 : : }
908 : :
909 : : bool
910 : 23 : isNull(ConstElementPtr p) {
911 : 23 : return (!p);
912 : : }
913 : :
914 : : void
915 : 11 : removeIdentical(ElementPtr a, ConstElementPtr b) {
916 [ + - ]: 11 : if (!b) {
917 : 9 : return;
918 : : }
919 [ + + ][ + - ]: 11 : if (a->getType() != Element::map || b->getType() != Element::map) {
[ + + ]
920 [ + - ][ + - ]: 4 : isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
921 : : }
922 : :
923 : : // As maps do not allow entries with multiple keys, we can either iterate
924 : : // over a checking for identical entries in b or vice-versa. As elements
925 : : // are removed from a if a match is found, we choose to iterate over b to
926 : : // avoid problems with element removal affecting the iterator.
927 : 9 : const std::map<std::string, ConstElementPtr>& m = b->mapValue();
928 [ + + ]: 18 : for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
929 : 18 : it != m.end() ; ++it) {
930 [ + - ]: 9 : if (a->contains((*it).first)) {
931 [ + - ]: 18 : if (a->get((*it).first)->equals(*b->get((*it).first))) {
[ + - + + ]
932 : 7 : a->remove((*it).first);
933 : : }
934 : : }
935 : : }
936 : : }
937 : :
938 : : ConstElementPtr
939 : 15 : removeIdentical(ConstElementPtr a, ConstElementPtr b) {
940 : 15 : ElementPtr result = Element::createMap();
941 : :
942 [ - + ]: 15 : if (!b) {
943 : : return (result);
944 : : }
945 : :
946 [ + - ][ + - ]: 15 : if (a->getType() != Element::map || b->getType() != Element::map) {
[ - + ]
947 [ # # ][ # # ]: 0 : isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
[ # # ]
948 : : }
949 : :
950 [ + - ]: 15 : const std::map<std::string, ConstElementPtr>& m = a->mapValue();
951 [ + + ]: 30 : for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
952 : 30 : it != m.end() ; ++it) {
953 [ + - ][ + + ]: 40 : if (!b->contains((*it).first) ||
[ + + ][ + + ]
954 [ + - ][ + - ]: 35 : !a->get((*it).first)->equals(*b->get((*it).first))) {
[ + - ][ + + ]
[ + + ][ # # ]
[ # # ]
955 [ + - ]: 16 : result->set((*it).first, (*it).second);
956 : : }
957 : : }
958 : :
959 : : return (result);
960 : : }
961 : :
962 : : void
963 : 39 : merge(ElementPtr element, ConstElementPtr other) {
964 [ + + + - ]: 77 : if (element->getType() != Element::map ||
[ + + ]
965 : 38 : other->getType() != Element::map) {
966 [ + - ][ + - ]: 2 : isc_throw(TypeError, "merge arguments not MapElements");
967 : : }
968 : :
969 : 38 : const std::map<std::string, ConstElementPtr>& m = other->mapValue();
970 [ + + ]: 71 : for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
971 : 71 : it != m.end() ; ++it) {
972 [ + - ][ + + ]: 33 : if ((*it).second && (*it).second->getType() != Element::null) {
[ + + ]
973 [ + - ]: 60 : element->set((*it).first, (*it).second);
974 [ + - ]: 3 : } else if (element->contains((*it).first)) {
975 : 3 : element->remove((*it).first);
976 : : }
977 : : }
978 : 38 : }
979 : :
980 : : }
981 : 139 : }
|