data.h 21 KB
Newer Older
Jelte Jansen's avatar
Jelte Jansen committed
1
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$

17 18 19
#ifndef _ISC_DATA_H
#define _ISC_DATA_H 1

20 21
#include "config.h"

22 23 24 25
#include <string>
#include <vector>
#include <map>
#include <boost/shared_ptr.hpp>
26
#include <stdexcept>
27
#include <exceptions/exceptions.h>
28

Jelte Jansen's avatar
Jelte Jansen committed
29
namespace isc { namespace data {
30

31 32 33 34 35 36 37 38 39
class Element;
// todo: describe the rationale behind ElementPtr?
typedef boost::shared_ptr<Element> ElementPtr;

///
/// \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)
///
40
class TypeError : public isc::Exception {
41
public:
Jelte Jansen's avatar
Jelte Jansen committed
42
    TypeError(const char* file, size_t line, const char* what) :
43
        isc::Exception(file, line, what) {}
44 45 46 47 48 49
};

///
/// \brief A standard Data module exception that is thrown if a parse
/// error is encountered when constructing an Element from a string
///
Jelte Jansen's avatar
Jelte Jansen committed
50 51 52 53
// 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 ParseError : public std::runtime_error {
54
public:
Jelte Jansen's avatar
Jelte Jansen committed
55
    ParseError(const std::string &err) : std::runtime_error(err) {};
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
};

///
/// \brief A standard Data module exception that is thrown if an error
/// is found when decoding an Element from wire format
///
class DecodeError : public std::exception {
public:
    DecodeError(std::string m = "Wire-format data is invalid") : msg(m) {}
    ~DecodeError() throw() {}
    const char* what() const throw() { return msg.c_str(); }
private:
    std::string msg;
};

///
/// \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::createFromString()
///
/// 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 {
    
private:
    // technically the type could be omitted; is it useful?
    // should we remove it or replace it with a pure virtual
    // function getType?
    int type;

protected:
    Element(int t) { type = t; }

public:
100 101 102
    // any is a special type used in list specifications, specifying
    // that the elements can be of any type
    enum types { integer, real, boolean, string, list, map, any };
103 104 105 106 107
    // base class; make dtor virtual
    virtual ~Element() {};

    /// \return the type of this element
    int getType() { return type; };
108 109 110 111

    /// \returns true if the other ElementPtr has the same type and
    ///          value
    virtual bool equals(ElementPtr other) = 0;
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
    
    // pure virtuals, every derived class must implement these

    /// 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
    /// A MapElement will be represented as { "name1": \<value1\>, "name2", \<value2\>, etc }
    /// A ListElement will be represented as [ \<item1\>, \<item2\>, etc ]
    /// All other elements will be represented directly
    ///
    /// \return std::string containing the string representation
    virtual std::string str() = 0;

    /// Returns the wireformat for the Element and all its child
    /// elements.
    ///
    /// \param omit_length If this is non-zero, the item length will
    ///        be omitted from the wire format
    /// \return std::string containing the element in wire format
Jelte Jansen's avatar
Jelte Jansen committed
131 132
    std::string toWire(int omit_length = 1);
    virtual void toWire(std::stringstream& out, int omit_length = 1) = 0;
133 134 135 136 137 138 139 140 141 142

    /// \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
    //@{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
143 144 145 146 147 148
    virtual int intValue() { isc_throw(TypeError, "intValue() called on non-integer Element"); };
    virtual double doubleValue() { isc_throw(TypeError, "doubleValue() called on non-double Element"); };
    virtual bool boolValue() { isc_throw(TypeError, "boolValue() called on non-Bool Element"); };
    virtual std::string stringValue() { isc_throw(TypeError, "stringValue() called on non-string Element"); };
    virtual const std::vector<boost::shared_ptr<Element> >& listValue() { isc_throw(TypeError, "listValue() called on non-list Element"); }; // replace with real exception or empty vector?
    virtual const std::map<std::string, boost::shared_ptr<Element> >& mapValue() { isc_throw(TypeError, "mapValue() called on non-map Element"); }; // replace with real exception or empty map?
149 150 151 152 153 154 155 156 157 158 159
    //@}

    /// \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
    ///
    //@{
160 161 162 163 164 165 166 167
    virtual bool getValue(int& t UNUSED_PARAM) { return false; };
    virtual bool getValue(double& t UNUSED_PARAM) { return false; };
    virtual bool getValue(bool& t UNUSED_PARAM) { return false; };
    virtual bool getValue(std::string& t UNUSED_PARAM) { return false; };
    virtual bool getValue(std::vector<ElementPtr>& t UNUSED_PARAM)
    { return false; };
    virtual bool getValue(std::map<std::string, ElementPtr>& t UNUSED_PARAM)
    { return false; };
168 169 170 171 172 173 174 175 176
    //@}
    ///
    /// \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
    ///
    //@{
177 178 179 180 181 182 183 184 185
    virtual bool setValue(const int v UNUSED_PARAM) { return false; };
    virtual bool setValue(const double v UNUSED_PARAM) { return false; };
    virtual bool setValue(const bool t UNUSED_PARAM) { return false; };
    virtual bool setValue(const std::string& v UNUSED_PARAM) { return false; };
    virtual bool setValue(const std::vector<ElementPtr>& v UNUSED_PARAM)
    { return false; };
    virtual bool setValue(const std::map<std::string,
                          ElementPtr>& v UNUSED_PARAM)
    { return false; };
186 187 188 189 190 191 192 193 194 195 196 197 198 199
    //@}



