1
   2
   3
   4
   5
   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
 279
 280
 281
 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
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 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
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 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
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 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
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 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
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef ISC_DATA_H
#define ISC_DATA_H 1

#include <util/bigints.h>

#include <iostream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <map><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <stdexcept><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <stdint.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <exceptions/exceptions.h>

namespace isc { namespace data {

class Element;
// todo: describe the rationale behind ElementPtr?
typedef boost::shared_ptr<Element> ElementPtr;
typedef boost::shared_ptr<const Element> ConstElementPtr;

///
/// @brief A standard Data module exception that is thrown if a function
/// is called for an Element that has a wrong type (e.g. int_value on a
/// ListElement)
///
class TypeError : public isc::Exception {
public:
    TypeError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

///
/// @brief A standard Data module exception that is thrown if a parse
/// error is encountered when constructing an Element from a string
///
// i'd like to use Exception here but we need one that is derived from
// runtime_error (as this one is directly based on external data, and
// i want to add some values to any static data string that is provided)
class JSONError : public isc::Exception {
public:
    JSONError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

///
/// @brief The @c Element class represents a piece of data, used by
/// the command channel and configuration parts.
///
/// An @c Element can contain simple types (int, real, string, bool and
/// None), and composite types (list and string->element maps)
///
/// Elements should in calling functions usually be referenced through
/// an @c ElementPtr, which can be created using the factory functions
/// @c Element::create() and @c Element::fromJSON()
///
/// Notes to developers: Element is a base class, implemented by a
/// specific subclass for each type (IntElement, BoolElement, etc).
/// Element does define all functions for all types, and defaults to
/// raising a @c TypeError for functions that are not supported for
/// the type in question.
///
class Element {

public:
    /// @brief Represents the position of the data element within a
    /// configuration string.
    ///
    /// Position comprises a file name, line number and an offset within this
    /// line where the element value starts. For example, if the JSON string is
    ///
    /// @code
    /// { "foo": "some string",
    ///   "bar": 123 }
    /// \endcode
    ///
    /// the position of the element "bar" is: line_ = 2; pos_ = 9, because
    /// beginning of the value "123" is at offset 9 from the beginning of
    /// the second line, including whitespaces.
    ///
    /// Note that the @c Position structure is used as an argument to @c Element
    /// constructors and factory functions to avoid ambiguity and so that the
    /// uint32_t arguments holding line number and position within the line are
    /// not confused with the @c Element values passed to these functions.
    struct Position {
        std::string file_; ///< File name.
        uint32_t line_;    ///< Line number.
        uint32_t pos_;     ///< Position within the line.

        /// @brief Default constructor.
        Position() : file_(""), line_(0), pos_(0) {
        }

        /// @brief Constructor.
        ///
        /// @param file File name.
        /// @param line Line number.
        /// @param pos Position within the line.
        Position(const std::string& file, const uint32_t line,
                 const uint32_t pos)
            : file_(file), line_(line), pos_(pos) {
        }

        /// @brief Returns the position in the textual format.
        ///
        /// The returned position has the following format: file:line:pos.
        std::string str() const;
    };

    /// @brief Returns @c Position object with line_ and pos_ set to 0, and
    /// with an empty file name.
    ///
    /// The object containing two zeros is a default for most of the
    /// methods creating @c Element objects. The returned value is static
    /// so as it is not created everytime the function with the default
    /// position argument is called.
    static const Position& ZERO_POSITION() {
        static Position position("", 0, 0);
        return (position);
    }

    /// @brief The types that an Element can hold
    ///
    /// Some of these types need to match their associated integer from the
    /// parameter_data_type database table, so let the enums be explicitly
    /// mapped to integers, to reduce the chance of messing up.
    ///
    /// any is a special type used in list specifications, specifying that the
    /// elements can be of any type.
    enum types : int {
        integer = 0,
        real = 1,
        boolean = 2,
        null = 3,
        string = 4,
        bigint = 5,
        list = 6,
        map = 7,
        any = 8,
    };

private:
    // technically the type could be omitted; is it useful?
    // should we remove it or replace it with a pure virtual
    // function getType?
    types type_;

    /// @brief Position of the element in the configuration string.
    Position position_;

protected:

    /// @brief Constructor.
    ///
    /// @param t Element type.
    /// @param pos Structure holding position of the value of the data element.
    /// It comprises the line number and the position within this line. The values
    /// held in this structure are used for error logging purposes.
    Element(types t, const Position& pos = ZERO_POSITION())
        : type_(t), position_(pos) {
    }


public:
    // base class; make dtor virtual
    virtual ~Element() {}

    /// @return the type of this element
    types getType() const { return (type_); }

