1 | // |
2 | // XMLConfiguration.cpp |
3 | // |
4 | // Library: Util |
5 | // Package: Configuration |
6 | // Module: XMLConfiguration |
7 | // |
8 | // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Util/XMLConfiguration.h" |
16 | |
17 | |
18 | #ifndef POCO_UTIL_NO_XMLCONFIGURATION |
19 | |
20 | |
21 | #include "Poco/SAX/InputSource.h" |
22 | #include "Poco/DOM/DOMParser.h" |
23 | #include "Poco/DOM/Element.h" |
24 | #include "Poco/DOM/Attr.h" |
25 | #include "Poco/DOM/Text.h" |
26 | #include "Poco/XML/XMLWriter.h" |
27 | #include "Poco/Exception.h" |
28 | #include "Poco/NumberParser.h" |
29 | #include "Poco/NumberFormatter.h" |
30 | #include <set> |
31 | |
32 | |
33 | namespace Poco { |
34 | namespace Util { |
35 | |
36 | |
37 | XMLConfiguration::XMLConfiguration(): |
38 | _delim('.') |
39 | { |
40 | loadEmpty("config" ); |
41 | } |
42 | |
43 | |
44 | XMLConfiguration::XMLConfiguration(char delim): |
45 | _delim(delim) |
46 | { |
47 | loadEmpty("config" ); |
48 | } |
49 | |
50 | |
51 | XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource): |
52 | _delim('.') |
53 | { |
54 | load(pInputSource); |
55 | } |
56 | |
57 | |
58 | XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource, char delim): |
59 | _delim(delim) |
60 | { |
61 | load(pInputSource); |
62 | } |
63 | |
64 | |
65 | XMLConfiguration::XMLConfiguration(std::istream& istr): |
66 | _delim('.') |
67 | { |
68 | load(istr); |
69 | } |
70 | |
71 | |
72 | XMLConfiguration::XMLConfiguration(std::istream& istr, char delim): |
73 | _delim(delim) |
74 | { |
75 | load(istr); |
76 | } |
77 | |
78 | |
79 | XMLConfiguration::XMLConfiguration(const std::string& path): |
80 | _delim('.') |
81 | { |
82 | load(path); |
83 | } |
84 | |
85 | |
86 | XMLConfiguration::XMLConfiguration(const std::string& path, char delim): |
87 | _delim(delim) |
88 | { |
89 | load(path); |
90 | } |
91 | |
92 | |
93 | XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument): |
94 | _delim('.') |
95 | { |
96 | load(pDocument); |
97 | } |
98 | |
99 | |
100 | XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument, char delim): |
101 | _delim(delim) |
102 | { |
103 | load(pDocument); |
104 | } |
105 | |
106 | |
107 | XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode): |
108 | _delim('.') |
109 | { |
110 | load(pNode); |
111 | } |
112 | |
113 | |
114 | XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode, char delim): |
115 | _delim(delim) |
116 | { |
117 | load(pNode); |
118 | } |
119 | |
120 | |
121 | XMLConfiguration::~XMLConfiguration() |
122 | { |
123 | } |
124 | |
125 | |
126 | void XMLConfiguration::load(Poco::XML::InputSource* pInputSource, unsigned long namePoolSize) |
127 | { |
128 | poco_check_ptr (pInputSource); |
129 | |
130 | Poco::XML::DOMParser parser(namePoolSize); |
131 | parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false); |
132 | parser.setFeature(Poco::XML::DOMParser::FEATURE_FILTER_WHITESPACE, true); |
133 | Poco::XML::AutoPtr<Poco::XML::Document> pDoc = parser.parse(pInputSource); |
134 | load(pDoc); |
135 | } |
136 | |
137 | |
138 | void XMLConfiguration::load(Poco::XML::InputSource* pInputSource) |
139 | { |
140 | load(pInputSource, POCO_XML_NAMEPOOL_DEFAULT_SIZE); |
141 | } |
142 | |
143 | |
144 | void XMLConfiguration::load(std::istream& istr) |
145 | { |
146 | Poco::XML::InputSource src(istr); |
147 | load(&src); |
148 | } |
149 | |
150 | |
151 | void XMLConfiguration::load(const std::string& path) |
152 | { |
153 | Poco::XML::InputSource src(path); |
154 | load(&src); |
155 | } |
156 | |
157 | |
158 | void XMLConfiguration::load(const Poco::XML::Document* pDocument) |
159 | { |
160 | poco_check_ptr (pDocument); |
161 | |
162 | _pDocument = Poco::XML::AutoPtr<Poco::XML::Document>(const_cast<Poco::XML::Document*>(pDocument), true); |
163 | _pRoot = Poco::XML::AutoPtr<Poco::XML::Node>(pDocument->documentElement(), true); |
164 | } |
165 | |
166 | |
167 | void XMLConfiguration::load(const Poco::XML::Node* pNode) |
168 | { |
169 | poco_check_ptr (pNode); |
170 | |
171 | if (pNode->nodeType() == Poco::XML::Node::DOCUMENT_NODE) |
172 | { |
173 | load(static_cast<const Poco::XML::Document*>(pNode)); |
174 | } |
175 | else |
176 | { |
177 | _pDocument = Poco::XML::AutoPtr<Poco::XML::Document>(pNode->ownerDocument(), true); |
178 | _pRoot = Poco::XML::AutoPtr<Poco::XML::Node>(const_cast<Poco::XML::Node*>(pNode), true); |
179 | } |
180 | } |
181 | |
182 | |
183 | void XMLConfiguration::loadEmpty(const std::string& rootElementName) |
184 | { |
185 | _pDocument = new Poco::XML::Document; |
186 | _pRoot = _pDocument->createElement(rootElementName); |
187 | _pDocument->appendChild(_pRoot); |
188 | } |
189 | |
190 | |
191 | void XMLConfiguration::save(const std::string& path) const |
192 | { |
193 | Poco::XML::DOMWriter writer; |
194 | writer.setNewLine("\n" ); |
195 | writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT); |
196 | writer.writeNode(path, _pDocument); |
197 | } |
198 | |
199 | |
200 | void XMLConfiguration::save(std::ostream& ostr) const |
201 | { |
202 | Poco::XML::DOMWriter writer; |
203 | writer.setNewLine("\n" ); |
204 | writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT); |
205 | writer.writeNode(ostr, _pDocument); |
206 | } |
207 | |
208 | |
209 | void XMLConfiguration::save(Poco::XML::DOMWriter& writer, const std::string& path) const |
210 | { |
211 | writer.writeNode(path, _pDocument); |
212 | } |
213 | |
214 | |
215 | void XMLConfiguration::save(Poco::XML::DOMWriter& writer, std::ostream& ostr) const |
216 | { |
217 | writer.writeNode(ostr, _pDocument); |
218 | } |
219 | |
220 | |
221 | bool XMLConfiguration::getRaw(const std::string& key, std::string& value) const |
222 | { |
223 | const Poco::XML::Node* pNode = findNode(key); |
224 | if (pNode) |
225 | { |
226 | value = pNode->innerText(); |
227 | return true; |
228 | } |
229 | else return false; |
230 | } |
231 | |
232 | |
233 | void XMLConfiguration::setRaw(const std::string& key, const std::string& value) |
234 | { |
235 | std::string::const_iterator it = key.begin(); |
236 | Poco::XML::Node* pNode = findNode(it, key.end(), _pRoot, true); |
237 | if (pNode) |
238 | { |
239 | unsigned short nodeType = pNode->nodeType(); |
240 | if (Poco::XML::Node::ATTRIBUTE_NODE == nodeType) |
241 | { |
242 | pNode->setNodeValue(value); |
243 | } |
244 | else if (Poco::XML::Node::ELEMENT_NODE == nodeType) |
245 | { |
246 | Poco::XML::Node* pChildNode = pNode->firstChild(); |
247 | if (pChildNode) |
248 | { |
249 | if (Poco::XML::Node::TEXT_NODE == pChildNode->nodeType()) |
250 | { |
251 | pChildNode->setNodeValue(value); |
252 | } |
253 | } |
254 | else |
255 | { |
256 | Poco::AutoPtr<Poco::XML::Node> pText = _pDocument->createTextNode(value); |
257 | pNode->appendChild(pText); |
258 | } |
259 | } |
260 | } |
261 | else throw NotFoundException("Node not found in XMLConfiguration" , key); |
262 | } |
263 | |
264 | |
265 | void XMLConfiguration::enumerate(const std::string& key, Keys& range) const |
266 | { |
267 | using Poco::NumberFormatter; |
268 | |
269 | std::multiset<std::string> keySet; |
270 | const Poco::XML::Node* pNode = findNode(key); |
271 | if (pNode) |
272 | { |
273 | const Poco::XML::Node* pChild = pNode->firstChild(); |
274 | while (pChild) |
275 | { |
276 | if (pChild->nodeType() == Poco::XML::Node::ELEMENT_NODE) |
277 | { |
278 | const std::string& nodeName = pChild->nodeName(); |
279 | int n = (int) keySet.count(nodeName); |
280 | if (n) |
281 | range.push_back(nodeName + "[" + NumberFormatter::format(n) + "]" ); |
282 | else |
283 | range.push_back(nodeName); |
284 | keySet.insert(nodeName); |
285 | } |
286 | pChild = pChild->nextSibling(); |
287 | } |
288 | } |
289 | } |
290 | |
291 | |
292 | void XMLConfiguration::removeRaw(const std::string& key) |
293 | { |
294 | Poco::XML::Node* pNode = findNode(key); |
295 | |
296 | if (pNode) |
297 | { |
298 | if (pNode->nodeType() == Poco::XML::Node::ELEMENT_NODE) |
299 | { |
300 | Poco::XML::Node* pParent = pNode->parentNode(); |
301 | if (pParent) |
302 | { |
303 | pParent->removeChild(pNode); |
304 | } |
305 | } |
306 | else if (pNode->nodeType() == Poco::XML::Node::ATTRIBUTE_NODE) |
307 | { |
308 | Poco::XML::Attr* pAttr = dynamic_cast<Poco::XML::Attr*>(pNode); |
309 | Poco::XML::Element* pOwner = pAttr->ownerElement(); |
310 | if (pOwner) |
311 | { |
312 | pOwner->removeAttributeNode(pAttr); |
313 | } |
314 | } |
315 | } |
316 | } |
317 | |
318 | |
319 | const Poco::XML::Node* XMLConfiguration::findNode(const std::string& key) const |
320 | { |
321 | std::string::const_iterator it = key.begin(); |
322 | Poco::XML::Node* pRoot = const_cast<Poco::XML::Node*>(_pRoot.get()); |
323 | return findNode(it, key.end(), pRoot); |
324 | } |
325 | |
326 | |
327 | Poco::XML::Node* XMLConfiguration::findNode(const std::string& key) |
328 | { |
329 | std::string::const_iterator it = key.begin(); |
330 | Poco::XML::Node* pRoot = const_cast<Poco::XML::Node*>(_pRoot.get()); |
331 | return findNode(it, key.end(), pRoot); |
332 | } |
333 | |
334 | |
335 | Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create) const |
336 | { |
337 | if (pNode && it != end) |
338 | { |
339 | if (*it == '[') |
340 | { |
341 | ++it; |
342 | if (it != end && *it == '@') |
343 | { |
344 | ++it; |
345 | std::string attr; |
346 | while (it != end && *it != ']' && *it != '=') attr += *it++; |
347 | if (it != end && *it == '=') |
348 | { |
349 | ++it; |
350 | std::string value; |
351 | if (it != end && *it == '\'') |
352 | { |
353 | ++it; |
354 | while (it != end && *it != '\'') value += *it++; |
355 | if (it != end) ++it; |
356 | } |
357 | else |
358 | { |
359 | while (it != end && *it != ']') value += *it++; |
360 | } |
361 | if (it != end) ++it; |
362 | return findNode(it, end, findElement(attr, value, pNode), create); |
363 | } |
364 | else |
365 | { |
366 | if (it != end) ++it; |
367 | return findAttribute(attr, pNode, create); |
368 | } |
369 | } |
370 | else |
371 | { |
372 | std::string index; |
373 | while (it != end && *it != ']') index += *it++; |
374 | if (it != end) ++it; |
375 | return findNode(it, end, findElement(Poco::NumberParser::parse(index), pNode, create), create); |
376 | } |
377 | } |
378 | else |
379 | { |
380 | while (it != end && *it == _delim) ++it; |
381 | std::string key; |
382 | while (it != end && *it != _delim && *it != '[') key += *it++; |
383 | return findNode(it, end, findElement(key, pNode, create), create); |
384 | } |
385 | } |
386 | else return pNode; |
387 | } |
388 | |
389 | |
390 | Poco::XML::Node* XMLConfiguration::findElement(const std::string& name, Poco::XML::Node* pNode, bool create) |
391 | { |
392 | Poco::XML::Node* pChild = pNode->firstChild(); |
393 | while (pChild) |
394 | { |
395 | if (pChild->nodeType() == Poco::XML::Node::ELEMENT_NODE && pChild->nodeName() == name) |
396 | return pChild; |
397 | pChild = pChild->nextSibling(); |
398 | } |
399 | if (create) |
400 | { |
401 | Poco::AutoPtr<Poco::XML::Element> pElem = pNode->ownerDocument()->createElement(name); |
402 | pNode->appendChild(pElem); |
403 | return pElem; |
404 | } |
405 | else return 0; |
406 | } |
407 | |
408 | |
409 | Poco::XML::Node* XMLConfiguration::findElement(int index, Poco::XML::Node* pNode, bool create) |
410 | { |
411 | Poco::XML::Node* pRefNode = pNode; |
412 | if (index > 0) |
413 | { |
414 | pNode = pNode->nextSibling(); |
415 | while (pNode) |
416 | { |
417 | if (pNode->nodeName() == pRefNode->nodeName()) |
418 | { |
419 | if (--index == 0) break; |
420 | } |
421 | pNode = pNode->nextSibling(); |
422 | } |
423 | } |
424 | if (!pNode && create) |
425 | { |
426 | if (index == 1) |
427 | { |
428 | Poco::AutoPtr<Poco::XML::Element> pElem = pRefNode->ownerDocument()->createElement(pRefNode->nodeName()); |
429 | pRefNode->parentNode()->appendChild(pElem); |
430 | return pElem; |
431 | } |
432 | else throw Poco::InvalidArgumentException("Element index out of range." ); |
433 | } |
434 | return pNode; |
435 | } |
436 | |
437 | |
438 | Poco::XML::Node* XMLConfiguration::findElement(const std::string& attr, const std::string& value, Poco::XML::Node* pNode) |
439 | { |
440 | Poco::XML::Node* pRefNode = pNode; |
441 | Poco::XML::Element* pElem = dynamic_cast<Poco::XML::Element*>(pNode); |
442 | if (!(pElem && pElem->getAttribute(attr) == value)) |
443 | { |
444 | pNode = pNode->nextSibling(); |
445 | while (pNode) |
446 | { |
447 | if (pNode->nodeName() == pRefNode->nodeName()) |
448 | { |
449 | pElem = dynamic_cast<Poco::XML::Element*>(pNode); |
450 | if (pElem && pElem->getAttribute(attr) == value) break; |
451 | } |
452 | pNode = pNode->nextSibling(); |
453 | } |
454 | } |
455 | return pNode; |
456 | } |
457 | |
458 | |
459 | Poco::XML::Node* XMLConfiguration::findAttribute(const std::string& name, Poco::XML::Node* pNode, bool create) |
460 | { |
461 | Poco::XML::Node* pResult(0); |
462 | Poco::XML::Element* pElem = dynamic_cast<Poco::XML::Element*>(pNode); |
463 | if (pElem) |
464 | { |
465 | pResult = pElem->getAttributeNode(name); |
466 | if (!pResult && create) |
467 | { |
468 | Poco::AutoPtr<Poco::XML::Attr> pAttr = pNode->ownerDocument()->createAttribute(name); |
469 | pElem->setAttributeNode(pAttr); |
470 | return pAttr; |
471 | } |
472 | } |
473 | return pResult; |
474 | } |
475 | |
476 | |
477 | } } // namespace Poco::Util |
478 | |
479 | #endif // POCO_UTIL_NO_XMLCONFIGURATION |
480 | |