ecere/sys/JSON: add support for writing public base classes members.
[sdk] / ecere / src / sys / JSON.ec
1 namespace sys;
2
3 import "System"
4 import "Array"
5
6 default:
7 __attribute__((unused)) static void UnusedFunction()
8 {
9    int a;
10    a.OnGetDataFromString(null);
11    a.OnGetString(null, 0, 0);
12    a.OnFree();
13 }
14 extern int __ecereVMethodID_class_OnGetDataFromString;
15 extern int __ecereVMethodID_class_OnGetString;
16 extern int __ecereVMethodID_class_OnFree;
17 private:
18
19 public enum JSONResult { syntaxError, success, typeMismatch, noItem };
20
21 public enum SetBool : uint
22 {
23    unset, false, true;
24
25    /*public property bool     // NOT WORKING!
26    {
27       set { return value ? true : false; }
28       get { return (this == true); }
29    }*/
30 };
31
32
33 public class JSONParser
34 {
35 public:
36    File f;
37    char ch;
38
39    void SkipEmpty()
40    {
41       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'))
42       {
43          f.Getc(&ch);
44       }
45    }
46
47    JSONResult GetValue(Class type, DataValue value)
48    {
49       JSONResult result = syntaxError;
50       ch = 0;
51       SkipEmpty();
52       if(ch == '\"')
53       {
54          String string;
55          result = GetString(&string);
56          if(result)
57          {
58             Property prop;
59             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
60             {
61                value.p = string;
62             }
63             else if(type && (type.type == enumClass || type.type == unitClass))
64             {
65                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
66                   result = success;
67                else
68                   result = typeMismatch;
69                delete string;
70             }
71             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
72             {
73                // TOFIX: Add more conversion property support... Expecting void * compatible here
74                value.p = ((void *(*)())(void *)prop.Set)(string);
75                result = success;
76                delete string;
77             }
78             else if(type && (type.type == structClass))
79             {
80                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
81                   result = success;
82                else
83                   result = typeMismatch;
84                delete string;
85             }
86             else
87             {
88                delete string;
89                result = typeMismatch;
90             }
91          }
92       }
93       else if(ch == '[')
94       {
95          Container array;
96          if(type && eClass_IsDerived(type, class(Map)))
97          {
98             result = GetMap(type, (Map *)&array);
99          }
100          else
101             result = GetArray(type, &array);
102
103          if(result == success && type && eClass_IsDerived(type, class(Container)))
104          {
105             value.p = array;
106          }
107          else
108          {
109             if(array)
110                array.Free();
111             delete array;
112             if(result != success)
113                result = typeMismatch;
114          }
115       }
116       else if(ch == '-' || isdigit(ch))
117       {
118          result = GetNumber(type, value);
119       }
120       else if(ch == '{')
121       {
122          void * object = value.p;
123          result = GetObject(type, &object);
124          if(result)
125          {
126             if(type && type.type == structClass);
127             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
128             {
129                value.p = object;
130             }
131             else
132             {
133                result = typeMismatch;
134                if(type)
135                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
136             }
137          }
138       }
139       else if(isalpha(ch))
140       {
141          char buffer[256];
142          int c = 0;
143          while(c < sizeof(buffer)-1 && isalpha(ch))
144          {
145             buffer[c++] = ch;
146             if(!f.Getc(&ch)) break;
147          }
148          buffer[c] = 0;
149          result = success;
150
151          if(type)
152          {
153             if(!strcmp(type.name, "bool"))
154             {
155                if(!strcmpi(buffer, "false")) value.i = 0;
156                else if(!strcmpi(buffer, "true")) value.i = 1;
157                else
158                   result = typeMismatch;
159             }
160             else if(!strcmp(type.name, "SetBool"))
161             {
162                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
163                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
164                else
165                   result = typeMismatch;
166             }
167             else if(!strcmpi(buffer, "null"))
168             {
169                value.p = 0;
170             }
171             else
172                result = typeMismatch;
173          }
174          else
175             result = typeMismatch;
176       }
177       else if(ch == '}' || ch == ']')
178          result = noItem;
179       return result;
180    }
181
182    JSONResult GetArray(Class type, Container * array)
183    {
184       JSONResult result = syntaxError;
185       SkipEmpty();
186       *array = null;
187       if(ch == '[')
188       {
189          *array = eInstance_New(type);
190          result = success;
191          while(result)
192          {
193             DataValue value { };
194             Class arrayType = null;
195             JSONResult itemResult;
196
197             if(eClass_IsDerived(type, class(Container)))
198             {
199                arrayType = type.templateArgs[0].dataTypeClass;
200             }
201
202             itemResult = GetValue(arrayType, value);
203             if(itemResult == success)
204             {
205                // TODO: Verify the matching between template type and uint64
206                uint64 t;
207                if(arrayType.type == structClass)
208                {
209                   t = (uint64)(uintptr)value.p;
210                }
211                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
212                {
213                   t = value.ui64; //*(uint64 *)&value.d;
214                }
215                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
216                {
217                   t = value.ui; //f*(uint *)&value.f;
218                }
219                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
220                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
221                {
222                   t = value.ui64;
223                }
224                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
225                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
226                {
227                   t = value.i;
228                }
229                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
230                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
231                   !strcmp(arrayType.dataTypeString, "int16"))
232                {
233                   t = value.s;
234                }
235                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
236                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
237                {
238                   t = value.c;
239                }
240                else
241                {
242                   t = (uint64)(uintptr)value.p;
243                }
244                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
245             }
246             else
247             {
248                if(itemResult == typeMismatch)
249                {
250                   if(arrayType)
251                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
252                }
253                else if(itemResult == noItem)
254                   result = success;
255                else
256                   result = itemResult;
257             }
258
259             if(result != syntaxError)
260             {
261                if(ch != ']' && ch != ',')
262                {
263                   ch = 0;
264                   SkipEmpty();
265                }
266                if(ch == ']')
267                {
268                   break;
269                }
270                else if(ch != ',')
271                   result = syntaxError;
272             }
273          }
274       }
275       ch = 0;
276       return result;
277    }
278
279    JSONResult GetMap(Class type, Map * map)
280    {
281       JSONResult result = syntaxError;
282       SkipEmpty();
283       *map = null;
284       if(ch == '[')
285       {
286          Class mapNodeType = type.templateArgs[0].dataTypeClass;
287          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
288          Property keyProp = null;
289          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
290             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
291
292          *map = eInstance_New(type);
293          result = success;
294
295          while(result)
296          {
297             DataValue value { };
298
299             JSONResult itemResult;
300
301             itemResult = GetValue(mapNodeType, value);
302             if(itemResult == success)
303             {
304                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
305                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
306                // Must free String keys here
307                delete s;
308             }
309             else
310             {
311                if(itemResult == typeMismatch)
312                {
313                   if(mapNodeType)
314                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
315                }
316                else if(itemResult == noItem)
317                   result = success;
318                else
319                   result = itemResult;
320             }
321
322             if(result != syntaxError)
323             {
324                if(ch != ']' && ch != ',')
325                {
326                   ch = 0;
327                   SkipEmpty();
328                }
329                if(ch == ']')
330                {
331                   break;
332                }
333                else if(ch != ',')
334                   result = syntaxError;
335             }
336          }
337       }
338       ch = 0;
339       return result;
340    }
341
342    JSONResult GetString(String * string)
343    {
344       JSONResult result = syntaxError;
345       Array<char> buffer { minAllocSize = 256 };
346       bool escaped = false;
347
348       *string = null;
349       SkipEmpty();
350       if(ch == '\"')
351       {
352          while(f.Getc(&ch))
353          {
354             if(ch == '\\' && !escaped)
355                escaped = true;
356             else
357             {
358                if(escaped)
359                {
360                   if(ch == 'b') ch = '\b';
361                   else if(ch == 'f') ch = '\f';
362                   else if(ch == 'n') ch = '\n';
363                   else if(ch == 'r') ch = '\r';
364                   else if(ch == 't') ch = '\t';
365                   else if(ch == 'u')
366                   {
367                      // SKIP FOR NOW...
368                      char unicode[4];
369                      f.Getc(&unicode[0]);
370                      f.Getc(&unicode[1]);
371                      f.Getc(&unicode[2]);
372                      f.Getc(&unicode[3]);
373                   }
374                   escaped = false;
375                }
376                else if(ch == '\"')
377                {
378                   break;
379                }
380                buffer.Add(ch);
381                if(buffer.minAllocSize < buffer.count)
382                   buffer.minAllocSize *= 2;
383             }
384          }
385          buffer.Add(0);
386          *string = CopyString(buffer.array);
387          result = success;
388       }
389       delete buffer;
390       if(ch != ',' && ch != '}')
391          ch = 0;
392       return result;
393    }
394
395    public JSONResult GetObject(Class objectType, void ** object)
396    {
397       JSONResult result = syntaxError;
398       if(!objectType || objectType.type != structClass)
399          *object = null;
400       SkipEmpty();
401       if(ch == '{')
402       {
403          Class mapKeyClass = null, mapDataClass = null;
404
405          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
406          {
407             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
408             mapDataClass = objectType.templateArgs[2].dataTypeClass;
409          }
410
411          result = success;
412          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
413          {
414             *object = eInstance_New(objectType);
415          }
416          else if(objectType && objectType.type != structClass)
417          {
418             *object = eSystem_New(objectType.typeSize);
419          }
420
421          while(result)
422          {
423             String string;
424             ch = 0;
425             if(GetString(&string))
426             {
427                DataMember member = null;
428                Property prop = null;
429                Class type = null;
430                bool isKey = false;
431
432                if(objectType)
433                {
434                   string[0] = (char)tolower(string[0]);
435                   if(mapKeyClass && !strcmp(string, "key"))
436                   {
437                      prop = eClass_FindProperty(objectType, "key", objectType.module);
438                      type = mapKeyClass;
439                      isKey = true;
440                   }
441                   else if(mapDataClass && !strcmp(string, "value"))
442                   {
443                      prop = eClass_FindProperty(objectType, "value", objectType.module);
444                      type = mapDataClass;
445                   }
446                   else
447                   {
448                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
449                      if(member)
450                      {
451                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
452                         if(!type)
453                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
454                      }
455                      else if(!member)
456                      {
457                         prop = eClass_FindProperty(objectType, string, objectType.module);
458                         if(prop)
459                         {
460                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
461                            if(!type)
462                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
463                         }
464                         else
465                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
466                      }
467                   }
468                }
469                // Find Member in Object Class
470                {
471                   DataValue value { };
472
473                   if(type && type.type == structClass)
474                   {
475                      value.p = (byte *)*object + member._class.offset + member.offset;
476                   }
477                   ch = 0;
478                   SkipEmpty();
479                   if(ch == ':')
480                   {
481                      JSONResult itemResult = GetValue(type, value);
482                      if(itemResult != syntaxError)
483                      {
484                         if(prop || member)
485                         {
486                            if(!type)
487                            {
488                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
489                            }
490                            else if(itemResult == success)
491                            {
492                               // Set value
493                               if(member)
494                               {
495                                  // TOFIX: How to swiftly handle classes with base data type?
496                                  if(type.type == structClass)
497                                     ;
498                                  else if(type.type == normalClass || type.type == noHeadClass)
499                                  {
500                                     void ** ptr = (void**)((byte *)*object + member._class.offset + member.offset);
501                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
502                                     {
503                                        Container container = (Container)*ptr;
504                                        container.Free();
505                                        delete container;
506                                     }
507                                     *ptr = value.p;
508                                  }
509                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
510                                  {
511                                     *(double *)((byte *)*object + member._class.offset + member.offset) = value.d;
512                                  }
513                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
514                                  {
515                                     *(float *)((byte *)*object + member._class.offset + member.offset) = value.f;
516                                  }
517                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
518                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
519                                  {
520                                     *(uint64 *)((byte *)*object + member._class.offset + member.offset) = value.ui64;
521                                  }
522                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
523                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
524                                  {
525                                     *(int *)((byte *)*object + member._class.offset + member.offset) = value.i;
526                                  }
527                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
528                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
529                                     !strcmp(type.dataTypeString, "int16"))
530                                  {
531                                     *(short *)((byte *)*object + member._class.offset + member.offset) = value.s;
532                                  }
533                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
534                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
535                                  {
536                                     *(char *)((byte *)*object + member._class.offset + member.offset) = value.c;
537                                  }
538                                  else
539                                  {
540                                     *(void **)((byte *)*object + member._class.offset + member.offset) = value.p;
541                                  }
542                               }
543                               else if(prop && prop.Set)
544                               {
545                                  if(!strcmp(type.dataTypeString, "char *"))
546                                  {
547                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
548                                     if(!isKey)
549                                        delete value.p;
550                                  }
551                                  // TOFIX: How to swiftly handle classes with base data type?
552                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
553                                  {
554                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
555                                  }
556                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
557                                  {
558                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
559                                  }
560                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
561                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
562                                  {
563                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
564                                  }
565                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
566                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
567                                  {
568                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
569                                  }
570                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
571                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
572                                     !strcmp(type.dataTypeString, "int16"))
573                                  {
574                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
575                                  }
576                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
577                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
578                                  {
579                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
580                                  }
581                                  else
582                                  {
583                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
584                                  }
585                               }
586                            }
587                            else
588                            {
589                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
590                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
591                            }
592                         }
593                      }
594                   }
595                   else
596                      result = syntaxError;
597                }
598             }
599             else if(ch && ch != '}' && ch != ',')
600                result = syntaxError;
601             delete string;
602
603             if(result)
604             {
605                SkipEmpty();
606                if(ch == '}')
607                {
608                   break;
609                }
610                else if(ch != ',')
611                   result = syntaxError;
612             }
613          }
614       }
615       ch = 0;
616       return result;
617    }
618
619    JSONResult GetNumber(Class type, DataValue value)
620    {
621       JSONResult result = syntaxError;
622       char buffer[256];
623       int c = 0;
624       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
625       {
626          buffer[c++] = ch;
627          if(!f.Getc(&ch)) break;
628       }
629       buffer[c] = 0;
630       //if(strchr(buffer, '.'))
631
632       // TOFIX: How to swiftly handle classes with base data type?
633       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
634       {
635          value.d = strtod(buffer, null);
636          result = success;
637       }
638       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
639       {
640          value.f = (float)strtod(buffer, null);
641          result = success;
642       }
643       // TOFIX: int64 looks for class long long?
644       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
645       else if(!strcmp(type.dataTypeString, "int64"))
646       {
647          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
648          result = success;
649       }
650       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
651       {
652          value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
653          result = success;
654       }
655       else
656       {
657          value.i = strtol(buffer, null, 10);
658          result = success;
659       }
660       return result;
661    }
662 }
663
664 bool WriteMap(File f, Class type, Map map, int indent)
665 {
666    if(map)
667    {
668       int i;
669       bool isFirst = true;
670       MapIterator it { map = map };
671       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
672       f.Puts("[\n");
673       indent++;
674
675       while(it.Next())
676       {
677          MapNode n = (MapNode)it.pointer;
678          if(!isFirst)
679             f.Puts(",\n");
680          else
681             isFirst = false;
682          for(i = 0; i<indent; i++) f.Puts("   ");
683          _WriteJSONObject(f, mapNodeClass, n, indent);
684       }
685       f.Puts("\n");
686       indent--;
687       for(i = 0; i<indent; i++) f.Puts("   ");
688       f.Puts("]");
689    }
690    else
691       f.Puts("null");
692    return true;
693 }
694
695 bool WriteArray(File f, Class type, Container array, int indent)
696 {
697    if(array)
698    {
699       int i;
700       bool isFirst = true;
701       Iterator it { array };
702       Class arrayType = type.templateArgs[0].dataTypeClass;
703       f.Puts("[\n");
704       indent++;
705
706       while(it.Next())
707       {
708          DataValue value { };
709          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
710          if(!isFirst)
711             f.Puts(",\n");
712          else
713             isFirst = false;
714
715          // Add value
716          // TODO: Verify the matching between template type and uint64
717          if(arrayType.type == structClass)
718          {
719             value.p = (void *)(uintptr)t;
720          }
721          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
722          {
723             value.ui64 = t;
724             //value.d = *(double *)&t;
725          }
726          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
727          {
728             value.ui = (uint)t;
729             //value.f = *(float *)&t;
730          }
731          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
732             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
733          {
734             value.ui64 = t;
735          }
736          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
737             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
738          {
739             value.i = (int)t;
740          }
741          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
742             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
743             !strcmp(arrayType.dataTypeString, "int16"))
744          {
745             value.s = (short)t;
746          }
747          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
748             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
749          {
750             value.c = (char)t;
751          }
752          else
753          {
754             value.p = (void *)(uintptr)t;
755          }
756          for(i = 0; i<indent; i++) f.Puts("   ");
757          WriteValue(f, arrayType, value, indent);
758       }
759       f.Puts("\n");
760       indent--;
761       for(i = 0; i<indent; i++) f.Puts("   ");
762       f.Puts("]");
763    }
764    else
765       f.Puts("null");
766    return true;
767 }
768
769 bool WriteNumber(File f, Class type, DataValue value, int indent)
770 {
771    char buffer[1024];
772    bool needClass = false;
773    buffer[0] = 0;
774    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
775       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
776    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
777       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
778    else if(!strcmp(type.dataTypeString, "int64"))
779       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
780    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
781       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
782    else if(!strcmp(type.dataTypeString, "int"))
783       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
784    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
785       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
786    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
787       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
788    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
789       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
790    else if(!strcmp(type.dataTypeString, "char"))
791       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
792    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
793       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
794    f.Puts(buffer);
795    return true;
796 }
797
798 bool WriteValue(File f, Class type, DataValue value, int indent)
799 {
800    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
801    {
802       if(!value.p)
803          f.Puts("null");
804       else
805       {
806          f.Puts("\"");
807          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
808          {
809             int c = 0;
810             int b = 0;
811             char buffer[1024];
812             char * string = value.p;
813             char ch;
814             while(true)
815             {
816                ch = string[c++];
817                if(ch == '\"')
818                {
819                   buffer[b] = 0;
820                   f.Puts(buffer);
821                   f.Puts("\\\"");
822                   b = 0;
823                }
824                else if(ch == '\\')
825                {
826                   buffer[b] = 0;
827                   f.Puts(buffer);
828                   f.Puts("\\\\");
829                   b = 0;
830                }
831                else if(b == sizeof(buffer)-2 || !ch)
832                {
833                   buffer[b++] = ch;
834                   if(ch) buffer[b] = 0;
835                   f.Puts(buffer);
836                   b = 0;
837                   if(!ch) break;
838                }
839                else
840                   buffer[b++] = ch;
841             }
842          }
843          /*else
844             f.Puts(value.p);*/
845          f.Puts("\"");
846       }
847    }
848    else if(!strcmp(type.name, "bool"))
849    {
850       if(value.i)
851          f.Puts("true");
852       else
853          f.Puts("false");
854    }
855    else if(!strcmp(type.name, "SetBool"))
856    {
857       if(value.i == SetBool::true)
858          f.Puts("true");
859       else if(value.i == SetBool::false)
860          f.Puts("false");
861       else
862          f.Puts("unset");
863    }
864    else if(type.type == enumClass || type.type == unitClass)
865    {
866       f.Puts("\"");
867       WriteNumber(f, type, value, indent);
868       f.Puts("\"");
869    }
870    else if(eClass_IsDerived(type, class(Map)))
871    {
872       WriteMap(f, type, value.p, indent);
873    }
874    else if(eClass_IsDerived(type, class(Container)))
875    {
876       WriteArray(f, type, value.p, indent);
877    }
878    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || type.type == bitClass)
879    {
880       _WriteJSONObject(f, type, value.p, indent);
881    }
882    else if(type.type == systemClass)
883    {
884       WriteNumber(f, type, value, indent);
885    }
886    return true;
887 }
888
889 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
890 {
891    bool result = false;
892    if(object)
893    {
894       result = _WriteJSONObject(f, objectType, object, indent);
895       f.Puts("\n");
896    }
897    return result;
898 }
899
900 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
901 {
902    if(object)
903    {
904       const char * string = null;
905
906       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
907       {
908          char buffer[1024];
909          buffer[0] = 0;
910          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
911       }
912       if(string)
913       {
914          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
915          if(string[0] == '\"')
916             f.Puts(string);
917          else
918          {
919             f.Puts("\"");
920             f.Puts(string);
921             f.Puts("\"");
922          }
923       }
924       else
925       {
926          Property prop;
927          int c;
928          bool isFirst = true;
929          Class mapKeyClass = null, mapDataClass = null;
930          Class baseClass;
931          List<Class> bases { };
932
933          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
934          {
935             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
936             mapDataClass = objectType.templateArgs[2].dataTypeClass;
937          }
938
939          f.Puts("{\n");
940          indent++;
941
942          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
943          {
944             if(baseClass.isInstanceClass || !baseClass.base)
945                break;
946             bases.Insert(null, baseClass);
947          }
948
949          for(baseClass : bases)
950          {
951             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
952             {
953                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
954                if(prop.isProperty)
955                {
956                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
957                   {
958                      DataValue value { };
959                      Class type;
960
961                      if(mapKeyClass && !strcmp(prop.name, "key"))
962                         type = mapKeyClass;
963                      else if(mapDataClass && !strcmp(prop.name, "value"))
964                         type = mapDataClass;
965                      else
966                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
967                      if(!type)
968                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
969                      if(!type)
970                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
971                      else
972                      {
973                         // TOFIX: How to swiftly handle classes with base data type?
974                         if(type == class(double) || !strcmp(type.dataTypeString, "double"))
975                         {
976                            value.d = ((double (*)(void *))(void *)prop.Get)(object);
977                         }
978                         else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
979                         {
980                            value.f = ((float (*)(void *))(void *)prop.Get)(object);
981                         }
982                         else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
983                            !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
984                         {
985                            value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
986                         }
987                         else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
988                            !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
989                         {
990                            value.i = ((int (*)(void *))(void *)prop.Get)(object);
991                         }
992                         else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
993                            !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
994                            !strcmp(type.dataTypeString, "int16"))
995                         {
996                            value.s = ((short (*)(void *))(void *)prop.Get)(object);
997                         }
998                         else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
999                            !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1000                         {
1001                            value.c = ((char (*)(void *))(void *)prop.Get)(object);
1002                         }
1003                         else
1004                         {
1005                            value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1006                         }
1007
1008                         if(!isFirst) f.Puts(",\n");
1009                         for(c = 0; c<indent; c++) f.Puts("   ");
1010
1011                         f.Puts("\"");
1012                         f.Putc((char)toupper(prop.name[0]));
1013                         f.Puts(prop.name+1);
1014                         f.Puts("\" : ");
1015                         WriteValue(f, type, value, indent);
1016                         isFirst = false;
1017                      }
1018                   }
1019                }
1020                else
1021                {
1022                   DataMember member = (DataMember)prop;
1023                   DataValue value { };
1024                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1025                   if(!type)
1026                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1027
1028                   if(type)
1029                   {
1030                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1031                      {
1032                         if(type.type == structClass)
1033                            value.p = (void *)((byte *)object + member._class.offset + member.offset);
1034                         else
1035                            value.p = *(void **)((byte *)object + member._class.offset + member.offset);
1036                         if(!value.p)
1037                            continue;
1038                      }
1039                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1040                      {
1041                         value.d = *(double *)((byte *)object + member._class.offset + member.offset);
1042                      }
1043                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1044                      {
1045                         value.f = *(float *)((byte *)object + member._class.offset + member.offset);
1046                      }
1047                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1048                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1049                      {
1050                         value.ui64 = *(uint64 *)((byte *)object + member._class.offset + member.offset);
1051                      }
1052                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1053                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1054                      {
1055                         value.i = *(int *)((byte *)object + member._class.offset + member.offset);
1056                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1057                            if(!value.i)
1058                               continue;
1059                      }
1060                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1061                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1062                         !strcmp(type.dataTypeString, "int16"))
1063                      {
1064                         value.s = *(short *)((byte *)object + member._class.offset + member.offset);
1065                      }
1066                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1067                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1068                      {
1069                         value.c = *(char *)((byte *)object + member._class.offset + member.offset);
1070                      }
1071                      else
1072                      {
1073                         value.i = *(int *)((byte *)object + member._class.offset + member.offset);
1074                      }
1075
1076                      if(!isFirst) f.Puts(",\n");
1077                      for(c = 0; c<indent; c++) f.Puts("   ");
1078
1079                      f.Puts("\"");
1080                      f.Putc((char)toupper(member.name[0]));
1081                      f.Puts(member.name+1);
1082                      f.Puts("\" : ");
1083                      WriteValue(f, type, value, indent);
1084                      isFirst = false;
1085                   }
1086                }
1087             }
1088          }
1089
1090          delete bases;
1091
1092          indent--;
1093          f.Puts("\n");
1094          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1095       }
1096    }
1097    else
1098       f.Puts("null");
1099    return true;
1100 }