value.cc 19 KB
Newer Older
1
#include "radixcore/value.hh"
Norby, Tom's avatar
Norby, Tom committed
2
3
4

#include <cassert>
#include <cstdlib>  // atoi/atof, etc
LEFEBVREJP email's avatar
LEFEBVREJP email committed
5
#include <cstring>  // strdup, free
Norby, Tom's avatar
Norby, Tom committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#include <sstream>
#include "radixbug/bug.hh"

namespace radix
{
Value::Value()
    : m_allocated(false)
    , m_type(TYPE_NULL)
{
}
Value::Value(const Value& orig) { this->copy_from(orig); }
Value::Value(Value&& orig)
    : m_allocated(orig.m_allocated)
    , m_type(orig.m_type)
    , m_data(orig.m_data)
{
  orig.m_allocated = false;
  orig.m_type      = TYPE_NULL;
}
Value::Value(bool v)
{
  m_data.m_bool = v;
  m_type        = TYPE_BOOLEAN;
}
Value::Value(int v)
{
  m_data.m_int = v;
  m_type       = TYPE_INTEGER;
}
Value::Value(double v)
{
  m_data.m_double = v;
  m_type          = TYPE_DOUBLE;
}
Value::Value(const char* v)
{
  radix_require(v);
  m_data.m_string = strdup(v);
  m_type          = TYPE_STRING;
  m_allocated     = true;
}
Value::Value(const std::string& v)
{
  m_data.m_string = strdup(v.c_str());
  m_type          = TYPE_STRING;
  m_allocated     = true;
}
Value::Value(const DataArray& v)
{
  m_data.m_array = new DataArray(v);
  m_type         = TYPE_ARRAY;
  m_allocated    = true;
}
Value::Value(const DataObject& v)
{
  m_data.m_object = new DataObject(v);
  m_type          = TYPE_OBJECT;
  m_allocated     = true;
}
void Value::copy_from(const Value& orig)
{
  m_allocated = orig.m_allocated;
  m_type      = orig.m_type;
  switch (m_type)
  {
    case TYPE_NULL:
      break;
    case TYPE_BOOLEAN:
      m_data.m_bool = orig.m_data.m_bool;
      break;
    case TYPE_INTEGER:
      m_data.m_int = orig.m_data.m_int;
      break;
    case TYPE_DOUBLE:
      m_data.m_double = orig.m_data.m_double;
      break;
    case TYPE_STRING:
      radix_check(m_allocated);
      m_data.m_string = strdup(orig.m_data.m_string);
      break;
    case TYPE_ARRAY:
      radix_check(m_allocated);
      m_data.m_array = new DataArray(*orig.m_data.m_array);
      radix_ensure(m_data.m_array);
      break;
    case TYPE_OBJECT:
      radix_check(m_allocated);
      m_data.m_object = new DataObject(*orig.m_data.m_object);
      radix_ensure(m_data.m_object);
      break;
  }
}

Value::Type Value::type() const { return m_type; }

std::string Value::categoryString() const
{
  switch (m_type)
  {
    case Type::TYPE_INTEGER:
    case Type::TYPE_DOUBLE:
      return "number";
    case Type::TYPE_STRING:
      return "string";
    case Type::TYPE_OBJECT:
      return "object";
    case Type::TYPE_ARRAY:
      return "array";
    case Type::TYPE_BOOLEAN:
      return "boolean";
    default:
      return "null";
  }
}

Value& Value::operator=(const Value& orig)
{
  // release any allocated memory
  nullify();
  // copy from the originator
  copy_from(orig);
  return *this;
}
Value& Value::operator=(Value&& orig)
{
  // release any allocated memory
  nullify();
  m_allocated = orig.m_allocated;
  m_data      = orig.m_data;
  m_type      = orig.m_type;

  orig.m_allocated = false;
  orig.m_type      = TYPE_NULL;

  return *this;
}
Value& Value::operator=(bool v)
{
  nullify();
  m_type        = TYPE_BOOLEAN;
  m_data.m_bool = v;
  return *this;
}
Value& Value::operator=(int v)
{
  nullify();
  m_type       = TYPE_INTEGER;
  m_data.m_int = v;
  return *this;
}
Value& Value::operator=(double v)
{
  nullify();
  m_type          = TYPE_DOUBLE;
  m_data.m_double = v;
  return *this;
}
Value& Value::operator=(const char* v)
{
  nullify();
  radix_require(v);
  m_data.m_string = strdup(v);
  m_type          = TYPE_STRING;
  m_allocated     = true;
  return *this;
}
Value& Value::operator=(const std::string& v)
{
  nullify();
  m_data.m_string = strdup(v.c_str());
  m_type          = TYPE_STRING;
  m_allocated     = true;
  return *this;
}
Value& Value::operator=(const DataArray& v)
{
  nullify();
  m_data.m_array = new DataArray(v);
  m_type         = TYPE_ARRAY;
  m_allocated    = true;
  return *this;
}
Value& Value::operator=(const DataObject& v)
{
  nullify();
  m_data.m_object = new DataObject(v);
  m_type          = TYPE_OBJECT;
  m_allocated     = true;
  return *this;
}

void Value::assign(DataObject* obj)
{
  if (obj == nullptr)
  {
    m_type      = TYPE_NULL;
    m_allocated = false;
    return;
  }
  nullify();
  m_type          = TYPE_OBJECT;
  m_allocated     = true;
  m_data.m_object = obj;
}
void Value::assign(DataArray* array)
{
  if (array == nullptr)
  {
    m_type      = TYPE_NULL;
    m_allocated = false;
    return;
  }
  nullify();
  m_type         = TYPE_ARRAY;
  m_allocated    = true;
  m_data.m_array = array;
}
void Value::nullify()
{
  switch (m_type)
  {
    case TYPE_NULL:
      break;
    case TYPE_BOOLEAN:
      break;
    case TYPE_INTEGER:
      break;
    case TYPE_DOUBLE:
      break;
    case TYPE_STRING:
      radix_check(m_allocated);
      free(m_data.m_string);
      break;
    case TYPE_ARRAY:
      radix_check(m_allocated);
      delete m_data.m_array;
      break;
    case TYPE_OBJECT:
      radix_check(m_allocated);
      delete m_data.m_object;
      break;
  }
  m_type      = TYPE_NULL;
  m_allocated = false;
}

Value::~Value() { this->nullify(); }

int Value::to_int() const
{
  switch (m_type)
  {
    case TYPE_NULL:
      return 0;

    case TYPE_BOOLEAN:
      return int(m_data.m_bool);

    case TYPE_INTEGER:
      return m_data.m_int;

    case TYPE_DOUBLE:
      return int(m_data.m_double);

    case TYPE_STRING:
      radix_ensure(m_allocated);
      radix_ensure(m_data.m_string);
      return atoi(m_data.m_string);

    case TYPE_ARRAY:
      radix_not_implemented("conversion of array to integer");

    case TYPE_OBJECT:
LEFEBVREJP email's avatar
LEFEBVREJP email committed
279
      radix_not_implemented("conversion of object to integer");
Norby, Tom's avatar
Norby, Tom committed
280
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
281
  radix_not_implemented("unknown type conversion to integer");
Norby, Tom's avatar
Norby, Tom committed
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
}
double Value::to_double() const
{
  switch (m_type)
  {
    case TYPE_NULL:
      return 0.0;

    case TYPE_BOOLEAN:
      return double(m_data.m_bool);

    case TYPE_INTEGER:
      return double(m_data.m_int);

    case TYPE_DOUBLE:
      return m_data.m_double;

    case TYPE_STRING:
      radix_ensure(m_allocated);
      radix_ensure(m_data.m_string);
      return atof(m_data.m_string);

    case TYPE_ARRAY:
      radix_not_implemented("conversion of array to double");

    case TYPE_OBJECT:
LEFEBVREJP email's avatar
LEFEBVREJP email committed
308
      radix_not_implemented("conversion of object to double");
Norby, Tom's avatar
Norby, Tom committed
309
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
310
  radix_not_implemented("unknown type conversion to double");
Norby, Tom's avatar
Norby, Tom committed
311
312
313
314
315
316
317
318
319
320
321
322
}
bool Value::to_bool() const
{
  switch (m_type)
  {
    case TYPE_NULL:
      return false;

    case TYPE_BOOLEAN:
      return m_data.m_bool;

    case TYPE_INTEGER:
323
      return m_data.m_int != 0;
Norby, Tom's avatar
Norby, Tom committed
324
325
326
327
328

    case TYPE_DOUBLE:
      return m_data.m_double ? true : false;

    case TYPE_STRING:
329
      radix_not_implemented("conversion of string to boolean");
Norby, Tom's avatar
Norby, Tom committed
330

331
332
    case TYPE_ARRAY:
      radix_not_implemented("conversion of array to double");
Norby, Tom's avatar
Norby, Tom committed
333

334
    case TYPE_OBJECT:
LEFEBVREJP email's avatar
LEFEBVREJP email committed
335
      radix_not_implemented("conversion of object to double");
Norby, Tom's avatar
Norby, Tom committed
336
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
337
  radix_not_implemented("unknown type conversion to bool");
Norby, Tom's avatar
Norby, Tom committed
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
}
const char* Value::to_cstring() const
{
  switch (m_type)
  {
    case TYPE_NULL:
    case TYPE_BOOLEAN:
    case TYPE_INTEGER:
    case TYPE_DOUBLE:
      return nullptr;
    case TYPE_STRING:
      radix_check(m_allocated);
      return m_data.m_string;

    case TYPE_ARRAY:
353
      radix_not_implemented("conversion of array to cstring");
Norby, Tom's avatar
Norby, Tom committed
354

355
    case TYPE_OBJECT:
LEFEBVREJP email's avatar
LEFEBVREJP email committed
356
      radix_not_implemented("conversion of object to cstring");
Norby, Tom's avatar
Norby, Tom committed
357
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
358
  radix_not_implemented("unknown type conversion to cstring");
Norby, Tom's avatar
Norby, Tom committed
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
}
std::string Value::to_string() const
{
  switch (m_type)
  {
    case TYPE_NULL:
      return "null";
    case TYPE_BOOLEAN:
      return m_data.m_bool ? "true" : "false";
    case TYPE_INTEGER:
      return std::to_string(m_data.m_int);
    case TYPE_DOUBLE:
      return std::to_string(m_data.m_double);
    case TYPE_STRING:
      radix_check(m_allocated);
      return m_data.m_string;

    case TYPE_ARRAY:
377
      radix_not_implemented("conversion of array to string");
Norby, Tom's avatar
Norby, Tom committed
378

379
380
    case TYPE_OBJECT:
      std::stringstream str;
Norby, Tom's avatar
Norby, Tom committed
381
382
383
      to_object()->pack_json(str);
      return str.str();
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
384
  radix_not_implemented("unknown type conversion to string");
Norby, Tom's avatar
Norby, Tom committed
385
386
387
388
}

DataArray* Value::to_array() const
{
LEFEBVREJP email's avatar
LEFEBVREJP email committed
389
390
391
  radix_insist(convertable(TYPE_ARRAY),
               "Value object must be convertable to an array");
  return m_data.m_array;
Norby, Tom's avatar
Norby, Tom committed
392
393
394
}
DataObject* Value::to_object() const
{
LEFEBVREJP email's avatar
LEFEBVREJP email committed
395
396
397
  radix_insist(convertable(TYPE_OBJECT),
               "Value object must be convertable to an object");
  return m_data.m_object;
Norby, Tom's avatar
Norby, Tom committed
398
399
400
401
}

const DataArray& Value::as_array() const
{
LEFEBVREJP email's avatar
LEFEBVREJP email committed
402
403
404
  radix_insist(convertable(TYPE_ARRAY),
               "Value object must be convertable to an array");
  return *(m_data.m_array);
Norby, Tom's avatar
Norby, Tom committed
405
406
407
408
}

DataArray& Value::as_array()
{
LEFEBVREJP email's avatar
LEFEBVREJP email committed
409
410
411
  radix_insist(convertable(TYPE_ARRAY),
               "Value object must be convertable to an array");
  return *(m_data.m_array);
Norby, Tom's avatar
Norby, Tom committed
412
413
414
415
}

const DataObject& Value::as_object() const
{
LEFEBVREJP email's avatar
LEFEBVREJP email committed
416
417
418
  radix_insist(convertable(TYPE_OBJECT),
               "Value object must be convertable to an object");
  return *(m_data.m_object);
Norby, Tom's avatar
Norby, Tom committed
419
420
421
422
}

DataObject& Value::as_object()
{
LEFEBVREJP email's avatar
LEFEBVREJP email committed
423
424
425
  radix_insist(convertable(TYPE_OBJECT),
               "Value object must be convertable to an object");
  return *(m_data.m_object);
Norby, Tom's avatar
Norby, Tom committed
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
}
bool Value::convertable(Value::Type to) const
{
  switch (to)
  {
    case TYPE_NULL:
      return (is_number() && to_double() == 0.0);
    case TYPE_BOOLEAN:
    case TYPE_INTEGER:
    case TYPE_DOUBLE:
      return is_bool() || is_null() || is_number();
    case TYPE_STRING:
      return is_string();
    case TYPE_ARRAY:
      return is_array();
    case TYPE_OBJECT:
      return is_object();
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
444
  radix_not_implemented("unknown type conversion");
Norby, Tom's avatar
Norby, Tom committed
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
}

Value& Value::operator[](const std::string& name)
{
  radix_check(is_object());
  DataObject* o = to_object();
  return o->operator[](name);
}
const Value& Value::operator[](const std::string& name) const
{
  radix_check(is_object());
  const DataObject* o = to_object();
  return o->operator[](name);
}
Value& Value::operator[](size_t i)
{
  radix_check(is_array());
  DataArray* a = to_array();
  return a->operator[](i);
}
const Value& Value::operator[](size_t i) const
{
  radix_check(is_array());
  DataArray* a = to_array();
  return a->operator[](i);
}
bool Value::empty() const
{
  if (is_array())
  {
    radix_check(to_array());
    return to_array()->empty();
  }
  if (is_object())
  {
    radix_check(to_object());
    return to_object()->empty();
  }
  return is_null();
}
size_t Value::size() const
{
  if (is_array())
  {
    radix_check(to_array());
    return to_array()->size();
  }
  if (is_object())
  {
    radix_check(to_object());
    return to_object()->size();
  }
  return 0;
}

LEFEBVREJP email's avatar
LEFEBVREJP email committed
500
501
bool Value::format_json(std::ostream& out, int indent_level, int level,
                        bool order_by_insert) const
Norby, Tom's avatar
Norby, Tom committed
502
503
504
{
  if (is_object())
  {
LEFEBVREJP email's avatar
LEFEBVREJP email committed
505
    to_object()->format_json(out, indent_level, level, order_by_insert);
Norby, Tom's avatar
Norby, Tom committed
506
507
508
  }
  else if (is_array())
  {
LEFEBVREJP email's avatar
LEFEBVREJP email committed
509
    to_array()->format_json(out, indent_level, level, order_by_insert);
Norby, Tom's avatar
Norby, Tom committed
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  }
  else
  {
    switch (type())
    {
      case TYPE_STRING:
        out << "\"" << to_string() << "\"";
        break;
      case TYPE_BOOLEAN:
        out << std::boolalpha << to_bool();
        break;
      case TYPE_INTEGER:
        out << to_int();
        break;
      case TYPE_DOUBLE:
        // the precision can be set by the caller
        out << to_double();
        break;
      case TYPE_NULL:
        out << "null";
        break;
      default:
LEFEBVREJP email's avatar
LEFEBVREJP email committed
532
        radix_not_implemented("unknown Object value type json emission");
Norby, Tom's avatar
Norby, Tom committed
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
    }
  }
  return out.good();
}
bool Value::pack_json(std::ostream& out) const
{
  if (is_object())
  {
    to_object()->pack_json(out);
  }
  else if (is_array())
  {
    to_array()->pack_json(out);
  }
  else
  {
    switch (type())
    {
      case TYPE_STRING:
        out << "\"" << to_string() << "\"";
        break;
      case TYPE_BOOLEAN:
        out << std::boolalpha << to_bool();
        break;
      case TYPE_INTEGER:
        out << to_int();
        break;
      case TYPE_DOUBLE:
        // the precision can be set by the caller
        out << to_double();
        break;
      case TYPE_NULL:
        out << "null";
        break;
      default:
LEFEBVREJP email's avatar
LEFEBVREJP email committed
568
        radix_not_implemented("unknown Object value type json emission");
Norby, Tom's avatar
Norby, Tom committed
569
570
571
572
573
574
575
576
577
578
579
580
    }
  }
  return out.good();
}
DataArray::DataArray() {}
DataArray::~DataArray() {}
DataArray::DataArray(const DataArray& orig)
    : m_data(orig.m_data)
{
}
size_t DataArray::size() const { return m_data.size(); }
bool DataArray::empty() const { return m_data.empty(); }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
581
582
bool DataArray::format_json(std::ostream& out, int indent_level, int level,
                            bool order_by_insert) const
Norby, Tom's avatar
Norby, Tom committed
583
584
585
586
587
588
589
590
591
592
593
{
  radix_require(indent_level >= 0);
  radix_require(level >= 0);
  if (empty())
  {
    out << "[]";
    return true;
  }
  out << "[" << std::endl;
  std::string indent = std::string(indent_level * (level + 1), ' ');
  out << indent;
LEFEBVREJP email's avatar
LEFEBVREJP email committed
594
  at(0).format_json(out, indent_level, level + 1, order_by_insert);
Norby, Tom's avatar
Norby, Tom committed
595
596
597

  for (size_t i = 1, count = size(); i < count; ++i)
  {
LEFEBVREJP email's avatar
LEFEBVREJP email committed
598
599
600
    out << "," << std::endl << indent;
    if (!at(i).format_json(out, indent_level, level + 1, order_by_insert))
      return false;
Norby, Tom's avatar
Norby, Tom committed
601
  }
LEFEBVREJP email's avatar
LEFEBVREJP email committed
602
603
  out << std::endl;
  out << std::string(indent_level * level, ' ') << "]";
Norby, Tom's avatar
Norby, Tom committed
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
  return out.good();
}
bool DataArray::pack_json(std::ostream& out) const
{
  if (empty())
  {
    out << "[]";
    return true;
  }
  out << "[";
  at(0).pack_json(out);

  for (size_t i = 1, count = size(); i < count; ++i)
  {
    out << ",";
    if (!at(i).pack_json(out)) return false;
  }
  out << "]";
  return out.good();
}

void DataArray::merge(const DataArray& rhs)
{
  // loop over every item in rhs array
  size_t index = 0;
  for (auto item : rhs)
  {
    radix_line("Checking inbound array index(" << index << ")");
    if (size() <= index)
    {
      radix_line("lhs doesn't have index. adding rhs index to lhs");
      this->push_back(item);
    }
    else
    {
      // must check the type of lhs[index] against that of rhs[index]
      Value& child = m_data.at(index);
      // if Value is an object do a depth-first merge
      if (child.is_object())
      {
        // check that my object is indeed an object
        if (!item.is_object())
        {
          std::stringstream ss;
          ss << "Attempting to merge data arrary where member index(" << index
             << ") differs in type." << std::endl
             << "Left-hand side type(object) differs from right-hand "
                "side type("
             << item.categoryString() << ")";
          throw std::logic_error(ss.str());
        }
        // merge objects
        child.to_object()->merge(item.as_object());
      }
      else if (child.is_array())
      {  // if Value is an array do a depth-first merge
        if (!item.is_array())
        {
          std::stringstream ss;
          ss << "Attempting to merge data array where member index(" << index
             << " differs in type."
             << "Left-hand side type(array) differs from right-hand "
                "side type("
             << item.categoryString() << ")";
          throw std::logic_error(ss.str());
        }
        // merge array
        child.to_array()->merge(item.as_array());
      }
    }
    ++index;
  }
}
DataObject::DataObject() {}
DataObject::DataObject(const DataObject& orig)
LEFEBVREJP email's avatar
LEFEBVREJP email committed
679
680
    : m_insert_order(orig.m_insert_order)
    , m_data(orig.m_data)
Norby, Tom's avatar
Norby, Tom committed
681
682
683
684
685
686
687
688
689
690
{
}

DataObject::~DataObject() {}
size_t DataObject::size() const { return m_data.size(); }
bool DataObject::empty() const { return m_data.empty(); }

Value& DataObject::operator[](const std::string& name)
{
  // since c++11 std::map<>[] does insertion if key doesn't exist
LEFEBVREJP email's avatar
LEFEBVREJP email committed
691
  m_insert_order.push_back(name);
Norby, Tom's avatar
Norby, Tom committed
692
693
  return m_data[name];
}
LEFEBVREJP email's avatar
LEFEBVREJP email committed
694

Norby, Tom's avatar
Norby, Tom committed
695
696
697
698
699
700
701
702
703
704
const Value& DataObject::operator[](const std::string& name) const
{
  auto itr = m_data.find(name);

  if (itr == m_data.end())
  {
    throw std::out_of_range(name + " not found in object");
  }
  return itr->second;
}
LEFEBVREJP email's avatar
LEFEBVREJP email committed
705
706
707
708
709
710
711
712
713
714

std::pair<DataObject::storage_type::iterator, bool> DataObject::insert(
    const std::pair<std::string, Value>& v)
{
  m_insert_order.push_back(v.first);
  return m_data.insert(v);
}

bool DataObject::format_json(std::ostream& out, int indent_level, int level,
                             bool order_by_insert) const
Norby, Tom's avatar
Norby, Tom committed
715
716
717
718
719
720
721
722
723
724
725
726
{
  radix_require(indent_level >= 0);
  radix_check(level >= 0);
  if (empty())
  {
    out << "{}";
    return true;
  }

  out << "{" << std::endl;
  std::string indent = std::string(indent_level * (level + 1), ' ');
  out << indent;
LEFEBVREJP email's avatar
LEFEBVREJP email committed
727
728
729
730
731
732
733
734
735
736
737
738

  // determine order to print (map default of sorted, or order of insertion)
  std::vector<std::string> order;
  if (order_by_insert)
    order = m_insert_order;
  else
    for (auto itr = m_data.begin(); itr != m_data.end(); ++itr)
      order.push_back(itr->first);

  auto itr = order.begin();
  out << "\"" << *itr << "\" : ";
  m_data.at(*itr).format_json(out, indent_level, level + 1, order_by_insert);
Norby, Tom's avatar
Norby, Tom committed
739
  ++itr;
LEFEBVREJP email's avatar
LEFEBVREJP email committed
740
  for (; itr != order.end(); ++itr)
Norby, Tom's avatar
Norby, Tom committed
741
  {
LEFEBVREJP email's avatar
LEFEBVREJP email committed
742
743
744
745
746
    out << "," << std::endl;
    out << indent << "\"" << *itr << "\" : ";
    if (!m_data.at(*itr).format_json(out, indent_level, level + 1,
                                     order_by_insert))
      return false;
Norby, Tom's avatar
Norby, Tom committed
747
748
  }

LEFEBVREJP email's avatar
LEFEBVREJP email committed
749
750
  out << std::endl;

Norby, Tom's avatar
Norby, Tom committed
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  out << std::string(indent_level * (level), ' ') << "}";
  return out.good();
}
bool DataObject::pack_json(std::ostream& out) const
{
  if (empty())
  {
    out << "{}";
    return true;
  }

  out << "{";
  auto itr = begin();
  out << "\"" << itr->first << "\":";
  itr->second.pack_json(out);
  ++itr;
  for (; itr != end(); ++itr)
  {
    out << ",";
    out << "\"" << itr->first << "\":";
    if (!itr->second.pack_json(out)) return false;
  }

  out << "}";
  return out.good();
}

void DataObject::merge(const DataObject& rhs)
{
  // merge every Value, Object and Array from 'obj'
  // that does not exist in 'this'
  for (auto& item : rhs)
  {
    radix_line("Checking inbound " << item.first);
    if (contains(item.first))
    {
      radix_line("Destination contains " << item.first);
      Value& child = m_data.at(item.first);
      // if Value is an object do a depth-first merge
      if (child.is_object())
      {
        // check that my object is indeed an object
        if (!item.second.is_object())
        {
          std::stringstream ss;
          ss << "Attempting to merge data objects where member(" << item.first
             << ") differs in type." << std::endl
             << "Left-hand side type(object) differs from right-hand "
                "side type("
             << item.second.categoryString() << ")";
          throw std::logic_error(ss.str());
        }
        // merge objects
        child.to_object()->merge(item.second.as_object());
      }
      else if (child.is_array())
      {  // if Value is an array do a depth-first merge
        if (!item.second.is_array())
        {
          std::stringstream ss;
          ss << "Attempting to merge data objects where member(" << item.first
             << " differs in type."
             << "Left-hand side type(array) differs from right-hand "
                "side type("
             << item.second.categoryString() << ")";
          throw std::logic_error(ss.str());
        }
        // merge array
        child.to_array()->merge(item.second.as_array());
      }
    }
    else
    {  // no destination to merge so we can simply copy
      radix_line("Destination does not contain " << item.first
                                                 << ". Copying...");
      this->insert(item);
    }
  }
}
}  // namespace radix