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