    // 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
200 201
    virtual ElementPtr get(const int i UNUSED_PARAM)
    { isc_throw(TypeError, "get(int) called on a non-list Element"); };
202 203 204 205
    /// 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
206 207 208
    virtual void set(const size_t i UNUSED_PARAM,
                     ElementPtr element UNUSED_PARAM)
    { isc_throw(TypeError, "set(int, element) called on a non-list Element"); };
209 210
    /// Adds an ElementPtr to the list
    /// \param element The ElementPtr to add
211 212
    virtual void add(ElementPtr element UNUSED_PARAM)
    { isc_throw(TypeError, "add() called on a non-list Element"); };
213 214 215
    /// Removes the element at the given position. If the index is out
    /// of nothing happens.
    /// \param i The index of the element to remove.
216 217
    virtual void remove(const int i UNUSED_PARAM)
    { isc_throw(TypeError, "remove(int) called on a non-list Element"); };
218
    /// Returns the number of elements in the list.
JINMEI Tatuya's avatar
JINMEI Tatuya committed
219
    virtual size_t size() { isc_throw(TypeError, "size() called on a non-list Element"); };
220 221 222 223 224 225 226 227 228 229
    //@}
    
    /// \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
230 231
    virtual ElementPtr get(const std::string& name UNUSED_PARAM)
    { isc_throw(TypeError, "get(string) called on a non-map Element"); } ;
232 233
    /// Sets the ElementPtr at the given key
    /// \param name The key of the Element to set
234 235 236
    virtual void set(const std::string& name UNUSED_PARAM,
                     ElementPtr element UNUSED_PARAM)
    { isc_throw(TypeError, "set(name, element) called on a non-map Element"); };
237 238
    /// Remove the ElementPtr at the given key
    /// \param name The key of the Element to remove
239 240
    virtual void remove(const std::string& name UNUSED_PARAM)
    { isc_throw(TypeError, "remove(string) called on a non-map Element"); };
241 242 243
    /// Checks if there is data at the given key
    /// \param name The key of the Element to remove
    /// \return true if there is data at the key, false if not.
244 245
    virtual bool contains(const std::string& name UNUSED_PARAM)
    { isc_throw(TypeError, "contains(string) called on a non-map Element"); }
246 247 248 249 250 251 252 253 254 255 256 257 258
    /// 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).
259 260
    virtual ElementPtr find(const std::string& identifier UNUSED_PARAM)
    { isc_throw(TypeError, "find(string) called on a non-map Element"); };
261 262 263 264
    /// 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.
265 266 267
    virtual bool find(const std::string& identifier UNUSED_PARAM,
                      ElementPtr& t UNUSED_PARAM)
    { return false; };
268 269 270
    //@}

    /// \name Factory functions
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
    // 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.
    /// If there is a memory allocation problem, these functions will
    /// return a NULL ElementPtr, which can be checked with
    /// Element::is_null(ElementPtr ep).
    //@{
    static ElementPtr create(const int i);
    static ElementPtr create(const double d);
    static ElementPtr create(const bool b);
    static ElementPtr create(const std::string& s);
    // 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) { return create(std::string(s)); }; 
    static ElementPtr create(const std::vector<ElementPtr>& v);
    static ElementPtr create(const std::map<std::string, ElementPtr>& m);
    //@}

    /// \name Compound factory functions

    /// \brief These functions will parse the given string representation
    /// of a compound element. If there is a parse error, an exception
    /// of the type isc::data::ParseError is thrown.

