1 // Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef ISC_DATA_H 8 #define ISC_DATA_H 1 9 10 #include <iostream> 11 #include <map> 12 #include <stdexcept> 13 #include <string> 14 #include <vector> 15 16 #include <boost/shared_ptr.hpp> 17 18 #include <stdint.h> 19 20 #include <exceptions/exceptions.h> 21 22 namespace isc { namespace data { 23 24 class Element; 25 // todo: describe the rationale behind ElementPtr? 26 typedef boost::shared_ptr<Element> ElementPtr; 27 typedef boost::shared_ptr<const Element> ConstElementPtr; 28 29 /// 30 /// @brief A standard Data module exception that is thrown if a function 31 /// is called for an Element that has a wrong type (e.g. int_value on a 32 /// ListElement) 33 /// 34 class TypeError : public isc::Exception { 35 public: TypeError(const char * file,size_t line,const char * what)36 TypeError(const char* file, size_t line, const char* what) : 37 isc::Exception(file, line, what) {} 38 }; 39 40 /// 41 /// @brief A standard Data module exception that is thrown if a parse 42 /// error is encountered when constructing an Element from a string 43 /// 44 // i'd like to use Exception here but we need one that is derived from 45 // runtime_error (as this one is directly based on external data, and 46 // i want to add some values to any static data string that is provided) 47 class JSONError : public isc::Exception { 48 public: JSONError(const char * file,size_t line,const char * what)49 JSONError(const char* file, size_t line, const char* what) : 50 isc::Exception(file, line, what) {} 51 }; 52 53 /// 54 /// @brief The @c Element class represents a piece of data, used by 55 /// the command channel and configuration parts. 56 /// 57 /// An @c Element can contain simple types (int, real, string, bool and 58 /// None), and composite types (list and string->element maps) 59 /// 60 /// Elements should in calling functions usually be referenced through 61 /// an @c ElementPtr, which can be created using the factory functions 62 /// @c Element::create() and @c Element::fromJSON() 63 /// 64 /// Notes to developers: Element is a base class, implemented by a 65 /// specific subclass for each type (IntElement, BoolElement, etc). 66 /// Element does define all functions for all types, and defaults to 67 /// raising a @c TypeError for functions that are not supported for 68 /// the type in question. 69 /// 70 class Element { 71 72 public: 73 /// @brief Represents the position of the data element within a 74 /// configuration string. 75 /// 76 /// Position comprises a file name, line number and an offset within this 77 /// line where the element value starts. For example, if the JSON string is 78 /// 79 /// @code 80 /// { "foo": "some string", 81 /// "bar": 123 } 82 /// \endcode 83 /// 84 /// the position of the element "bar" is: line_ = 2; pos_ = 9, because 85 /// beginning of the value "123" is at offset 9 from the beginning of 86 /// the second line, including whitespaces. 87 /// 88 /// Note that the @c Position structure is used as an argument to @c Element 89 /// constructors and factory functions to avoid ambiguity and so that the 90 /// uint32_t arguments holding line number and position within the line are 91 /// not confused with the @c Element values passed to these functions. 92 struct Position { 93 std::string file_; ///< File name. 94 uint32_t line_; ///< Line number. 95 uint32_t pos_; ///< Position within the line. 96 97 /// @brief Default constructor. PositionPosition98 Position() : file_(""), line_(0), pos_(0) { 99 } 100 101 /// @brief Constructor. 102 /// 103 /// @param file File name. 104 /// @param line Line number. 105 /// @param pos Position within the line. PositionPosition106 Position(const std::string& file, const uint32_t line, 107 const uint32_t pos) 108 : file_(file), line_(line), pos_(pos) { 109 } 110 111 /// @brief Returns the position in the textual format. 112 /// 113 /// The returned position has the following format: file:line:pos. 114 std::string str() const; 115 }; 116 117 /// @brief Returns @c Position object with line_ and pos_ set to 0, and 118 /// with an empty file name. 119 /// 120 /// The object containing two zeros is a default for most of the 121 /// methods creating @c Element objects. The returned value is static 122 /// so as it is not created everytime the function with the default 123 /// position argument is called. ZERO_POSITION()124 static const Position& ZERO_POSITION() { 125 static Position position("", 0, 0); 126 return (position); 127 } 128 129 private: 130 // technically the type could be omitted; is it useful? 131 // should we remove it or replace it with a pure virtual 132 // function getType? 133 int type_; 134 135 /// @brief Position of the element in the configuration string. 136 Position position_; 137 138 protected: 139 140 /// @brief Constructor. 141 /// 142 /// @param t Element type. 143 /// @param pos Structure holding position of the value of the data element. 144 /// It comprises the line number and the position within this line. The values 145 /// held in this structure are used for error logging purposes. 146 Element(int t, const Position& pos = ZERO_POSITION()) type_(t)147 : type_(t), position_(pos) { 148 } 149 150 151 public: 152 153 // any is a special type used in list specifications, specifying 154 // that the elements can be of any type 155 enum types { integer, real, boolean, null, string, list, map, any }; 156 // base class; make dtor virtual ~Element()157 virtual ~Element() {}; 158 159 /// @return the type of this element getType()160 int getType() const { return (type_); } 161 162 /// @brief Returns position where the data element's value starts in a 163 /// configuration string. 164 /// 165 /// @warning The returned reference is valid as long as the object which 166 /// created it lives. getPosition()167 const Position& getPosition() const { return (position_); } 168 169 /// Returns a string representing the Element and all its 170 /// child elements; note that this is different from stringValue(), 171 /// which only returns the single value of a StringElement 172 /// 173 /// The resulting string will contain the Element in JSON format. 174 /// 175 /// @return std::string containing the string representation 176 std::string str() const; 177 178 /// Returns the wireformat for the Element and all its child 179 /// elements. 180 /// 181 /// @return std::string containing the element in wire format 182 std::string toWire() const; 183 void toWire(std::ostream& out) const; 184 185 /// @brief Add the position to a TypeError message 186 /// should be used in place of isc_throw(TypeError, error) 187 #define throwTypeError(error) \ 188 { \ 189 std::string msg_ = error; \ 190 if ((position_.file_ != "") || \ 191 (position_.line_ != 0) || \ 192 (position_.pos_ != 0)) { \ 193 msg_ += " in (" + position_.str() + ")"; \ 194 } \ 195 isc_throw(TypeError, msg_); \ 196 } 197 198 /// @name pure virtuals, every derived class must implement these 199 200 /// @return true if the other ElementPtr has the same type and value 201 virtual bool equals(const Element& other) const = 0; 202 203 /// Converts the Element to JSON format and appends it to 204 /// the given stringstream. 205 virtual void toJSON(std::ostream& ss) const = 0; 206 207 /// @name Type-specific getters 208 /// 209 /// @brief These functions only 210 /// work on their corresponding Element type. For all other 211 /// types, a TypeError is thrown. 212 /// If you want an exception-safe getter method, use 213 /// getValue() below 214 //@{ intValue()215 virtual int64_t intValue() const 216 { throwTypeError("intValue() called on non-integer Element"); }; doubleValue()217 virtual double doubleValue() const 218 { throwTypeError("doubleValue() called on non-double Element"); }; boolValue()219 virtual bool boolValue() const 220 { throwTypeError("boolValue() called on non-Bool Element"); }; stringValue()221 virtual std::string stringValue() const 222 { throwTypeError("stringValue() called on non-string Element"); }; listValue()223 virtual const std::vector<ElementPtr>& listValue() const { 224 // replace with real exception or empty vector? 225 throwTypeError("listValue() called on non-list Element"); 226 }; mapValue()227 virtual const std::map<std::string, ConstElementPtr>& mapValue() const { 228 // replace with real exception or empty map? 229 throwTypeError("mapValue() called on non-map Element"); 230 }; 231 //@} 232 233 /// @name Exception-safe getters 234 /// 235 /// @brief The getValue() functions return false if the given reference 236 /// is of another type than the element contains 237 /// By default it always returns false; the derived classes 238 /// override the function for their type, copying their 239 /// data to the given reference and returning true 240 /// 241 //@{ 242 virtual bool getValue(int64_t& t) const; 243 virtual bool getValue(double& t) const; 244 virtual bool getValue(bool& t) const; 245 virtual bool getValue(std::string& t) const; 246 virtual bool getValue(std::vector<ElementPtr>& t) const; 247 virtual bool getValue(std::map<std::string, ConstElementPtr>& t) const; 248 //@} 249 250 /// 251 /// @name Exception-safe setters. 252 /// 253 /// @brief Return false if the Element is not 254 /// the right type. Set the value and return true if the Elements 255 /// is of the correct type 256 /// 257 /// Notes: Read notes of IntElement definition about the use of 258 /// long long int, long int and int. 259 //@{ 260 virtual bool setValue(const long long int v); setValue(const long int i)261 bool setValue(const long int i) { return (setValue(static_cast<long long int>(i))); }; setValue(const int i)262 bool setValue(const int i) { return (setValue(static_cast<long long int>(i))); }; 263 virtual bool setValue(const double v); 264 virtual bool setValue(const bool t); 265 virtual bool setValue(const std::string& v); 266 virtual bool setValue(const std::vector<ElementPtr>& v); 267 virtual bool setValue(const std::map<std::string, ConstElementPtr>& v); 268 //@} 269 270 // Other functions for specific subtypes 271 272 /// @name ListElement functions 273 /// 274 /// @brief If the Element on which these functions are called are not 275 /// an instance of ListElement, a TypeError exception is thrown. 276 //@{ 277 /// Returns the ElementPtr at the given index. If the index is out 278 /// of bounds, this function throws an std::out_of_range exception. 279 /// @param i The position of the ElementPtr to return 280 virtual ConstElementPtr get(const int i) const; 281 282 /// @brief returns element as non-const pointer 283 /// 284 /// @param i The position of the ElementPtr to retrieve 285 /// @return specified element pointer 286 virtual ElementPtr getNonConst(const int i) const; 287 288 /// Sets the ElementPtr at the given index. If the index is out 289 /// of bounds, this function throws an std::out_of_range exception. 290 /// @param i The position of the ElementPtr to set 291 /// @param element The ElementPtr to set at the position 292 virtual void set(const size_t i, ElementPtr element); 293 294 /// Adds an ElementPtr to the list 295 /// @param element The ElementPtr to add 296 virtual void add(ElementPtr element); 297 298 /// Removes the element at the given position. If the index is out 299 /// of nothing happens. 300 /// @param i The index of the element to remove. 301 virtual void remove(const int i); 302 303 /// Returns the number of elements in the list. 304 virtual size_t size() const; 305 306 /// Return true if there are no elements in the list. 307 virtual bool empty() const; 308 //@} 309 310 311 /// @name MapElement functions 312 /// 313 /// @brief If the Element on which these functions are called are not 314 /// an instance of MapElement, a TypeError exception is thrown. 315 //@{ 316 /// Returns the ElementPtr at the given key 317 /// @param name The key of the Element to return 318 /// @return The ElementPtr at the given key, or null if not present 319 virtual ConstElementPtr get(const std::string& name) const; 320 321 /// Sets the ElementPtr at the given key 322 /// @param name The key of the Element to set 323 /// @param element The ElementPtr to set at the given key. 324 virtual void set(const std::string& name, ConstElementPtr element); 325 326 /// Remove the ElementPtr at the given key 327 /// @param name The key of the Element to remove 328 virtual void remove(const std::string& name); 329 330 /// Checks if there is data at the given key 331 /// @param name The key of the Element checked for existence 332 /// @return true if there is data at the key, false if not. 333 virtual bool contains(const std::string& name) const; 334 335 /// Recursively finds any data at the given identifier. The 336 /// identifier is a /-separated list of names of nested maps, with 337 /// the last name being the leaf that is returned. 338 /// 339 /// For instance, if you have a MapElement that contains another 340 /// MapElement at the key "foo", and that second MapElement contains 341 /// Another Element at key "bar", the identifier for that last 342 /// element from the first is "foo/bar". 343 /// 344 /// @param identifier The identifier of the element to find 345 /// @return The ElementPtr at the given identifier. Returns a 346 /// null ElementPtr if it is not found, which can be checked with 347 /// Element::is_null(ElementPtr e). 348 virtual ConstElementPtr find(const std::string& identifier) const; 349 350 /// See @c Element::find() 351 /// @param identifier The identifier of the element to find 352 /// @param t Reference to store the resulting ElementPtr, if found. 353 /// @return true if the element was found, false if not. 354 virtual bool find(const std::string& identifier, ConstElementPtr& t) const; 355 //@} 356 357 /// @name Factory functions 358 359 // TODO: should we move all factory functions to a different class 360 // so as not to burden the Element base with too many functions? 361 // and/or perhaps even to a separate header? 362 363 /// @name Direct factory functions 364 /// @brief These functions simply wrap the given data directly 365 /// in an Element object, and return a reference to it, in the form 366 /// of an @c ElementPtr. 367 /// These factory functions are exception-free (unless there is 368 /// no memory available, in which case bad_alloc is raised by the 369 /// underlying system). 370 /// (Note that that is different from an NullElement, which 371 /// represents an empty value, and is created with Element::create()) 372 /// 373 /// Notes: Read notes of IntElement definition about the use of 374 /// long long int, long int and int. 375 //@{ 376 static ElementPtr create(const Position& pos = ZERO_POSITION()); 377 static ElementPtr create(const long long int i, 378 const Position& pos = ZERO_POSITION()); 379 static ElementPtr create(const int i, 380 const Position& pos = ZERO_POSITION()); 381 static ElementPtr create(const long int i, 382 const Position& pos = ZERO_POSITION()); 383 static ElementPtr create(const double d, 384 const Position& pos = ZERO_POSITION()); 385 static ElementPtr create(const bool b, 386 const Position& pos = ZERO_POSITION()); 387 static ElementPtr create(const std::string& s, 388 const Position& pos = ZERO_POSITION()); 389 // need both std:string and char *, since c++ will match 390 // bool before std::string when you pass it a char * 391 static ElementPtr create(const char *s, 392 const Position& pos = ZERO_POSITION()); 393 394 /// @brief Creates an empty ListElement type ElementPtr. 395 /// 396 /// @param pos A structure holding position of the data element value 397 /// in the configuration string. It is used for error logging purposes. 398 static ElementPtr createList(const Position& pos = ZERO_POSITION()); 399 400 /// @brief Creates an empty MapElement type ElementPtr. 401 /// 402 /// @param pos A structure holding position of the data element value 403 /// in the configuration string. It is used for error logging purposes. 404 static ElementPtr createMap(const Position& pos = ZERO_POSITION()); 405 //@} 406 407 /// @name Compound factory functions 408 409 /// @brief These functions will parse the given string (JSON) 410 /// representation of a compound element. If there is a parse 411 /// error, an exception of the type isc::data::JSONError is thrown. 412 413 //@{ 414 /// Creates an Element from the given JSON string 415 /// @param in The string to parse the element from 416 /// @param preproc specified whether preprocessing (e.g. comment removal) 417 /// should be performed 418 /// @return An ElementPtr that contains the element(s) specified 419 /// in the given string. 420 static ElementPtr fromJSON(const std::string& in, bool preproc = false); 421 422 /// Creates an Element from the given input stream containing JSON 423 /// formatted data. 424 /// 425 /// @param in The string to parse the element from 426 /// @param preproc specified whether preprocessing (e.g. comment removal) 427 /// should be performed 428 /// @throw JSONError 429 /// @return An ElementPtr that contains the element(s) specified 430 /// in the given input stream. 431 static ElementPtr fromJSON(std::istream& in, bool preproc = false); 432 433 /// Creates an Element from the given input stream containing JSON 434 /// formatted data. 435 /// 436 /// @param in The string to parse the element from 437 /// @param file_name specified input file name (used in error reporting) 438 /// @param preproc specified whether preprocessing (e.g. comment removal) 439 /// should be performed 440 /// @throw JSONError 441 /// @return An ElementPtr that contains the element(s) specified 442 /// in the given input stream. 443 /// @throw JSONError 444 static ElementPtr fromJSON(std::istream& in, const std::string& file_name, 445 bool preproc = false); 446 447 /// Creates an Element from the given input stream, where we keep 448 /// track of the location in the stream for error reporting. 449 /// 450 /// @param in The string to parse the element from. 451 /// @param file The input file name. 452 /// @param line A reference to the int where the function keeps 453 /// track of the current line. 454 /// @param pos A reference to the int where the function keeps 455 /// track of the current position within the current line. 456 /// @throw JSONError 457 /// @return An ElementPtr that contains the element(s) specified 458 /// in the given input stream. 459 // make this one private? 460 /// @throw JSONError 461 static ElementPtr fromJSON(std::istream& in, const std::string& file, 462 int& line, int &pos); 463 464 /// Reads contents of specified file and interprets it as JSON. 465 /// 466 /// @param file_name name of the file to read 467 /// @param preproc specified whether preprocessing (e.g. comment removal) 468 /// should be performed 469 /// @return An ElementPtr that contains the element(s) specified 470 /// if the given file. 471 static ElementPtr fromJSONFile(const std::string& file_name, 472 bool preproc = false); 473 //@} 474 475 /// @name Type name conversion functions 476 477 /// Returns the name of the given type as a string 478 /// 479 /// @param type The type to return the name of 480 /// @return The name of the type, or "unknown" if the type 481 /// is not known. 482 static std::string typeToName(Element::types type); 483 484 /// Converts the string to the corresponding type 485 /// Throws a TypeError if the name is unknown. 486 /// 487 /// @param type_name The name to get the type of 488 /// @return the corresponding type value 489 static Element::types nameToType(const std::string& type_name); 490 491 /// @brief input text preprocessor 492 /// 493 /// This method performs preprocessing of the input stream (which is 494 /// expected to contain a text version of to be parsed JSON). For now the 495 /// sole supported operation is bash-style (line starting with #) comment 496 /// removal, but it will be extended later to cover more cases (C, C++ style 497 /// comments, file inclusions, maybe macro replacements?). 498 /// 499 /// This method processes the whole input stream. It reads all contents of 500 /// the input stream, filters the content and returns the result in a 501 /// different stream. 502 /// 503 /// @param in input stream to be preprocessed 504 /// @param out output stream (filtered content will be written here) 505 static void preprocess(std::istream& in, std::stringstream& out); 506 507 /// @name Wire format factory functions 508 509 /// These function pparse the wireformat at the given stringstream 510 /// (of the given length). If there is a parse error an exception 511 /// of the type isc::cc::DecodeError is raised. 512 513 //@{ 514 /// Creates an Element from the wire format in the given 515 /// stringstream of the given length. 516 /// Since the wire format is JSON, this is the same as 517 /// fromJSON, and could be removed. 518 /// 519 /// @param in The input stringstream. 520 /// @param length The length of the wireformat data in the stream 521 /// @return ElementPtr with the data that is parsed. 522 static ElementPtr fromWire(std::stringstream& in, int length); 523 524 /// Creates an Element from the wire format in the given string 525 /// Since the wire format is JSON, this is the same as 526 /// fromJSON, and could be removed. 527 /// 528 /// @param s The input string 529 /// @return ElementPtr with the data that is parsed. 530 static ElementPtr fromWire(const std::string& s); 531 //@} 532 533 /// @brief Remove all empty maps and lists from this Element and its 534 /// descendants. removeEmptyContainersRecursively()535 void removeEmptyContainersRecursively() { 536 if (type_ == list || type_ == map) { 537 size_t s(size()); 538 for (size_t i = 0; i < s; ++i) { 539 // Get child. 540 ElementPtr child; 541 if (type_ == list) { 542 child = getNonConst(i); 543 } else if (type_ == map) { 544 std::string const key(get(i)->stringValue()); 545 // The ElementPtr - ConstElementPtr disparity between 546 // ListElement and MapElement is forcing a const cast here. 547 // It's undefined behavior to modify it after const casting. 548 // The options are limited. I've tried templating, moving 549 // this function from a member function to free-standing and 550 // taking the Element template as argument. I've tried 551 // making it a virtual function with overridden 552 // implementations in ListElement and MapElement. Nothing 553 // works. 554 child = boost::const_pointer_cast<Element>(get(key)); 555 } 556 557 // Makes no sense to continue for non-container children. 558 if (child->getType() != list && child->getType() != map) { 559 continue; 560 } 561 562 // Recurse if not empty. 563 if (!child->empty()){ 564 child->removeEmptyContainersRecursively(); 565 } 566 567 // When returning from recursion, remove if empty. 568 if (child->empty()) { 569 remove(i); 570 --i; 571 --s; 572 } 573 } 574 } 575 } 576 }; 577 578 /// Notes: IntElement type is changed to int64_t. 579 /// Due to C++ problems on overloading and automatic type conversion, 580 /// (C++ tries to convert integer type values and reference/pointer 581 /// if value types do not match exactly) 582 /// We decided the storage as int64_t, 583 /// three (long long, long, int) override function definitions 584 /// and cast int/long/long long to int64_t via long long. 585 /// Therefore, call by value methods (create, setValue) have three 586 /// (int,long,long long) definitions. Others use int64_t. 587 /// 588 class IntElement : public Element { 589 int64_t i; 590 public: 591 IntElement(int64_t v, const Position& pos = ZERO_POSITION()) Element(integer,pos)592 : Element(integer, pos), i(v) { } intValue()593 int64_t intValue() const { return (i); } 594 using Element::getValue; getValue(int64_t & t)595 bool getValue(int64_t& t) const { t = i; return (true); } 596 using Element::setValue; setValue(long long int v)597 bool setValue(long long int v) { i = v; return (true); } 598 void toJSON(std::ostream& ss) const; 599 bool equals(const Element& other) const; 600 }; 601 602 class DoubleElement : public Element { 603 double d; 604 605 public: 606 DoubleElement(double v, const Position& pos = ZERO_POSITION()) Element(real,pos)607 : Element(real, pos), d(v) {}; doubleValue()608 double doubleValue() const { return (d); } 609 using Element::getValue; getValue(double & t)610 bool getValue(double& t) const { t = d; return (true); } 611 using Element::setValue; setValue(const double v)612 bool setValue(const double v) { d = v; return (true); } 613 void toJSON(std::ostream& ss) const; 614 bool equals(const Element& other) const; 615 }; 616 617 class BoolElement : public Element { 618 bool b; 619 620 public: 621 BoolElement(const bool v, const Position& pos = ZERO_POSITION()) Element(boolean,pos)622 : Element(boolean, pos), b(v) {}; boolValue()623 bool boolValue() const { return (b); } 624 using Element::getValue; getValue(bool & t)625 bool getValue(bool& t) const { t = b; return (true); } 626 using Element::setValue; setValue(const bool v)627 bool setValue(const bool v) { b = v; return (true); } 628 void toJSON(std::ostream& ss) const; 629 bool equals(const Element& other) const; 630 }; 631 632 class NullElement : public Element { 633 public: 634 NullElement(const Position& pos = ZERO_POSITION()) Element(null,pos)635 : Element(null, pos) {}; 636 void toJSON(std::ostream& ss) const; 637 bool equals(const Element& other) const; 638 }; 639 640 class StringElement : public Element { 641 std::string s; 642 643 public: 644 StringElement(std::string v, const Position& pos = ZERO_POSITION()) Element(string,pos)645 : Element(string, pos), s(v) {}; stringValue()646 std::string stringValue() const { return (s); } 647 using Element::getValue; getValue(std::string & t)648 bool getValue(std::string& t) const { t = s; return (true); } 649 using Element::setValue; setValue(const std::string & v)650 bool setValue(const std::string& v) { s = v; return (true); } 651 void toJSON(std::ostream& ss) const; 652 bool equals(const Element& other) const; 653 }; 654 655 class ListElement : public Element { 656 std::vector<ElementPtr> l; 657 658 public: 659 ListElement(const Position& pos = ZERO_POSITION()) Element(list,pos)660 : Element(list, pos) {} listValue()661 const std::vector<ElementPtr>& listValue() const { return (l); } 662 using Element::getValue; getValue(std::vector<ElementPtr> & t)663 bool getValue(std::vector<ElementPtr>& t) const { 664 t = l; 665 return (true); 666 } 667 using Element::setValue; setValue(const std::vector<ElementPtr> & v)668 bool setValue(const std::vector<ElementPtr>& v) { 669 l = v; 670 return (true); 671 } 672 using Element::get; get(int i)673 ConstElementPtr get(int i) const { return (l.at(i)); } getNonConst(int i)674 ElementPtr getNonConst(int i) const { return (l.at(i)); } 675 using Element::set; set(size_t i,ElementPtr e)676 void set(size_t i, ElementPtr e) { 677 l.at(i) = e; 678 } add(ElementPtr e)679 void add(ElementPtr e) { l.push_back(e); }; 680 using Element::remove; remove(int i)681 void remove(int i) { l.erase(l.begin() + i); }; 682 void toJSON(std::ostream& ss) const; size()683 size_t size() const { return (l.size()); } empty()684 bool empty() const { return (l.empty()); } 685 bool equals(const Element& other) const; 686 687 /// @brief Sorts the elements inside the list. 688 /// 689 /// The list must contain elements of the same type. 690 /// Call with the key by which you want to sort when the list contains maps. 691 /// Nested lists are not supported. 692 /// Call without a parameter when sorting any other type. 693 /// 694 /// @param index the key by which you want to sort when the list contains 695 /// maps 696 void sort(std::string const& index = std::string()); 697 }; 698 699 class MapElement : public Element { 700 std::map<std::string, ConstElementPtr> m; 701 702 public: Element(map,pos)703 MapElement(const Position& pos = ZERO_POSITION()) : Element(map, pos) {} 704 // @todo should we have direct iterators instead of exposing the std::map 705 // here? mapValue()706 const std::map<std::string, ConstElementPtr>& mapValue() const override { 707 return (m); 708 } 709 using Element::getValue; getValue(std::map<std::string,ConstElementPtr> & t)710 bool getValue(std::map<std::string, ConstElementPtr>& t) const override { 711 t = m; 712 return (true); 713 } 714 using Element::setValue; setValue(const std::map<std::string,ConstElementPtr> & v)715 bool setValue(const std::map<std::string, ConstElementPtr>& v) override { 716 m = v; 717 return (true); 718 } 719 using Element::get; get(const std::string & s)720 ConstElementPtr get(const std::string& s) const override { 721 auto found = m.find(s); 722 return (found != m.end() ? found->second : ConstElementPtr()); 723 } 724 725 /// @brief Get the i-th element in the map. 726 /// 727 /// Useful when required to iterate with an index. 728 /// 729 /// @param i the position of the element you want to return 730 /// @return the element at position i get(int const i)731 ConstElementPtr get(int const i) const override { 732 auto it(m.begin()); 733 std::advance(it, i); 734 return create(it->first); 735 } 736 737 using Element::set; 738 void set(const std::string& key, ConstElementPtr value) override; 739 using Element::remove; remove(const std::string & s)740 void remove(const std::string& s) override { m.erase(s); } 741 742 /// @brief Remove the i-th element from the map. 743 /// 744 /// @param i the position of the element you want to remove remove(int const i)745 void remove(int const i) override { 746 auto it(m.begin()); 747 std::advance(it, i); 748 m.erase(it); 749 } 750 contains(const std::string & s)751 bool contains(const std::string& s) const override { 752 return (m.find(s) != m.end()); 753 } 754 void toJSON(std::ostream& ss) const override; 755 756 // we should name the two finds better... 757 // find the element at id; raises TypeError if one of the 758 // elements at path except the one we're looking for is not a 759 // mapelement. 760 // returns an empty element if the item could not be found 761 ConstElementPtr find(const std::string& id) const override; 762 763 // find the Element at 'id', and store the element pointer in t 764 // returns true if found, or false if not found (either because 765 // it doesn't exist or one of the elements in the path is not 766 // a MapElement) 767 bool find(const std::string& id, ConstElementPtr& t) const override; 768 769 /// @brief Returns number of stored elements 770 /// 771 /// @return number of elements. size()772 size_t size() const override { 773 return (m.size()); 774 } 775 776 bool equals(const Element& other) const override; 777 empty()778 bool empty() const override { return (m.empty()); } 779 }; 780 781 /// Checks whether the given ElementPtr is a NULL pointer 782 /// @param p The ElementPtr to check 783 /// @return true if it is NULL, false if not. 784 bool isNull(ConstElementPtr p); 785 786 /// 787 /// @brief Remove all values from the first ElementPtr that are 788 /// equal in the second. Both ElementPtrs MUST be MapElements 789 /// The use for this function is to end up with a MapElement that 790 /// only contains new and changed values (for ModuleCCSession and 791 /// configuration update handlers) 792 /// Raises a TypeError if a or b are not MapElements 793 void removeIdentical(ElementPtr a, ConstElementPtr b); 794 795 /// @brief Create a new ElementPtr from the first ElementPtr, removing all 796 /// values that are equal in the second. Both ElementPtrs MUST be MapElements. 797 /// The returned ElementPtr will be a MapElement that only contains new and 798 /// changed values (for ModuleCCSession and configuration update handlers). 799 /// Raises a TypeError if a or b are not MapElements 800 ConstElementPtr removeIdentical(ConstElementPtr a, ConstElementPtr b); 801 802 /// @brief Merges the data from other into element. 803 /// (on the first level). Both elements must be 804 /// MapElements. 805 /// Every string,value pair in other is copied into element 806 /// (the ElementPtr of value is copied, this is not a new object) 807 /// Unless the value is a NullElement, in which case the 808 /// key is removed from element, rather than setting the value to 809 /// the given NullElement. 810 /// This way, we can remove values from for instance maps with 811 /// configuration data (which would then result in reverting back 812 /// to the default). 813 /// Raises a TypeError if either ElementPtr is not a MapElement 814 void merge(ElementPtr element, ConstElementPtr other); 815 816 /// @brief Copy the data up to a nesting level. 817 /// 818 /// The copy is a deep copy so nothing is shared if it is not 819 /// under the given nesting level. 820 /// 821 /// @param from the pointer to the element to copy 822 /// @param level nesting level (default is 100, 0 means shallow copy, 823 /// negative means outbound and perhaps looping forever). 824 /// @return a pointer to a fresh copy 825 /// @throw raises a BadValue is a null pointer occurs. 826 ElementPtr copy(ConstElementPtr from, int level = 100); 827 828 /// @brief Compares the data with other using unordered lists 829 /// 830 /// This comparison function handles lists (JSON arrays) as 831 /// unordered multi sets (multi means an item can occurs more 832 /// than once as soon as it occurs the same number of times). 833 bool isEquivalent(ConstElementPtr a, ConstElementPtr b); 834 835 /// @brief Pretty prints the data into stream. 836 /// 837 /// This operator converts the @c ConstElementPtr into a string and 838 /// inserts it into the output stream @c out with an initial 839 /// indentation @c indent and add at each level @c step spaces. 840 /// For maps if there is a comment property it is printed first. 841 /// 842 /// @param element A @c ConstElementPtr to pretty print 843 /// @param out A @c std::ostream on which the print operation is performed 844 /// @param indent An initial number of spaces to add each new line 845 /// @param step A number of spaces to add to indentation at a new level 846 void prettyPrint(ConstElementPtr element, std::ostream& out, 847 unsigned indent = 0, unsigned step = 2); 848 849 /// @brief Pretty prints the data into string 850 /// 851 /// This operator converts the @c ConstElementPtr into a string with 852 /// an initial indentation @c indent and add at each level @c step spaces. 853 /// For maps if there is a comment property it is printed first. 854 /// 855 /// @param element A @c ConstElementPtr to pretty print 856 /// @param indent An initial number of spaces to add each new line 857 /// @param step A number of spaces to add to indentation at a new level 858 /// @return a string where element was pretty printed 859 std::string prettyPrint(ConstElementPtr element, 860 unsigned indent = 0, unsigned step = 2); 861 862 /// @brief Insert Element::Position as a string into stream. 863 /// 864 /// This operator converts the @c Element::Position into a string and 865 /// inserts it into the output stream @c out. 866 /// 867 /// @param out A @c std::ostream object on which the insertion operation is 868 /// performed. 869 /// @param pos The @c Element::Position structure to insert. 870 /// @return A reference to the same @c std::ostream object referenced by 871 /// parameter @c out after the insertion operation. 872 std::ostream& operator<<(std::ostream& out, const Element::Position& pos); 873 874 /// @brief Insert the Element as a string into stream. 875 /// 876 /// This method converts the @c ElementPtr into a string with 877 /// @c Element::str() and inserts it into the 878 /// output stream @c out. 879 /// 880 /// This function overloads the global operator<< to behave as described in 881 /// ostream::operator<< but applied to @c ElementPtr objects. 882 /// 883 /// @param out A @c std::ostream object on which the insertion operation is 884 /// performed. 885 /// @param e The @c ElementPtr object to insert. 886 /// @return A reference to the same @c std::ostream object referenced by 887 /// parameter @c out after the insertion operation. 888 std::ostream& operator<<(std::ostream& out, const Element& e); 889 890 bool operator==(const Element& a, const Element& b); 891 bool operator!=(const Element& a, const Element& b); 892 bool operator<(const Element& a, const Element& b); 893 894 } // namespace data 895 } // namespace isc 896 897 #endif // ISC_DATA_H 898 899 // Local Variables: 900 // mode: c++ 901 // End: 902