    /// @brief Returns position where the data element's value starts in a
    /// configuration string.
    ///
    /// @warning The returned reference is valid as long as the object which
    /// created it lives.
    const Position& getPosition() const { return (position_); }

    /// Returns a string representing the Element and all its
    /// child elements; note that this is different from stringValue(),
    /// which only returns the single value of a StringElement
    ///
    /// The resulting string will contain the Element in JSON format.
    ///
    /// @return std::string containing the string representation
    std::string str() const;

    /// Returns the wireformat for the Element and all its child
    /// elements.
    ///
    /// @return std::string containing the element in wire format
    std::string toWire() const;
    void toWire(std::ostream& out) const;

    /// @brief Add the position to a TypeError message
    /// should be used in place of isc_throw(TypeError, error)
#define throwTypeError(error)                   \
    {                                           \
        std::string msg_ = error;               \
        if ((position_.file_ != "") ||          \
            (position_.line_ != 0) ||           \
            (position_.pos_ != 0)) {            \
            msg_ += " in (" + position_.str() + ")";   \
        }                                       \
        isc_throw(TypeError, msg_);             \
    }

    /// @name pure virtuals, every derived class must implement these

    /// @return true if the other ElementPtr has the same value and the same
    /// type (or a different and compatible type), false otherwise.
    virtual bool equals(const Element& other) const = 0;<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class

    /// Converts the Element to JSON format and appends it to
    /// the given stringstream.
    virtual void toJSON(std::ostream& ss) const = 0;<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class<--- Virtual function in base class

    /// @name Type-specific getters
    ///
    /// @brief These functions only
    /// work on their corresponding Element type. For all other
    /// types, a TypeError is thrown.
    /// If you want an exception-safe getter method, use
    /// getValue() below
    //@{
    virtual int64_t intValue() const<--- Virtual function in base class
    { throwTypeError("intValue() called on non-integer Element"); }
    virtual isc::util::int128_t bigIntValue() const {
        throwTypeError("bigIntValue() called on non-big-integer Element");
    }
    virtual double doubleValue() const<--- Virtual function in base class
    { throwTypeError("doubleValue() called on non-double Element"); }
    virtual bool boolValue() const<--- Virtual function in base class
    { throwTypeError("boolValue() called on non-Bool Element"); }
    virtual std::string stringValue() const<--- Virtual function in base class
    { throwTypeError("stringValue() called on non-string Element"); }
    virtual const std::vector<ElementPtr>& listValue() const {<--- Virtual function in base class
        // replace with real exception or empty vector?
        throwTypeError("listValue() called on non-list Element");
    }
    virtual const std::map<std::string, ConstElementPtr>& mapValue() const {
        // replace with real exception or empty map?
        throwTypeError("mapValue() called on non-map Element");
    }
    //@}

    /// @name Exception-safe getters
    ///
    /// @brief The getValue() functions return false if the given reference
    /// is of another type than the element contains
    /// By default it always returns false; the derived classes
    /// override the function for their type, copying their
    /// data to the given reference and returning true
    ///
    //@{
    virtual bool getValue(int64_t& t) const;<--- Virtual function in base class
    virtual bool getValue(double& t) const;<--- Virtual function in base class
    virtual bool getValue(bool& t) const;<--- Virtual function in base class
    virtual bool getValue(std::string& t) const;<--- Virtual function in base class
    virtual bool getValue(std::vector<ElementPtr>& t) const;<--- Virtual function in base class
    virtual bool getValue(std::map<std::string, ConstElementPtr>& t) const;
    //@}

    ///
    /// @name Exception-safe setters.
    ///
    /// @brief Return false if the Element is not
    /// the right type. Set the value and return true if the Elements
    /// is of the correct type
    ///
    /// Notes: Read notes of IntElement definition about the use of
    ///        long long int, long int and int.
    //@{
    virtual bool setValue(const long long int v);<--- Virtual function in base class
    virtual bool setValue(const isc::util::int128_t& v);
    bool setValue(const long int i) { return (setValue(static_cast<long long int>(i))); }
    bool setValue(const int i) { return (setValue(static_cast<long long int>(i))); }
    virtual bool setValue(const double v);<--- Virtual function in base class
    virtual bool setValue(const bool t);<--- Virtual function in base class
    virtual bool setValue(const std::string& v);<--- Virtual function in base class
    virtual bool setValue(const std::vector<ElementPtr>& v);<--- Virtual function in base class
    virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
    //@}

    // Other functions for specific subtypes

