ecere/econ,ecere/json: handle identifiers and values with _ and digits in GetIdentifi...
[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 == 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 == '\"' || (!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 != '=')
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 == '}')
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 == '=')
688                   {
689                      while(1)
690                      {
691                         eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos);
692                         if(!curMember) break;
693                         if(!strcmp(curMember.name, string))
694                            break;
695                      }
696                   }
697                   else
698                      eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos);
699                   if(curMember)
700                   {
701                      prop = curMember.isProperty ? (Property)curMember : null;
702                      member = curMember.isProperty ? null : curMember;
703
704                      if(mapKeyClass && !strcmp(prop ? prop.name : member.name, "key"))
705                      {
706                         type = mapKeyClass;
707                         isTemplateArg = true;
708                         isKey = true;
709                      }
710                      else if(mapDataClass && !strcmp(prop ? prop.name : member.name, "value"))
711                      {
712                         type = mapDataClass;
713                         isTemplateArg = true;
714                         if(member)
715                            offset = member._class.offset + member.offset;
716                      }
717                      else if(prop)
718                         type = superFindClass(prop.dataTypeString, objectType.module);
719                      else if(member)
720                      {
721                         type = superFindClass(member.dataTypeString, objectType.module);
722                         offset = member._class.offset + member.offset;
723                      }
724                   }
725                   else
726                   {
727                      if(ch == '=')
728                         PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
729                      else
730                         PrintLn("Warning: default member assignment: no more members");
731                   }
732                }
733                if(objectType && !eCON)
734                {
735                   string[0] = (char)tolower(string[0]);
736                   if(mapKeyClass && !strcmp(string, "key"))
737                   {
738                      prop = eClass_FindProperty(objectType, "key", objectType.module);
739                      type = mapKeyClass;
740                      isTemplateArg = true;
741                      isKey = true;
742                   }
743                   else if(mapDataClass && !strcmp(string, "value"))
744                   {
745                      prop = eClass_FindProperty(objectType, "value", objectType.module);
746                      type = mapDataClass;
747                      isTemplateArg = true;
748                   }
749                   else
750                   {
751                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
752                      if(member)
753                      {
754                         type = superFindClass(member.dataTypeString, objectType.module);
755                         offset = member._class.offset + member.offset;
756                      }
757                      else if(!member)
758                      {
759                         prop = eClass_FindProperty(objectType, string, objectType.module);
760                         if(prop)
761                            type = superFindClass(prop.dataTypeString, objectType.module);
762                         else
763                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
764                      }
765                   }
766                }
767                // Find Member in Object Class
768                {
769                   DataValue value { };
770
771                   if(type && type.type == structClass)
772                   {
773                      if(member)
774                      {
775                         value.p = (byte *)*object + offset;
776                         memset(value.p, 0, type.structSize);
777                      }
778                      else if(prop)
779                      {
780                         value.p = new0 byte[type.structSize];
781                      }
782                   }
783                   if(!eCON)
784                   {
785                      ch = 0;
786                      SkipEmpty();
787                   }
788                   if(eCON && ch != '=')
789                   {
790                      f.Seek(seek-1, start);
791                      ch = 0;
792                   }
793                   if(ch == (eCON ? '=' : ':') || (eCON && type && (prop || member)))
794                   {
795                      JSONResult itemResult = GetValue(type, value);
796                      if(itemResult != syntaxError)
797                      {
798                         if(prop || member)
799                         {
800                            if(!type)
801                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
802                            else if(itemResult == success)
803                            {
804                               // Set value
805                               if(member)
806                               {
807                                  // TOFIX: How to swiftly handle classes with base data type?
808                                  if(type.type == structClass)
809                                     ;
810                                  else if(type.type == normalClass || type.type == noHeadClass)
811                                  {
812                                     void ** ptr = (void**)((byte *)*object + offset);
813                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
814                                     {
815                                        Container container = (Container)*ptr;
816                                        container.Free();
817                                        delete container;
818                                     }
819                                     *ptr = value.p;
820                                  }
821                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
822                                  {
823                                     *(double *)((byte *)*object + offset) = value.d;
824                                  }
825                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
826                                  {
827                                     *(float *)((byte *)*object + offset) = value.f;
828                                  }
829                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
830                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
831                                  {
832                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
833                                  }
834                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
835                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
836                                  {
837                                     *(int *)((byte *)*object + offset) = value.i;
838                                  }
839                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
840                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
841                                     !strcmp(type.dataTypeString, "int16"))
842                                  {
843                                     *(short *)((byte *)*object + offset) = value.s;
844                                  }
845                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
846                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
847                                  {
848                                     *(char *)((byte *)*object + offset) = value.c;
849                                  }
850                                  else
851                                  {
852                                     *(void **)((byte *)*object + offset) = value.p;
853                                  }
854                               }
855                               else if(prop && prop.Set)
856                               {
857                                  if(!strcmp(type.dataTypeString, "char *"))
858                                  {
859                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
860                                     if(!isKey)
861                                        delete value.p;
862                                  }
863                                  else if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
864                                  {
865                                     // TOFIX: How to swiftly handle classes with base data type?
866                                     if(type == class(double) || !strcmp(type.dataTypeString, "double"))
867                                     {
868                                        ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
869                                     }
870                                     else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
871                                     {
872                                        ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
873                                     }
874                                     else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
875                                        !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
876                                     {
877                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
878                                     }
879                                     else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
880                                        !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
881                                     {
882                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
883                                     }
884                                     else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
885                                        !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
886                                        !strcmp(type.dataTypeString, "int16"))
887                                     {
888                                        ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
889                                     }
890                                     else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
891                                        !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
892                                     {
893                                        ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
894                                     }
895                                     else
896                                     {
897                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
898                                     }
899                                  }
900                                  else
901                                  {
902                                     if(isTemplateArg)
903                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, (uint64)(uintptr)value.p);
904                                     else
905                                        ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
906                                  }
907                               }
908                            }
909                            else
910                            {
911                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
912                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
913                            }
914                         }
915                      }
916                   }
917                   else
918                      result = syntaxError;
919
920                   if(prop && type && type.type == structClass)
921                   {
922                      delete value.p;
923                   }
924                }
925             }
926             else if(ch && ch != '}' && ch != ',' && (!eCON || ch != ';'))
927                result = syntaxError;
928             delete string;
929
930             if(result)
931             {
932                SkipEmpty();
933                if(ch == '}')
934                {
935                   break;
936                }
937                else if(ch != ',' && (!eCON || ch != ';'))
938                   result = syntaxError;
939             }
940          }
941       }
942       ch = 0;
943       return result;
944    }
945
946    JSONResult GetNumber(Class type, DataValue value)
947    {
948       JSONResult result = success;
949       char buffer[256];
950       int c = 0;
951       bool comment = false;
952       if(eCON)
953       {
954          while(c < sizeof(buffer)-1 && (comment || ch == '-' || ch == '.' || tolower(ch) == 'f' ||
955                      tolower(ch) == 'x' || tolower(ch) == 'e' || ch == '+' || isdigit(ch) || ch == '/'))
956          {
957             if(!comment && ch == '/')
958             {
959                if(f.Getc(&ch))
960                {
961                   if(ch == '*')
962                      comment = true;
963                }
964                else
965                {
966                   result = syntaxError;
967                   break;
968                }
969             }
970             else if(comment && ch == '*')
971             {
972                if(f.Getc(&ch))
973                {
974                   if(ch == '/')
975                      comment = false;
976                }
977                else
978                {
979                   result = syntaxError;
980                   break;
981                }
982             }
983             else if(!comment)
984                buffer[c++] = ch;
985             if(!f.Getc(&ch)) break;
986          }
987       }
988       else
989       {
990          while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
991          {
992             buffer[c++] = ch;
993             if(!f.Getc(&ch)) break;
994          }
995       }
996       buffer[c] = 0;
997       //if(strchr(buffer, '.'))
998       if(result == syntaxError)
999          return result;
1000       if(!type) return success;
1001       result = syntaxError;
1002
1003       // TOFIX: How to swiftly handle classes with base data type?
1004       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1005       {
1006          value.d = strtod(buffer, null);
1007          result = success;
1008       }
1009       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1010       {
1011          value.f = (float)strtod(buffer, null);
1012          result = success;
1013       }
1014       // TOFIX: int64 looks for class long long?
1015       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
1016       else if(!strcmp(type.dataTypeString, "int64"))
1017       {
1018          value.i64 = strtol(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1019          result = success;
1020       }
1021       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
1022       {
1023          value.ui64 = strtoul(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1024          result = success;
1025       }
1026       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int"))
1027       {
1028          value.ui = (uint)strtoul(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1029          result = success;
1030       }
1031       else
1032       {
1033          value.i = (int)strtol(buffer, null, eCON ? 0 : 10);
1034          result = success;
1035       }
1036
1037       if(result == success && type.type == unitClass)
1038       {
1039          // Convert to reference unit
1040          Property prop;
1041          for(prop = type.conversions.first; prop; prop = prop.next)
1042          {
1043             bool refProp = false;
1044             if(!strcmp(prop.name, type.base.fullName))
1045                refProp = true;
1046             else
1047             {
1048                Class c = eSystem_FindClass(type.module, prop.name);
1049                if(!c)
1050                   c = eSystem_FindClass(type.module.application, prop.name);
1051                if(c)
1052                {
1053                   Property p;
1054                   for(p = c.conversions.first; p; p = p.next)
1055                   {
1056                      if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
1057                      {
1058                         refProp = true;
1059                         break;
1060                      }
1061                   }
1062                }
1063             }
1064             if(refProp)
1065             {
1066                if(prop.Set && prop.Get)
1067                {
1068                   const String dts = type.base.dataTypeString;
1069                   if(!strcmp(dts, "double"))
1070                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
1071                   else if(!strcmp(dts, "float"))
1072                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
1073                   else if(!strcmp(dts, "int"))
1074                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
1075                   else if(!strcmp(dts, "int64"))
1076                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
1077                   else if(!strcmp(dts, "unsigned int"))
1078                      value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
1079                   else if(!strcmp(dts, "uint64"))
1080                      value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
1081                }
1082                else
1083                   break;
1084             }
1085          }
1086       }
1087       return result;
1088    }
1089
1090    JSONResult GetColorAlpha(String string, DataValue value)
1091    {
1092       ColorAlpha color = 0;
1093       DefinedColor c = 0;
1094       if(string)
1095       {
1096          if(string[0] == '0' && string[1] == 'x')
1097             color = (uint)strtoul(string, null, 0);
1098          else
1099          {
1100             char *d;
1101             byte a = 255;
1102             if((d = strchr(string, ',')))
1103             {
1104                a = (byte)atoi(string);
1105                d += 2;
1106             }
1107             else
1108                d = string;
1109             if(c.class::OnGetDataFromString(d))
1110             {
1111                color.a = a;
1112                color.color = c;
1113             }
1114             else
1115                color = (uint)strtoul(string, null, 16);
1116          }
1117       }
1118       value.i = color;
1119       return success;
1120    }
1121 }
1122
1123 static bool WriteMap(File f, Class type, Map map, int indent, bool eCON)
1124 {
1125    if(map)
1126    {
1127       int i;
1128       bool isFirst = true;
1129       MapIterator it { map = map };
1130       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
1131       f.Puts("[\n");
1132       indent++;
1133
1134       while(it.Next())
1135       {
1136          MapNode n = (MapNode)it.pointer;
1137          if(!isFirst)
1138             f.Puts(",\n");
1139          else
1140             isFirst = false;
1141          for(i = 0; i<indent; i++) f.Puts("   ");
1142          WriteONObject(f, mapNodeClass, n, indent, eCON, eCON ? true : false);
1143       }
1144       f.Puts("\n");
1145       indent--;
1146       for(i = 0; i<indent; i++) f.Puts("   ");
1147       f.Puts("]");
1148    }
1149    else
1150       f.Puts("null");
1151    return true;
1152 }
1153
1154 static bool WriteArray(File f, Class type, Container array, int indent, bool eCON)
1155 {
1156    if(array)
1157    {
1158       int i;
1159       bool isFirst = true;
1160       Iterator it { array };
1161       Class arrayType = type.templateArgs[0].dataTypeClass;
1162       f.Puts("[\n");
1163       indent++;
1164
1165       while(it.Next())
1166       {
1167          DataValue value { };
1168          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
1169          if(!isFirst)
1170             f.Puts(",\n");
1171          else
1172             isFirst = false;
1173
1174          // Add value
1175          // TODO: Verify the matching between template type and uint64
1176          if(arrayType.type == structClass)
1177          {
1178             value.p = (void *)(uintptr)t;
1179          }
1180          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
1181          {
1182             value.ui64 = t;
1183             //value.d = *(double *)&t;
1184          }
1185          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
1186          {
1187             value.ui = (uint)t;
1188             //value.f = *(float *)&t;
1189          }
1190          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
1191             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
1192          {
1193             value.ui64 = t;
1194          }
1195          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
1196             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
1197          {
1198             value.i = (int)t;
1199          }
1200          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
1201             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
1202             !strcmp(arrayType.dataTypeString, "int16"))
1203          {
1204             value.s = (short)t;
1205          }
1206          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
1207             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
1208          {
1209             value.c = (char)t;
1210          }
1211          else
1212          {
1213             value.p = (void *)(uintptr)t;
1214          }
1215          for(i = 0; i<indent; i++) f.Puts("   ");
1216          WriteValue(f, arrayType, value, indent, eCON);
1217       }
1218       f.Puts("\n");
1219       indent--;
1220       for(i = 0; i<indent; i++) f.Puts("   ");
1221       f.Puts("]");
1222    }
1223    else
1224       f.Puts("null");
1225    return true;
1226 }
1227
1228 static bool WriteNumber(File f, Class type, DataValue value, int indent, bool eCON)
1229 {
1230    char buffer[1024];
1231    bool needClass = eCON;
1232    bool quote;
1233    buffer[0] = 0;
1234    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1235       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
1236    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1237       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
1238    else if(!strcmp(type.dataTypeString, "int64"))
1239       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
1240    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
1241       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
1242    else if(!strcmp(type.dataTypeString, "int"))
1243       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
1244    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
1245       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
1246    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
1247       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
1248    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
1249       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
1250    else if(!strcmp(type.dataTypeString, "char"))
1251       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
1252    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
1253       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
1254
1255    quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' '))) ||
1256            (type.type == enumClass && !eCON);
1257    if(quote) f.Puts("\"");
1258    f.Puts(buffer);
1259    if(quote) f.Puts("\"");
1260    return true;
1261 }
1262
1263 public bool WriteColorAlpha(File f, Class type, DataValue value, int indent, bool eCON)
1264 {
1265    char buffer[1024];
1266    char * string = buffer;
1267    ColorAlpha color = value.i;
1268    int a = color.a;
1269    int len;
1270    DefinedColor c = color;
1271    buffer[0] = '\0';
1272    if(a != 255)
1273    {
1274       a.class::OnGetString(buffer, null, null);
1275       len = strlen(buffer);
1276       buffer[len++] = ',';
1277       buffer[len++] = ' ';
1278       buffer[len] = '\0';
1279       string += len;
1280    }
1281    if(!c.class::OnGetString(string, null, null))
1282       sprintf(buffer, "0x%x", color);
1283    if(!eCON)
1284       f.Puts("\"");
1285    f.Puts(buffer);
1286    if(!eCON)
1287       f.Puts("\"");
1288    return true;
1289 }
1290
1291 static bool WriteValue(File f, Class type, DataValue value, int indent, bool eCON)
1292 {
1293    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
1294    {
1295       if(!value.p)
1296          f.Puts("null");
1297       else
1298       {
1299          f.Puts("\"");
1300          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
1301          if(eCON)
1302          {
1303             int c = 0;
1304             int b = 0;
1305             char buffer[1024];
1306             char * string = value.p;
1307             char ch;
1308             while(true)
1309             {
1310                ch = string[c++];
1311                if(ch == '\"')
1312                {
1313                   buffer[b] = 0;
1314                   f.Puts(buffer);
1315                   f.Puts("\\\"");
1316                   b = 0;
1317                }
1318                else if(ch == '\\')
1319                {
1320                   buffer[b] = 0;
1321                   f.Puts(buffer);
1322                   f.Puts("\\\\");
1323                   b = 0;
1324                }
1325                else if(ch == '\t')
1326                {
1327                   buffer[b] = 0;
1328                   f.Puts(buffer);
1329                   f.Puts("\\t");
1330                   b = 0;
1331                }
1332                else if(ch == '\n')
1333                {
1334                   int i;
1335                   buffer[b] = 0;
1336                   f.Puts(buffer);
1337                   f.Puts("\\n\"\n");
1338                   for(i = 0; i<indent; i++) f.Puts("   ");
1339                   f.Puts("   \"");
1340                   b = 0;
1341                }
1342                else if(b == sizeof(buffer)-2 || !ch)
1343                {
1344                   buffer[b++] = ch;
1345                   if(ch) buffer[b] = 0;
1346                   f.Puts(buffer);
1347                   b = 0;
1348                   if(!ch) break;
1349                }
1350                else
1351                   buffer[b++] = ch;
1352             }
1353          }
1354          else
1355          {
1356             int c = 0;
1357             int b = 0;
1358             char buffer[1024];
1359             char * string = value.p;
1360             char ch;
1361             while(true)
1362             {
1363                ch = string[c++];
1364                if(ch == '\"')
1365                {
1366                   buffer[b] = 0;
1367                   f.Puts(buffer);
1368                   f.Puts("\\\"");
1369                   b = 0;
1370                }
1371                else if(ch == '\\')
1372                {
1373                   buffer[b] = 0;
1374                   f.Puts(buffer);
1375                   f.Puts("\\\\");
1376                   b = 0;
1377                }
1378                else if(b == sizeof(buffer)-2 || !ch)
1379                {
1380                   buffer[b++] = ch;
1381                   if(ch) buffer[b] = 0;
1382                   f.Puts(buffer);
1383                   b = 0;
1384                   if(!ch) break;
1385                }
1386                else
1387                   buffer[b++] = ch;
1388             }
1389          }
1390          /*else
1391             f.Puts(value.p);*/
1392          f.Puts("\"");
1393       }
1394    }
1395    else if(!strcmp(type.name, "bool"))
1396    {
1397       if(value.i)
1398          f.Puts("true");
1399       else
1400          f.Puts("false");
1401    }
1402    else if(!strcmp(type.name, "SetBool"))
1403    {
1404       if(value.i == SetBool::true)
1405          f.Puts("true");
1406       else if(value.i == SetBool::false)
1407          f.Puts("false");
1408       else
1409          f.Puts("unset");
1410    }
1411    else if(type.type == enumClass)
1412       WriteNumber(f, type, value, indent, eCON);
1413    else if(eClass_IsDerived(type, class(Map)))
1414    {
1415       WriteMap(f, type, value.p, indent, eCON);
1416    }
1417    else if(eClass_IsDerived(type, class(Container)))
1418    {
1419       WriteArray(f, type, value.p, indent, eCON);
1420    }
1421    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
1422    {
1423       WriteONObject(f, type, value.p, indent, eCON, false);
1424    }
1425    else if(eClass_IsDerived(type, class(ColorAlpha)))
1426    {
1427       WriteColorAlpha(f, type, value, indent, eCON);
1428    }
1429    else if(type.type == bitClass)
1430    {
1431       Class dataType;
1432       dataType = superFindClass(type.dataTypeString, type.module);
1433       WriteNumber(f, dataType, value, indent, eCON);
1434    }
1435    else if(type.type == systemClass || type.type == unitClass)
1436    {
1437       WriteNumber(f, type, value, indent, eCON);
1438    }
1439    return true;
1440 }
1441
1442 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
1443 {
1444    bool result = false;
1445    if(object)
1446    {
1447       result = WriteONObject(f, objectType, object, indent, false, false);
1448       f.Puts("\n");
1449    }
1450    return result;
1451 }
1452
1453 public bool WriteECONObject(File f, Class objectType, void * object, int indent)
1454 {
1455    bool result = false;
1456    if(object)
1457    {
1458       result = WriteONObject(f, objectType, object, indent, true, false);
1459       f.Puts("\n");
1460    }
1461    return result;
1462 }
1463
1464 static bool WriteONObject(File f, Class objectType, void * object, int indent, bool eCON, bool omitDefaultIdentifier)
1465 {
1466    if(object)
1467    {
1468       const char * string = null;
1469
1470       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
1471       {
1472          char buffer[1024];
1473          buffer[0] = 0;
1474          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
1475       }
1476       if(string)
1477       {
1478          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
1479          if(string[0] == '\"')
1480             f.Puts(string);
1481          else
1482          {
1483             f.Puts("\"");
1484             f.Puts(string);
1485             f.Puts("\"");
1486          }
1487       }
1488       else
1489       {
1490          Class _class = (eCON && objectType.type == normalClass) ? ((Instance)object)._class : objectType;
1491          Property prop;
1492          int c;
1493          bool isFirst = true;
1494          Class mapKeyClass = null, mapDataClass = null;
1495          Class baseClass;
1496          List<Class> bases { };
1497
1498          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
1499          {
1500             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
1501             mapDataClass = objectType.templateArgs[2].dataTypeClass;
1502          }
1503
1504          if(eCON && _class != objectType && eClass_IsDerived(_class, objectType))
1505          {
1506             f.Puts(_class.name);
1507             f.Puts(" ");
1508          }
1509
1510          f.Puts("{\n");
1511          indent++;
1512
1513          for(baseClass = _class; baseClass; baseClass = baseClass.base)
1514          {
1515             if(baseClass.isInstanceClass || !baseClass.base)
1516                break;
1517             bases.Insert(null, baseClass);
1518          }
1519
1520          for(baseClass : bases)
1521          {
1522             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
1523             {
1524                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
1525                if(prop.isProperty)
1526                {
1527                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
1528                   {
1529                      DataValue value { };
1530                      bool isTemplateArg = false;
1531                      Class type;
1532
1533                      if(mapKeyClass && !strcmp(prop.name, "key"))
1534                      {
1535                         isTemplateArg = true;
1536                         type = mapKeyClass;
1537                      }
1538                      else if(mapDataClass && !strcmp(prop.name, "value"))
1539                      {
1540                         isTemplateArg = true;
1541                         type = mapDataClass;
1542                      }
1543                      else
1544                         type = superFindClass(prop.dataTypeString, _class.module);
1545
1546                      if(!type)
1547                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
1548                      else
1549                      {
1550                         if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
1551                         {
1552                            // TOFIX: How to swiftly handle classes with base data type?
1553                            if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1554                            {
1555                               value.d = ((double (*)(void *))(void *)prop.Get)(object);
1556                            }
1557                            else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1558                            {
1559                               value.f = ((float (*)(void *))(void *)prop.Get)(object);
1560                            }
1561                            else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1562                               !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1563                            {
1564                               value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
1565                            }
1566                            else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1567                               !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1568                            {
1569                               value.i = ((int (*)(void *))(void *)prop.Get)(object);
1570                            }
1571                            else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1572                               !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1573                               !strcmp(type.dataTypeString, "int16"))
1574                            {
1575                               value.s = ((short (*)(void *))(void *)prop.Get)(object);
1576                            }
1577                            else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1578                               !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1579                            {
1580                               value.c = ((char (*)(void *))(void *)prop.Get)(object);
1581                            }
1582                         }
1583                         else if(type.type == structClass)
1584                         {
1585                            value.p = new0 byte[type.structSize];
1586                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1587                         }
1588                         else
1589                         {
1590                            if(isTemplateArg)
1591                               value.p = (void *)(uintptr)((uint64 (*)(void *))(void *)prop.Get)(object);
1592                            else
1593                               value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1594                         }
1595
1596                         if(!isFirst) f.Puts(",\n");
1597                         for(c = 0; c<indent; c++) f.Puts("   ");
1598
1599                         if(!eCON)
1600                         {
1601                            f.Puts("\"");
1602                            f.Putc((char)toupper(prop.name[0]));
1603                            f.Puts(prop.name+1);
1604                            f.Puts("\" : ");
1605                         }
1606                         else if(!omitDefaultIdentifier)
1607                         {
1608                            f.Puts(prop.name);
1609                            f.Puts(" = ");
1610                         }
1611                         WriteValue(f, type, value, indent, eCON);
1612                         isFirst = false;
1613                         if(type.type == structClass)
1614                            delete value.p;
1615                      }
1616                   }
1617                }
1618                else
1619                {
1620                   DataMember member = (DataMember)prop;
1621                   DataValue value { };
1622                   uint offset;
1623                   Class type = superFindClass(member.dataTypeString, _class.module);
1624                   offset = member._class.offset + member.offset;
1625
1626                   if(type)
1627                   {
1628                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1629                      {
1630                         if(type.type == structClass)
1631                            value.p = (void *)((byte *)object + offset);
1632                         else
1633                            value.p = *(void **)((byte *)object + offset);
1634                         if(!value.p)
1635                            continue;
1636                      }
1637                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1638                      {
1639                         value.d = *(double *)((byte *)object + offset);
1640                      }
1641                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1642                      {
1643                         value.f = *(float *)((byte *)object + offset);
1644                      }
1645                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1646                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1647                      {
1648                         value.ui64 = *(uint64 *)((byte *)object + offset);
1649                      }
1650                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1651                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1652                      {
1653                         value.i = *(int *)((byte *)object + offset);
1654                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1655                            if(!value.i)
1656                               continue;
1657                      }
1658                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1659                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1660                         !strcmp(type.dataTypeString, "int16"))
1661                      {
1662                         value.s = *(short *)((byte *)object + offset);
1663                      }
1664                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1665                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1666                      {
1667                         value.c = *(char *)((byte *)object + offset);
1668                      }
1669                      else
1670                      {
1671                         value.i = *(int *)((byte *)object + offset);
1672                      }
1673
1674                      if(!isFirst) f.Puts(",\n");
1675                      for(c = 0; c<indent; c++) f.Puts("   ");
1676
1677                      if(!eCON)
1678                      {
1679                         f.Puts("\"");
1680                         f.Putc((char)toupper(member.name[0]));
1681                         f.Puts(member.name+1);
1682                         f.Puts("\" : ");
1683                      }
1684                      else if(!omitDefaultIdentifier)
1685                      {
1686                         f.Puts(member.name);
1687                         f.Puts(" = ");
1688                      }
1689                      WriteValue(f, type, value, indent, eCON);
1690                      isFirst = false;
1691                   }
1692                }
1693             }
1694          }
1695
1696          delete bases;
1697
1698          indent--;
1699          f.Puts("\n");
1700          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1701       }
1702    }
1703    else
1704       f.Puts("null");
1705    return true;
1706 }
1707
1708 static Class superFindClass(const String name, Module alternativeModule)
1709 {
1710    Class _class = eSystem_FindClass(__thisModule, name);
1711    if(!_class && alternativeModule)
1712       _class = eSystem_FindClass(alternativeModule, name);
1713    if(!_class)
1714       _class = eSystem_FindClass(__thisModule.application, name);
1715    return _class;
1716 }
1717
1718 static bool isSubclass(Class type, const String name)
1719 {
1720    Class _class = superFindClass(name, type.module);
1721    if(eClass_IsDerived(_class, type))
1722       return true;
1723    return false;
1724 }