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
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
    // 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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    
    // 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
129
130
    std::string toWire(int omit_length = 1);
    virtual void toWire(std::stringstream& out, int omit_length = 1) = 0;
131
132
133
134
135
136
137
138
139
140

    /// \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
141
142
143
144
145
146
    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?
147
148
149
150
151
152
153
154
155
156
157
    //@}

    /// \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
    ///
    //@{
158
159
160
161
162
163
    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);
164
165
166
167
168
169
170
171
172
    //@}
    ///
    /// \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
    ///
    //@{
173
174
175
176
177
178
    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);
179
180
181
182
183
184
185
186
187
188
189
190
191
192
    //@}



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

    /// \name Factory functions
251
    
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
    // 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);
292
    static ElementPtr createFromString(std::istream& in, const std::string& file_name) throw(ParseError);
293
294
295
296
297
298
299
300
301
302
303
    /// 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
304
    static ElementPtr createFromString(std::istream& in, const std::string& file, int& line, int &pos) throw(ParseError);
305
306
307
308
309
310
311
    //@}

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

class DoubleElement : public Element {
    double d;

public:
    DoubleElement(double v) : Element(real), d(v) {};
    double doubleValue() { return d; }
348
    using Element::getValue;
349
    bool getValue(double& t) { t = d; return true; };
350
    using Element::setValue;
351
352
    bool setValue(const double v) { d = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
353
    void toWire(std::stringstream& ss, int omit_length = 1);
354
    bool equals(ElementPtr other);
355
356
357
358
359
360
361
362
};

class BoolElement : public Element {
    bool b;

public:
    BoolElement(const bool v) : Element(boolean), b(v) {};
    bool boolValue() { return b; }
363
    using Element::getValue;
364
    bool getValue(bool& t) { t = b; return true; };
365
    using Element::setValue;
366
367
    bool setValue(const bool v) { b = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
368
    void toWire(std::stringstream& ss, int omit_length = 1);
369
    bool equals(ElementPtr other);
370
371
372
373
374
375
376
377
};

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

public:
    StringElement(std::string v) : Element(string), s(v) {};
    std::string stringValue() { return s; };
378
    using Element::getValue;
379
    bool getValue(std::string& t) { t = s; return true; };
380
    using Element::setValue;
381
382
    bool setValue(const std::string& v) { s = v; return true; };
    std::string str();
Jelte Jansen's avatar
Jelte Jansen committed
383
    void toWire(std::stringstream& ss, int omit_length = 1);
384
    bool equals(ElementPtr other);
385
386
387
388
389
390
391
392
};

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

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

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

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

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

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

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

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

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

495
#endif // _ISC_DATA_H
496
497
498
499

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