    /// @name ListElement functions
    ///
    /// @brief If the Element on which these functions are called are not
    /// an instance of ListElement, a TypeError exception is thrown.
    //@{
    /// Returns the ElementPtr at the given index. If the index is out
    /// of bounds, this function throws an std::out_of_range exception.
    /// @param i The position of the ElementPtr to return
    virtual ConstElementPtr get(const int i) const;

    /// @brief returns element as non-const pointer
    ///
    /// @param i The position of the ElementPtr to retrieve
    /// @return specified element pointer
    virtual ElementPtr getNonConst(const int i) const;<--- Virtual function in base class

    /// Sets the ElementPtr at the given index. If the index is out
    /// of bounds, this function throws an std::out_of_range exception.
    /// @param i The position of the ElementPtr to set
    /// @param element The ElementPtr to set at the position
    virtual void set(const size_t i, ElementPtr element);<--- Virtual function in base class

    /// Adds an ElementPtr to the list
    /// @param element The ElementPtr to add
    virtual void add(ElementPtr element);<--- Virtual function in base class

    /// Removes the element at the given position. If the index is out
    /// of nothing happens.
    /// @param i The index of the element to remove.
    virtual void remove(const int i);<--- Virtual function in base class

    /// Returns the number of elements in the list.
    virtual size_t size() const;<--- Virtual function in base class

    /// Return true if there are no elements in the list.
    virtual bool empty() const;<--- Virtual function in base class
    //@}


    /// @name MapElement functions
    ///
    /// @brief If the Element on which these functions are called are not
    /// an instance of MapElement, a TypeError exception is thrown.
    //@{
    /// Returns the ElementPtr at the given key
    /// @param name The key of the Element to return
    /// @return The ElementPtr at the given key, or null if not present
    virtual ConstElementPtr get(const std::string& name) const;

    /// Sets the ElementPtr at the given key
    /// @param name The key of the Element to set
    /// @param element The ElementPtr to set at the given key.
    virtual void set(const std::string& name, ConstElementPtr element);

    /// Remove the ElementPtr at the given key
    /// @param name The key of the Element to remove
    virtual void remove(const std::string& name);

    /// Checks if there is data at the given key
    /// @param name The key of the Element checked for existence
    /// @return true if there is data at the key, false if not.
    virtual bool contains(const std::string& name) const;

    /// Recursively finds any data at the given identifier. The
    /// identifier is a /-separated list of names of nested maps, with
    /// the last name being the leaf that is returned.
    ///
    /// For instance, if you have a MapElement that contains another
    /// MapElement at the key "foo", and that second MapElement contains
    /// Another Element at key "bar", the identifier for that last
    /// element from the first is "foo/bar".
    ///
    /// @param identifier The identifier of the element to find
    /// @return The ElementPtr at the given identifier. Returns a
    /// null ElementPtr if it is not found, which can be checked with
    /// Element::is_null(ElementPtr e).
    virtual ConstElementPtr find(const std::string& identifier) const;

    /// See @c Element::find()
    /// @param identifier The identifier of the element to find
    /// @param t Reference to store the resulting ElementPtr, if found.
    /// @return true if the element was found, false if not.
    virtual bool find(const std::string& identifier, ConstElementPtr& t) const;
    //@}

    /// @name Factory functions

    // TODO: should we move all factory functions to a different class
    // so as not to burden the Element base with too many functions?
    // and/or perhaps even to a separate header?

    /// @name Direct factory functions
    /// @brief These functions simply wrap the given data directly
    /// in an Element object, and return a reference to it, in the form
    /// of an @c ElementPtr.
    /// These factory functions are exception-free (unless there is
    /// no memory available, in which case bad_alloc is raised by the
    /// underlying system).
    /// (Note that that is different from an NullElement, which
    /// represents an empty value, and is created with Element::create())
    ///
    /// Notes: Read notes of IntElement definition about the use of
    ///        long long int, long int and int.
    //@{
    static ElementPtr create(const Position& pos = ZERO_POSITION());
    static ElementPtr create(const long long int i,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const isc::util::int128_t& i,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const int i,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const long int i,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const uint32_t i,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const double d,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const bool b,
                             const Position& pos = ZERO_POSITION());
    static ElementPtr create(const std::string& s,
                             const Position& pos = ZERO_POSITION());
    // need both std:string and char *, since c++ will match
    // bool before std::string when you pass it a char *
    static ElementPtr create(const char *s,
                             const Position& pos = ZERO_POSITION());

    /// @brief Creates an empty ListElement type ElementPtr.
    ///
    /// @param pos A structure holding position of the data element value
    /// in the configuration string. It is used for error logging purposes.
    static ElementPtr createList(const Position& pos = ZERO_POSITION());

