config_data.cc 8.99 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
//
// 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.

15
#include <config/config_data.h>
16
17
18
19
20
21
22
23

#include <boost/foreach.hpp>

#include <string>
#include <iostream>

using namespace isc::data;

24
25
namespace {

26
27
28
// Returns the '_spec' part of a list or map specification (recursively,
// i.e. if it is a list of lists or maps, will return the spec of the
// inner-most list or map).
29
30
31
32
33
34
//
// \param spec_part the list or map specification (part)
// \return the value of spec_part's "list_item_spec" or "map_item_spec",
//         or the original spec_part, if it is not a MapElement or does
//         not contain "list_item_spec" or "map_item_spec"
ConstElementPtr findListOrMapSubSpec(ConstElementPtr spec_part) {
35
    while (spec_part->getType() == Element::map &&
36
37
38
39
40
41
42
43
44
45
46
47
48
           (spec_part->contains("list_item_spec") ||
            spec_part->contains("map_item_spec"))) {
        if (spec_part->contains("list_item_spec")) {
            spec_part = spec_part->get("list_item_spec");
        } else {
            spec_part = spec_part->get("map_item_spec");
        }
    }
    return spec_part;
}

// Returns a specific Element in a given specification ListElement
//
49
50
51
52
53
// \exception DataNotFoundError if the given identifier does not
// point to an existing element. Since we are dealing with the
// specification here, and not the config data itself, this should
// not happen, and is a code bug.
//
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
// \param spec_part ListElement to find the element in
// \param id_part the name of the element to find (must match the value
//                "item_name" in the list item
// \param id_full the full identifier id_part is a part of, this is
//                used to better report any errors
ConstElementPtr findItemInSpecList(ConstElementPtr spec_part,
                                   const std::string& id_part,
                                   const std::string& id_full)
{
    bool found = false;
    BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
        if (list_el->getType() == Element::map &&
            list_el->contains("item_name") &&
            list_el->get("item_name")->stringValue() == id_part) {
            spec_part = list_el;
            found = true;
        }
    }
    if (!found) {
        isc_throw(isc::config::DataNotFoundError,
                  id_part + " in " + id_full + " not found");
    }
    return (spec_part);
}

} // anonymous namespace

