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