data.h 19.6 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 20 21 22 23
#ifndef _ISC_DATA_H
#define _ISC_DATA_H 1

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

Jelte Jansen's avatar
Jelte Jansen committed
27
namespace isc { namespace data {
28

29 30 31 32 33 34 35 36 37
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)
///
38
class TypeError : public isc::Exception {
39
public:
Jelte Jansen's avatar
Jelte Jansen committed
40
    TypeError(const char* file, size_t line, const char* what) :
41
        isc::Exception(file, line, what) {}
42 43 44 45 46 47
};

///
/// \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
48 49 50 51
// 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 {
52
public:
Jelte Jansen's avatar
Jelte Jansen committed
53
    ParseError(const std::string &err) : std::runtime_error(err) {};
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
};

///
/// \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:
98 99
    // any is a special type used in list specifications, specifying
    // that the elements can be of any type
100
    enum types { integer, real, boolean, null, string, list, map, any };
101 102 103 104 105
    // base class; make dtor virtual
    virtual ~Element() {};

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

    /// \returns true if the other ElementPtr has the same type and
    ///          value
    virtual bool equals(ElementPtr other) = 0;
110 111 112
    
    // pure virtuals, every derived class must implement these

113 114
    virtual void toJSON(std::stringstream& ss) = 0;

115 116 117 118 119 120 121 122
    /// 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
123
    std::string str();
124 125 126 127 128 129 130

    /// 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
131 132
    std::string toWire();
    void toWire(std::stringstream& out);
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
    virtual bool getValue(int& t);
    virtual bool getValue(double& t);
    virtual bool getValue(bool& t);
    virtual bool getValue(std::string& t);
    virtual bool getValue(std::vector<ElementPtr>& t);
    virtual bool getValue(std::map<std::string, ElementPtr>& t);
166 167 168 169 170 171 172 173 174
    //@}
    ///
    /// \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
    ///
    //@{
175 176 177 178 179 180
    virtual bool setValue(const int v);
    virtual bool setValue(const double v);
    virtual bool setValue(const bool t);
    virtual bool setValue(const std::string& v);
    virtual bool setValue(const std::vector<ElementPtr>& v);
    virtual bool setValue(const std::map<std::string, ElementPtr>& v);
181 182 183 184 185 186 187 188 189 190 191 192 193 194
    //@}



    // 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
195
    virtual ElementPtr get(const int i);
196 197 198 199
    /// 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
200
    virtual void set(const size_t i, ElementPtr element);
201 202
    /// Adds an ElementPtr to the list
    /// \param element The ElementPtr to add
203
    virtual void add(ElementPtr element);
204 205 206
    /// Removes the element at the given position. If the index is out
    /// of nothing happens.
    /// \param i The index of the element to remove.
207
    virtual void remove(const int i);
208
    /// Returns the number of elements in the list.
209
    virtual size_t size();
210 211 212 213 214 215 216 217 218 219
    //@}
    
    /// \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
220
    virtual ElementPtr get(const std::string& name);
221 222
    /// Sets the ElementPtr at the given key
    /// \param name The key of the Element to set
223
    virtual void set(const std::string& name, ElementPtr element);
224 225
    /// Remove the ElementPtr at the given key
    /// \param name The key of the Element to remove
226
    virtual void remove(const std::string& name);
227 228 229
    /// 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.
230
    virtual bool contains(const std::string& name);
231 232 233 234 235 236 237 238 239 240 241 242 243
    /// 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).
244
    virtual ElementPtr find(const std::string& identifier);
245 246 247 248
    /// 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.
249
    virtual bool find(const std::string& identifier, ElementPtr& t);
250 251 252
    //@}

    /// \name Factory functions
253
    
254 255 256 257 258 259 260 261 262 263 264
    // 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).
265 266
    /// (Note that that is different from an NullElement, which
    /// represents an empty value, and is created with Element::create())
267
    //@{
268
    static ElementPtr create();
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
    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);
297
    static ElementPtr createFromString(std::istream& in, const std::string& file_name) throw(ParseError);
298 299 300 301 302 303 304 305 306 307 308
    /// 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
309
    static ElementPtr createFromString(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
310 311 312 313 314 315 316
    //@}

    /// \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.
317
    
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    //@{
    /// 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; }
338
    using Element::getValue;
339
    bool getValue(int& t) { t = i; return true; };
340
    using Element::setValue;
341
    bool setValue(const int v) { i = v; return true; };
342
    void toJSON(std::stringstream& ss);
343
    bool equals(ElementPtr other);
344 345 346 347 348 349 350 351
};

class DoubleElement : public Element {
    double d;

public:
    DoubleElement(double v) : Element(real), d(v) {};
    double doubleValue() { return d; }
352
    using Element::getValue;
353
    bool getValue(double& t) { t = d; return true; };
354
    using Element::setValue;
355
    bool setValue(const double v) { d = v; return true; };
356
    void toJSON(std::stringstream& ss);
357
    bool equals(ElementPtr other);
358 359 360 361 362 363 364 365
};

class BoolElement : public Element {
    bool b;

public:
    BoolElement(const bool v) : Element(boolean), b(v) {};
    bool boolValue() { return b; }
366
    using Element::getValue;
367
    bool getValue(bool& t) { t = b; return true; };
368
    using Element::setValue;
369
    bool setValue(const bool v) { b = v; return true; };
370 371 372 373 374 375 376 377
    void toJSON(std::stringstream& ss);
    bool equals(ElementPtr other);
};

class NullElement : public Element {
public:
    NullElement() : Element(null) {};
    void toJSON(std::stringstream& ss);
378
    bool equals(ElementPtr other);
379 380 381 382 383 384 385 386
};

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

public:
    StringElement(std::string v) : Element(string), s(v) {};
    std::string stringValue() { return s; };
387
    using Element::getValue;
388
    bool getValue(std::string& t) { t = s; return true; };
389
    using Element::setValue;
390
    bool setValue(const std::string& v) { s = v; return true; };
391
    void toJSON(std::stringstream& ss);
392
    bool equals(ElementPtr other);
393 394 395 396 397 398 399 400
};

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; }
401
    using Element::getValue;
402
    bool getValue(std::vector<ElementPtr>& t) { t = l; return true; };
403
    using Element::setValue;
404
    bool setValue(const std::vector<ElementPtr>& v) { l = v; return true; };
405
    using Element::get;
406
    ElementPtr get(int i) { return l.at(i); };
407
    using Element::set;
408 409
    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); };
410
    using Element::remove;
411
    void remove(int i) { l.erase(l.begin() + i); };
412
    void toJSON(std::stringstream& ss);
413
    size_t size() { return l.size(); }
414
    bool equals(ElementPtr other);
415 416 417 418 419 420
};

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

public:
421
    MapElement(const std::map<std::string, ElementPtr>& v) : Element(map), m(v) {};
422
    const std::map<std::string, ElementPtr>& mapValue() { return m; }
423
    using Element::getValue;
424
    bool getValue(std::map<std::string, ElementPtr>& t) { t = m; return true; };
425
    using Element::setValue;
426
    bool setValue(std::map<std::string, ElementPtr>& v) { m = v; return true; };
427
    using Element::get;
428
    ElementPtr get(const std::string& s) { if (contains(s)) { return m[s]; } else { return ElementPtr();} };
429
    using Element::set;
430
    void set(const std::string& s, ElementPtr p) { m[s] = p; };
431
    using Element::remove;
432 433
    void remove(const std::string& s) { m.erase(s); }
    bool contains(const std::string& s) { return m.find(s) != m.end(); }
434
    void toJSON(std::stringstream& ss);
435 436 437 438 439 440 441 442 443 444 445 446 447
    
    // 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);
448 449

    bool equals(ElementPtr other);
450 451 452 453 454 455 456
};

/// 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);

457 458
///
/// \brief Remove all values from the first ElementPtr that are
459
/// equal in the second. Both ElementPtrs MUST be MapElements
460 461 462 463 464
/// 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);
465

466 467 468 469 470 471 472 473 474 475
/// \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);

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
///
/// \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
491
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e);
492

493
bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
494
} }
495

496
#endif // _ISC_DATA_H
497 498 499 500

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