036d6838fc548f2fa6416ade4d279f35571c28ba
[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 ECONParser : JSONParser
35 {
36    eCON = true;
37 }
38
39 public class JSONParser
40 {
41 public:
42    File f;
43 private:
44    char ch;
45    bool eCON;
46
47    void SkipEmpty()
48    {
49       if(eCON)
50       {
51          char pch;
52          bool lineComment = false;
53          bool comment = false;
54          while(!f.Eof() && (!ch || lineComment || comment || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/'))
55          {
56             pch = ch;
57             f.Getc(&ch);
58             if(!lineComment && !comment && pch == '/')
59             {
60                if(ch == '/')
61                   lineComment = true;
62                else if(ch == '*')
63                   comment = true;
64             }
65             else if(lineComment && ch == '\n')
66                lineComment = false;
67             else if(comment && pch == '*' && ch == '/')
68                comment = false;
69          }
70       }
71       else
72       {
73          while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/'))
74          {
75             f.Getc(&ch);
76          }
77       }
78    }
79
80    void SkipExtraSemicolon()
81    {
82       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ';'))
83       {
84          f.Getc(&ch);
85       }
86    }
87
88    JSONResult GetValue(Class type, DataValue value)
89    {
90       JSONResult result = syntaxError;
91       ch = 0;
92       SkipEmpty();
93       if(ch == '\"')
94       {
95          String string;
96          result = GetString(&string);
97          if(result)
98          {
99             Property prop;
100             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
101             {
102                value.p = string;
103             }
104             else if(type && (type.type == enumClass || type.type == unitClass))
105             {
106                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
107                   result = success;
108                else
109                   result = typeMismatch;
110                delete string;
111             }
112             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
113             {
114                // TOFIX: Add more conversion property support... Expecting void * compatible here
115                value.p = ((void *(*)())(void *)prop.Set)(string);
116                result = success;
117                delete string;
118             }
119             else if(type && eClass_IsDerived(type, class(ColorAlpha)))
120             {
121                result = GetColorAlpha(string, value);
122                delete string;
123             }
124             else if(type && (type.type == structClass))
125             {
126                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
127                   result = success;
128                else
129                   result = typeMismatch;
130                delete string;
131             }
132             else
133             {
134                delete string;
135                result = typeMismatch;
136             }
137          }
138       }
139       else if(ch == '[')
140       {
141          Container array;
142          if(type && eClass_IsDerived(type, class(Map)))
143          {
144             result = GetMap(type, (Map *)&array);
145          }
146          else
147             result = GetArray(type, &array);
148
149          if(result == success && type && eClass_IsDerived(type, class(Container)))
150          {
151             value.p = array;
152          }
153          else
154          {
155             if(array)
156                array.Free();
157             delete array;
158             if(result != success)
159                result = typeMismatch;
160          }
161       }
162       else if(ch == '-' || isdigit(ch))
163       {
164          result = GetNumber(type, value);
165       }
166       else if(ch == '{')
167       {
168          void * object = value.p;
169          result = GetObject(type, &object);
170          if(result)
171          {
172             if(type && type.type == structClass);
173             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
174             {
175                value.p = object;
176             }
177             else
178             {
179                result = typeMismatch;
180                if(type)
181                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
182             }
183          }
184       }
185       else if(isalpha(ch) || ch == '_')
186       {
187          if(eCON)
188          {
189             String string;
190             if(GetIdentifier(&string, null))
191             {
192                result = success;
193                if(eCON && type && (type.type == enumClass || type.type == unitClass))
194                {
195                   // should this be set by calling __ecereVMethodID_class_OnGetDataFromString ?
196                   if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
197                      result = success;
198                   else
199                      result = typeMismatch;
200                }
201                else if(type && !strcmp(type.name, "bool"))
202                {
203                   if(!strcmpi(string, "false")) value.i = 0;
204                   else if(!strcmpi(string, "true")) value.i = 1;
205                   else
206                      result = typeMismatch;
207                }
208                else if(type && !strcmp(type.name, "SetBool"))
209                {
210                   if(!strcmpi(string, "false")) value.i = SetBool::false;
211                   else if(!strcmpi(string, "true")) value.i = SetBool::true;
212                   else
213                      result = typeMismatch;
214                }
215                else if(type && !strcmpi(string, "null"))
216                {
217                   if(type.type != structClass)
218                      value.p = 0;
219                }
220                else if(isSubclass(type, string))
221                {
222                   void * object = value.p;
223                   Class subtype = superFindClass(string, type.module);
224                   SkipEmpty();
225                   result = GetObject(subtype, &object);
226                   if(result)
227                   {
228                      if(subtype && subtype.type == structClass);
229                      else if(subtype && (subtype.type == normalClass || subtype.type == noHeadClass || subtype.type == bitClass))
230                      {
231                         value.p = object;
232                      }
233                      else
234                      {
235                         result = typeMismatch;
236                         if(subtype)
237                            ((void (*)(void *, void *))(void *)subtype._vTbl[__ecereVMethodID_class_OnFree])(subtype, object);
238                      }
239                   }
240                }
241                else
242                   result = typeMismatch;
243             }
244             delete string;
245          }
246          else
247          {
248             char buffer[256];
249             int c = 0;
250             while(c < sizeof(buffer)-1 && (isalpha(ch) || isdigit(ch) || ch == '_'))
251             {
252                buffer[c++] = ch;
253                if(!f.Getc(&ch)) break;
254             }
255             buffer[c] = 0;
256             result = success;
257
258             if(type)
259             {
260                if(!strcmp(type.name, "bool"))
261                {
262                   if(!strcmpi(buffer, "false")) value.i = 0;
263                   else if(!strcmpi(buffer, "true")) value.i = 1;
264                   else
265                      result = typeMismatch;
266                }
267                else if(!strcmp(type.name, "SetBool"))
268                {
269                   if(!strcmpi(buffer, "false")) value.i = SetBool::false;
270                   else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
271                   else
272                      result = typeMismatch;
273                }
274                else if(!strcmpi(buffer, "null"))
275                {
276                   if(type.type != structClass)
277                      value.p = 0;
278                }
279                else
280                   result = typeMismatch;
281             }
282             else
283                result = typeMismatch;
284          }
285       }
286       else if(ch == '}' || ch == ']')
287          result = noItem;
288       if(result == typeMismatch)
289          PrintLn("Warning: Value type mismatch");
290       return result;
291    }
292
293    JSONResult GetArray(Class type, Container * array)
294    {
295       JSONResult result = syntaxError;
296       SkipEmpty();
297       *array = null;
298       if(ch == '[')
299       {
300          *array = eInstance_New(type);
301          result = success;
302          while(result)
303          {
304             DataValue value { };
305             Class arrayType = null;
306             JSONResult itemResult;
307
308             if(eClass_IsDerived(type, class(Container)))
309             {
310                arrayType = type.templateArgs[0].dataTypeClass;
311             }
312
313             if(arrayType && arrayType.type == structClass)
314                value.p = new0 byte[arrayType.structSize];
315             itemResult = GetValue(arrayType, value);
316             if(itemResult == success)
317             {
318                // TODO: Verify the matching between template type and uint64
319                uint64 t;
320                if(arrayType.type == structClass)
321                {
322                   t = (uint64)(uintptr)value.p;
323                }
324                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
325                {
326                   t = value.ui64; //*(uint64 *)&value.d;
327                }
328                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
329                {
330                   t = value.ui; //f*(uint *)&value.f;
331                }
332                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
333                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
334                {
335                   t = value.ui64;
336                }
337                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
338                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
339                {
340                   t = value.i;
341                }
342                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
343                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
344                   !strcmp(arrayType.dataTypeString, "int16"))
345                {
346                   t = value.s;
347                }
348                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
349                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
350                {
351                   t = value.c;
352                }
353                else
354                {
355                   t = (uint64)(uintptr)value.p;
356                }
357                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
358
359                if(arrayType && arrayType.type == structClass)
360                   delete value.p;
361             }
362             else
363             {
364                if(itemResult == typeMismatch)
365                {
366                   if(arrayType)
367                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
368                }
369                else if(itemResult == noItem)
370                   result = success;
371                else
372                   result = itemResult;
373             }
374
375             if(result != syntaxError)
376             {
377                if(ch != ']' && ch != ',')
378                {
379                   ch = 0;
380                   SkipEmpty();
381                }
382                if(ch == ']')
383                {
384                   break;
385                }
386                else if(ch != ',')
387                   result = syntaxError;
388             }
389          }
390       }
391       ch = 0;
392       return result;
393    }
394
395    JSONResult GetMap(Class type, Map * map)
396    {
397       JSONResult result = syntaxError;
398       SkipEmpty();
399       *map = null;
400       if(ch == '[')
401       {
402          Class mapNodeType = type.templateArgs[0].dataTypeClass;
403          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
404          Property keyProp = null;
405          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
406             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
407
408          *map = eInstance_New(type);
409          result = success;
410
411          while(result)
412          {
413             DataValue value { };
414
415             JSONResult itemResult;
416
417             itemResult = GetValue(mapNodeType, value);
418             if(itemResult == success)
419             {
420                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
421                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
422                // Must free String keys here
423                delete s;
424             }
425             else
426             {
427                if(itemResult == typeMismatch)
428                {
429                   if(mapNodeType)
430                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
431                }
432                else if(itemResult == noItem)
433                   result = success;
434                else
435                   result = itemResult;
436             }
437
438             if(result != syntaxError)
439             {
440                if(ch != ']' && ch != ',')
441                {
442                   ch = 0;
443                   SkipEmpty();
444                }
445                if(ch == ']')
446                {
447                   break;
448                }
449                else if(ch != ',')
450                   result = syntaxError;
451             }
452          }
453       }
454       ch = 0;
455       return result;
456    }
457
458    JSONResult GetIdentifier(String * string, bool * wasQuoted)
459    {
460       JSONResult result = syntaxError;
461       Array<char> buffer { minAllocSize = 256 };
462       bool comment = false;
463       bool quoted = false;
464
465       *string = null;
466       SkipEmpty();
467       if(ch == '\"')
468          quoted = true;
469       else
470          buffer.Add(ch);
471       result = success;
472       while(f.Getc(&ch))
473       {
474          if(!comment && ch == '/')
475          {
476             if(f.Getc(&ch))
477             {
478                if(ch == '/')
479                   break;
480                else if(ch == '*')
481                   comment = true;
482                else
483                {
484                   result = syntaxError;
485                   break;
486                }
487             }
488             else
489             {
490                result = syntaxError;
491                break;
492             }
493          }
494          else if(comment && ch == '*')
495          {
496             if(f.Getc(&ch))
497             {
498                if(ch == '/')
499                {
500                   comment = false;
501                   ch = 0;
502                }
503             }
504             else
505             {
506                result = syntaxError;
507                break;
508             }
509          }
510          else if(ch == '\"' || (!quoted && !comment && ch && !isalpha(ch) && !isdigit(ch) && ch != '_'))
511          {
512             if(quoted && ch == '\"' && wasQuoted)
513                *wasQuoted = true;
514             break;
515          }
516          else if(!comment && ch)
517          {
518             buffer.Add(ch);
519             if(buffer.minAllocSize < buffer.count)
520                buffer.minAllocSize *= 2;
521          }
522       }
523       if(result != syntaxError)
524       {
525          buffer.Add(0);
526          *string = CopyString(buffer.array);
527       }
528       delete buffer;
529       if(ch != ',' && ch != '}' && ch != ';' && ch != '/' && ch != '=' && ch != ':')
530          ch = 0;
531       return result;
532    }
533
534    JSONResult GetString(String * string)
535    {
536       JSONResult result = syntaxError;
537       Array<char> buffer { minAllocSize = 256 };
538       bool escaped = false;
539
540       *string = null;
541       SkipEmpty();
542       if(ch == '\"' || eCON)
543       {
544          while(f.Getc(&ch))
545          {
546             if(ch == '\\' && !escaped)
547                escaped = true;
548             else
549             {
550                if(escaped)
551                {
552                   if(ch == 'b') ch = '\b';
553                   else if(ch == 'f') ch = '\f';
554                   else if(ch == 'n') ch = '\n';
555                   else if(ch == 'r') ch = '\r';
556                   else if(ch == 't') ch = '\t';
557                   else if(ch == 'u')
558                   {
559                      // SKIP FOR NOW...
560                      char unicode[4];
561                      f.Getc(&unicode[0]);
562                      f.Getc(&unicode[1]);
563                      f.Getc(&unicode[2]);
564                      f.Getc(&unicode[3]);
565                   }
566                   escaped = false;
567                }
568                else if(eCON && ch == '\"')
569                {
570                   int seekback = 0;
571                   char pch;
572                   bool lineComment = false;
573                   bool comment = false;
574                   while(!f.Eof())
575                   {
576                      pch = ch;
577                      f.Getc(&ch);
578                      seekback--;
579                      if(!lineComment && !comment && pch == '/')
580                      {
581                         if(ch == '/')
582                            lineComment = true;
583                         else if(ch == '*')
584                            comment = true;
585                      }
586                      else if(lineComment && ch == '\n')
587                         lineComment = false;
588                      else if(comment && pch == '*' && ch == '/')
589                         comment = false;
590                      else if(ch == '=' || ch == ':' || ch == ';' || ch == ',' || ch == ']' || ch == '}')
591                      {
592                         ch = 0;
593                         seekback = -1;
594                         break;
595                      }
596                      else if(ch == '\"')
597                      {
598                         seekback = 0;
599                         ch = 0;
600                         break;
601                      }
602                   }
603                   if(seekback != 0)
604                   {
605                      f.Seek(seekback, current);
606                      break;
607                   }
608                }
609                else if((!eCON && ch == '\"'))
610                {
611                   break;
612                }
613                if(ch)
614                {
615                   buffer.Add(ch);
616                   if(buffer.minAllocSize < buffer.count)
617                      buffer.minAllocSize *= 2;
618                }
619             }
620          }
621          buffer.Add(0);
622          *string = CopyString(buffer.array);
623          result = success;
624       }
625       delete buffer;
626       if(ch != ',' && ch != '}' && (!eCON || (ch != ';' && ch != '/')))
627          ch = 0;
628       return result;
629    }
630
631    public JSONResult GetObject(Class objectType, void ** object)
632    {
633       JSONResult result = syntaxError;
634       if(!objectType || objectType.type != structClass)
635          *object = null;
636       SkipEmpty();
637       if(ch == '{')
638       {
639          Class mapKeyClass = null, mapDataClass = null;
640          Class curClass = null;
641          DataMember curMember = null;
642          DataMember subMemberStack[256];
643          int subMemberStackPos = 0;
644
645          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
646          {
647             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
648             mapDataClass = objectType.templateArgs[2].dataTypeClass;
649          }
650
651          result = success;
652          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
653          {
654             *object = eInstance_New(objectType);
655          }
656          else if(objectType && objectType.type != structClass)
657          {
658             *object = eSystem_New(objectType.typeSize);
659          }
660
661          while(result)
662          {
663             String string;
664             bool wasQuoted = false;
665             int seek;
666             ch = 0;
667             if(eCON)
668             {
669                SkipExtraSemicolon();
670                if(ch == '}')
671                   break;
672             }
673             SkipEmpty();
674             seek = f.Tell();
675             if(eCON ? GetIdentifier(&string, &wasQuoted) : GetString(&string))
676             {
677                DataMember member = null;
678                Property prop = null;
679                Class type = null;
680                bool isKey = false;
681                bool isTemplateArg = false;
682                uint offset = 0;
683                if(eCON)
684                {
685                   SkipEmpty();
686                   prop = null; member = null;
687                   if(ch == '=' || ch == ':')
688                   {
689                      if(wasQuoted)
690                         string[0] = (char)tolower(string[0]);
691                      while(1)
692                      {
693                         eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos);
694                         if(!curMember) break;
695                         if(!strcmp(curMember.name, string))
696                            break;
697                      }
698                   }
699                   else
700                      eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos);
701                   if(curMember)
702                   {
703                      prop = curMember.isProperty ? (Property)curMember : null;
704                      member = curMember.isProperty ? null : curMember;
705
706                      if(mapKeyClass && !strcmp(prop ? prop.name : member.name, "key"))
707                      {
708                         type = mapKeyClass;
709                         isTemplateArg = true;
710                         isKey = true;
711                      }
712                      else if(mapDataClass && !strcmp(prop ? prop.name : member.name, "value"))
713                      {
714                         type = mapDataClass;
715                         isTemplateArg = true;
716                         if(member)
717                            offset = member._class.offset + member.offset;
718                      }
719                      else if(prop)
720                         type = superFindClass(prop.dataTypeString, objectType.module);
721                      else if(member)
722                      {
723                         type = superFindClass(member.dataTypeString, objectType.module);
724                         offset = member._class.offset + member.offset;
725                      }
726                   }
727                   else
728                   {
729                      if(ch == '=' || ch == ':')
730                         PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
731                      else
732                         PrintLn("Warning: default member assignment: no more members");
733                   }
734                }
735                if(objectType && !eCON)
736                {
737                   string[0] = (char)tolower(string[0]);
738                   if(mapKeyClass && !strcmp(string, "key"))
739                   {
740                      prop = eClass_FindProperty(objectType, "key", objectType.module);
741                      type = mapKeyClass;
742                      isTemplateArg = true;
743                      isKey = true;
744                   }
745                   else if(mapDataClass && !strcmp(string, "value"))
746                   {
747                      prop = eClass_FindProperty(objectType, "value", objectType.module);
748                      type = mapDataClass;
749                      isTemplateArg = true;
750                   }
751                   else
752                   {
753                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
754                      if(member)
755                      {
756                         type = superFindClass(member.dataTypeString, objectType.module);
757                         offset = member._class.offset + member.offset;
758                      }
759                      else if(!member)
760                      {
761                         prop = eClass_FindProperty(objectType, string, objectType.module);
762                         if(prop)
763                            type = superFindClass(prop.dataTypeString, objectType.module);
764                         else
765                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
766                      }
767                   }
768                }
769                if(type && type.templateClass && type.templateClass == class(Container))
770                {
771                   char * br = strchr(type.fullName, '<');
772                   if(br)
773                   {
774                      char className[1024];
775                      strcpy(className, "Array");
776                      strcat(className, br);
777                      type = superFindClass(className, objectType.module);
778                   }
779                }
780
781                // Find Member in Object Class
782                {
783                   DataValue value { };
784
785                   if(type && type.type == structClass)
786                   {
787                      if(member)
788                      {
789                         value.p = (byte *)*object + offset;
790                         memset(value.p, 0, type.structSize);
791                      }
792                      else if(prop)
793                      {
794                         value.p = new0 byte[type.structSize];
795                      }
796                   }
797                   if(!eCON)
798                   {
799                      ch = 0;
800                      SkipEmpty();
801                   }
802                   if(eCON && ch != '=' && ch != ':')
803                   {
804                      f.Seek(seek-1, start);
805                      ch = 0;
806                   }
807                   if((ch == ':' || (eCON && ch == '=')) || (eCON && type && (prop || member)))
808                   {
809                      JSONResult itemResult = GetValue(type, value);
810                      if(itemResult != syntaxError)
811                      {
812                         if(prop || member)
813                         {
814                            if(!type)
815                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
816                            else if(itemResult == success)
817                            {
818                               // Set value
819                               if(member)
820                               {
821                                  // TOFIX: How to swiftly handle classes with base data type?
822                                  if(type.type == structClass)
823                                     ;
824                                  else if(type.type == normalClass || type.type == noHeadClass)
825                                  {
826                                     void ** ptr = (void**)((byte *)*object + offset);
827                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
828                                     {
829                                        Container container = (Container)*ptr;
830                                        container.Free();
831                                        delete container;
832                                     }
833                                     *ptr = value.p;
834                                  }
835                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
836                                  {
837                                     *(double *)((byte *)*object + offset) = value.d;
838                                  }
839                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
840                                  {
841                                     *(float *)((byte *)*object + offset) = value.f;
842                                  }
843                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
844                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
845                                  {
846                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
847                                  }
848                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
849                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
850                                  {
851                                     *(int *)((byte *)*object + offset) = value.i;
852                                  }
853                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
854                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
855                                     !strcmp(type.dataTypeString, "int16"))
856                                  {
857                                     *(short *)((byte *)*object + offset) = value.s;
858                                  }
859                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
860                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
861                                  {
862                                     *(char *)((byte *)*object + offset) = value.c;
863                                  }
864                                  else
865                                  {
866                                     *(void **)((byte *)*object + offset) = value.p;
867                                  }
868                               }
869                               else if(prop && prop.Set)
870                               {
871                                  if(!strcmp(type.dataTypeString, "char *"))
872                                  {
873                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
874                                     if(!isKey)
875                                        delete value.p;
876                                  }
877                                  else if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
878                                  {
879                                     // TOFIX: How to swiftly handle classes with base data type?
880                                     if(type == class(double) || !strcmp(type.dataTypeString, "double"))
881                                     {
882                                        ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
883                                     }
884                                     else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
885                                     {
886                                        ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
887                                     }
888                                     else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
889                                        !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
890                                     {
891                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
892                                     }
893                                     else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
894                                        !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
895                                     {
896                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
897                                     }
898                                     else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
899                                        !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
900                                        !strcmp(type.dataTypeString, "int16"))
901                                     {
902                                        ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
903                                     }
904                                     else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
905                                        !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
906                                     {
907                                        ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
908                                     }
909                                     else
910                                     {
911                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
912                                     }
913                                  }
914                                  else
915                                  {
916                                     if(isTemplateArg)
917                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, (uint64)(uintptr)value.p);
918                                     else
919                                        ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
920                                  }
921                               }
922                            }
923                            else
924                            {
925                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
926                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
927                            }
928                         }
929                      }
930                   }
931                   else
932                      result = syntaxError;
933
934                   if(prop && type && type.type == structClass)
935                   {
936                      delete value.p;
937                   }
938                }
939             }
940             else if(ch && ch != '}' && ch != ',' && (!eCON || ch != ';'))
941                result = syntaxError;
942             delete string;
943
944             if(result)
945             {
946                SkipEmpty();
947                if(ch == '}')
948                {
949                   break;
950                }
951                else if(ch != ',' && (!eCON || ch != ';'))
952                   result = syntaxError;
953             }
954          }
955       }
956       ch = 0;
957       return result;
958    }
959
960    JSONResult GetNumber(Class type, DataValue value)
961    {
962       JSONResult result = success;
963       char buffer[256];
964       int c = 0;
965       bool comment = false;
966       bool hexMode = false;
967       if(eCON)
968       {
969          while(c < sizeof(buffer)-1 && (comment || ch == '-' || ch == '.' || tolower(ch) == 'f' ||
970                      (c == 1 && tolower(ch) == 'x' && buffer[0] == '0') || tolower(ch) == 'e' || ch == '+' || isdigit(ch) || ch == '/' ||
971                      (hexMode && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))))
972          {
973             if(!comment && ch == '/')
974             {
975                if(f.Getc(&ch))
976                {
977                   if(ch == '*')
978                      comment = true;
979                }
980                else
981                {
982                   result = syntaxError;
983                   break;
984                }
985             }
986             else if(comment && ch == '*')
987             {
988                if(f.Getc(&ch))
989                {
990                   if(ch == '/')
991                      comment = false;
992                }
993                else
994                {
995                   result = syntaxError;
996                   break;
997                }
998             }
999             else if(!comment)
1000             {
1001                if(c == 1 && ch == 'x' && buffer[0] == '0')
1002                   hexMode = true;
1003                buffer[c++] = ch;
1004             }
1005             if(!f.Getc(&ch)) break;
1006          }
1007       }
1008       else
1009       {
1010          while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
1011          {
1012             buffer[c++] = ch;
1013             if(!f.Getc(&ch)) break;
1014          }
1015       }
1016       buffer[c] = 0;
1017       //if(strchr(buffer, '.'))
1018       if(result == syntaxError)
1019          return result;
1020       if(!type) return success;
1021       result = syntaxError;
1022
1023       // TOFIX: How to swiftly handle classes with base data type?
1024       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1025       {
1026          value.d = strtod(buffer, null);
1027          result = success;
1028       }
1029       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1030       {
1031          value.f = (float)strtod(buffer, null);
1032          result = success;
1033       }
1034       // TOFIX: int64 looks for class long long?
1035       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
1036       else if(!strcmp(type.dataTypeString, "int64"))
1037       {
1038          value.i64 = strtol(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1039          result = success;
1040       }
1041       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
1042       {
1043          value.ui64 = strtoul(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1044          result = success;
1045       }
1046       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1047       {
1048          value.ui = (uint)strtoul(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1049          result = success;
1050       }
1051       else
1052       {
1053          value.i = (int)strtol(buffer, null, eCON ? 0 : 10);
1054          result = success;
1055       }
1056
1057       if(result == success && type.type == unitClass)
1058       {
1059          // Convert to reference unit
1060          Property prop;
1061          for(prop = type.conversions.first; prop; prop = prop.next)
1062          {
1063             bool refProp = false;
1064             if(!strcmp(prop.name, type.base.fullName))
1065                refProp = true;
1066             else
1067             {
1068                Class c = eSystem_FindClass(type.module, prop.name);
1069                if(!c)
1070                   c = eSystem_FindClass(type.module.application, prop.name);
1071                if(c)
1072                {
1073                   Property p;
1074                   for(p = c.conversions.first; p; p = p.next)
1075                   {
1076                      if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
1077                      {
1078                         refProp = true;
1079                         break;
1080                      }
1081                   }
1082                }
1083             }
1084             if(refProp)
1085             {
1086                if(prop.Set && prop.Get)
1087                {
1088                   const String dts = type.base.dataTypeString;
1089                   if(!strcmp(dts, "double"))
1090                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
1091                   else if(!strcmp(dts, "float"))
1092                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
1093                   else if(!strcmp(dts, "int"))
1094                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
1095                   else if(!strcmp(dts, "int64"))
1096                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
1097                   else if(!strcmp(dts, "unsigned int"))
1098                      value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
1099                   else if(!strcmp(dts, "uint64"))
1100                      value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
1101                }
1102                else
1103                   break;
1104             }
1105          }
1106       }
1107       return result;
1108    }
1109
1110    JSONResult GetColorAlpha(String string, DataValue value)
1111    {
1112       ColorAlpha color = 0;
1113       DefinedColor c = 0;
1114       if(string)
1115       {
1116          if(string[0] == '0' && string[1] == 'x')
1117             color = (uint)strtoul(string, null, 0);
1118          else
1119          {
1120             char *d;
1121             byte a = 255;
1122             if((d = strchr(string, ',')))
1123             {
1124                a = (byte)atoi(string);
1125                d += 2;
1126             }
1127             else
1128                d = string;
1129             if(c.class::OnGetDataFromString(d))
1130             {
1131                color.a = a;
1132                color.color = c;
1133             }
1134             else
1135                color = (uint)strtoul(string, null, 16);
1136          }
1137       }
1138       value.i = color;
1139       return success;
1140    }
1141 }
1142
1143 static bool WriteMap(File f, Class type, Map map, int indent, bool eCON)
1144 {
1145    if(map)
1146    {
1147       int i;
1148       bool isFirst = true;
1149       MapIterator it { map = map };
1150       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
1151       f.Puts("[\n");
1152       indent++;
1153
1154       while(it.Next())
1155       {
1156          MapNode n = (MapNode)it.pointer;
1157          if(!isFirst)
1158             f.Puts(",\n");
1159          else
1160             isFirst = false;
1161          for(i = 0; i<indent; i++) f.Puts("   ");
1162          WriteONObject(f, mapNodeClass, n, indent, eCON, eCON ? true : false);
1163       }
1164       f.Puts("\n");
1165       indent--;
1166       for(i = 0; i<indent; i++) f.Puts("   ");
1167       f.Puts("]");
1168    }
1169    else
1170       f.Puts("null");
1171    return true;
1172 }
1173
1174 static bool WriteArray(File f, Class type, Container array, int indent, bool eCON)
1175 {
1176    if(array)
1177    {
1178       int i;
1179       bool isFirst = true;
1180       Iterator it { array };
1181       Class arrayType = type.templateArgs[0].dataTypeClass;
1182       f.Puts("[\n");
1183       indent++;
1184
1185       while(it.Next())
1186       {
1187          DataValue value { };
1188          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
1189          if(!isFirst)
1190             f.Puts(",\n");
1191          else
1192             isFirst = false;
1193
1194          // Add value
1195          // TODO: Verify the matching between template type and uint64
1196          if(arrayType.type == structClass)
1197          {
1198             value.p = (void *)(uintptr)t;
1199          }
1200          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
1201          {
1202             value.ui64 = t;
1203             //value.d = *(double *)&t;
1204          }
1205          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
1206          {
1207             value.ui = (uint)t;
1208             //value.f = *(float *)&t;
1209          }
1210          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
1211             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
1212          {
1213             value.ui64 = t;
1214          }
1215          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
1216             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
1217          {
1218             value.i = (int)t;
1219          }
1220          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
1221             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
1222             !strcmp(arrayType.dataTypeString, "int16"))
1223          {
1224             value.s = (short)t;
1225          }
1226          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
1227             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
1228          {
1229             value.c = (char)t;
1230          }
1231          else
1232          {
1233             value.p = (void *)(uintptr)t;
1234          }
1235          for(i = 0; i<indent; i++) f.Puts("   ");
1236          WriteValue(f, arrayType, value, indent, eCON);
1237       }
1238       f.Puts("\n");
1239       indent--;
1240       for(i = 0; i<indent; i++) f.Puts("   ");
1241       f.Puts("]");
1242    }
1243    else
1244       f.Puts("null");
1245    return true;
1246 }
1247
1248 static bool WriteNumber(File f, Class type, DataValue value, int indent, bool eCON, bool useHex)
1249 {
1250    char buffer[1024];
1251    bool needClass = eCON;
1252    bool quote;
1253    buffer[0] = 0;
1254    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1255       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
1256    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1257       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
1258    else if(!strcmp(type.dataTypeString, "int64"))
1259       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
1260    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
1261    {
1262       if(useHex)
1263          sprintf(buffer, __runtimePlatform == win32 ? "0x%016I64X" : "0x%016llX", value.ui64);
1264       else
1265          ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
1266    }
1267    else if(!strcmp(type.dataTypeString, "int"))
1268       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
1269    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
1270    {
1271       if(useHex)
1272          sprintf(buffer, "0x%08X", value.ui);
1273       else
1274          ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
1275    }
1276    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
1277       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
1278    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
1279       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
1280    else if(!strcmp(type.dataTypeString, "char"))
1281       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
1282    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
1283       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
1284
1285    quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' '))) ||
1286            (type.type == enumClass && !eCON);
1287    if(quote) f.Puts("\"");
1288    f.Puts(buffer);
1289    if(quote) f.Puts("\"");
1290    return true;
1291 }
1292
1293 public bool WriteColorAlpha(File f, Class type, DataValue value, int indent, bool eCON)
1294 {
1295    char buffer[1024];
1296    char * string = buffer;
1297    ColorAlpha color = value.i;
1298    int a = color.a;
1299    int len;
1300    DefinedColor c = color;
1301    buffer[0] = '\0';
1302    if(a != 255)
1303    {
1304       a.class::OnGetString(buffer, null, null);
1305       len = strlen(buffer);
1306       buffer[len++] = ',';
1307       buffer[len++] = ' ';
1308       buffer[len] = '\0';
1309       string += len;
1310    }
1311    if(!c.class::OnGetString(string, null, null))
1312       sprintf(buffer, "0x%x", color);
1313    if(!eCON)
1314       f.Puts("\"");
1315    f.Puts(buffer);
1316    if(!eCON)
1317       f.Puts("\"");
1318    return true;
1319 }
1320
1321 static bool WriteValue(File f, Class type, DataValue value, int indent, bool eCON)
1322 {
1323    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
1324    {
1325       if(!value.p)
1326          f.Puts("null");
1327       else
1328       {
1329          f.Puts("\"");
1330          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
1331          if(eCON)
1332          {
1333             int c = 0;
1334             int b = 0;
1335             char buffer[1024];
1336             char * string = value.p;
1337             char ch;
1338             while(true)
1339             {
1340                ch = string[c++];
1341                if(ch == '\"')
1342                {
1343                   buffer[b] = 0;
1344                   f.Puts(buffer);
1345                   f.Puts("\\\"");
1346                   b = 0;
1347                }
1348                else if(ch == '\\')
1349                {
1350                   buffer[b] = 0;
1351                   f.Puts(buffer);
1352                   f.Puts("\\\\");
1353                   b = 0;
1354                }
1355                else if(ch == '\t')
1356                {
1357                   buffer[b] = 0;
1358                   f.Puts(buffer);
1359                   f.Puts("\\t");
1360                   b = 0;
1361                }
1362                else if(ch == '\n')
1363                {
1364                   int i;
1365                   buffer[b] = 0;
1366                   f.Puts(buffer);
1367                   f.Puts("\\n\"\n");
1368                   for(i = 0; i<indent; i++) f.Puts("   ");
1369                   f.Puts("   \"");
1370                   b = 0;
1371                }
1372                else if(b == sizeof(buffer)-2 || !ch)
1373                {
1374                   buffer[b++] = ch;
1375                   if(ch) buffer[b] = 0;
1376                   f.Puts(buffer);
1377                   b = 0;
1378                   if(!ch) break;
1379                }
1380                else
1381                   buffer[b++] = ch;
1382             }
1383          }
1384          else
1385          {
1386             int c = 0;
1387             int b = 0;
1388             char buffer[1024];
1389             char * string = value.p;
1390             char ch;
1391             while(true)
1392             {
1393                ch = string[c++];
1394                if(ch == '\"')
1395                {
1396                   buffer[b] = 0;
1397                   f.Puts(buffer);
1398                   f.Puts("\\\"");
1399                   b = 0;
1400                }
1401                else if(ch == '\\')
1402                {
1403                   buffer[b] = 0;
1404                   f.Puts(buffer);
1405                   f.Puts("\\\\");
1406                   b = 0;
1407                }
1408                else if(b == sizeof(buffer)-2 || !ch)
1409                {
1410                   buffer[b++] = ch;
1411                   if(ch) buffer[b] = 0;
1412                   f.Puts(buffer);
1413                   b = 0;
1414                   if(!ch) break;
1415                }
1416                else
1417                   buffer[b++] = ch;
1418             }
1419          }
1420          /*else
1421             f.Puts(value.p);*/
1422          f.Puts("\"");
1423       }
1424    }
1425    else if(!strcmp(type.name, "bool"))
1426    {
1427       if(value.i)
1428          f.Puts("true");
1429       else
1430          f.Puts("false");
1431    }
1432    else if(!strcmp(type.name, "SetBool"))
1433    {
1434       if(value.i == SetBool::true)
1435          f.Puts("true");
1436       else if(value.i == SetBool::false)
1437          f.Puts("false");
1438       else
1439          f.Puts("unset");
1440    }
1441    else if(type.type == enumClass)
1442       WriteNumber(f, type, value, indent, eCON, false);
1443    else if(eClass_IsDerived(type, class(Map)))
1444    {
1445       WriteMap(f, type, value.p, indent, eCON);
1446    }
1447    else if(eClass_IsDerived(type, class(Container)))
1448    {
1449       WriteArray(f, type, value.p, indent, eCON);
1450    }
1451    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
1452    {
1453       WriteONObject(f, type, value.p, indent, eCON, false);
1454    }
1455    else if(eClass_IsDerived(type, class(ColorAlpha)))
1456    {
1457       WriteColorAlpha(f, type, value, indent, eCON);
1458    }
1459    else if(type.type == bitClass)
1460    {
1461       Class dataType;
1462       dataType = superFindClass(type.dataTypeString, type.module);
1463       WriteNumber(f, dataType, value, indent, eCON, true);
1464    }
1465    else if(type.type == systemClass || type.type == unitClass)
1466    {
1467       WriteNumber(f, type, value, indent, eCON, false);
1468    }
1469    return true;
1470 }
1471
1472 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
1473 {
1474    bool result = false;
1475    if(object)
1476    {
1477       result = WriteONObject(f, objectType, object, indent, false, false);
1478       f.Puts("\n");
1479    }
1480    return result;
1481 }
1482
1483 public bool WriteECONObject(File f, Class objectType, void * object, int indent)
1484 {
1485    bool result = false;
1486    if(object)
1487    {
1488       result = WriteONObject(f, objectType, object, indent, true, false);
1489       f.Puts("\n");
1490    }
1491    return result;
1492 }
1493
1494 static bool WriteONObject(File f, Class objectType, void * object, int indent, bool eCON, bool omitDefaultIdentifier)
1495 {
1496    if(object)
1497    {
1498       const char * string = null;
1499
1500       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
1501       {
1502          char buffer[1024];
1503          buffer[0] = 0;
1504          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
1505       }
1506       if(string)
1507       {
1508          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
1509          if(string[0] == '\"')
1510             f.Puts(string);
1511          else
1512          {
1513             f.Puts("\"");
1514             f.Puts(string);
1515             f.Puts("\"");
1516          }
1517       }
1518       else
1519       {
1520          Class _class = (eCON && objectType.type == normalClass) ? ((Instance)object)._class : objectType;
1521          Property prop;
1522          int c;
1523          bool isFirst = true;
1524          Class mapKeyClass = null, mapDataClass = null;
1525          Class baseClass;
1526          List<Class> bases { };
1527
1528          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
1529          {
1530             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
1531             mapDataClass = objectType.templateArgs[2].dataTypeClass;
1532          }
1533
1534          if(eCON && _class != objectType && eClass_IsDerived(_class, objectType))
1535          {
1536             f.Puts(_class.name);
1537             f.Puts(" ");
1538          }
1539
1540          f.Puts("{\n");
1541          indent++;
1542
1543          for(baseClass = _class; baseClass; baseClass = baseClass.base)
1544          {
1545             if(baseClass.isInstanceClass || !baseClass.base)
1546                break;
1547             bases.Insert(null, baseClass);
1548          }
1549
1550          for(baseClass : bases)
1551          {
1552             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
1553             {
1554                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
1555                if(prop.isProperty)
1556                {
1557                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
1558                   {
1559                      DataValue value { };
1560                      bool isTemplateArg = false;
1561                      Class type;
1562
1563                      if(mapKeyClass && !strcmp(prop.name, "key"))
1564                      {
1565                         isTemplateArg = true;
1566                         type = mapKeyClass;
1567                      }
1568                      else if(mapDataClass && !strcmp(prop.name, "value"))
1569                      {
1570                         isTemplateArg = true;
1571                         type = mapDataClass;
1572                      }
1573                      else
1574                         type = superFindClass(prop.dataTypeString, _class.module);
1575
1576                      if(!type)
1577                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
1578                      else
1579                      {
1580                         if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
1581                         {
1582                            // TOFIX: How to swiftly handle classes with base data type?
1583                            if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1584                            {
1585                               value.d = ((double (*)(void *))(void *)prop.Get)(object);
1586                            }
1587                            else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1588                            {
1589                               value.f = ((float (*)(void *))(void *)prop.Get)(object);
1590                            }
1591                            else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1592                               !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1593                            {
1594                               value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
1595                            }
1596                            else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1597                               !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1598                            {
1599                               value.i = ((int (*)(void *))(void *)prop.Get)(object);
1600                            }
1601                            else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1602                               !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1603                               !strcmp(type.dataTypeString, "int16"))
1604                            {
1605                               value.s = ((short (*)(void *))(void *)prop.Get)(object);
1606                            }
1607                            else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1608                               !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1609                            {
1610                               value.c = ((char (*)(void *))(void *)prop.Get)(object);
1611                            }
1612                         }
1613                         else if(type.type == structClass)
1614                         {
1615                            value.p = new0 byte[type.structSize];
1616                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1617                         }
1618                         else
1619                         {
1620                            if(isTemplateArg)
1621                               value.p = (void *)(uintptr)((uint64 (*)(void *))(void *)prop.Get)(object);
1622                            else
1623                               value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1624                         }
1625
1626                         if(!isFirst) f.Puts(",\n");
1627                         for(c = 0; c<indent; c++) f.Puts("   ");
1628
1629                         if(!eCON)
1630                         {
1631                            f.Puts("\"");
1632                            f.Putc((char)toupper(prop.name[0]));
1633                            f.Puts(prop.name+1);
1634                            f.Puts("\" : ");
1635                         }
1636                         else if(!omitDefaultIdentifier)
1637                         {
1638                            f.Puts(prop.name);
1639                            f.Puts(" = ");
1640                         }
1641                         WriteValue(f, type, value, indent, eCON);
1642                         isFirst = false;
1643                         if(type.type == structClass)
1644                            delete value.p;
1645                      }
1646                   }
1647                }
1648                else
1649                {
1650                   DataMember member = (DataMember)prop;
1651                   DataValue value { };
1652                   uint offset;
1653                   Class type = superFindClass(member.dataTypeString, _class.module);
1654                   offset = member._class.offset + member.offset;
1655
1656                   if(type)
1657                   {
1658                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1659                      {
1660                         if(type.type == structClass)
1661                            value.p = (void *)((byte *)object + offset);
1662                         else
1663                            value.p = *(void **)((byte *)object + offset);
1664                         if(!value.p)
1665                            continue;
1666                      }
1667                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1668                      {
1669                         value.d = *(double *)((byte *)object + offset);
1670                      }
1671                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1672                      {
1673                         value.f = *(float *)((byte *)object + offset);
1674                      }
1675                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1676                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1677                      {
1678                         value.ui64 = *(uint64 *)((byte *)object + offset);
1679                      }
1680                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1681                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1682                      {
1683                         value.i = *(int *)((byte *)object + offset);
1684                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1685                            if(!value.i)
1686                               continue;
1687                      }
1688                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1689                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1690                         !strcmp(type.dataTypeString, "int16"))
1691                      {
1692                         value.s = *(short *)((byte *)object + offset);
1693                      }
1694                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1695                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1696                      {
1697                         value.c = *(char *)((byte *)object + offset);
1698                      }
1699                      else
1700                      {
1701                         value.i = *(int *)((byte *)object + offset);
1702                      }
1703
1704                      if(!isFirst) f.Puts(",\n");
1705                      for(c = 0; c<indent; c++) f.Puts("   ");
1706
1707                      if(!eCON)
1708                      {
1709                         f.Puts("\"");
1710                         f.Putc((char)toupper(member.name[0]));
1711                         f.Puts(member.name+1);
1712                         f.Puts("\" : ");
1713                      }
1714                      else if(!omitDefaultIdentifier)
1715                      {
1716                         f.Puts(member.name);
1717                         f.Puts(" = ");
1718                      }
1719                      WriteValue(f, type, value, indent, eCON);
1720                      isFirst = false;
1721                   }
1722                }
1723             }
1724          }
1725
1726          delete bases;
1727
1728          indent--;
1729          f.Puts("\n");
1730          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1731       }
1732    }
1733    else
1734       f.Puts("null");
1735    return true;
1736 }
1737
1738 static Class superFindClass(const String name, Module alternativeModule)
1739 {
1740    Class _class = eSystem_FindClass(__thisModule, name);
1741    if(!_class && alternativeModule)
1742       _class = eSystem_FindClass(alternativeModule, name);
1743    if(!_class)
1744       _class = eSystem_FindClass(__thisModule.application, name);
1745    return _class;
1746 }
1747
1748 static bool isSubclass(Class type, const String name)
1749 {
1750    Class _class = superFindClass(name, type.module);
1751    if(eClass_IsDerived(_class, type))
1752       return true;
1753    return false;
1754 }