    /// @brief Creates an empty MapElement type ElementPtr.
    ///
    /// @param pos A structure holding position of the data element value
    /// in the configuration string. It is used for error logging purposes.
    static ElementPtr createMap(const Position& pos = ZERO_POSITION());
    //@}

    /// @name Compound factory functions

    /// @brief These functions will parse the given string (JSON)
    /// representation  of a compound element. If there is a parse
    /// error, an exception of the type isc::data::JSONError is thrown.

    //@{
    /// Creates an Element from the given JSON string
    /// @param in The string to parse the element from
    /// @param preproc specified whether preprocessing (e.g. comment removal)
    ///                should be performed
    /// @return An ElementPtr that contains the element(s) specified
    /// in the given string.
    static ElementPtr fromJSON(const std::string& in, bool preproc = false);

    /// Creates an Element from the given input stream containing JSON
    /// formatted data.
    ///
    /// @param in The string to parse the element from
    /// @param preproc specified whether preprocessing (e.g. comment removal)
    ///                should be performed
    /// @throw JSONError
    /// @return An ElementPtr that contains the element(s) specified
    /// in the given input stream.
    static ElementPtr fromJSON(std::istream& in, bool preproc = false);

    /// Creates an Element from the given input stream containing JSON
    /// formatted data.
    ///
    /// @param in The string to parse the element from
    /// @param file_name specified input file name (used in error reporting)
    /// @param preproc specified whether preprocessing (e.g. comment removal)
    ///                should be performed
    /// @throw JSONError
    /// @return An ElementPtr that contains the element(s) specified
    /// in the given input stream.
    /// @throw JSONError
    static ElementPtr fromJSON(std::istream& in, const std::string& file_name,
                               bool preproc = false);

    /// Creates an Element from the given input stream, where we keep
    /// track of the location in the stream for error reporting.
    ///
    /// @param in The string to parse the element from.
    /// @param file The input file name.
    /// @param line A reference to the int where the function keeps
    /// track of the current line.
    /// @param pos A reference to the int where the function keeps
    /// track of the current position within the current line.
    /// @throw JSONError
    /// @return An ElementPtr that contains the element(s) specified
    /// in the given input stream.
    // make this one private?
    /// @throw JSONError
    static ElementPtr fromJSON(std::istream& in, const std::string& file,
                               int& line, int &pos);

    /// Reads contents of specified file and interprets it as JSON.
    ///
    /// @param file_name name of the file to read
    /// @param preproc specified whether preprocessing (e.g. comment removal)
    ///                should be performed
    /// @return An ElementPtr that contains the element(s) specified
    /// if the given file.
    static ElementPtr fromJSONFile(const std::string& file_name,
                                   bool preproc = false);
    //@}

    /// @name Type name conversion functions

    /// Returns the name of the given type as a string
    ///
    /// @param type The type to return the name of
    /// @return The name of the type, or "unknown" if the type
    ///         is not known.
    static std::string typeToName(Element::types type);

    /// Converts the string to the corresponding type
    /// Throws a TypeError if the name is unknown.
    ///
    /// @param type_name The name to get the type of
    /// @return the corresponding type value
    static Element::types nameToType(const std::string& type_name);

    /// @brief input text preprocessor
    ///
    /// This method performs preprocessing of the input stream (which is
    /// expected to contain a text version of to be parsed JSON). For now the
    /// sole supported operation is bash-style (line starting with #) comment
    /// removal, but it will be extended later to cover more cases (C, C++ style
    /// comments, file inclusions, maybe macro replacements?).
    ///
    /// This method processes the whole input stream. It reads all contents of
    /// the input stream, filters the content and returns the result in a
    /// different stream.
    ///
    /// @param in input stream to be preprocessed
    /// @param out output stream (filtered content will be written here)
    static void preprocess(std::istream& in, std::stringstream& out);

    /// @name Wire format factory functions

    /// These function pparse the wireformat at the given stringstream
    /// (of the given length). If there is a parse error an exception
    /// of the type isc::cc::DecodeError is raised.

    //@{
    /// Creates an Element from the wire format in the given
    /// stringstream of the given length.
    /// Since the wire format is JSON, this is the same as
    /// fromJSON, and could be removed.
    ///
    /// @param in The input stringstream.
    /// @param length The length of the wireformat data in the stream
    /// @return ElementPtr with the data that is parsed.
    static ElementPtr fromWire(std::stringstream& in, int length);

    /// Creates an Element from the wire format in the given string
    /// Since the wire format is JSON, this is the same as
    /// fromJSON, and could be removed.
    ///
    /// @param s The input string
    /// @return ElementPtr with the data that is parsed.
    static ElementPtr fromWire(const std::string& s);
    //@}

