ecere/src/sys/JSON: Skipping '/'
[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' || ch == '/'))
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(objectType.module, member.dataTypeString);
468                         if(!type)
469                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
470
471                         offset = member._class.offset + member.offset;
472                      }
473                      else if(!member)
474                      {
475                         prop = eClass_FindProperty(objectType, string, objectType.module);
476                         if(prop)
477                         {
478                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
479                            if(!type)
480                               type = eSystem_FindClass(objectType.module, prop.dataTypeString);
481                            if(!type)
482                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
483                         }
484                         else
485                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
486                      }
487                   }
488                }
489                // Find Member in Object Class
490                {
491                   DataValue value { };
492
493                   if(type && type.type == structClass)
494                   {
495                      if(member)
496                      {
497                         value.p = (byte *)*object + offset;
498                         memset(value.p, 0, type.structSize);
499                      }
500                      else if(prop)
501                      {
502                         value.p = new0 byte[type.structSize];
503                      }
504                   }
505                   ch = 0;
506                   SkipEmpty();
507                   if(ch == ':')
508                   {
509                      JSONResult itemResult = GetValue(type, value);
510                      if(itemResult != syntaxError)
511                      {
512                         if(prop || member)
513                         {
514                            if(!type)
515                            {
516                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
517                            }
518                            else if(itemResult == success)
519                            {
520                               // Set value
521                               if(member)
522                               {
523                                  // TOFIX: How to swiftly handle classes with base data type?
524                                  if(type.type == structClass)
525                                     ;
526                                  else if(type.type == normalClass || type.type == noHeadClass)
527                                  {
528                                     void ** ptr = (void**)((byte *)*object + offset);
529                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
530                                     {
531                                        Container container = (Container)*ptr;
532                                        container.Free();
533                                        delete container;
534                                     }
535                                     *ptr = value.p;
536                                  }
537                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
538                                  {
539                                     *(double *)((byte *)*object + offset) = value.d;
540                                  }
541                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
542                                  {
543                                     *(float *)((byte *)*object + offset) = value.f;
544                                  }
545                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
546                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
547                                  {
548                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
549                                  }
550                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
551                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
552                                  {
553                                     *(int *)((byte *)*object + offset) = value.i;
554                                  }
555                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
556                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
557                                     !strcmp(type.dataTypeString, "int16"))
558                                  {
559                                     *(short *)((byte *)*object + offset) = value.s;
560                                  }
561                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
562                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
563                                  {
564                                     *(char *)((byte *)*object + offset) = value.c;
565                                  }
566                                  else
567                                  {
568                                     *(void **)((byte *)*object + offset) = value.p;
569                                  }
570                               }
571                               else if(prop && prop.Set)
572                               {
573                                  if(!strcmp(type.dataTypeString, "char *"))
574                                  {
575                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
576                                     if(!isKey)
577                                        delete value.p;
578                                  }
579                                  else if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
580                                  {
581                                     // TOFIX: How to swiftly handle classes with base data type?
582                                     if(type == class(double) || !strcmp(type.dataTypeString, "double"))
583                                     {
584                                        ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
585                                     }
586                                     else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
587                                     {
588                                        ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
589                                     }
590                                     else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
591                                        !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
592                                     {
593                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
594                                     }
595                                     else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
596                                        !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
597                                     {
598                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
599                                     }
600                                     else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
601                                        !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
602                                        !strcmp(type.dataTypeString, "int16"))
603                                     {
604                                        ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
605                                     }
606                                     else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
607                                        !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
608                                     {
609                                        ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
610                                     }
611                                     else
612                                     {
613                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
614                                     }
615                                  }
616                                  else
617                                  {
618                                     if(isTemplateArg)
619                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, (uint64)(uintptr)value.p);
620                                     else
621                                        ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
622                                  }
623                               }
624                            }
625                            else
626                            {
627                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
628                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
629                            }
630                         }
631                      }
632                   }
633                   else
634                      result = syntaxError;
635
636                   if(prop && type && type.type == structClass)
637                   {
638                      delete value.p;
639                   }
640                }
641             }
642             else if(ch && ch != '}' && ch != ',')
643                result = syntaxError;
644             delete string;
645
646             if(result)
647             {
648                SkipEmpty();
649                if(ch == '}')
650                {
651                   break;
652                }
653                else if(ch != ',')
654                   result = syntaxError;
655             }
656          }
657       }
658       ch = 0;
659       return result;
660    }
661
662    JSONResult GetNumber(Class type, DataValue value)
663    {
664       JSONResult result = syntaxError;
665       char buffer[256];
666       int c = 0;
667       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
668       {
669          buffer[c++] = ch;
670          if(!f.Getc(&ch)) break;
671       }
672       buffer[c] = 0;
673       //if(strchr(buffer, '.'))
674       if(!type) return success;
675
676       // TOFIX: How to swiftly handle classes with base data type?
677       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
678       {
679          value.d = strtod(buffer, null);
680          result = success;
681       }
682       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
683       {
684          value.f = (float)strtod(buffer, null);
685          result = success;
686       }
687       // TOFIX: int64 looks for class long long?
688       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
689       else if(!strcmp(type.dataTypeString, "int64"))
690       {
691          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
692          result = success;
693       }
694       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
695       {
696          value.ui64 = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
697          result = success;
698       }
699       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int"))
700       {
701          value.ui = (uint)strtoul(buffer, null, 10);  // TOFIX: 64 bit support
702          result = success;
703       }
704       else
705       {
706          value.i = (int)strtol(buffer, null, 10);
707          result = success;
708       }
709
710       if(result == success && type.type == unitClass)
711       {
712          // Convert to reference unit
713          Property prop;
714          for(prop = type.conversions.first; prop; prop = prop.next)
715          {
716             bool refProp = false;
717             if(!strcmp(prop.name, type.base.fullName))
718                refProp = true;
719             else
720             {
721                Class c = eSystem_FindClass(type.module, prop.name);
722                if(!c)
723                   c = eSystem_FindClass(type.module.application, prop.name);
724                if(c)
725                {
726                   Property p;
727                   for(p = c.conversions.first; p; p = p.next)
728                   {
729                      if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
730                      {
731                         refProp = true;
732                         break;
733                      }
734                   }
735                }
736             }
737             if(refProp)
738             {
739                if(prop.Set && prop.Get)
740                {
741                   const String dts = type.base.dataTypeString;
742                   if(!strcmp(dts, "double"))
743                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
744                   else if(!strcmp(dts, "float"))
745                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
746                   else if(!strcmp(dts, "int"))
747                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
748                   else if(!strcmp(dts, "int64"))
749                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
750                   else if(!strcmp(dts, "unsigned int"))
751                      value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
752                   else if(!strcmp(dts, "uint64"))
753                      value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
754                }
755                else
756                   break;
757             }
758          }
759       }
760       return result;
761    }
762
763    JSONResult GetColorAlpha(String string, DataValue value)
764    {
765       ColorAlpha color = 0;
766       DefinedColor c = 0;
767       if(string)
768       {
769          if(string[0] == '0' && string[1] == 'x')
770             color = (uint)strtoul(string, null, 0);
771          else
772          {
773             char *d;
774             byte a = 255;
775             if((d = strchr(string, ',')))
776             {
777                a = (byte)atoi(string);
778                d += 2;
779             }
780             else
781                d = string;
782             if(c.class::OnGetDataFromString(d))
783             {
784                color.a = a;
785                color.color = c;
786             }
787             else
788                color = (uint)strtoul(string, null, 16);
789          }
790       }
791       value.i = color;
792       return success;
793    }
794 }
795
796 bool WriteMap(File f, Class type, Map map, int indent)
797 {
798    if(map)
799    {
800       int i;
801       bool isFirst = true;
802       MapIterator it { map = map };
803       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
804       f.Puts("[\n");
805       indent++;
806
807       while(it.Next())
808       {
809          MapNode n = (MapNode)it.pointer;
810          if(!isFirst)
811             f.Puts(",\n");
812          else
813             isFirst = false;
814          for(i = 0; i<indent; i++) f.Puts("   ");
815          _WriteJSONObject(f, mapNodeClass, n, indent);
816       }
817       f.Puts("\n");
818       indent--;
819       for(i = 0; i<indent; i++) f.Puts("   ");
820       f.Puts("]");
821    }
822    else
823       f.Puts("null");
824    return true;
825 }
826
827 bool WriteArray(File f, Class type, Container array, int indent)
828 {
829    if(array)
830    {
831       int i;
832       bool isFirst = true;
833       Iterator it { array };
834       Class arrayType = type.templateArgs[0].dataTypeClass;
835       f.Puts("[\n");
836       indent++;
837
838       while(it.Next())
839       {
840          DataValue value { };
841          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
842          if(!isFirst)
843             f.Puts(",\n");
844          else
845             isFirst = false;
846
847          // Add value
848          // TODO: Verify the matching between template type and uint64
849          if(arrayType.type == structClass)
850          {
851             value.p = (void *)(uintptr)t;
852          }
853          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
854          {
855             value.ui64 = t;
856             //value.d = *(double *)&t;
857          }
858          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
859          {
860             value.ui = (uint)t;
861             //value.f = *(float *)&t;
862          }
863          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
864             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
865          {
866             value.ui64 = t;
867          }
868          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
869             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
870          {
871             value.i = (int)t;
872          }
873          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
874             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
875             !strcmp(arrayType.dataTypeString, "int16"))
876          {
877             value.s = (short)t;
878          }
879          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
880             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
881          {
882             value.c = (char)t;
883          }
884          else
885          {
886             value.p = (void *)(uintptr)t;
887          }
888          for(i = 0; i<indent; i++) f.Puts("   ");
889          WriteValue(f, arrayType, value, indent);
890       }
891       f.Puts("\n");
892       indent--;
893       for(i = 0; i<indent; i++) f.Puts("   ");
894       f.Puts("]");
895    }
896    else
897       f.Puts("null");
898    return true;
899 }
900
901 bool WriteNumber(File f, Class type, DataValue value, int indent)
902 {
903    char buffer[1024];
904    bool needClass = false;
905    bool quote;
906    buffer[0] = 0;
907    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
908       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
909    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
910       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
911    else if(!strcmp(type.dataTypeString, "int64"))
912       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
913    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
914       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
915    else if(!strcmp(type.dataTypeString, "int"))
916       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
917    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
918       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
919    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
920       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
921    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
922       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
923    else if(!strcmp(type.dataTypeString, "char"))
924       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
925    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
926       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
927
928    quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' ')));
929    if(quote) f.Puts("\"");
930    f.Puts(buffer);
931    if(quote) f.Puts("\"");
932    return true;
933 }
934
935 public bool WriteColorAlpha(File f, Class type, DataValue value, int indent)
936 {
937    char buffer[1024];
938    char * string = buffer;
939    ColorAlpha color = value.i;
940    int a = color.a;
941    int len;
942    DefinedColor c = color;
943    buffer[0] = '\0';
944    if(a != 255)
945    {
946       a.class::OnGetString(buffer, null, null);
947       len = strlen(buffer);
948       buffer[len++] = ',';
949       buffer[len++] = ' ';
950       buffer[len] = '\0';
951       string += len;
952    }
953    if(!c.class::OnGetString(string, null, null))
954       sprintf(buffer, "0x%x", color);
955    f.Puts("\"");
956    f.Puts(buffer);
957    f.Puts("\"");
958    return true;
959 }
960
961 bool WriteValue(File f, Class type, DataValue value, int indent)
962 {
963    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
964    {
965       if(!value.p)
966          f.Puts("null");
967       else
968       {
969          f.Puts("\"");
970          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
971          {
972             int c = 0;
973             int b = 0;
974             char buffer[1024];
975             char * string = value.p;
976             char ch;
977             while(true)
978             {
979                ch = string[c++];
980                if(ch == '\"')
981                {
982                   buffer[b] = 0;
983                   f.Puts(buffer);
984                   f.Puts("\\\"");
985                   b = 0;
986                }
987                else if(ch == '\\')
988                {
989                   buffer[b] = 0;
990                   f.Puts(buffer);
991                   f.Puts("\\\\");
992                   b = 0;
993                }
994                else if(b == sizeof(buffer)-2 || !ch)
995                {
996                   buffer[b++] = ch;
997                   if(ch) buffer[b] = 0;
998                   f.Puts(buffer);
999                   b = 0;
1000                   if(!ch) break;
1001                }
1002                else
1003                   buffer[b++] = ch;
1004             }
1005          }
1006          /*else
1007             f.Puts(value.p);*/
1008          f.Puts("\"");
1009       }
1010    }
1011    else if(!strcmp(type.name, "bool"))
1012    {
1013       if(value.i)
1014          f.Puts("true");
1015       else
1016          f.Puts("false");
1017    }
1018    else if(!strcmp(type.name, "SetBool"))
1019    {
1020       if(value.i == SetBool::true)
1021          f.Puts("true");
1022       else if(value.i == SetBool::false)
1023          f.Puts("false");
1024       else
1025          f.Puts("unset");
1026    }
1027    else if(type.type == enumClass)
1028    {
1029       f.Puts("\"");
1030       WriteNumber(f, type, value, indent);
1031       f.Puts("\"");
1032    }
1033    else if(eClass_IsDerived(type, class(Map)))
1034    {
1035       WriteMap(f, type, value.p, indent);
1036    }
1037    else if(eClass_IsDerived(type, class(Container)))
1038    {
1039       WriteArray(f, type, value.p, indent);
1040    }
1041    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
1042    {
1043       _WriteJSONObject(f, type, value.p, indent);
1044    }
1045    else if(eClass_IsDerived(type, class(ColorAlpha)))
1046    {
1047       WriteColorAlpha(f, type, value, indent);
1048    }
1049    else if(type.type == bitClass)
1050    {
1051       Class dataType;
1052       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
1053       if(!dataType)
1054          dataType = eSystem_FindClass(type.module, type.dataTypeString);
1055       if(!dataType)
1056          dataType = eSystem_FindClass(__thisModule.application, type.dataTypeString);
1057       WriteNumber(f, dataType, value, indent);
1058    }
1059    else if(type.type == systemClass || type.type == unitClass)
1060    {
1061       WriteNumber(f, type, value, indent);
1062    }
1063    return true;
1064 }
1065
1066 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
1067 {
1068    bool result = false;
1069    if(object)
1070    {
1071       result = _WriteJSONObject(f, objectType, object, indent);
1072       f.Puts("\n");
1073    }
1074    return result;
1075 }
1076
1077 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
1078 {
1079    if(object)
1080    {
1081       const char * string = null;
1082
1083       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
1084       {
1085          char buffer[1024];
1086          buffer[0] = 0;
1087          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
1088       }
1089       if(string)
1090       {
1091          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
1092          if(string[0] == '\"')
1093             f.Puts(string);
1094          else
1095          {
1096             f.Puts("\"");
1097             f.Puts(string);
1098             f.Puts("\"");
1099          }
1100       }
1101       else
1102       {
1103          Property prop;
1104          int c;
1105          bool isFirst = true;
1106          Class mapKeyClass = null, mapDataClass = null;
1107          Class baseClass;
1108          List<Class> bases { };
1109
1110          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
1111          {
1112             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
1113             mapDataClass = objectType.templateArgs[2].dataTypeClass;
1114          }
1115
1116          f.Puts("{\n");
1117          indent++;
1118
1119          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
1120          {
1121             if(baseClass.isInstanceClass || !baseClass.base)
1122                break;
1123             bases.Insert(null, baseClass);
1124          }
1125
1126          for(baseClass : bases)
1127          {
1128             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
1129             {
1130                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
1131                if(prop.isProperty)
1132                {
1133                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
1134                   {
1135                      DataValue value { };
1136                      bool isTemplateArg = false;
1137                      Class type;
1138
1139                      if(mapKeyClass && !strcmp(prop.name, "key"))
1140                      {
1141                         isTemplateArg = true;
1142                         type = mapKeyClass;
1143                      }
1144                      else if(mapDataClass && !strcmp(prop.name, "value"))
1145                      {
1146                         isTemplateArg = true;
1147                         type = mapDataClass;
1148                      }
1149                      else
1150                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
1151
1152                      if(!type)
1153                         type = eSystem_FindClass(objectType.module, prop.dataTypeString);
1154                      if(!type)
1155                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
1156                      if(!type)
1157                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
1158                      else
1159                      {
1160                         if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
1161                         {
1162                            // TOFIX: How to swiftly handle classes with base data type?
1163                            if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1164                            {
1165                               value.d = ((double (*)(void *))(void *)prop.Get)(object);
1166                            }
1167                            else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1168                            {
1169                               value.f = ((float (*)(void *))(void *)prop.Get)(object);
1170                            }
1171                            else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1172                               !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1173                            {
1174                               value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
1175                            }
1176                            else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1177                               !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1178                            {
1179                               value.i = ((int (*)(void *))(void *)prop.Get)(object);
1180                            }
1181                            else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1182                               !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1183                               !strcmp(type.dataTypeString, "int16"))
1184                            {
1185                               value.s = ((short (*)(void *))(void *)prop.Get)(object);
1186                            }
1187                            else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1188                               !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1189                            {
1190                               value.c = ((char (*)(void *))(void *)prop.Get)(object);
1191                            }
1192                         }
1193                         else if(type.type == structClass)
1194                         {
1195                            value.p = new0 byte[type.structSize];
1196                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1197                         }
1198                         else
1199                         {
1200                            if(isTemplateArg)
1201                               value.p = (void *)(uintptr)((uint64 (*)(void *))(void *)prop.Get)(object);
1202                            else
1203                               value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1204                         }
1205
1206                         if(!isFirst) f.Puts(",\n");
1207                         for(c = 0; c<indent; c++) f.Puts("   ");
1208
1209                         f.Puts("\"");
1210                         f.Putc((char)toupper(prop.name[0]));
1211                         f.Puts(prop.name+1);
1212                         f.Puts("\" : ");
1213                         WriteValue(f, type, value, indent);
1214                         isFirst = false;
1215                         if(type.type == structClass)
1216                            delete value.p;
1217                      }
1218                   }
1219                }
1220                else
1221                {
1222                   DataMember member = (DataMember)prop;
1223                   DataValue value { };
1224                   uint offset;
1225                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1226                   if(!type)
1227                      type = eSystem_FindClass(objectType.module, member.dataTypeString);
1228                   if(!type)
1229                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1230
1231                   offset = member._class.offset + member.offset;
1232
1233                   if(type)
1234                   {
1235                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1236                      {
1237                         if(type.type == structClass)
1238                            value.p = (void *)((byte *)object + offset);
1239                         else
1240                            value.p = *(void **)((byte *)object + offset);
1241                         if(!value.p)
1242                            continue;
1243                      }
1244                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1245                      {
1246                         value.d = *(double *)((byte *)object + offset);
1247                      }
1248                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1249                      {
1250                         value.f = *(float *)((byte *)object + offset);
1251                      }
1252                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1253                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1254                      {
1255                         value.ui64 = *(uint64 *)((byte *)object + offset);
1256                      }
1257                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1258                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1259                      {
1260                         value.i = *(int *)((byte *)object + offset);
1261                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1262                            if(!value.i)
1263                               continue;
1264                      }
1265                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1266                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1267                         !strcmp(type.dataTypeString, "int16"))
1268                      {
1269                         value.s = *(short *)((byte *)object + offset);
1270                      }
1271                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1272                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1273                      {
1274                         value.c = *(char *)((byte *)object + offset);
1275                      }
1276                      else
1277                      {
1278                         value.i = *(int *)((byte *)object + offset);
1279                      }
1280
1281                      if(!isFirst) f.Puts(",\n");
1282                      for(c = 0; c<indent; c++) f.Puts("   ");
1283
1284                      f.Puts("\"");
1285                      f.Putc((char)toupper(member.name[0]));
1286                      f.Puts(member.name+1);
1287                      f.Puts("\" : ");
1288                      WriteValue(f, type, value, indent);
1289                      isFirst = false;
1290                   }
1291                }
1292             }
1293          }
1294
1295          delete bases;
1296
1297          indent--;
1298          f.Puts("\n");
1299          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1300       }
1301    }
1302    else
1303       f.Puts("null");
1304    return true;
1305 }