| 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 | |
| 36 | namespace Poco { |
| 37 | namespace Util { |
| 38 | |
| 39 | |
| 40 | PropertyListConfiguration::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 | |
| 51 | PropertyListConfiguration::PropertyListConfiguration(const std::string& path) |
| 52 | { |
| 53 | load(path); |
| 54 | } |
| 55 | |
| 56 | |
| 57 | PropertyListConfiguration::PropertyListConfiguration(std::istream& istr) |
| 58 | { |
| 59 | load(istr); |
| 60 | } |
| 61 | |
| 62 | PropertyListConfiguration::~PropertyListConfiguration() |
| 63 | { |
| 64 | } |
| 65 | |
| 66 | |
| 67 | void 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 | |
| 79 | void 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 | |
| 92 | void PropertyListConfiguration::save(const std::string& path) const |
| 93 | { |
| 94 | std::ofstream out(path); |
| 95 | save(out); |
| 96 | } |
| 97 | |
| 98 | void 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 | |
| 106 | void 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 | |
| 123 | void 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 | |
| 140 | void 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 | |
| 155 | void 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 | |
| 172 | bool 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 | |
| 193 | bool PropertyListConfiguration::(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 | |
| 211 | void 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 | |
| 223 | void 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 | |
| 250 | void 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 | |
| 262 | Poco::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 | |
| 268 | const 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 | |
| 274 | Poco::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 | |