    /// @brief Remove all empty maps and lists from this Element and its
    /// descendants.
    void removeEmptyContainersRecursively() {
        if (type_ == list || type_ == map) {
            size_t s(size());
            for (size_t i = 0; i < s; ++i) {
                // Get child.
                ElementPtr child;
                if (type_ == list) {
                    child = getNonConst(i);
                } else if (type_ == map) {
                    std::string const key(get(i)->stringValue());<--- Shadow variable
                    // The ElementPtr - ConstElementPtr disparity between
                    // ListElement and MapElement is forcing a const cast here.
                    // It's undefined behavior to modify it after const casting.
                    // The options are limited. I've tried templating, moving
                    // this function from a member function to free-standing and
                    // taking the Element template as argument. I've tried
                    // making it a virtual function with overridden
                    // implementations in ListElement and MapElement. Nothing
                    // works.
                    child = boost::const_pointer_cast<Element>(get(key));
                }

                // Makes no sense to continue for non-container children.
                if (child->getType() != list && child->getType() != map) {
                    continue;
                }

                // Recurse if not empty.
                if (!child->empty()){
                    child->removeEmptyContainersRecursively();
                }

                // When returning from recursion, remove if empty.
                if (child->empty()) {
                    remove(i);
                    --i;
                    --s;
                }
            }
        }
    }
};

/// Notes: IntElement type is changed to int64_t.
///        Due to C++ problems on overloading and automatic type conversion,
///          (C++ tries to convert integer type values and reference/pointer
///           if value types do not match exactly)
///        We decided the storage as int64_t,
///           three (long long, long, int) override function definitions
///           and cast int/long/long long to int64_t via long long.
///        Therefore, call by value methods (create, setValue) have three
///        (int,long,long long) definitions. Others use int64_t.
///
class IntElement : public Element {
    int64_t i;
public:
    IntElement(int64_t v, const Position& pos = ZERO_POSITION())
        : Element(integer, pos), i(v) { }
    int64_t intValue() const { return (i); }<--- Function in derived class
    using Element::getValue;
    bool getValue(int64_t& t) const { t = i; return (true); }<--- Function in derived class
    using Element::setValue;
    bool setValue(long long int v) { i = v; return (true); }<--- Function in derived class
    void toJSON(std::ostream& ss) const;<--- Function in derived class
    bool equals(const Element& other) const;<--- Function in derived class
};

/// @brief Wrapper over int128_t
class BigIntElement : public Element {
    using int128_t = isc::util::int128_t;
    using Element::getValue;
    using Element::setValue;

public:
    /// @brief Constructor
    BigIntElement(const int128_t& v, const Position& pos = ZERO_POSITION())
        : Element(bigint, pos), i_(v) {
    }

    /// @brief Retrieve the underlying big integer value.
    ///
    /// @return the underlying value
    int128_t bigIntValue() const override {
        return (i_);
    }

    /// @brief Sets the underlying big integer value.
    ///
    /// @return true for no reason
    bool setValue(const int128_t& v) override {
        i_ = v;
        return (true);
    }

    /// @brief Converts the Element to JSON format and appends it to the given
    /// stringstream.
    void toJSON(std::ostream& ss) const override;

