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