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