1 | // |
2 | // JSONConfiguration.cpp |
3 | // |
4 | // Library: Util |
5 | // Package: JSON |
6 | // Module: JSONConfiguration |
7 | // |
8 | // Copyright (c) 2012, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | |
16 | #include "Poco/Util/JSONConfiguration.h" |
17 | |
18 | |
19 | #ifndef POCO_UTIL_NO_JSONCONFIGURATION |
20 | |
21 | |
22 | #include "Poco/FileStream.h" |
23 | #include "Poco/StringTokenizer.h" |
24 | #include "Poco/JSON/Parser.h" |
25 | #include "Poco/JSON/Query.h" |
26 | #include "Poco/RegularExpression.h" |
27 | #include "Poco/NumberParser.h" |
28 | |
29 | |
30 | namespace Poco { |
31 | namespace Util { |
32 | |
33 | |
34 | JSONConfiguration::JSONConfiguration() : _object(new JSON::Object()) |
35 | { |
36 | } |
37 | |
38 | |
39 | JSONConfiguration::JSONConfiguration(const std::string& path) |
40 | { |
41 | load(path); |
42 | } |
43 | |
44 | |
45 | JSONConfiguration::JSONConfiguration(std::istream& istr) |
46 | { |
47 | load(istr); |
48 | } |
49 | |
50 | |
51 | JSONConfiguration::JSONConfiguration(const JSON::Object::Ptr& object) : _object(object) |
52 | { |
53 | } |
54 | |
55 | |
56 | JSONConfiguration::~JSONConfiguration() |
57 | { |
58 | } |
59 | |
60 | |
61 | void JSONConfiguration::load(const std::string& path) |
62 | { |
63 | Poco::FileInputStream fis(path); |
64 | load(fis); |
65 | } |
66 | |
67 | |
68 | void JSONConfiguration::load(std::istream& istr) |
69 | { |
70 | JSON::Parser parser; |
71 | parser.parse(istr); |
72 | DynamicAny result = parser.result(); |
73 | if ( result.type() == typeid(JSON::Object::Ptr) ) |
74 | { |
75 | _object = result.extract<JSON::Object::Ptr>(); |
76 | } |
77 | } |
78 | |
79 | |
80 | void JSONConfiguration::loadEmpty(const std::string& root) |
81 | { |
82 | _object = new JSON::Object(); |
83 | JSON::Object::Ptr rootObject = new JSON::Object(); |
84 | _object->set(root, rootObject); |
85 | } |
86 | |
87 | |
88 | bool JSONConfiguration::getRaw(const std::string & key, std::string & value) const |
89 | { |
90 | JSON::Query query(_object); |
91 | Poco::DynamicAny result = query.find(key); |
92 | if ( ! result.isEmpty() ) |
93 | { |
94 | value = result.convert<std::string>(); |
95 | return true; |
96 | } |
97 | return false; |
98 | } |
99 | |
100 | |
101 | void JSONConfiguration::getIndexes(std::string& name, std::vector<int>& indexes) |
102 | { |
103 | indexes.clear(); |
104 | |
105 | RegularExpression::MatchVec matches; |
106 | int firstOffset = -1; |
107 | int offset = 0; |
108 | RegularExpression regex("\\[([0-9]+)\\]" ); |
109 | while(regex.match(name, offset, matches) > 0 ) |
110 | { |
111 | if ( firstOffset == -1 ) |
112 | { |
113 | firstOffset = static_cast<int>(matches[0].offset); |
114 | } |
115 | std::string num = name.substr(matches[1].offset, matches[1].length); |
116 | indexes.push_back(NumberParser::parse(num)); |
117 | offset = static_cast<int>(matches[0].offset + matches[0].length); |
118 | } |
119 | |
120 | if ( firstOffset != -1 ) |
121 | { |
122 | name = name.substr(0, firstOffset); |
123 | } |
124 | } |
125 | |
126 | |
127 | JSON::Object::Ptr JSONConfiguration::findStart(const std::string& key, std::string& lastPart) |
128 | { |
129 | JSON::Object::Ptr currentObject = _object; |
130 | |
131 | StringTokenizer tokenizer(key, "." ); |
132 | lastPart = tokenizer[tokenizer.count() - 1]; |
133 | |
134 | for(int i = 0; i < tokenizer.count() - 1; ++i) |
135 | { |
136 | std::vector<int> indexes; |
137 | std::string name = tokenizer[i]; |
138 | getIndexes(name, indexes); |
139 | |
140 | DynamicAny result = currentObject->get(name); |
141 | |
142 | if ( result.isEmpty() ) // Not found |
143 | { |
144 | if ( indexes.empty() ) // We want an object, create it |
145 | { |
146 | JSON::Object::Ptr newObject = new JSON::Object(); |
147 | currentObject->set(name, newObject); |
148 | currentObject = newObject; |
149 | } |
150 | else // We need an array |
151 | { |
152 | JSON::Array::Ptr newArray; |
153 | JSON::Array::Ptr parentArray; |
154 | JSON::Array::Ptr topArray; |
155 | for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end(); ++it) |
156 | { |
157 | newArray = new JSON::Array(); |
158 | if ( topArray.isNull() ) |
159 | { |
160 | topArray = newArray; |
161 | } |
162 | |
163 | if ( ! parentArray.isNull() ) |
164 | { |
165 | parentArray->add(newArray); |
166 | } |
167 | |
168 | for(int j = 0; j <= *it - 1; ++j) |
169 | { |
170 | Poco::DynamicAny nullValue; |
171 | newArray->add(nullValue); |
172 | } |
173 | |
174 | parentArray = newArray; |
175 | } |
176 | |
177 | currentObject->set(name, topArray); |
178 | currentObject = new JSON::Object(); |
179 | newArray->add(currentObject); |
180 | } |
181 | } |
182 | else // We have a value |
183 | { |
184 | if ( indexes.empty() ) // We want an object |
185 | { |
186 | if ( result.type() == typeid(JSON::Object::Ptr) ) |
187 | { |
188 | currentObject = result.extract<JSON::Object::Ptr>(); |
189 | } |
190 | else |
191 | { |
192 | throw SyntaxException("Expected a JSON object" ); |
193 | } |
194 | } |
195 | else |
196 | { |
197 | if ( result.type() == typeid(JSON::Array::Ptr) ) |
198 | { |
199 | JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>(); |
200 | |
201 | for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it) |
202 | { |
203 | JSON::Array::Ptr currentArray = arr; |
204 | arr = arr->getArray(*it); |
205 | if ( arr.isNull() ) |
206 | { |
207 | arr = new JSON::Array(); |
208 | currentArray->add(arr); |
209 | } |
210 | } |
211 | |
212 | result = arr->get(*indexes.rbegin()); |
213 | if ( result.isEmpty() ) // Index doesn't exist |
214 | { |
215 | JSON::Object::Ptr newObject = new JSON::Object(); |
216 | arr->add(newObject); |
217 | currentObject = newObject; |
218 | } |
219 | else // Index is available |
220 | { |
221 | if ( result.type() == typeid(JSON::Object::Ptr) ) |
222 | { |
223 | currentObject = result.extract<JSON::Object::Ptr>(); |
224 | } |
225 | else |
226 | { |
227 | throw SyntaxException("Expected a JSON object" ); |
228 | } |
229 | } |
230 | } |
231 | else |
232 | { |
233 | throw SyntaxException("Expected a JSON array" ); |
234 | } |
235 | } |
236 | } |
237 | } |
238 | return currentObject; |
239 | } |
240 | |
241 | |
242 | void JSONConfiguration::setValue(const std::string& key, const Poco::DynamicAny& value) |
243 | { |
244 | |
245 | std::string sValue; |
246 | |
247 | value.convert<std::string>(sValue); |
248 | KeyValue kv(key, sValue); |
249 | |
250 | if (eventsEnabled()) |
251 | { |
252 | propertyChanging(this, kv); |
253 | } |
254 | |
255 | std::string lastPart; |
256 | JSON::Object::Ptr parentObject = findStart(key, lastPart); |
257 | |
258 | std::vector<int> indexes; |
259 | getIndexes(lastPart, indexes); |
260 | |
261 | if ( indexes.empty() ) // No Array |
262 | { |
263 | parentObject->set(lastPart, value); |
264 | } |
265 | else |
266 | { |
267 | DynamicAny result = parentObject->get(lastPart); |
268 | if ( result.isEmpty() ) |
269 | { |
270 | result = JSON::Array::Ptr(new JSON::Array()); |
271 | parentObject->set(lastPart, result); |
272 | } |
273 | else if ( result.type() != typeid(JSON::Array::Ptr) ) |
274 | { |
275 | throw SyntaxException("Expected a JSON array" ); |
276 | } |
277 | |
278 | JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>(); |
279 | for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it) |
280 | { |
281 | JSON::Array::Ptr nextArray = arr->getArray(*it); |
282 | if ( nextArray.isNull() ) |
283 | { |
284 | for(int i = static_cast<int>(arr->size()); i <= *it; ++i) |
285 | { |
286 | Poco::DynamicAny nullValue; |
287 | arr->add(nullValue); |
288 | } |
289 | nextArray = new JSON::Array(); |
290 | arr->add(nextArray); |
291 | } |
292 | arr = nextArray; |
293 | } |
294 | arr->set(indexes.back(), value); |
295 | } |
296 | |
297 | if (eventsEnabled()) |
298 | { |
299 | propertyChanged(this, kv); |
300 | } |
301 | } |
302 | |
303 | |
304 | void JSONConfiguration::setString(const std::string& key, const std::string& value) |
305 | { |
306 | setValue(key, value); |
307 | } |
308 | |
309 | |
310 | void JSONConfiguration::setRaw(const std::string& key, const std::string& value) |
311 | { |
312 | setValue(key, value); |
313 | } |
314 | |
315 | |
316 | void JSONConfiguration::setInt(const std::string& key, int value) |
317 | { |
318 | setValue(key, value); |
319 | } |
320 | |
321 | |
322 | void JSONConfiguration::setBool(const std::string& key, bool value) |
323 | { |
324 | setValue(key, value); |
325 | } |
326 | |
327 | |
328 | void JSONConfiguration::setDouble(const std::string& key, double value) |
329 | { |
330 | setValue(key, value); |
331 | } |
332 | |
333 | |
334 | void JSONConfiguration::enumerate(const std::string& key, Keys& range) const |
335 | { |
336 | JSON::Query query(_object); |
337 | Poco::DynamicAny result = query.find(key); |
338 | if ( result.type() == typeid(JSON::Object::Ptr) ) |
339 | { |
340 | JSON::Object::Ptr object = result.extract<JSON::Object::Ptr>(); |
341 | object->getNames(range); |
342 | } |
343 | } |
344 | |
345 | |
346 | void JSONConfiguration::save(std::ostream& ostr, unsigned int indent) const |
347 | { |
348 | _object->stringify(ostr, indent); |
349 | } |
350 | |
351 | |
352 | void JSONConfiguration::removeRaw(const std::string& key) |
353 | |
354 | { |
355 | |
356 | std::string lastPart; |
357 | JSON::Object::Ptr parentObject = findStart(key, lastPart); |
358 | std::vector<int> indexes; |
359 | getIndexes(lastPart, indexes); |
360 | |
361 | if ( indexes.empty() ) // No Array |
362 | { |
363 | parentObject->remove(lastPart); |
364 | } |
365 | else |
366 | { |
367 | DynamicAny result = parentObject->get(lastPart); |
368 | if (!result.isEmpty() && result.type() == typeid(JSON::Array::Ptr)) |
369 | { |
370 | |
371 | JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>(); |
372 | for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it) |
373 | { |
374 | arr = arr->getArray(*it); |
375 | } |
376 | arr->remove(indexes.back()); |
377 | } |
378 | } |
379 | |
380 | } |
381 | |
382 | |
383 | } } // namespace Poco::Util |
384 | |
385 | |
386 | #endif // POCO_UTIL_NO_JSONCONFIGURATION |
387 | |