    /// @brief Checks whether the other Element is equal.
    ///
    /// @return true if the other ElementPtr has the same value and the same
    /// type (or a different and compatible type), false otherwise.
    bool equals(const Element& other) const override;

private:
    /// @brief the underlying stored value
    int128_t i_;
};

class DoubleElement : public Element {
    double d;

public:
    DoubleElement(double v, const Position& pos = ZERO_POSITION())
        : Element(real, pos), d(v) {}
    double doubleValue() const { return (d); }<--- Function in derived class
    using Element::getValue;
    bool getValue(double& t) const { t = d; return (true); }<--- Function in derived class
    using Element::setValue;
    bool setValue(const double v) { d = v; return (true); }<--- Function in derived class
    void toJSON(std::ostream& ss) const;<--- Function in derived class
    bool equals(const Element& other) const;<--- Function in derived class
};

class BoolElement : public Element {
    bool b;

public:
    BoolElement(const bool v, const Position& pos = ZERO_POSITION())
        : Element(boolean, pos), b(v) {}
    bool boolValue() const { return (b); }<--- Function in derived class
    using Element::getValue;
    bool getValue(bool& t) const { t = b; return (true); }<--- Function in derived class
    using Element::setValue;
    bool setValue(const bool v) { b = v; return (true); }<--- Function in derived class
    void toJSON(std::ostream& ss) const;<--- Function in derived class
    bool equals(const Element& other) const;<--- Function in derived class
};

class NullElement : public Element {
public:
    NullElement(const Position& pos = ZERO_POSITION())
        : Element(null, pos) {}
    void toJSON(std::ostream& ss) const;<--- Function in derived class
    bool equals(const Element& other) const;<--- Function in derived class
};

class StringElement : public Element {
    std::string s;

public:
    StringElement(std::string v, const Position& pos = ZERO_POSITION())<--- Function parameter 'v' should be passed by const reference.
        : Element(string, pos), s(v) {}
    std::string stringValue() const { return (s); }<--- Function in derived class
    using Element::getValue;
    bool getValue(std::string& t) const { t = s; return (true); }<--- Function in derived class
    using Element::setValue;
    bool setValue(const std::string& v) { s = v; return (true); }<--- Function in derived class
    void toJSON(std::ostream& ss) const;<--- Function in derived class
    bool equals(const Element& other) const;<--- Function in derived class
};

class ListElement : public Element {
    std::vector<ElementPtr> l;

public:
    ListElement(const Position& pos = ZERO_POSITION())
        : Element(list, pos) {}
    const std::vector<ElementPtr>& listValue() const { return (l); }<--- Function in derived class
    using Element::getValue;
    bool getValue(std::vector<ElementPtr>& t) const {<--- Function in derived class
        t = l;
        return (true);
    }
    using Element::setValue;
    bool setValue(const std::vector<ElementPtr>& v) {<--- Function in derived class
        l = v;
        return (true);
    }
    using Element::get;
    ConstElementPtr get(int i) const { return (l.at(i)); }
    ElementPtr getNonConst(int i) const  { return (l.at(i)); }<--- Function in derived class
    using Element::set;
    void set(size_t i, ElementPtr e) {<--- Function in derived class
        l.at(i) = e;
    }
    void add(ElementPtr e) { l.push_back(e); }<--- Function in derived class
    using Element::remove;
    void remove(int i) { l.erase(l.begin() + i); }<--- Function in derived class
    void toJSON(std::ostream& ss) const;<--- Function in derived class
    size_t size() const { return (l.size()); }<--- Function in derived class
    bool empty() const { return (l.empty()); }<--- Function in derived class
    bool equals(const Element& other) const;<--- Function in derived class

    /// @brief Sorts the elements inside the list.
    ///
    /// The list must contain elements of the same type.
    /// Call with the key by which you want to sort when the list contains maps.
    /// Nested lists are not supported.
    /// Call without a parameter when sorting any other type.
    ///
    /// @param index the key by which you want to sort when the list contains
    /// maps
    void sort(std::string const& index = std::string());
};

class MapElement : public Element {
    std::map<std::string, ConstElementPtr> m;

public:
    MapElement(const Position& pos = ZERO_POSITION()) : Element(map, pos) {}
    // @todo should we have direct iterators instead of exposing the std::map
    // here?
    const std::map<std::string, ConstElementPtr>& mapValue() const override {
        return (m);
    }
    using Element::getValue;
    bool getValue(std::map<std::string, ConstElementPtr>& t) const override {
        t = m;
        return (true);
    }
    using Element::setValue;
    bool setValue(const std::map<std::string, ConstElementPtr>& v) override {
        m = v;
        return (true);
    }
    using Element::get;
    ConstElementPtr get(const std::string& s) const override {
        auto found = m.find(s);
        return (found != m.end() ? found->second : ConstElementPtr());
    }

    /// @brief Get the i-th element in the map.
    ///
    /// Useful when required to iterate with an index.
    ///
    /// @param i the position of the element you want to return
    /// @return the element at position i
    ConstElementPtr get(int const i) const override {
        auto it(m.begin());
        std::advance(it, i);
        return create(it->first);
    }

    using Element::set;
    void set(const std::string& key, ConstElementPtr value) override;
    using Element::remove;
    void remove(const std::string& s) override { m.erase(s); }

    /// @brief Remove the i-th element from the map.
    ///
    /// @param i the position of the element you want to remove
    void remove(int const i) override {
        auto it(m.begin());
        std::advance(it, i);
        m.erase(it);
    }

    bool contains(const std::string& s) const override {
        return (m.find(s) != m.end());
    }
    void toJSON(std::ostream& ss) const override;

    // we should name the two finds better...
    // find the element at id; raises TypeError if one of the
    // elements at path except the one we're looking for is not a
    // mapelement.
    // returns an empty element if the item could not be found
    ConstElementPtr find(const std::string& id) const override;

    // find the Element at 'id', and store the element pointer in t
    // returns true if found, or false if not found (either because
    // it doesn't exist or one of the elements in the path is not
    // a MapElement)
    bool find(const std::string& id, ConstElementPtr& t) const override;

