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