    //@{
    /// Creates an Element from the given string
    /// \param in The string to parse the element from
    /// \return An ElementPtr that contains the element(s) specified
    /// in the given string.
    static ElementPtr createFromString(const std::string& in);
    /// Creates an Element from the given input stream
    /// \param in The string to parse the element from
    /// \return An ElementPtr that contains the element(s) specified
    /// in the given input stream.
    static ElementPtr createFromString(std::istream& in) throw(ParseError);
312
    static ElementPtr createFromString(std::istream& in, const std::string& file_name) throw(ParseError);
313 314 315 316 317 318 319 320 321 322 323
    /// 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 line A reference to the int where the function keeps
    /// track of the current line.
    /// \param line A reference to the int where the function keeps
    /// track of the current position within the current line.
    /// \return An ElementPtr that contains the element(s) specified
    /// in the given input stream.
    // make this one private?
Jelte Jansen's avatar
Jelte Jansen committed
324
    static ElementPtr createFromString(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
325 326 327 328 329 330 331
    //@}

    /// \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.
332
    
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    //@{
    /// Creates an Element from the wire format in the given
    /// stringstream of the given length.
    /// \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
    /// \param s The input string
    /// \return ElementPtr with the data that is parsed.
    static ElementPtr fromWire(const std::string& s);
    //@}
};

class IntElement : public Element {
    int i;

public:
    IntElement(int v) : Element(integer), i(v) { };
    int intValue() { return i; }
353
    using Element::getValue;
354
    bool getValue(int& t) { t = i; return true; };
355
    using Element::setValue;
356 357
    bool setValue(const int v) { i = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
358
    void toWire(std::stringstream& ss, int omit_length = 1);
359
    bool equals(ElementPtr other);
360 361 362 363 364 365 366 367
};

class DoubleElement : public Element {
    double d;

public:
    DoubleElement(double v) : Element(real), d(v) {};
    double doubleValue() { return d; }
368
    using Element::getValue;
369
    bool getValue(double& t) { t = d; return true; };
370
    using Element::setValue;
371 372
    bool setValue(const double v) { d = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
373
    void toWire(std::stringstream& ss, int omit_length = 1);
374
    bool equals(ElementPtr other);
375 376 377 378 379 380 381 382
};

class BoolElement : public Element {
    bool b;

public:
    BoolElement(const bool v) : Element(boolean), b(v) {};
    bool boolValue() { return b; }
383
    using Element::getValue;
384
    bool getValue(bool& t) { t = b; return true; };
385
    using Element::setValue;
386 387
    bool setValue(const bool v) { b = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
388
    void toWire(std::stringstream& ss, int omit_length = 1);
389
    bool equals(ElementPtr other);
390 391 392 393 394 395 396 397
};

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

public:
    StringElement(std::string v) : Element(string), s(v) {};
    std::string stringValue() { return s; };
398
    using Element::getValue;
399
    bool getValue(std::string& t) { t = s; return true; };
400
    using Element::setValue;
401 402
    bool setValue(const std::string& v) { s = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
403
    void toWire(std::stringstream& ss, int omit_length = 1);
404
    bool equals(ElementPtr other);
405 406 407 408 409 410 411 412
};

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

public:
    ListElement(std::vector<ElementPtr> v) : Element(list), l(v) {};
    const std::vector<ElementPtr>& listValue() { return l; }
413
    using Element::getValue;
414
    bool getValue(std::vector<ElementPtr>& t) { t = l; return true; };
415
    using Element::setValue;
416
    bool setValue(const std::vector<ElementPtr>& v) { l = v; return true; };
417
    using Element::get;
418
    ElementPtr get(int i) { return l.at(i); };
419
    using Element::set;
420 421
    void set(size_t i, ElementPtr e) { if (i <= l.size()) {l[i] = e;} else { throw std::out_of_range("vector::_M_range_check"); } };
    void add(ElementPtr e) { l.push_back(e); };
422
    using Element::remove;
423 424
    void remove(int i) { l.erase(l.begin() + i); };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
425
    void toWire(std::stringstream& ss, int omit_length = 1);
426
    size_t size() { return l.size(); }
427
    bool equals(ElementPtr other);
428 429 430 431 432 433 434 435
};

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

public:
    MapElement(std::map<std::string, ElementPtr> v) : Element(map), m(v) {};
    const std::map<std::string, ElementPtr>& mapValue() { return m; }
436
    using Element::getValue;
437
    bool getValue(std::map<std::string, ElementPtr>& t) { t = m; return true; };
438
    using Element::setValue;
439
    bool setValue(std::map<std::string, ElementPtr>& v) { m = v; return true; };
440
    using Element::get;
441
    ElementPtr get(const std::string& s) { if (contains(s)) { return m[s]; } else { return ElementPtr();} };
442
    using Element::set;
443
    void set(const std::string& s, ElementPtr p) { m[s] = p; };
444
    using Element::remove;
445 446 447
    void remove(const std::string& s) { m.erase(s); }
    bool contains(const std::string& s) { return m.find(s) != m.end(); }
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
448
    void toWire(std::stringstream& ss, int omit_length = 1);
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
    
    //
    // Encode into the CC wire format.
    //
    void toWire(std::ostream& ss);

    // 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
    ElementPtr find(const std::string& id);

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

    bool equals(ElementPtr other);
469 470 471 472 473 474 475
};

/// 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(ElementPtr p);

476 477
///
/// \brief Remove all values from the first ElementPtr that are
478
/// equal in the second. Both ElementPtrs MUST be MapElements
479 480 481 482 483
/// 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, const ElementPtr b);
484

485 486 487 488 489 490 491 492 493 494
/// \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 an empty ElementPtr, in which case the
/// whole key is removed from element.
/// Raises a TypeError if either ElementPtr is not a MapElement
void merge(ElementPtr element, const ElementPtr other);

495 496
} }

497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
///
/// \brief Insert the Element as a string into stream.
///
/// This method converts the \c ElemetPtr 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 os 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 os after the insertion operation.
Jelte Jansen's avatar
Jelte Jansen committed
512
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e);
513

514 515
bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b);

516
#endif // _ISC_DATA_H
517 518 519 520

// Local Variables: 
// mode: c++
// End: