data.h 19.3 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 100
    // 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 };
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
    // base class; make dtor virtual
    virtual ~Element() {};

    /// \return the type of this element
    int getType() { return type; };
    
    // 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
125 126
    std::string toWire(int omit_length = 1);
    virtual void toWire(std::stringstream& out, int omit_length = 1) = 0;
127 128 129 130 131 132 133 134 135 136

    /// \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
    //@{
Jelte Jansen's avatar
Jelte Jansen committed
137 138 139 140 141 142
    virtual int intValue() { dns_throw(TypeError, "intValue() called on non-integer Element"); };
    virtual double doubleValue() { dns_throw(TypeError, "doubleValue() called on non-double Element"); };
    virtual bool boolValue() { dns_throw(TypeError, "boolValue() called on non-Bool Element"); };
    virtual std::string stringValue() { dns_throw(TypeError, "stringValue() called on non-string Element"); };
    virtual const std::vector<boost::shared_ptr<Element> >& listValue() { dns_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() { dns_throw(TypeError, "mapValue() called on non-map Element"); }; // replace with real exception or empty map?
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
    //@}

    /// \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(int& t) { return false; };
    virtual bool getValue(double& t) { return false; };
    virtual bool getValue(bool& t) { return false; };
    virtual bool getValue(std::string& t) { return false; };
    virtual bool getValue(std::vector<ElementPtr>& t) { return false; };
    virtual bool getValue(std::map<std::string, ElementPtr>& t) { return false; };
    //@}
    ///
    /// \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
    ///
    //@{
    virtual bool setValue(const int v) { return false; };
    virtual bool setValue(const double v) { return false; };
    virtual bool setValue(const bool t) { return false; };
    virtual bool setValue(const std::string& v) { return false; };
    virtual bool setValue(const std::vector<ElementPtr>& v) { return false; };
    virtual bool setValue(const std::map<std::string, ElementPtr>& v) { return false; };
    //@}



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

    /// \name Factory functions
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
    // 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);
288
    static ElementPtr createFromString(std::istream& in, const std::string& file_name) throw(ParseError);
289 290 291 292 293 294 295 296 297 298 299
    /// 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
300
    static ElementPtr createFromString(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
301 302 303 304 305 306 307
    //@}

    /// \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.
308
    
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    //@{
    /// 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; }
329
    using Element::getValue;
330
    bool getValue(int& t) { t = i; return true; };
331
    using Element::setValue;
332 333
    bool setValue(const int v) { i = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
334
    void toWire(std::stringstream& ss, int omit_length = 1);
335 336 337 338 339 340 341 342
};

class DoubleElement : public Element {
    double d;

public:
    DoubleElement(double v) : Element(real), d(v) {};
    double doubleValue() { return d; }
343
    using Element::getValue;
344
    bool getValue(double& t) { t = d; return true; };
345
    using Element::setValue;
346 347
    bool setValue(const double v) { d = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
348
    void toWire(std::stringstream& ss, int omit_length = 1);
349 350 351 352 353 354 355 356
};

class BoolElement : public Element {
    bool b;

public:
    BoolElement(const bool v) : Element(boolean), b(v) {};
    bool boolValue() { return b; }
357
    using Element::getValue;
358
    bool getValue(bool& t) { t = b; return true; };
359
    using Element::setValue;
360 361
    bool setValue(const bool v) { b = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
362
    void toWire(std::stringstream& ss, int omit_length = 1);
363 364 365 366 367 368 369 370
};

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

public:
    StringElement(std::string v) : Element(string), s(v) {};
    std::string stringValue() { return s; };
371
    using Element::getValue;
372
    bool getValue(std::string& t) { t = s; return true; };
373
    using Element::setValue;
374 375
    bool setValue(const std::string& v) { s = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
376
    void toWire(std::stringstream& ss, int omit_length = 1);
377 378 379 380 381 382 383 384
};

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; }
385
    using Element::getValue;
386
    bool getValue(std::vector<ElementPtr>& t) { t = l; return true; };
387
    using Element::setValue;
388
    bool setValue(const std::vector<ElementPtr>& v) { l = v; return true; };
389
    using Element::get;
390
    ElementPtr get(int i) { return l.at(i); };
391
    using Element::set;
392 393
    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); };
394
    using Element::remove;
395 396
    void remove(int i) { l.erase(l.begin() + i); };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
397
    void toWire(std::stringstream& ss, int omit_length = 1);
398 399 400 401 402 403 404 405 406
    size_t size() { return l.size(); }
};

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; }
407
    using Element::getValue;
408
    bool getValue(std::map<std::string, ElementPtr>& t) { t = m; return true; };
409
    using Element::setValue;
410
    bool setValue(std::map<std::string, ElementPtr>& v) { m = v; return true; };
411
    using Element::get;
412
    ElementPtr get(const std::string& s) { if (contains(s)) { return m[s]; } else { return ElementPtr();} };
413
    using Element::set;
414
    void set(const std::string& s, ElementPtr p) { m[s] = p; };
415
    using Element::remove;
416 417 418
    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
419
    void toWire(std::stringstream& ss, int omit_length = 1);
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
    
    //
    // 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);
};

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


446 447
} }

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
///
/// \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
463
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e);
464 465

#endif // _ISC_DATA_H
466 467 468 469

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