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