    /// @brief Returns number of stored elements
    ///
    /// @return number of elements.
    size_t size() const override {
        return (m.size());
    }

    bool equals(const Element& other) const override;

    bool empty() const override { return (m.empty()); }
};

/// Checks whether the given ElementPtr is a NULL pointer
/// @param p The ElementPtr to check
/// @return true if it is NULL, false if not.
bool isNull(ConstElementPtr p);

///
/// @brief Remove all values from the first ElementPtr that are
/// equal in the second. Both ElementPtrs MUST be MapElements
/// The use for this function is to end up with a MapElement that
/// only contains new and changed values (for ModuleCCSession and
/// configuration update handlers)
/// Raises a TypeError if a or b are not MapElements
void removeIdentical(ElementPtr a, ConstElementPtr b);

/// @brief Create a new ElementPtr from the first ElementPtr, removing all
/// values that are equal in the second. Both ElementPtrs MUST be MapElements.
/// The returned ElementPtr will be a MapElement that only contains new and
/// changed values (for ModuleCCSession and configuration update handlers).
/// Raises a TypeError if a or b are not MapElements
ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b);

/// @brief Merges the data from other into element. (on the first level). Both
/// elements must be MapElements. Every string, value pair in other is copied
/// into element (the ElementPtr of value is copied, this is not a new object)
/// Unless the value is a NullElement, in which case the key is removed from
/// element, rather than setting the value to the given NullElement.
/// This way, we can remove values from for instance maps with configuration
/// data (which would then result in reverting back to the default).
/// Raises a TypeError if either ElementPtr is not a MapElement
void merge(ElementPtr element, ConstElementPtr other);

/// @brief Function used to check if two MapElements refer to the same
/// configuration data. It can check if the two MapElements have the same or
/// have equivalent value for some members.
/// e.g.
/// (
///  left->get("prefix")->stringValue() == right->get("prefix")->stringValue() &&
///  left->get("prefix-len")->intValue() == right->get("prefix-len")->intValue() &&
///  left->get("delegated-len")->intValue() == right->get("delegated-len")->intValue()
/// )
typedef std::function<bool (ElementPtr&, ElementPtr&)> MatchTestFunc;

/// @brief Function used to check if the data provided for the element contains
/// only information used for identification, or it contains extra useful data.
typedef std::function<bool (ElementPtr&)> NoDataTestFunc;

/// @brief Function used to check if the key is used for identification
typedef std::function<bool (const std::string&)> IsKeyTestFunc;

/// @brief Structure holding the test functions used to traverse the element
/// hierarchy.
struct HierarchyTraversalTest {
    MatchTestFunc match_;
    NoDataTestFunc no_data_;
    IsKeyTestFunc is_key_;
};

/// @brief Mapping between a container name and functions used to match elements
/// inside the container.
typedef std::map<std::string, HierarchyTraversalTest> FunctionMap;

/// @brief Hierarchy descriptor of the containers in a specific Element
/// hierarchy tree. The position inside the vector indicates the level at which
/// the respective containers are located.
///
/// e.g.
/// {
///   { { "pools", { ... , ... } }, { "pd-pools", { ... , ... } }, { "option-data", { ... , ... } } },
///   { { "option-data", { ... , ... } } }
/// }
/// At first subnet level the 'pools', 'pd-pools' and 'option-data' containers
/// can be found.
/// At second subnet level the 'option-data' container can be found
/// (obviously only inside 'pools' and 'pd-pools' containers).
typedef std::vector<FunctionMap> HierarchyDescriptor;

/// @brief Merges the diff data by adding the missing elements from 'other'
/// to 'element' (recursively). Both elements must be the same Element type.
/// Raises a TypeError if elements are not the same Element type.
/// @note
/// for non map and list elements the values are updated with the new values
/// for maps:
///     - non map and list elements are added/updated with the new values
///     - list and map elements are processed recursively
/// for lists:
///     - regardless of the element type, all elements from 'other' are added to
///       'element'
///
/// @param element The element to which new data is added.
/// @param other The element containing the data which needs to be added.
/// @param hierarchy The hierarchy describing the elements relations and
/// identification keys.
/// @param key The container holding the current element.
/// @param idx The level inside the hierarchy the current element is located.
void mergeDiffAdd(ElementPtr& element, ElementPtr& other,
                  HierarchyDescriptor& hierarchy, std::string key,
                  size_t idx = 0);

/// @brief Merges the diff data by removing the data present in 'other' from
/// 'element' (recursively). Both elements must be the same Element type.
/// Raises a TypeError if elements are not the same Element type.
/// for non map and list elements the values are set to NullElement
/// for maps:
///     - non map and list elements are removed from the map
///     - list and map elements are processed recursively
/// for lists:
///     - regardless of the element type, all elements from 'other' matching
///       elements in 'element' are removed
///
/// @param element The element from which new data is removed.
/// @param other The element containing the data which needs to be removed.
/// @param hierarchy The hierarchy describing the elements relations and
/// identification keys.
/// @param key The container holding the current element.
/// @param idx The level inside the hierarchy the current element is located.
void mergeDiffDel(ElementPtr& element, ElementPtr& other,
                  HierarchyDescriptor& hierarchy, std::string key,
                  size_t idx = 0);

/// @brief Extends data by adding the specified 'extension' elements from
/// 'other' inside the 'container' element (recursively). Both elements must be
/// the same Element type.
/// Raises a TypeError if elements are not the same Element type.
///
/// @param container The container holding the data that must be extended.
/// @param extension The name of the element that contains the data that must be
/// added (if not already present) in order to extend the initial data.
/// @param element The element from which new data is added.
/// @param other The element containing the data which needs to be added.
/// @param hierarchy The hierarchy describing the elements relations and
/// identification keys.
/// @param key The container holding the current element.
/// @param idx The level inside the hierarchy the current element is located.
/// @param alter The flag which indicates if the current element should be
/// updated.
void extend(const std::string& container, const std::string& extension,
            ElementPtr& element, ElementPtr& other,
            HierarchyDescriptor& hierarchy, std::string key, size_t idx = 0,
            bool alter = false);

/// @brief Copy the data up to a nesting level.
///
/// The copy is a deep copy so nothing is shared if it is not
/// under the given nesting level.
///
/// @param from the pointer to the element to copy
/// @param level nesting level (default is 100, 0 means shallow copy,
/// negative means outbound and perhaps looping forever).
/// @return a pointer to a fresh copy
/// @throw raises a BadValue is a null pointer occurs.
ElementPtr copy(ConstElementPtr from, int level = 100);

/// @brief Compares the data with other using unordered lists
///
/// This comparison function handles lists (JSON arrays) as
/// unordered multi sets (multi means an item can occurs more
/// than once as soon as it occurs the same number of times).
bool isEquivalent(ConstElementPtr a, ConstElementPtr b);

/// @brief Pretty prints the data into stream.
///
/// This operator converts the @c ConstElementPtr into a string and
/// inserts it into the output stream @c out with an initial
/// indentation @c indent and add at each level @c step spaces.
/// For maps if there is a comment property it is printed first.
///
/// @param element A @c ConstElementPtr to pretty print
/// @param out A @c std::ostream on which the print operation is performed
/// @param indent An initial number of spaces to add each new line
/// @param step A number of spaces to add to indentation at a new level
void prettyPrint(ConstElementPtr element, std::ostream& out,
                 unsigned indent = 0, unsigned step = 2);

/// @brief Pretty prints the data into string
///
/// This operator converts the @c ConstElementPtr into a string with
/// an initial indentation @c indent and add at each level @c step spaces.
/// For maps if there is a comment property it is printed first.
///
/// @param element A @c ConstElementPtr to pretty print
/// @param indent An initial number of spaces to add each new line
/// @param step A number of spaces to add to indentation at a new level
/// @return a string where element was pretty printed
std::string prettyPrint(ConstElementPtr element,
                        unsigned indent = 0, unsigned step = 2);

/// @brief Insert Element::Position as a string into stream.
///
/// This operator converts the @c Element::Position into a string and
/// inserts it into the output stream @c out.
///
/// @param out A @c std::ostream object on which the insertion operation is
/// performed.
/// @param pos The @c Element::Position structure to insert.
/// @return A reference to the same @c std::ostream object referenced by
/// parameter @c out after the insertion operation.
std::ostream& operator<<(std::ostream& out, const Element::Position& pos);

/// @brief Insert the Element as a string into stream.
///
/// This method converts the @c ElementPtr into a string with
/// @c Element::str() and inserts it into the
/// output stream @c out.
///
/// This function overloads the global operator<< to behave as described in
/// ostream::operator<< but applied to @c ElementPtr objects.
///
/// @param out A @c std::ostream object on which the insertion operation is
/// performed.
/// @param e The @c ElementPtr object to insert.
/// @return A reference to the same @c std::ostream object referenced by
/// parameter @c out after the insertion operation.
std::ostream& operator<<(std::ostream& out, const Element& e);

bool operator==(const Element& a, const Element& b);
bool operator!=(const Element& a, const Element& b);
bool operator<(const Element& a, const Element& b);

}  // namespace data
}  // namespace isc

#endif // ISC_DATA_H