1//
2// PropertyListConfiguration.cpp
3//
4//
5// Library: Util
6// Package: Configuration
7// Module: PropertyListConfiguration
8//
9// Copyright (c) 2017-2018, Applied Informatics Software Engineering GmbH.
10// and Contributors.
11//
12// SPDX-License-Identifier: BSL-1.0
13//
14
15
16#include "Poco/Util/PropertyListConfiguration.h"
17
18
19#ifndef POCO_UTIL_NO_XMLCONFIGURATION
20
21#include "Poco/DOM/DOMImplementation.h"
22#include "Poco/DOM/DocumentType.h"
23#include "Poco/SAX/InputSource.h"
24#include "Poco/DOM/DOMParser.h"
25#include "Poco/DOM/Element.h"
26#include "Poco/DOM/Text.h"
27#include "Poco/SAX/XMLReader.h"
28#include "Poco/XML/XMLWriter.h"
29#include "Poco/NumberFormatter.h"
30#include "Poco/DOM/DOMWriter.h"
31#include "Poco/Base64Encoder.h"
32#include "Poco/Base64Decoder.h"
33#include <fstream>
34#include <sstream>
35
36namespace Poco {
37namespace Util {
38
39
40PropertyListConfiguration::PropertyListConfiguration()
41{
42 Poco::AutoPtr<Poco::XML::DocumentType> doctype = Poco::XML::DOMImplementation::instance().createDocumentType("plist", "-//Apple//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd");
43 _pDocument = new Poco::XML::Document(doctype);
44 _pRoot = _pDocument->createElement("plist");
45 _pRoot->setAttribute("version", "1.0");
46 _pDocument->appendChild(_pRoot);
47 Poco::AutoPtr<Poco::XML::Element> pElem = _pDocument->createElement("dict");
48 _pRoot->appendChild(pElem);
49}
50
51PropertyListConfiguration::PropertyListConfiguration(const std::string& path)
52{
53 load(path);
54}
55
56
57PropertyListConfiguration::PropertyListConfiguration(std::istream& istr)
58{
59 load(istr);
60}
61
62PropertyListConfiguration::~PropertyListConfiguration()
63{
64}
65
66
67void PropertyListConfiguration::load(const std::string& path)
68{
69 Poco::XML::InputSource src(path);
70
71 Poco::XML::DOMParser parser;
72 parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false);
73 parser.setFeature(Poco::XML::DOMParser::FEATURE_FILTER_WHITESPACE, true);
74
75 _pDocument = parser.parse(&src);
76 _pRoot = Poco::XML::AutoPtr<Poco::XML::Element>(_pDocument->documentElement(), true);
77}
78
79void PropertyListConfiguration::load(std::istream& istr)
80{
81 Poco::XML::InputSource src(istr);
82
83 Poco::XML::DOMParser parser;
84 parser.setFeature(Poco::XML::XMLReader::FEATURE_NAMESPACES, false);
85 parser.setFeature(Poco::XML::DOMParser::FEATURE_FILTER_WHITESPACE, true);
86
87 _pDocument = parser.parse(&src);
88 _pRoot = Poco::XML::AutoPtr<Poco::XML::Element>(_pDocument->documentElement(), true);
89
90}
91
92void PropertyListConfiguration::save(const std::string& path) const
93{
94 std::ofstream out(path);
95 save(out);
96}
97
98void PropertyListConfiguration::save(std::ostream& ostr) const
99{
100 Poco::XML::DOMWriter writer;
101 writer.setNewLine("\n");
102 writer.setOptions(Poco::XML::XMLWriter::WRITE_XML_DECLARATION | Poco::XML::XMLWriter::PRETTY_PRINT);
103 writer.writeNode(ostr, _pDocument);
104}
105
106void PropertyListConfiguration::setInt(const std::string& key, int value)
107{
108 // set this, so it fires event
109 AbstractConfiguration::setInt(key, value);
110
111 // find the node we just inserted
112 Poco::XML::Node* thekey = findNode(key, true);
113 Poco::XML::Node* thevalue = thekey->nextSibling();
114
115 // change the value to an integer element
116 Poco::AutoPtr<Poco::XML::Node> pElem = _pDocument->createElement("integer");
117 thevalue->parentNode()->replaceChild(pElem, thevalue);
118 thevalue = pElem;
119 Poco::AutoPtr<Poco::XML::Node> pText = _pDocument->createTextNode(Poco::NumberFormatter::format(value));
120 thevalue->appendChild(pText);
121}
122
123void PropertyListConfiguration::setDouble(const std::string& key, double value)
124{
125 // set this, so it fires event
126 AbstractConfiguration::setDouble(key, value);
127
128 // find the node we just inserted
129 Poco::XML::Node* thekey = findNode(key, true);
130 Poco::XML::Node* thevalue = thekey->nextSibling();
131
132 // change the value to an integer element
133 Poco::AutoPtr<Poco::XML::Node> pElem = _pDocument->createElement("real");
134 thevalue->parentNode()->replaceChild(pElem, thevalue);
135 thevalue = pElem;
136 Poco::AutoPtr<Poco::XML::Node> pText = _pDocument->createTextNode(Poco::NumberFormatter::format(value));
137 thevalue->appendChild(pText);
138}
139
140void PropertyListConfiguration::setBool(const std::string& key, bool value)
141{
142 // set this, so it fires event
143 AbstractConfiguration::setBool(key, value);
144
145 // find the node we just inserted
146 Poco::XML::Node* thekey = findNode(key, true);
147 Poco::XML::Node* thevalue = thekey->nextSibling();
148
149 // change the value to a boolean element
150 Poco::AutoPtr<Poco::XML::Node> pElem = _pDocument->createElement(value ? "true" : "false");
151 thevalue->parentNode()->replaceChild(pElem, thevalue);
152 thevalue = pElem;
153}
154
155void PropertyListConfiguration::setData(const std::string& key, std::istream &istr)
156{
157 Poco::XML::Node* thekey = findNode(key, true);
158 Poco::XML::Node* thevalue = thekey->nextSibling();
159
160 Poco::AutoPtr<Poco::XML::Node> pElem = _pDocument->createElement("data");
161 thevalue->parentNode()->replaceChild(pElem, thevalue);
162 thevalue = pElem;
163
164 std::ostringstream ostr;
165 Poco::Base64Encoder encoder(ostr);
166 encoder << istr.rdbuf();
167 encoder.close();
168 Poco::AutoPtr<Poco::XML::Node> pText = _pDocument->createTextNode(ostr.str());
169 thevalue->appendChild(pText);
170}
171
172bool PropertyListConfiguration::getData(const std::string& key, std::ostream &ostr)
173{
174 const Poco::XML::Node* thekey = findNode(key);
175
176 if (thekey)
177 {
178 Poco::XML::Node* thevalue = thekey->nextSibling();
179 if (thevalue->firstChild())
180 {
181 std::stringstream strs(thevalue->firstChild()->nodeValue());
182 Poco::Base64Decoder decoder(strs);
183 ostr << decoder.rdbuf();
184 }
185 else
186 return false;
187 return true;
188 }
189
190 return false;
191}
192
193bool PropertyListConfiguration::getRaw(const std::string& key, std::string& value) const
194{
195 const Poco::XML::Node* thekey = findNode(key);
196
197 if (thekey)
198 {
199 Poco::XML::Node* thevalue = thekey->nextSibling();
200 if (thevalue->firstChild())
201 value = thevalue->firstChild()->nodeValue();
202 else
203 value = thevalue->nodeName(); // the case for true/false
204 return true;
205 }
206
207 return false;
208}
209
210
211void PropertyListConfiguration::setRaw(const std::string& key, const std::string& value)
212{
213 Poco::XML::Node* thekey = findNode(key, true);
214 Poco::XML::Node* thevalue = thekey->nextSibling();
215
216 Poco::AutoPtr<Poco::XML::Node> pElem = _pDocument->createElement("string");
217 thevalue->parentNode()->replaceChild(pElem, thevalue);
218 thevalue = pElem;
219 Poco::AutoPtr<Poco::XML::Node> pText = _pDocument->createTextNode(value);
220 thevalue->appendChild(pText);
221}
222
223void PropertyListConfiguration::enumerate(const std::string& key, Keys& range) const
224{
225 const Poco::XML::Node* thekey = _pRoot->firstChild();
226
227 if (key.length() != 0)
228 {
229 thekey = findNode(key);
230 if (!thekey)
231 return;
232
233 thekey = thekey->nextSibling();
234 }
235
236 if (thekey && thekey->nodeName() == "dict")
237 {
238 Poco::XML::Node* pChild = thekey->firstChild();
239 while (pChild != NULL)
240 {
241 if (pChild->nodeName() == "key")
242 {
243 range.push_back(pChild->firstChild()->nodeValue());
244 }
245 pChild = pChild->nextSibling();
246 }
247 }
248}
249
250void PropertyListConfiguration::removeRaw(const std::string& key)
251{
252 Poco::XML::Node* thekey = findNode(key);
253
254 if (thekey)
255 {
256 Poco::XML::Node* thevalue = thekey->nextSibling();
257 thekey->parentNode()->removeChild(thekey);
258 thevalue->parentNode()->removeChild(thevalue);
259 }
260}
261
262Poco::XML::Node* PropertyListConfiguration::findNode(const std::string& key, bool create)
263{
264 Poco::XML::Node* dict = const_cast<Poco::XML::Node*>(_pRoot->firstChild());
265 return findNode(key, dict, create);
266}
267
268const Poco::XML::Node* PropertyListConfiguration::findNode(const std::string& key, bool create) const
269{
270 Poco::XML::Node* dict = const_cast<Poco::XML::Node*>(_pRoot->firstChild());
271 return findNode(key, dict, create);
272}
273
274Poco::XML::Node* PropertyListConfiguration::findNode(const std::string& key, Poco::XML::Node* dict, bool create)
275{
276 if (!dict) return NULL;
277 if (dict->nodeName() != "dict") return NULL;
278
279 std::string firstkey = key;
280 std::string::size_type dot = key.find('.');
281 if (dot != std::string::npos)
282 {
283 firstkey = key.substr(0, dot);
284 }
285
286 Poco::XML::Node* pNode = dict->firstChild();
287 while (pNode != NULL)
288 {
289 if (pNode->nodeName() == "key" && pNode->firstChild()->nodeValue() == firstkey)
290 {
291 if (dot != std::string::npos) // keep searching
292 return findNode(key.substr(dot +1), pNode->nextSibling(), create);
293 else
294 return pNode;
295 }
296
297 pNode = pNode->nextSibling();
298 }
299
300 if (create)
301 {
302 Poco::AutoPtr<Poco::XML::Element> pElem, pNew;
303 Poco::AutoPtr<Poco::XML::Node> pText;
304
305 pNew = pElem = dict->ownerDocument()->createElement("key");
306 dict->appendChild(pElem);
307 pText = dict->ownerDocument()->createTextNode(firstkey);
308 pElem->appendChild(pText);
309
310 if (dot != std::string::npos)
311 {
312 pElem = dict->ownerDocument()->createElement("dict");
313 dict->appendChild(pElem);
314
315 return findNode(key.substr(dot + 1), pElem, true);
316 }
317 else
318 {
319 pElem = dict->ownerDocument()->createElement("string");
320 dict->appendChild(pElem);
321 pText = dict->ownerDocument()->createTextNode("");
322 pElem->appendChild(pText);
323
324 return pNew;
325 }
326 }
327
328 return NULL;
329}
330
331} } // namespace Poco::Util
332
333#endif // POCO_UTIL_NO_XMLCONFIGURATION
334