Branch data Line data Source code
1 : : // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
2 : : //
3 : : // Permission to use, copy, modify, and/or distribute this software for any
4 : : // purpose with or without fee is hereby granted, provided that the above
5 : : // copyright notice and this permission notice appear in all copies.
6 : : //
7 : : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
8 : : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9 : : // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
10 : : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11 : : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
12 : : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13 : : // PERFORMANCE OF THIS SOFTWARE.
14 : :
15 : : #include <config/config_data.h>
16 : :
17 : : #include <boost/foreach.hpp>
18 : :
19 : : #include <string>
20 : : #include <iostream>
21 : :
22 : : using namespace isc::data;
23 : :
24 : : namespace {
25 : :
26 : : // Returns the '_spec' part of a list or map specification (recursively,
27 : : // i.e. if it is a list of lists or maps, will return the spec of the
28 : : // inner-most list or map).
29 : : //
30 : : // \param spec_part the list or map specification (part)
31 : : // \return the value of spec_part's "list_item_spec" or "map_item_spec",
32 : : // or the original spec_part, if it is not a MapElement or does
33 : : // not contain "list_item_spec" or "map_item_spec"
34 : 23 : ConstElementPtr findListOrMapSubSpec(ConstElementPtr spec_part) {
35 [ + + ][ + + ]: 179 : while (spec_part->getType() == Element::map &&
[ + + ][ + + ]
36 [ + - ][ + - ]: 97 : (spec_part->contains("list_item_spec") ||
[ + + ][ # # ]
37 [ + - ][ + - ]: 82 : spec_part->contains("map_item_spec"))) {
[ + + ][ # # ]
38 [ + - ][ + + ]: 36 : if (spec_part->contains("list_item_spec")) {
39 [ + - ][ + - ]: 30 : spec_part = spec_part->get("list_item_spec");
40 : : } else {
41 [ + - ][ + - ]: 57 : spec_part = spec_part->get("map_item_spec");
42 : : }
43 : : }
44 : 23 : return spec_part;
45 : : }
46 : :
47 : : // Returns a specific Element in a given specification ListElement
48 : : //
49 : : // \exception DataNotFoundError if the given identifier does not
50 : : // point to an existing element. Since we are dealing with the
51 : : // specification here, and not the config data itself, this should
52 : : // not happen, and is a code bug.
53 : : //
54 : : // \param spec_part ListElement to find the element in
55 : : // \param id_part the name of the element to find (must match the value
56 : : // "item_name" in the list item
57 : : // \param id_full the full identifier id_part is a part of, this is
58 : : // used to better report any errors
59 : 78 : ConstElementPtr findItemInSpecList(ConstElementPtr spec_part,
60 : : const std::string& id_part,
61 : : const std::string& id_full)
62 : : {
63 : 78 : bool found = false;
64 [ + + + - ]: 1706 : BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
[ + - ][ + + ]
[ + + ]
65 [ + - ]: 1628 : if (list_el->getType() == Element::map &&
[ + - + + ]
[ + + ]
66 [ + - ][ + - ]: 814 : list_el->contains("item_name") &&
[ + - ][ # # ]
67 [ + - ][ + - ]: 1221 : list_el->get("item_name")->stringValue() == id_part) {
[ + - ][ + - ]
[ + - ][ + - ]
[ # # ][ # # ]
68 [ + - ]: 479 : spec_part = list_el;
69 : : found = true;
70 : : }
71 : : }
72 [ + + ]: 78 : if (!found) {
73 [ + - ][ + - ]: 12 : isc_throw(isc::config::DataNotFoundError,
[ + - ][ + - ]
[ + - ][ + - ]
74 : : id_part + " in " + id_full + " not found");
75 : : }
76 : 72 : return (spec_part);
77 : : }
78 : :
79 : : } // anonymous namespace
80 : :
81 : : namespace isc {
82 : : namespace config {
83 : :
84 : : //
85 : : // Return a part of a specification, as identified by the
86 : : // '/'-separated identifier.
87 : : // If it cannot be found, a DataNotFound error is thrown.
88 : : //
89 : : // Recursively goes through the Element. If it is a List,
90 : : // we search it contents to have 'items' (i.e. contain item_name)
91 : : // If it is a map, we search through the list contained in its
92 : : // 'map_item_spec' value. This code assumes the data has been
93 : : // validated and conforms to the specification.
94 : : static ConstElementPtr
95 : 59 : find_spec_part(ConstElementPtr spec, const std::string& identifier) {
96 [ + + ]: 59 : if (!spec) {
97 [ + - ][ + - ]: 2 : isc_throw(DataNotFoundError, "Empty specification");
[ + - ]
98 : : }
99 : :
100 : : ConstElementPtr spec_part = spec;
101 [ + + ]: 58 : if (identifier == "") {
102 [ + - ][ + - ]: 2 : isc_throw(DataNotFoundError, "Empty identifier");
[ + - ][ + - ]
103 : : }
104 [ + - ]: 114 : std::string id = identifier;
105 [ + - ]: 57 : size_t sep = id.find('/');
106 [ + + ]: 82 : while(sep != std::string::npos) {
107 [ + - ]: 52 : std::string part = id.substr(0, sep);
108 : :
109 [ + - ]: 26 : if (spec_part->getType() == Element::list) {
110 [ + + ][ + - ]: 26 : spec_part = findItemInSpecList(spec_part, part, identifier);
111 : : } else {
112 [ # # ][ # # ]: 0 : isc_throw(DataNotFoundError,
[ # # ][ # # ]
[ # # ][ # # ]
113 : : "Not a list of spec items: " + spec_part->str());
114 : : }
115 [ + - ]: 50 : id = id.substr(sep + 1);
116 [ + - ]: 25 : sep = id.find("/");
117 : :
118 : : // As long as we are not in the 'final' element as specified
119 : : // by the identifier, we want to automatically traverse list
120 : : // and map specifications
121 [ + - ][ + + ]: 25 : if (id != "" && id != "/") {
[ + - ][ - + ]
[ + + ]
122 [ + - ][ + - ]: 23 : spec_part = findListOrMapSubSpec(spec_part);
123 : : }
124 : : }
125 [ + - ][ + + ]: 56 : if (id != "" && id != "/") {
[ + - ][ - + ]
[ + + ]
126 [ + + ]: 54 : if (spec_part->getType() == Element::list) {
127 [ + + ][ + - ]: 52 : spec_part = findItemInSpecList(spec_part, id, identifier);
128 [ + - ]: 2 : } else if (spec_part->getType() == Element::map) {
129 [ + - ][ + - ]: 2 : if (spec_part->contains("map_item_spec")) {
[ - + ]
130 : : spec_part = findItemInSpecList(
131 : 0 : spec_part->get("map_item_spec"),
132 [ # # ][ # # ]: 0 : id, identifier);
[ # # ][ # # ]
133 : : } else {
134 : : // Either we already have the element we are looking
135 : : // for, or we are trying to reach something that does
136 : : // not exist (i.e. the code does not match the spec)
137 [ + - ][ + - ]: 6 : if (!spec_part->contains("item_name") ||
[ + - + + ]
[ + + ][ # # ]
138 [ + - ][ + - ]: 6 : spec_part->get("item_name")->stringValue() != id) {
[ + - ][ + - ]
[ + - ][ + - ]
[ # # ][ # # ]
139 [ + - ][ + - ]: 2 : isc_throw(DataNotFoundError, "Element above " + id +
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
140 : : " in " + identifier +
141 : : " is not a map: " + spec_part->str());
142 : : }
143 : : }
144 : : }
145 : : }
146 : 50 : return (spec_part);
147 : : }
148 : :
149 : : //
150 : : // Adds the names of the items in the given specification part.
151 : : // If recurse is true, maps will also have their children added.
152 : : // Result must be a ListElement
153 : : //
154 : : static void
155 : 11 : spec_name_list(ElementPtr result, ConstElementPtr spec_part,
156 : : const std::string& prefix, bool recurse = false)
157 : : {
158 [ + + ]: 11 : if (spec_part->getType() == Element::list) {
159 [ + + + - ]: 170 : BOOST_FOREACH(ConstElementPtr list_el, spec_part->listValue()) {
[ + - ][ + + ]
[ + + ]
160 [ + - ][ - + ]: 120 : if (list_el->getType() == Element::map &&
[ + - ]
161 [ + - ][ + - ]: 80 : list_el->contains("item_name")) {
[ + - ][ # # ]
162 [ + - ]: 120 : std::string new_prefix = prefix;
163 [ + - ][ + + ]: 40 : if (prefix != "") {
164 : : new_prefix += "/";
165 : : }
166 [ + - ][ + - ]: 120 : new_prefix += list_el->get("item_name")->stringValue();
[ + - ]
167 [ + + ][ + - ]: 104 : if (recurse && list_el->get("item_type")->stringValue() == "map") {
[ + - ][ + - ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ # # ]
[ # # ][ # # ]
168 [ + - ][ + - ]: 12 : spec_name_list(result, list_el->get("map_item_spec"), new_prefix, recurse);
[ + - ]
169 : : } else {
170 [ + - ][ + - ]: 72 : result->add(Element::create(new_prefix));
171 : : }
172 : : }
173 : : }
174 [ + - ][ - + ]: 3 : } else if (spec_part->getType() == Element::map &&
[ + - ]
175 [ + - ][ + - ]: 2 : spec_part->contains("map_item_spec")) {
[ + - ][ # # ]
176 : 2 : spec_name_list(result, spec_part->get("map_item_spec"), prefix,
177 [ + - ][ + - ]: 2 : recurse);
178 : : }
179 : 11 : }
180 : :
181 : : ConstElementPtr
182 : 56 : ConfigData::getValue(const std::string& identifier) const {
183 : : // 'fake' is set, but dropped by this function and
184 : : // serves no further purpose.
185 : : bool fake;
186 : 56 : return (getValue(fake, identifier));
187 : : }
188 : :
189 : : ConstElementPtr
190 : 67 : ConfigData::getValue(bool& is_default, const std::string& identifier) const {
191 : 67 : ConstElementPtr value = _config->find(identifier);
192 [ + + ]: 67 : if (value) {
193 : 17 : is_default = false;
194 : : } else {
195 : : ConstElementPtr spec_part =
196 [ + - ][ + + ]: 50 : find_spec_part(_module_spec.getConfigSpec(), identifier);
197 [ + - ][ + - ]: 43 : if (spec_part->contains("item_default")) {
[ + + ]
198 [ + - ][ + - ]: 80 : value = spec_part->get("item_default");
[ + - ]
199 : 40 : is_default = true;
200 : : } else {
201 : 3 : is_default = false;
202 : : value = ElementPtr();
203 : : }
204 : : }
205 : 60 : return (value);
206 : : }
207 : :
208 : : ConstElementPtr
209 : 8 : ConfigData::getDefaultValue(const std::string& identifier) const {
210 : : ConstElementPtr spec_part =
211 [ + + ]: 8 : find_spec_part(_module_spec.getConfigSpec(), identifier);
212 [ + - ][ + - ]: 6 : if (spec_part->contains("item_default")) {
[ + - ]
213 [ + - ][ + - ]: 12 : return spec_part->get("item_default");
214 : : } else {
215 [ # # ][ # # ]: 0 : isc_throw(DataNotFoundError, "No default for " + identifier);
[ # # ][ # # ]
[ # # ]
216 : : }
217 : : }
218 : :
219 : : /// Returns an ElementPtr pointing to a ListElement containing
220 : : /// StringElements with the names of the options at the given
221 : : /// identifier. If recurse is true, maps will be expanded as well
222 : : ConstElementPtr
223 : 6 : ConfigData::getItemList(const std::string& identifier, bool recurse) const {
224 : 6 : ElementPtr result = Element::createList();
225 [ + - ]: 6 : ConstElementPtr spec_part = getModuleSpec().getConfigSpec();
226 [ + - ][ + + ]: 6 : if (identifier != "" && identifier != "/") {
[ + - ][ - + ]
[ + + ]
227 [ + - ][ + - ]: 1 : spec_part = find_spec_part(spec_part, identifier);
228 : : }
229 [ + - ]: 6 : spec_name_list(result, spec_part, identifier, recurse);
230 : 6 : return (result);
231 : : }
232 : :
233 : : /// Returns an ElementPtr containing a MapElement with identifier->value
234 : : /// pairs.
235 : : ConstElementPtr
236 : 3 : ConfigData::getFullConfig() const {
237 : 3 : ElementPtr result = Element::createMap();
238 [ + - ][ + - ]: 3 : ConstElementPtr items = getItemList("", true);
239 [ + - ][ + + ]: 87 : BOOST_FOREACH(ConstElementPtr item, items->listValue()) {
[ + - ][ + - ]
[ + + ][ + + ]
240 [ + - ][ + - ]: 21 : result->set(item->stringValue(), getValue(item->stringValue()));
[ + - ][ + - ]
241 : : }
242 : 3 : return (result);
243 : : }
244 : :
245 : : }
246 : 110 : }
|