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