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