81
82
83
namespace isc {
namespace config {

84
85
86
87
88
89
90
91
92
93
//
// Return a part of a specification, as identified by the
// '/'-separated identifier.
// If it cannot be found, a DataNotFound error is thrown.
//
// Recursively goes through the Element. If it is a List,
// we search it contents to have 'items' (i.e. contain item_name)
// If it is a map, we search through the list contained in its
// 'map_item_spec' value. This code assumes the data has been
// validated and conforms to the specification.
94
95
static ConstElementPtr
find_spec_part(ConstElementPtr spec, const std::string& identifier) {
96
    if (!spec) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
97
        isc_throw(DataNotFoundError, "Empty specification");
98
    }
99

100
    ConstElementPtr spec_part = spec;
101
    if (identifier == "") {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
102
        isc_throw(DataNotFoundError, "Empty identifier");
103
104
105
106
107
    }
    std::string id = identifier;
    size_t sep = id.find('/');
    while(sep != std::string::npos) {
        std::string part = id.substr(0, sep);
108

109
        if (spec_part->getType() == Element::list) {
110
            spec_part = findItemInSpecList(spec_part, part, identifier);
111
        } else {
112
113
            isc_throw(DataNotFoundError,
                      "Not a list of spec items: " + spec_part->str());
114
        }
115
        id = id.substr(sep + 1);
116
        sep = id.find("/");
117
118
119
120
121
122
123

        // As long as we are not in the 'final' element as specified
        // by the identifier, we want to automatically traverse list
        // and map specifications
        if (id != "" && id != "/") {
            spec_part = findListOrMapSubSpec(spec_part);
        }
124
125
126
    }
    if (id != "" && id != "/") {
        if (spec_part->getType() == Element::list) {
127
            spec_part = findItemInSpecList(spec_part, id, identifier);
128
129
        } else if (spec_part->getType() == Element::map) {
            if (spec_part->contains("map_item_spec")) {
130
131
132
                spec_part = findItemInSpecList(
                                spec_part->get("map_item_spec"),
                                id, identifier);
133
            } else {
134
135
136
                // Either we already have the element we are looking
                // for, or we are trying to reach something that does
                // not exist (i.e. the code does not match the spec)
Jelte Jansen's avatar
Jelte Jansen committed
137
138
139
140
141
142
                if (!spec_part->contains("item_name") ||
                    spec_part->get("item_name")->stringValue() != id) {
                    isc_throw(DataNotFoundError, "Element above " + id +
                                                 " in " + identifier +
                                                 " is not a map: " + spec_part->str());
                }
143
144
145
            }
        }
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
146
    return (spec_part);
147
148
}

149
150
151
152
153
//
// Adds the names of the items in the given specification part.
// If recurse is true, maps will also have their children added.
// Result must be a ListElement
//
154
static void
155
156
spec_name_list(ElementPtr result, ConstElementPtr spec_part,
               const std::string& prefix, bool recurse = false)
157
158
{
    if (spec_part->getType() == Element::list) {
159
        BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
160
161
162
            if (list_el->getType() == Element::map &&
                list_el->contains("item_name")) {
                std::string new_prefix = prefix;
163
164
165
                if (prefix != "") {
                    new_prefix += "/";
                }
166
167
168
169
170
171
172
173
                new_prefix += list_el->get("item_name")->stringValue();
                if (recurse && list_el->get("item_type")->stringValue() == "map") {
                    spec_name_list(result, list_el->get("map_item_spec"), new_prefix, recurse);
                } else {
                    result->add(Element::create(new_prefix));
                }
            }
        }
174
175
176
177
    } else if (spec_part->getType() == Element::map &&
               spec_part->contains("map_item_spec")) {
        spec_name_list(result, spec_part->get("map_item_spec"), prefix,
                       recurse);
178
179
180
    }
}

181
182
ConstElementPtr
ConfigData::getValue(const std::string& identifier) const {
183
184
    // 'fake' is set, but dropped by this function and
    // serves no further purpose.
185
    bool fake;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
186
    return (getValue(fake, identifier));
187
188
}

189
190
191
ConstElementPtr
ConfigData::getValue(bool& is_default, const std::string& identifier) const {
    ConstElementPtr value = _config->find(identifier);
192
    if (value) {
193
        is_default = false;
194
    } else {
195
196
        ConstElementPtr spec_part =
            find_spec_part(_module_spec.getConfigSpec(), identifier);
Jelte Jansen's avatar
doxygen    
Jelte Jansen committed
197
198
199
200
201
        if (spec_part->contains("item_default")) {
            value = spec_part->get("item_default");
            is_default = true;
        } else {
            is_default = false;
202
            value = ElementPtr();
Jelte Jansen's avatar
doxygen    
Jelte Jansen committed
203
        }
204
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
205
    return (value);
206
207
}

208
209
210
211
212
213
214
215
216
217
218
ConstElementPtr
ConfigData::getDefaultValue(const std::string& identifier) const {
    ConstElementPtr spec_part =
        find_spec_part(_module_spec.getConfigSpec(), identifier);
    if (spec_part->contains("item_default")) {
        return spec_part->get("item_default");
    } else {
        isc_throw(DataNotFoundError, "No default for " + identifier);
    }
}

219
220
221
/// Returns an ElementPtr pointing to a ListElement containing
/// StringElements with the names of the options at the given
/// identifier. If recurse is true, maps will be expanded as well
222
223
ConstElementPtr
ConfigData::getItemList(const std::string& identifier, bool recurse) const {
224
    ElementPtr result = Element::createList();
225
    ConstElementPtr spec_part = getModuleSpec().getConfigSpec();
226
227
228
229
    if (identifier != "" && identifier != "/") {
        spec_part = find_spec_part(spec_part, identifier);
    }
    spec_name_list(result, spec_part, identifier, recurse);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
230
    return (result);
231
232
}

233
234
/// Returns an ElementPtr containing a MapElement with identifier->value
/// pairs.
235
236
ConstElementPtr
ConfigData::getFullConfig() const {
237
    ElementPtr result = Element::createMap();
238
    ConstElementPtr items = getItemList("", false);
239
    BOOST_FOREACH(ConstElementPtr item, items->listValue()) {
240
241
        result->set(item->stringValue(), getValue(item->stringValue()));
    }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
242
    return (result);
243
244
245
246
}

}
}