1 //////////////////////////////////////////////////////////////////////////////
2 //////////////////////////////////////////////////////////////////////////////
3 //
4 // copyright : (C) 2014 Eran Ifrah
5 // file name : json_node.cpp
6 //
7 // -------------------------------------------------------------------------
8 //
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 //////////////////////////////////////////////////////////////////////////////
15 //////////////////////////////////////////////////////////////////////////////
16
17 #include "json_node.h"
18 #include <stdlib.h>
19 #include <wx/filename.h>
20 #include <wx/ffile.h>
21 #include <wx/tokenzr.h>
22 //-#include "clFontHelper.h"
23
JSONRoot(const wxString & text)24 JSONRoot::JSONRoot(const wxString& text)
25 : _json(NULL)
26 {
27 _json = cJSON_Parse(text.mb_str(wxConvUTF8).data());
28 }
29
JSONRoot(int type)30 JSONRoot::JSONRoot(int type)
31 : _json(NULL)
32 {
33 if(type == cJSON_Array)
34 _json = cJSON_CreateArray();
35 else
36 _json = cJSON_CreateObject();
37 }
38
JSONRoot(const wxFileName & filename)39 JSONRoot::JSONRoot(const wxFileName& filename)
40 : _json(NULL)
41 {
42 wxString content;
43 wxFFile fp(filename.GetFullPath(), wxT("rb"));
44 if(fp.IsOpened()) {
45 if(fp.ReadAll(&content, wxConvUTF8)) {
46 _json = cJSON_Parse(content.mb_str(wxConvUTF8).data());
47 }
48 }
49
50 if(!_json) {
51 _json = cJSON_CreateObject();
52 }
53 }
54
~JSONRoot()55 JSONRoot::~JSONRoot()
56 {
57 if(_json) {
58 cJSON_Delete(_json);
59 _json = NULL;
60 }
61 }
62
save(const wxFileName & fn) const63 void JSONRoot::save(const wxFileName& fn) const
64 {
65 #if defined(LOGGING)
66 wxString filename = fn.GetFullPath(); //debugging
67 #endif
68 wxFFile fp(fn.GetFullPath(), wxT("w+b"));
69 if(fp.IsOpened()) {
70 fp.Write(toElement().format(), wxConvUTF8);
71 fp.Close();
72 }
73 }
74
toElement() const75 JSONElement JSONRoot::toElement() const
76 {
77 if(!_json) {
78 return JSONElement(NULL);
79 }
80 return JSONElement(_json);
81 }
82
errorString() const83 wxString JSONRoot::errorString() const { return _errorString; }
84
namedObject(const wxString & name) const85 JSONElement JSONElement::namedObject(const wxString& name) const
86 {
87 if(!_json) {
88 return JSONElement(NULL);
89 }
90
91 cJSON* obj = cJSON_GetObjectItem(_json, name.mb_str(wxConvUTF8).data());
92 if(!obj) {
93 return JSONElement(NULL);
94 }
95 return JSONElement(obj);
96 }
97
clear()98 void JSONRoot::clear()
99 {
100 int type = cJSON_Object;
101 if(_json) {
102 type = _json->type;
103 cJSON_Delete(_json);
104 _json = NULL;
105 }
106 if(type == cJSON_Array)
107 _json = cJSON_CreateArray();
108 else
109 _json = cJSON_CreateObject();
110 }
111
112 ///////////////////////////////////////////////////////////
113 ///////////////////////////////////////////////////////////
JSONElement(cJSON * json)114 JSONElement::JSONElement(cJSON* json)
115 : _json(json)
116 , _type(-1)
117 , _walker(NULL)
118 {
119 if(_json) {
120 _name = wxString(_json->string, wxConvUTF8);
121 _type = _json->type;
122 }
123 }
124
JSONElement(const wxString & name,const wxVariant & val,int type)125 JSONElement::JSONElement(const wxString& name, const wxVariant& val, int type)
126 : _json(NULL)
127 , _type(type)
128 , _walker(NULL)
129 {
130 _value = val;
131 _name = name;
132 }
133
arrayItem(int pos) const134 JSONElement JSONElement::arrayItem(int pos) const
135 {
136 if(!_json) {
137 return JSONElement(NULL);
138 }
139
140 if(_json->type != cJSON_Array) return JSONElement(NULL);
141
142 int size = cJSON_GetArraySize(_json);
143 if(pos >= size) return JSONElement(NULL);
144
145 return JSONElement(cJSON_GetArrayItem(_json, pos));
146 }
147
isNull() const148 bool JSONElement::isNull() const
149 {
150 if(!_json) {
151 return false;
152 }
153 return _json->type == cJSON_NULL;
154 }
155
toBool(bool defaultValue) const156 bool JSONElement::toBool(bool defaultValue) const
157 {
158 if(!_json) {
159 return defaultValue;
160 }
161
162 if(!isBool()) {
163 return defaultValue;
164 }
165
166 return _json->type == cJSON_True;
167 }
168
toString(const wxString & defaultValue) const169 wxString JSONElement::toString(const wxString& defaultValue) const
170 {
171 if(!_json) {
172 return defaultValue;
173 }
174
175 if(_json->type != cJSON_String) {
176 return defaultValue;
177 }
178
179 return wxString(_json->valuestring, wxConvUTF8);
180 }
181
isBool() const182 bool JSONElement::isBool() const
183 {
184 if(!_json) {
185 return false;
186 }
187
188 return _json->type == cJSON_True || _json->type == cJSON_False;
189 }
190
isString() const191 bool JSONElement::isString() const
192 {
193 if(!_json) {
194 return false;
195 }
196
197 return _json->type == cJSON_String;
198 }
199
append(const JSONElement & element)200 void JSONElement::append(const JSONElement& element)
201 {
202 if(!_json) {
203 return;
204 }
205
206 switch(element.getType()) {
207 case cJSON_False:
208 cJSON_AddFalseToObject(_json, element.getName().mb_str(wxConvUTF8).data());
209 break;
210
211 case cJSON_True:
212 cJSON_AddTrueToObject(_json, element.getName().mb_str(wxConvUTF8).data());
213 break;
214
215 case cJSON_NULL:
216 cJSON_AddNullToObject(_json, element.getName().mb_str(wxConvUTF8).data());
217 break;
218
219 case cJSON_Number:
220 cJSON_AddNumberToObject(_json, element.getName().mb_str(wxConvUTF8).data(), element.getValue().GetLong());
221 break;
222
223 case cJSON_String:
224 cJSON_AddStringToObject(_json, element.getName().mb_str(wxConvUTF8).data(),
225 element.getValue().GetString().mb_str(wxConvUTF8).data());
226 break;
227
228 case cJSON_Array:
229 case cJSON_Object:
230 cJSON_AddItemToObject(_json, element.getName().mb_str(wxConvUTF8).data(), element._json);
231 break;
232 }
233 }
234
arrayAppend(const JSONElement & element)235 void JSONElement::arrayAppend(const JSONElement& element)
236 {
237 if(!_json) {
238 return;
239 }
240
241 cJSON* p = NULL;
242 switch(element.getType()) {
243 case cJSON_False:
244 p = cJSON_CreateFalse();
245 break;
246
247 case cJSON_True:
248 p = cJSON_CreateTrue();
249 break;
250
251 case cJSON_NULL:
252 p = cJSON_CreateNull();
253 break;
254
255 case cJSON_Number:
256 p = cJSON_CreateNumber(element.getValue().GetDouble());
257 break;
258
259 case cJSON_String:
260 p = cJSON_CreateString(element.getValue().GetString().mb_str(wxConvUTF8).data());
261 break;
262 case cJSON_Array:
263 case cJSON_Object:
264 p = element._json;
265 break;
266 }
267 if(p) {
268 cJSON_AddItemToArray(_json, p);
269 }
270 }
271
createArray(const wxString & name)272 JSONElement JSONElement::createArray(const wxString& name)
273 {
274 JSONElement arr(cJSON_CreateArray());
275 arr.setName(name);
276 arr.setType(cJSON_Array);
277 return arr;
278 }
279
createObject(const wxString & name)280 JSONElement JSONElement::createObject(const wxString& name)
281 {
282 JSONElement obj(cJSON_CreateObject());
283 obj.setName(name);
284 obj.setType(cJSON_Object);
285 return obj;
286 }
287
FormatRawString(bool formatted) const288 char* JSONElement::FormatRawString(bool formatted) const
289 {
290 if(!_json) {
291 return NULL;
292 }
293
294 if(formatted) {
295 return cJSON_Print(_json);
296
297 } else {
298 return cJSON_PrintUnformatted(_json);
299 }
300 }
301
format() const302 wxString JSONElement::format() const
303 {
304 if(!_json) {
305 return wxT("");
306 }
307
308 char* p = cJSON_Print(_json);
309 wxString output(p, wxConvUTF8);
310 free(p);
311 return output;
312 }
313
toInt(int defaultVal) const314 int JSONElement::toInt(int defaultVal) const
315 {
316 if(!_json) {
317 return defaultVal;
318 }
319
320 if(_json->type != cJSON_Number) {
321 return defaultVal;
322 }
323
324 return _json->valueint;
325 }
326
toSize_t(size_t defaultVal) const327 size_t JSONElement::toSize_t(size_t defaultVal) const
328 {
329 if(!_json) {
330 return defaultVal;
331 }
332
333 if(_json->type != cJSON_Number) {
334 return defaultVal;
335 }
336
337 return (size_t)_json->valueint;
338 }
339
toDouble(double defaultVal) const340 double JSONElement::toDouble(double defaultVal) const
341 {
342 if(!_json) {
343 return defaultVal;
344 }
345
346 if(_json->type != cJSON_Number) {
347 return defaultVal;
348 }
349
350 return _json->valuedouble;
351 }
352
arraySize() const353 int JSONElement::arraySize() const
354 {
355 if(!_json) {
356 return 0;
357 }
358
359 if(_json->type != cJSON_Array) return 0;
360
361 return cJSON_GetArraySize(_json);
362 }
363
addProperty(const wxString & name,bool value)364 JSONElement& JSONElement::addProperty(const wxString& name, bool value)
365 {
366 if(value) {
367 append(JSONElement(name, value, cJSON_True));
368
369 } else {
370 append(JSONElement(name, value, cJSON_False));
371 }
372 return *this;
373 }
374
addProperty(const wxString & name,const wxString & value)375 JSONElement& JSONElement::addProperty(const wxString& name, const wxString& value)
376 {
377 append(JSONElement(name, value, cJSON_String));
378 return *this;
379 }
380
addProperty(const wxString & name,const wxChar * value)381 JSONElement& JSONElement::addProperty(const wxString& name, const wxChar* value)
382 {
383 append(JSONElement(name, wxString(value), cJSON_String));
384 return *this;
385 }
386
addProperty(const wxString & name,long value)387 JSONElement& JSONElement::addProperty(const wxString& name, long value)
388 {
389 append(JSONElement(name, value, cJSON_Number));
390 return *this;
391 }
392
addProperty(const wxString & name,const wxArrayString & arr)393 JSONElement& JSONElement::addProperty(const wxString& name, const wxArrayString& arr)
394 {
395 JSONElement arrEle = JSONElement::createArray(name);
396 for(size_t i = 0; i < arr.GetCount(); i++) {
397 arrEle.arrayAppend(arr.Item(i));
398 }
399 append(arrEle);
400 return *this;
401 }
402
arrayAppend(const wxString & value)403 void JSONElement::arrayAppend(const wxString& value) { arrayAppend(JSONElement(wxT(""), value, cJSON_String)); }
404
toArrayString(const wxArrayString & defaultValue) const405 wxArrayString JSONElement::toArrayString(const wxArrayString& defaultValue) const
406 {
407 if(!_json) {
408 return defaultValue;
409 }
410
411 if(_json->type != cJSON_Array) {
412 return defaultValue;
413 }
414
415 wxArrayString arr;
416 for(int i = 0; i < arraySize(); i++) {
417 arr.Add(arrayItem(i).toString());
418 }
419 return arr;
420 }
421
hasNamedObject(const wxString & name) const422 bool JSONElement::hasNamedObject(const wxString& name) const
423 {
424 if(!_json) {
425 return false;
426 }
427
428 cJSON* obj = cJSON_GetObjectItem(_json, name.mb_str(wxConvUTF8).data());
429 return obj != NULL;
430 }
431
addProperty(const wxString & name,const wxPoint & pt)432 JSONElement& JSONElement::addProperty(const wxString& name, const wxPoint& pt)
433 {
434 wxString szStr;
435 szStr << pt.x << _T(",") << pt.y;
436 return addProperty(name, szStr);
437 }
438
addProperty(const wxString & name,const wxSize & sz)439 JSONElement& JSONElement::addProperty(const wxString& name, const wxSize& sz)
440 {
441 wxString szStr;
442 szStr << sz.x << _T(",") << sz.y;
443 return addProperty(name, szStr);
444 }
445
addProperty(const wxString & name,const JSONElement & element)446 JSONElement& JSONElement::addProperty(const wxString& name, const JSONElement& element)
447 {
448 if(!_json) {
449 return *this;
450 }
451 cJSON_AddItemToObject(_json, name.mb_str(wxConvUTF8).data(), element._json);
452 return *this;
453 }
454
toPoint() const455 wxPoint JSONElement::toPoint() const
456 {
457 if(!_json) {
458 return wxDefaultPosition;
459 }
460
461 if(_json->type != cJSON_String) {
462 return wxDefaultPosition;
463 }
464
465 wxString str = wxString::Format(_T("%s"), _json->valuestring);
466 wxString x = str.BeforeFirst(',');
467 wxString y = str.AfterFirst(',');
468
469 long nX(-1), nY(-1);
470 if(!x.ToLong(&nX) || !y.ToLong(&nY)) return wxDefaultPosition;
471
472 return wxPoint(nX, nY);
473 }
474
toColour(const wxColour & defaultColour) const475 wxColour JSONElement::toColour(const wxColour& defaultColour) const
476 {
477 if(!_json) {
478 return defaultColour;
479 }
480
481 if(_json->type != cJSON_String) {
482 return defaultColour;
483 }
484
485 //-return wxColour(_json->valuestring); //(wx28 2019/04/18)-
486 wxString jsonValueStr(_json->valuestring, wxConvUTF8); //(wx28 2019/04/20)+
487 return wxColour(jsonValueStr); //(wx28 2019/04/20)+
488 }
489
toSize() const490 wxSize JSONElement::toSize() const
491 {
492 wxPoint pt = toPoint();
493 return wxSize(pt.x, pt.y);
494 }
495
removeProperty(const wxString & name)496 void JSONElement::removeProperty(const wxString& name)
497 {
498 // delete child property
499 if(!_json) {
500 return;
501 }
502 cJSON_DeleteItemFromObject(_json, name.mb_str(wxConvUTF8).data());
503 }
504
addProperty(const wxString & name,const wxStringMap_t & stringMap)505 JSONElement& JSONElement::addProperty(const wxString& name, const wxStringMap_t& stringMap)
506 {
507 if(!_json) return *this;
508
509 JSONElement arr = JSONElement::createArray(name);
510 wxStringMap_t::const_iterator iter = stringMap.begin();
511 for(; iter != stringMap.end(); ++iter) {
512 JSONElement obj = JSONElement::createObject();
513 obj.addProperty(_T("key"), iter->first);
514 obj.addProperty(_T("value"), iter->second);
515 arr.arrayAppend(obj);
516 }
517 append(arr);
518 return *this;
519 }
520
toStringMap() const521 wxStringMap_t JSONElement::toStringMap() const
522 {
523 wxStringMap_t res;
524 if(!_json) {
525 return res;
526 }
527
528 if(_json->type != cJSON_Array) {
529 return res;
530 }
531
532 for(int i = 0; i < arraySize(); ++i) {
533 wxString key = arrayItem(i).namedObject(_T("key")).toString();
534 wxString val = arrayItem(i).namedObject(_T("value")).toString();
535 res.insert(std::make_pair(key, val));
536 }
537 return res;
538 }
539
addProperty(const wxString & name,size_t value)540 JSONElement& JSONElement::addProperty(const wxString& name, size_t value) { return addProperty(name, (int)value); }
541
addProperty(const wxString & name,const wxColour & colour)542 JSONElement& JSONElement::addProperty(const wxString& name, const wxColour& colour)
543 {
544 wxString colourValue;
545 if(colour.IsOk()) {
546 colourValue = colour.GetAsString(wxC2S_HTML_SYNTAX);
547 }
548 return addProperty(name, colourValue);
549 }
550
firstChild()551 JSONElement JSONElement::firstChild()
552 {
553 _walker = NULL;
554 if(!_json) {
555 return JSONElement(NULL);
556 }
557
558 if(!_json->child) {
559 return JSONElement(NULL);
560 }
561
562 _walker = _json->child;
563 return JSONElement(_walker);
564 }
565
nextChild()566 JSONElement JSONElement::nextChild()
567 {
568 if(!_walker) {
569 return JSONElement(NULL);
570 }
571
572 JSONElement element(_walker->next);
573 _walker = _walker->next;
574 return element;
575 }
576
addProperty(const wxString & name,const char * value,const wxMBConv & conv)577 JSONElement& JSONElement::addProperty(const wxString& name, const char* value, const wxMBConv& conv)
578 {
579 return addProperty(name, wxString(value, conv));
580 }
581
addProperty(const wxString & name,const wxFont & font)582 JSONElement& JSONElement::addProperty(const wxString& name, const wxFont& font)
583 {
584 return addProperty(name, ToString(font));
585 }
586
toFont(const wxFont & defaultFont) const587 wxFont JSONElement::toFont(const wxFont& defaultFont) const
588 {
589 wxString str = toString();
590 if(str.IsEmpty()) return defaultFont;
591 wxFont f = FromString(str);
592 return f;
593 }
594
isArray() const595 bool JSONElement::isArray() const
596 {
597 if(!_json) {
598 return false;
599 }
600 return _json->type == cJSON_Array;
601 }
602
isNumber() const603 bool JSONElement::isNumber() const
604 {
605 if(!_json) {
606 return false;
607 }
608 return _json->type == cJSON_Number;
609 }
610 // ----------------------------------------------------------------------------
ToString(const wxFont & font)611 wxString JSONElement::ToString(const wxFont& font) //(2019/04/3)
612 // ----------------------------------------------------------------------------
613
614 {
615 if(!font.IsOk()) {
616 return _T("");
617 }
618 wxString str;
619 str << font.GetFaceName() << _T(";") << font.GetPointSize() << _T(";") << (int)font.GetFamily() << _T(";")
620 << (int)font.GetWeight() << _T(";") << (int)font.GetStyle();
621 return str;
622 }
623 // ----------------------------------------------------------------------------
FromString(const wxString & str) const624 wxFont JSONElement::FromString(const wxString& str) const //(2019/04/3)
625 // ----------------------------------------------------------------------------
626 {
627 wxArrayString parts = ::wxStringTokenize(str, _T(";"), wxTOKEN_STRTOK);
628 if(parts.size() != 5) return wxNullFont;
629
630 long iPointSize, iFamily, iWeight, iStyle;
631 wxString facename = parts.Item(0);
632 #if wxVERSION_NUMBER >= 3000 //(wx28 2019/04/18)
633 parts.Item(1).ToCLong(&iPointSize);
634 parts.Item(2).ToCLong(&iFamily);
635 parts.Item(3).ToCLong(&iWeight);
636 parts.Item(4).ToCLong(&iStyle);
637 #else
638 parts.Item(1).ToLong(&iPointSize);
639 parts.Item(2).ToLong(&iFamily);
640 parts.Item(3).ToLong(&iWeight);
641 parts.Item(4).ToLong(&iStyle);
642 #endif
643
644 bool bold = (iWeight == wxFONTWEIGHT_BOLD);
645 bool italic = (iStyle == wxFONTSTYLE_ITALIC);
646 #if wxVERSION_NUMBER >= 3000 //(wx28 2019/04/18)
647 wxFont font(wxFontInfo(iPointSize).Bold(bold).Italic(italic).FaceName(facename).Family((wxFontFamily)iFamily));
648 #else
649 //wxFont(int pointSize, wxFontFamily family, int style, wxFontWeight weight, const bool underline = false, const wxString& faceName = "", wxFontEncoding encoding = wxFONTENCODING_DEFAULT)
650 wxFont font(iPointSize, (wxFontFamily)iFamily, iWeight, false, _T("") ); //(wx28 2019/04/19)
651 wxUnusedVar(bold); wxUnusedVar(italic);
652 #endif
653 return font;
654 }
655