a34a7e27617a130f68a89713017bf4593ae14fba
[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          if(type.type == structClass || type.type == normalClass || type.type == noHeadClass)
169          {
170             void * object = value.p;
171             result = GetObject(type, &object);
172             if(result)
173             {
174                if(type && type.type == structClass);
175                else
176                   value.p = object;
177             }
178          }
179          else if(type.type == bitClass)
180          {
181             uint64 object = 0;
182             result = GetObject(type, (void **)&object);
183             if(result)
184                value.ui64 = object;
185          }
186          else
187          {
188             void * object = value.p;
189             result = GetObject(type, &object);
190             if(result)
191             {
192                result = typeMismatch;
193                if(type)
194                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
195             }
196          }
197       }
198       else if(isalpha(ch) || ch == '_')
199       {
200          if(eCON)
201          {
202             String string;
203             if(GetIdentifier(&string, null))
204             {
205                result = success;
206                if(eCON && type && (type.type == enumClass || type.type == unitClass))
207                {
208                   // should this be set by calling __ecereVMethodID_class_OnGetDataFromString ?
209                   if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
210                      result = success;
211                   else
212                      result = typeMismatch;
213                }
214                else if(type && !strcmp(type.name, "bool"))
215                {
216                   if(!strcmpi(string, "false")) value.i = 0;
217                   else if(!strcmpi(string, "true")) value.i = 1;
218                   else
219                      result = typeMismatch;
220                }
221                else if(type && !strcmp(type.name, "SetBool"))
222                {
223                   if(!strcmpi(string, "false")) value.i = SetBool::false;
224                   else if(!strcmpi(string, "true")) value.i = SetBool::true;
225                   else
226                      result = typeMismatch;
227                }
228                else if(type && !strcmpi(string, "null"))
229                {
230                   if(type.type != structClass)
231                      value.p = 0;
232                }
233                else if(isSubclass(type, string))
234                {
235                   void * object = value.p;
236                   Class subtype = superFindClass(string, type.module);
237                   SkipEmpty();
238                   result = GetObject(subtype, &object);
239                   if(result)
240                   {
241                      if(subtype && subtype.type == structClass);
242                      else if(subtype && (subtype.type == normalClass || subtype.type == noHeadClass || subtype.type == bitClass))
243                      {
244                         value.p = object;
245                      }
246                      else
247                      {
248                         result = typeMismatch;
249                         if(subtype)
250                            ((void (*)(void *, void *))(void *)subtype._vTbl[__ecereVMethodID_class_OnFree])(subtype, object);
251                      }
252                   }
253                }
254                else
255                   result = typeMismatch;
256             }
257             delete string;
258          }
259          else
260          {
261             char buffer[256];
262             int c = 0;
263             while(c < sizeof(buffer)-1 && (isalpha(ch) || isdigit(ch) || ch == '_'))
264             {
265                buffer[c++] = ch;
266                if(!f.Getc(&ch)) break;
267             }
268             buffer[c] = 0;
269             result = success;
270
271             if(type)
272             {
273                if(!strcmp(type.name, "bool"))
274                {
275                   if(!strcmpi(buffer, "false")) value.i = 0;
276                   else if(!strcmpi(buffer, "true")) value.i = 1;
277                   else
278                      result = typeMismatch;
279                }
280                else if(!strcmp(type.name, "SetBool"))
281                {
282                   if(!strcmpi(buffer, "false")) value.i = SetBool::false;
283                   else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
284                   else
285                      result = typeMismatch;
286                }
287                else if(!strcmpi(buffer, "null"))
288                {
289                   if(type.type != structClass)
290                      value.p = 0;
291                }
292                else
293                   result = typeMismatch;
294             }
295             else
296                result = typeMismatch;
297          }
298       }
299       else if(ch == '}' || ch == ']')
300          result = noItem;
301       if(result == typeMismatch)
302          PrintLn("Warning: Value type mismatch");
303       return result;
304    }
305
306    JSONResult GetArray(Class type, Container * array)
307    {
308       JSONResult result = syntaxError;
309       SkipEmpty();
310       *array = null;
311       if(ch == '[')
312       {
313          *array = eInstance_New(type);
314          result = success;
315          while(result)
316          {
317             DataValue value { };
318             Class arrayType = null;
319             JSONResult itemResult;
320
321             if(eClass_IsDerived(type, class(Container)))
322             {
323                arrayType = type.templateArgs[0].dataTypeClass;
324             }
325
326             if(arrayType && arrayType.type == structClass)
327                value.p = new0 byte[arrayType.structSize];
328             itemResult = GetValue(arrayType, value);
329             if(itemResult == success)
330             {
331                // TODO: Verify the matching between template type and uint64
332                uint64 t;
333                if(arrayType.type == structClass)
334                {
335                   t = (uint64)(uintptr)value.p;
336                }
337                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
338                {
339                   t = value.ui64; //*(uint64 *)&value.d;
340                }
341                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
342                {
343                   t = value.ui; //f*(uint *)&value.f;
344                }
345                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
346                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
347                {
348                   t = value.ui64;
349                }
350                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
351                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
352                {
353                   t = value.i;
354                }
355                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
356                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
357                   !strcmp(arrayType.dataTypeString, "int16"))
358                {
359                   t = value.s;
360                }
361                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
362                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
363                {
364                   t = value.c;
365                }
366                else
367                {
368                   t = (uint64)(uintptr)value.p;
369                }
370                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
371
372                if(arrayType && arrayType.type == structClass)
373                   delete value.p;
374             }
375             else
376             {
377                if(itemResult == typeMismatch)
378                {
379                   if(arrayType)
380                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
381                }
382                else if(itemResult == noItem)
383                   result = success;
384                else
385                   result = itemResult;
386             }
387
388             if(result != syntaxError)
389             {
390                if(ch != ']' && ch != ',')
391                {
392                   ch = 0;
393                   SkipEmpty();
394                }
395                if(ch == ']')
396                {
397                   break;
398                }
399                else if(ch != ',')
400                   result = syntaxError;
401             }
402          }
403       }
404       ch = 0;
405       return result;
406    }
407
408    JSONResult GetMap(Class type, Map * map)
409    {
410       JSONResult result = syntaxError;
411       SkipEmpty();
412       *map = null;
413       if(ch == '[')
414       {
415          Class mapNodeType = type.templateArgs[0].dataTypeClass;
416          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
417          Property keyProp = null;
418          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
419             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
420
421          *map = eInstance_New(type);
422          result = success;
423
424          while(result)
425          {
426             DataValue value { };
427
428             JSONResult itemResult;
429
430             itemResult = GetValue(mapNodeType, value);
431             if(itemResult == success)
432             {
433                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
434                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
435                // Must free String keys here
436                delete s;
437             }
438             else
439             {
440                if(itemResult == typeMismatch)
441                {
442                   if(mapNodeType)
443                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
444                }
445                else if(itemResult == noItem)
446                   result = success;
447                else
448                   result = itemResult;
449             }
450
451             if(result != syntaxError)
452             {
453                if(ch != ']' && ch != ',')
454                {
455                   ch = 0;
456                   SkipEmpty();
457                }
458                if(ch == ']')
459                {
460                   break;
461                }
462                else if(ch != ',')
463                   result = syntaxError;
464             }
465          }
466       }
467       ch = 0;
468       return result;
469    }
470
471    JSONResult GetIdentifier(String * string, bool * wasQuoted)
472    {
473       JSONResult result = syntaxError;
474       Array<char> buffer { minAllocSize = 256 };
475       bool comment = false;
476       bool quoted = false;
477
478       *string = null;
479       SkipEmpty();
480       if(ch == '\"')
481          quoted = true;
482       else
483          buffer.Add(ch);
484       result = success;
485       while(f.Getc(&ch))
486       {
487          if(!comment && ch == '/')
488          {
489             if(f.Getc(&ch))
490             {
491                if(ch == '/')
492                   break;
493                else if(ch == '*')
494                   comment = true;
495                else
496                {
497                   result = syntaxError;
498                   break;
499                }
500             }
501             else
502             {
503                result = syntaxError;
504                break;
505             }
506          }
507          else if(comment && ch == '*')
508          {
509             if(f.Getc(&ch))
510             {
511                if(ch == '/')
512                {
513                   comment = false;
514                   ch = 0;
515                }
516             }
517             else
518             {
519                result = syntaxError;
520                break;
521             }
522          }
523          else if(ch == '\"' || (!quoted && !comment && ch && !isalpha(ch) && !isdigit(ch) && ch != '_'))
524          {
525             if(quoted && ch == '\"' && wasQuoted)
526                *wasQuoted = true;
527             break;
528          }
529          else if(!comment && ch)
530          {
531             buffer.Add(ch);
532             if(buffer.minAllocSize < buffer.count)
533                buffer.minAllocSize *= 2;
534          }
535       }
536       if(result != syntaxError)
537       {
538          buffer.Add(0);
539          *string = CopyString(buffer.array);
540       }
541       delete buffer;
542       if(ch != ',' && ch != '}' && ch != ';' && ch != '/' && ch != '=' && ch != ':')
543          ch = 0;
544       return result;
545    }
546
547    JSONResult GetString(String * string)
548    {
549       JSONResult result = syntaxError;
550       Array<char> buffer { minAllocSize = 256 };
551       bool escaped = false;
552
553       *string = null;
554       SkipEmpty();
555       if(ch == '\"' || eCON)
556       {
557          while(f.Getc(&ch))
558          {
559             if(ch == '\\' && !escaped)
560                escaped = true;
561             else
562             {
563                if(escaped)
564                {
565                   if(ch == 'b') ch = '\b';
566                   else if(ch == 'f') ch = '\f';
567                   else if(ch == 'n') ch = '\n';
568                   else if(ch == 'r') ch = '\r';
569                   else if(ch == 't') ch = '\t';
570                   else if(ch == 'u')
571                   {
572                      // SKIP FOR NOW...
573                      char unicode[4];
574                      f.Getc(&unicode[0]);
575                      f.Getc(&unicode[1]);
576                      f.Getc(&unicode[2]);
577                      f.Getc(&unicode[3]);
578                   }
579                   escaped = false;
580                }
581                else if(eCON && ch == '\"')
582                {
583                   int seekback = 0;
584                   char pch;
585                   bool lineComment = false;
586                   bool comment = false;
587                   while(!f.Eof())
588                   {
589                      pch = ch;
590                      f.Getc(&ch);
591                      seekback--;
592                      if(!lineComment && !comment && pch == '/')
593                      {
594                         if(ch == '/')
595                            lineComment = true;
596                         else if(ch == '*')
597                            comment = true;
598                      }
599                      else if(lineComment && ch == '\n')
600                         lineComment = false;
601                      else if(comment && pch == '*' && ch == '/')
602                         comment = false;
603                      else if(ch == '=' || ch == ':' || ch == ';' || ch == ',' || ch == ']' || ch == '}')
604                      {
605                         ch = 0;
606                         seekback = -1;
607                         break;
608                      }
609                      else if(ch == '\"')
610                      {
611                         seekback = 0;
612                         ch = 0;
613                         break;
614                      }
615                   }
616                   if(seekback != 0)
617                   {
618                      f.Seek(seekback, current);
619                      break;
620                   }
621                }
622                else if((!eCON && ch == '\"'))
623                {
624                   break;
625                }
626                if(ch)
627                {
628                   buffer.Add(ch);
629                   if(buffer.minAllocSize < buffer.count)
630                      buffer.minAllocSize *= 2;
631                }
632             }
633          }
634          buffer.Add(0);
635          *string = CopyString(buffer.array);
636          result = success;
637       }
638       delete buffer;
639       if(ch != ',' && ch != '}' && (!eCON || (ch != ';' && ch != '/')))
640          ch = 0;
641       return result;
642    }
643
644    public JSONResult GetObject(Class objectType, void ** object)
645    {
646       JSONResult result = syntaxError;
647       if(!objectType || objectType.type != structClass)
648       {
649          if(objectType && objectType.type == bitClass)
650             *(uint64 *)object = 0;
651          else
652             *object = null;
653       }
654       SkipEmpty();
655       if(ch == '{')
656       {
657          Class mapKeyClass = null, mapDataClass = null;
658          Class curClass = null;
659          DataMember curMember = null;
660          DataMember subMemberStack[256];
661          int subMemberStackPos = 0;
662          uint64 bits = 0;
663
664          if(objectType.type == bitClass)
665          {
666             switch(objectType.typeSize)
667             {
668                case 1: bits = (byte  )*(uint64 *)object; break;
669                case 2: bits = (uint16)*(uint64 *)object; break;
670                case 4: bits = (uint32)*(uint64 *)object; break;
671                case 8: bits = (uint64)*(uint64 *)object; break;
672             }
673          }
674
675          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
676          {
677             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
678             mapDataClass = objectType.templateArgs[2].dataTypeClass;
679          }
680
681          result = success;
682          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
683             *object = eInstance_New(objectType);
684
685          while(result)
686          {
687             String string;
688             bool wasQuoted = false;
689             int seek;
690             ch = 0;
691             if(eCON)
692             {
693                SkipExtraSemicolon();
694                if(ch == '}')
695                   break;
696             }
697             SkipEmpty();
698             seek = f.Tell();
699             if(eCON ? GetIdentifier(&string, &wasQuoted) : GetString(&string))
700             {
701                DataMember member = null;
702                Property prop = null;
703                Class type = null;
704                bool isKey = false;
705                bool isTemplateArg = false;
706                uint offset = 0;
707                if(eCON)
708                {
709                   SkipEmpty();
710                   prop = null; member = null;
711                   if(ch == '=' || ch == ':')
712                   {
713                      if(wasQuoted)
714                         string[0] = (char)tolower(string[0]);
715                      while(1)
716                      {
717                         eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos);
718                         if(!curMember) break;
719                         if(!strcmp(curMember.name, string))
720                            break;
721                      }
722                   }
723                   else
724                      eClass_FindNextMember(objectType, &curClass, &curMember, subMemberStack, &subMemberStackPos);
725                   if(curMember)
726                   {
727                      prop = curMember.isProperty ? (Property)curMember : null;
728                      member = curMember.isProperty ? null : curMember;
729
730                      if(mapKeyClass && !strcmp(prop ? prop.name : member.name, "key"))
731                      {
732                         type = mapKeyClass;
733                         isTemplateArg = true;
734                         isKey = true;
735                      }
736                      else if(mapDataClass && !strcmp(prop ? prop.name : member.name, "value"))
737                      {
738                         type = mapDataClass;
739                         isTemplateArg = true;
740                         if(member)
741                            offset = member._class.offset + member.offset;
742                      }
743                      else if(prop)
744                         type = superFindClass(prop.dataTypeString, objectType.module);
745                      else if(member)
746                      {
747                         type = superFindClass(member.dataTypeString, objectType.module);
748                         offset = member._class.offset + member.offset;
749                      }
750                   }
751                   else
752                   {
753                      if(ch == '=' || ch == ':')
754                         PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
755                      else
756                         PrintLn("Warning: default member assignment: no more members");
757                   }
758                }
759                if(objectType && !eCON)
760                {
761                   string[0] = (char)tolower(string[0]);
762                   if(mapKeyClass && !strcmp(string, "key"))
763                   {
764                      prop = eClass_FindProperty(objectType, "key", objectType.module);
765                      type = mapKeyClass;
766                      isTemplateArg = true;
767                      isKey = true;
768                   }
769                   else if(mapDataClass && !strcmp(string, "value"))
770                   {
771                      prop = eClass_FindProperty(objectType, "value", objectType.module);
772                      type = mapDataClass;
773                      isTemplateArg = true;
774                   }
775                   else
776                   {
777                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
778                      if(member)
779                      {
780                         type = superFindClass(member.dataTypeString, objectType.module);
781                         offset = member._class.offset + member.offset;
782                      }
783                      else if(!member)
784                      {
785                         prop = eClass_FindProperty(objectType, string, objectType.module);
786                         if(prop)
787                            type = superFindClass(prop.dataTypeString, objectType.module);
788                         else
789                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
790                      }
791                   }
792                }
793                if(type && type.templateClass && type.templateClass == class(Container))
794                {
795                   char * br = strchr(type.fullName, '<');
796                   if(br)
797                   {
798                      char className[1024];
799                      strcpy(className, "Array");
800                      strcat(className, br);
801                      type = superFindClass(className, objectType.module);
802                   }
803                }
804
805                // Find Member in Object Class
806                {
807                   DataValue value { };
808
809                   if(type && type.type == structClass)
810                   {
811                      if(member)
812                      {
813                         value.p = (byte *)*object + offset;
814                         memset(value.p, 0, type.structSize);
815                      }
816                      else if(prop)
817                      {
818                         value.p = new0 byte[type.structSize];
819                      }
820                   }
821                   if(!eCON)
822                   {
823                      ch = 0;
824                      SkipEmpty();
825                   }
826                   if(eCON && ch != '=' && ch != ':')
827                   {
828                      f.Seek(seek-1, start);
829                      ch = 0;
830                   }
831                   if((ch == ':' || (eCON && ch == '=')) || (eCON && type && (prop || member)))
832                   {
833                      JSONResult itemResult = GetValue(type, value);
834                      if(itemResult != syntaxError)
835                      {
836                         if(prop || member)
837                         {
838                            if(!type)
839                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
840                            else if(itemResult == success)
841                            {
842                               BitMember bitMember = objectType.type == bitClass ? (BitMember) member : null;
843                               // Set value
844                               if(member)
845                               {
846                                  // TOFIX: How to swiftly handle classes with base data type?
847                                  if(type.type == structClass)
848                                     ;
849                                  else if(type.type == normalClass || type.type == noHeadClass)
850                                  {
851                                     void ** ptr = (void**)((byte *)*object + offset);
852                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
853                                     {
854                                        Container container = (Container)*ptr;
855                                        container.Free();
856                                        delete container;
857                                     }
858                                     *ptr = value.p;
859                                  }
860                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
861                                  {
862                                     if(objectType.type != bitClass)
863                                     {
864                                        *(double *)((byte *)*object + offset) = value.d;
865                                     }
866                                  }
867                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
868                                  {
869                                     if(objectType.type != bitClass)
870                                     {
871                                        *(float *)((byte *)*object + offset) = value.f;
872                                     }
873                                  }
874                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
875                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
876                                  {
877                                     if(objectType.type == bitClass)
878                                     {
879                                        bits &= ~bitMember.mask;
880                                        bits |= (value.ui64 << bitMember.pos) & bitMember.mask;
881                                     }
882                                     else
883                                     {
884                                        *(uint64 *)((byte *)*object + offset) = value.ui64;
885                                     }
886                                  }
887                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
888                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
889                                  {
890                                     if(objectType.type == bitClass)
891                                     {
892                                        bits &= ~bitMember.mask;
893                                        bits |= (value.ui << bitMember.pos) & bitMember.mask;
894                                     }
895                                     else
896                                     {
897                                        *(int *)((byte *)*object + offset) = value.i;
898                                     }
899                                  }
900                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
901                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
902                                     !strcmp(type.dataTypeString, "int16"))
903                                  {
904                                     if(objectType.type == bitClass)
905                                     {
906                                        bits &= ~bitMember.mask;
907                                        bits |= (value.us << bitMember.pos) & bitMember.mask;
908                                     }
909                                     else
910                                     {
911                                        *(short *)((byte *)*object + offset) = value.s;
912                                     }
913                                  }
914                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
915                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
916                                  {
917                                     if(objectType.type == bitClass)
918                                     {
919                                        bits &= ~bitMember.mask;
920                                        bits |= (value.uc << bitMember.pos) & bitMember.mask;
921                                     }
922                                     else
923                                     {
924                                        *(char *)((byte *)*object + offset) = value.c;
925                                     }
926                                  }
927                                  else
928                                  {
929                                     if(objectType.type != bitClass)
930                                        *(void **)((byte *)*object + offset) = value.p;
931                                  }
932                               }
933                               else if(prop && prop.Set)
934                               {
935                                  if(objectType.type == bitClass)
936                                  {
937                                     if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
938                                     {
939                                        switch(objectType.typeSize)
940                                        {
941                                           case 1:
942                                              switch(type.typeSize)
943                                              {
944                                                 case 1: ((byte (*)(byte, byte))  (void *)prop.Set)((byte)bits, value.uc); break;
945                                                 case 2: ((byte (*)(byte, uint16))(void *)prop.Set)((byte)bits, value.us); break;
946                                                 case 4: ((byte (*)(byte, uint32))(void *)prop.Set)((byte)bits, value.ui); break;
947                                                 case 8: ((byte (*)(byte, uint64))(void *)prop.Set)((byte)bits, value.ui64); break;
948                                              }
949                                              break;
950                                           case 2:
951                                              switch(type.typeSize)
952                                              {
953                                                 case 1: ((uint16 (*)(uint16, byte))  (void *)prop.Set)((uint16)bits, value.uc); break;
954                                                 case 2: ((uint16 (*)(uint16, uint16))(void *)prop.Set)((uint16)bits, value.us); break;
955                                                 case 4: ((uint16 (*)(uint16, uint32))(void *)prop.Set)((uint16)bits, value.ui); break;
956                                                 case 8: ((uint16 (*)(uint16, uint64))(void *)prop.Set)((uint16)bits, value.ui64); break;
957                                              }
958                                              break;
959                                           case 4:
960                                              switch(type.typeSize)
961                                              {
962                                                 case 1: ((uint32 (*)(uint32, byte))  (void *)prop.Set)((uint32)bits, value.uc); break;
963                                                 case 2: ((uint32 (*)(uint32, uint16))(void *)prop.Set)((uint32)bits, value.us); break;
964                                                 case 4: ((uint32 (*)(uint32, uint32))(void *)prop.Set)((uint32)bits, value.ui); break;
965                                                 case 8: ((uint32 (*)(uint32, uint64))(void *)prop.Set)((uint32)bits, value.ui64); break;
966                                              }
967                                              break;
968                                           case 8:
969                                              switch(type.typeSize)
970                                              {
971                                                 case 1: ((uint64 (*)(uint64, byte))  (void *)prop.Set)((uint64)bits, value.uc); break;
972                                                 case 2: ((uint64 (*)(uint64, uint16))(void *)prop.Set)((uint64)bits, value.us); break;
973                                                 case 4: ((uint64 (*)(uint64, uint32))(void *)prop.Set)((uint64)bits, value.ui); break;
974                                                 case 8: ((uint64 (*)(uint64, uint64))(void *)prop.Set)((uint64)bits, value.ui64); break;
975                                              }
976                                              break;
977                                        }
978                                     }
979                                     else
980                                        ; // TODO: Generate error
981                                  }
982                                  else if(!strcmp(type.dataTypeString, "char *"))
983                                  {
984                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
985                                     if(!isKey)
986                                        delete value.p;
987                                  }
988                                  else if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
989                                  {
990                                     // TOFIX: How to swiftly handle classes with base data type?
991                                     if(type == class(double) || !strcmp(type.dataTypeString, "double"))
992                                     {
993                                        ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
994                                     }
995                                     else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
996                                     {
997                                        ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
998                                     }
999                                     else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1000                                        !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1001                                     {
1002                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
1003                                     }
1004                                     else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1005                                        !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1006                                     {
1007                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
1008                                     }
1009                                     else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1010                                        !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1011                                        !strcmp(type.dataTypeString, "int16"))
1012                                     {
1013                                        ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
1014                                     }
1015                                     else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1016                                        !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1017                                     {
1018                                        ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
1019                                     }
1020                                     else
1021                                     {
1022                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
1023                                     }
1024                                  }
1025                                  else
1026                                  {
1027                                     if(isTemplateArg)
1028                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, (uint64)(uintptr)value.p);
1029                                     else
1030                                        ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
1031                                  }
1032                               }
1033                            }
1034                            else
1035                            {
1036                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
1037                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
1038                            }
1039                         }
1040                      }
1041                   }
1042                   else
1043                      result = syntaxError;
1044
1045                   if(prop && type && type.type == structClass)
1046                   {
1047                      delete value.p;
1048                   }
1049                }
1050             }
1051             else if(ch && ch != '}' && ch != ',' && (!eCON || ch != ';'))
1052                result = syntaxError;
1053             delete string;
1054
1055             if(result)
1056             {
1057                SkipEmpty();
1058                if(ch == '}')
1059                {
1060                   break;
1061                }
1062                else if(ch != ',' && (!eCON || ch != ';'))
1063                   result = syntaxError;
1064             }
1065          }
1066
1067          if(objectType.type == bitClass)
1068          {
1069             switch(objectType.typeSize)
1070             {
1071                case 1: *(uint64 *)object = (byte)   bits; break;
1072                case 2: *(uint64 *)object = (uint16) bits; break;
1073                case 4: *(uint64 *)object = (uint32) bits; break;
1074                case 8: *(uint64 *)object = (uint64) bits; break;
1075             }
1076          }
1077       }
1078       ch = 0;
1079       return result;
1080    }
1081
1082    JSONResult GetNumber(Class type, DataValue value)
1083    {
1084       JSONResult result = success;
1085       char buffer[256];
1086       int c = 0;
1087       bool comment = false;
1088       bool hexMode = false;
1089       if(eCON)
1090       {
1091          while(c < sizeof(buffer)-1 && (comment || ch == '-' || ch == '.' || tolower(ch) == 'f' ||
1092                      (c == 1 && tolower(ch) == 'x' && buffer[0] == '0') || tolower(ch) == 'e' || ch == '+' || isdigit(ch) || ch == '/' ||
1093                      (hexMode && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')))))
1094          {
1095             if(!comment && ch == '/')
1096             {
1097                if(f.Getc(&ch))
1098                {
1099                   if(ch == '*')
1100                      comment = true;
1101                }
1102                else
1103                {
1104                   result = syntaxError;
1105                   break;
1106                }
1107             }
1108             else if(comment && ch == '*')
1109             {
1110                if(f.Getc(&ch))
1111                {
1112                   if(ch == '/')
1113                      comment = false;
1114                }
1115                else
1116                {
1117                   result = syntaxError;
1118                   break;
1119                }
1120             }
1121             else if(!comment)
1122             {
1123                if(c == 1 && ch == 'x' && buffer[0] == '0')
1124                   hexMode = true;
1125                buffer[c++] = ch;
1126             }
1127             if(!f.Getc(&ch)) break;
1128          }
1129       }
1130       else
1131       {
1132          while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
1133          {
1134             buffer[c++] = ch;
1135             if(!f.Getc(&ch)) break;
1136          }
1137       }
1138       buffer[c] = 0;
1139       //if(strchr(buffer, '.'))
1140       if(result == syntaxError)
1141          return result;
1142       if(!type) return success;
1143       result = syntaxError;
1144
1145       // TOFIX: How to swiftly handle classes with base data type?
1146       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1147       {
1148          value.d = strtod(buffer, null);
1149          result = success;
1150       }
1151       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1152       {
1153          value.f = (float)strtod(buffer, null);
1154          result = success;
1155       }
1156       // TOFIX: int64 looks for class long long?
1157       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
1158       else if(!strcmp(type.dataTypeString, "int64"))
1159       {
1160          value.i64 = strtol(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1161          result = success;
1162       }
1163       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
1164       {
1165          value.ui64 = strtoul(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1166          result = success;
1167       }
1168       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1169       {
1170          value.ui = (uint)strtoul(buffer, null, eCON ? 0 : 10);  // TOFIX: 64 bit support
1171          result = success;
1172       }
1173       else
1174       {
1175          value.i = (int)strtol(buffer, null, eCON ? 0 : 10);
1176          result = success;
1177       }
1178
1179       if(result == success && type.type == unitClass)
1180       {
1181          // Convert to reference unit
1182          Property prop;
1183          for(prop = type.conversions.first; prop; prop = prop.next)
1184          {
1185             bool refProp = false;
1186             if(!strcmp(prop.name, type.base.fullName))
1187                refProp = true;
1188             else
1189             {
1190                Class c = eSystem_FindClass(type.module, prop.name);
1191                if(!c)
1192                   c = eSystem_FindClass(type.module.application, prop.name);
1193                if(c)
1194                {
1195                   Property p;
1196                   for(p = c.conversions.first; p; p = p.next)
1197                   {
1198                      if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
1199                      {
1200                         refProp = true;
1201                         break;
1202                      }
1203                   }
1204                }
1205             }
1206             if(refProp)
1207             {
1208                if(prop.Set && prop.Get)
1209                {
1210                   const String dts = type.base.dataTypeString;
1211                   if(!strcmp(dts, "double"))
1212                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
1213                   else if(!strcmp(dts, "float"))
1214                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
1215                   else if(!strcmp(dts, "int"))
1216                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
1217                   else if(!strcmp(dts, "int64"))
1218                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
1219                   else if(!strcmp(dts, "unsigned int"))
1220                      value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
1221                   else if(!strcmp(dts, "uint64"))
1222                      value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
1223                }
1224                else
1225                   break;
1226             }
1227          }
1228       }
1229       return result;
1230    }
1231
1232    JSONResult GetColorAlpha(String string, DataValue value)
1233    {
1234       ColorAlpha color = 0;
1235       DefinedColor c = 0;
1236       if(string)
1237       {
1238          if(string[0] == '0' && string[1] == 'x')
1239             color = (uint)strtoul(string, null, 0);
1240          else
1241          {
1242             char *d;
1243             byte a = 255;
1244             if((d = strchr(string, ',')))
1245             {
1246                a = (byte)atoi(string);
1247                d += 2;
1248             }
1249             else
1250                d = string;
1251             if(c.class::OnGetDataFromString(d))
1252             {
1253                color.a = a;
1254                color.color = c;
1255             }
1256             else
1257                color = (uint)strtoul(string, null, 16);
1258          }
1259       }
1260       value.i = color;
1261       return success;
1262    }
1263 }
1264
1265 static bool WriteMap(File f, Class type, Map map, int indent, bool eCON)
1266 {
1267    if(map)
1268    {
1269       int i;
1270       bool isFirst = true;
1271       MapIterator it { map = map };
1272       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
1273       f.Puts("[\n");
1274       indent++;
1275
1276       while(it.Next())
1277       {
1278          MapNode n = (MapNode)it.pointer;
1279          if(!isFirst)
1280             f.Puts(",\n");
1281          else
1282             isFirst = false;
1283          for(i = 0; i<indent; i++) f.Puts("   ");
1284          WriteONObject(f, mapNodeClass, n, indent, eCON, eCON ? true : false);
1285       }
1286       f.Puts("\n");
1287       indent--;
1288       for(i = 0; i<indent; i++) f.Puts("   ");
1289       f.Puts("]");
1290    }
1291    else
1292       f.Puts("null");
1293    return true;
1294 }
1295
1296 static bool WriteArray(File f, Class type, Container array, int indent, bool eCON)
1297 {
1298    if(array)
1299    {
1300       int i;
1301       bool isFirst = true;
1302       Iterator it { array };
1303       Class arrayType = type.templateArgs[0].dataTypeClass;
1304       f.Puts("[\n");
1305       indent++;
1306
1307       while(it.Next())
1308       {
1309          DataValue value { };
1310          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
1311          if(!isFirst)
1312             f.Puts(",\n");
1313          else
1314             isFirst = false;
1315
1316          // Add value
1317          // TODO: Verify the matching between template type and uint64
1318          if(arrayType.type == structClass)
1319          {
1320             value.p = (void *)(uintptr)t;
1321          }
1322          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
1323          {
1324             value.ui64 = t;
1325             //value.d = *(double *)&t;
1326          }
1327          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
1328          {
1329             value.ui = (uint)t;
1330             //value.f = *(float *)&t;
1331          }
1332          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
1333             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
1334          {
1335             value.ui64 = t;
1336          }
1337          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
1338             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
1339          {
1340             value.i = (int)t;
1341          }
1342          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
1343             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
1344             !strcmp(arrayType.dataTypeString, "int16"))
1345          {
1346             value.s = (short)t;
1347          }
1348          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
1349             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
1350          {
1351             value.c = (char)t;
1352          }
1353          else
1354          {
1355             value.p = (void *)(uintptr)t;
1356          }
1357          for(i = 0; i<indent; i++) f.Puts("   ");
1358          WriteValue(f, arrayType, value, indent, eCON);
1359       }
1360       f.Puts("\n");
1361       indent--;
1362       for(i = 0; i<indent; i++) f.Puts("   ");
1363       f.Puts("]");
1364    }
1365    else
1366       f.Puts("null");
1367    return true;
1368 }
1369
1370 static bool WriteNumber(File f, Class type, DataValue value, int indent, bool eCON, bool useHex)
1371 {
1372    char buffer[1024];
1373    bool needClass = eCON;
1374    bool quote;
1375    buffer[0] = 0;
1376    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1377       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
1378    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1379       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
1380    else if(!strcmp(type.dataTypeString, "int64"))
1381       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
1382    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
1383    {
1384       if(useHex)
1385          sprintf(buffer, __runtimePlatform == win32 ? "0x%016I64X" : "0x%016llX", value.ui64);
1386       else
1387          ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
1388    }
1389    else if(!strcmp(type.dataTypeString, "int"))
1390       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
1391    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
1392    {
1393       if(useHex)
1394          sprintf(buffer, "0x%08X", value.ui);
1395       else
1396          ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
1397    }
1398    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
1399       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
1400    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
1401       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
1402    else if(!strcmp(type.dataTypeString, "char"))
1403       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
1404    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
1405       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
1406
1407    quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' '))) ||
1408            (type.type == enumClass && !eCON);
1409    if(quote) f.Puts("\"");
1410    f.Puts(buffer);
1411    if(quote) f.Puts("\"");
1412    return true;
1413 }
1414
1415 public bool WriteColorAlpha(File f, Class type, DataValue value, int indent, bool eCON)
1416 {
1417    char buffer[1024];
1418    char * string = buffer;
1419    ColorAlpha color = value.i;
1420    int a = color.a;
1421    int len;
1422    DefinedColor c = color;
1423    buffer[0] = '\0';
1424    if(a != 255)
1425    {
1426       a.class::OnGetString(buffer, null, null);
1427       len = strlen(buffer);
1428       buffer[len++] = ',';
1429       buffer[len++] = ' ';
1430       buffer[len] = '\0';
1431       string += len;
1432    }
1433    if(!c.class::OnGetString(string, null, null))
1434       sprintf(buffer, "0x%x", color);
1435    if(!eCON)
1436       f.Puts("\"");
1437    f.Puts(buffer);
1438    if(!eCON)
1439       f.Puts("\"");
1440    return true;
1441 }
1442
1443 static bool WriteValue(File f, Class type, DataValue value, int indent, bool eCON)
1444 {
1445    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
1446    {
1447       if(!value.p)
1448          f.Puts("null");
1449       else
1450       {
1451          f.Puts("\"");
1452          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
1453          if(eCON)
1454          {
1455             int c = 0;
1456             int b = 0;
1457             char buffer[1024];
1458             char * string = value.p;
1459             char ch;
1460             while(true)
1461             {
1462                ch = string[c++];
1463                if(ch == '\"')
1464                {
1465                   buffer[b] = 0;
1466                   f.Puts(buffer);
1467                   f.Puts("\\\"");
1468                   b = 0;
1469                }
1470                else if(ch == '\\')
1471                {
1472                   buffer[b] = 0;
1473                   f.Puts(buffer);
1474                   f.Puts("\\\\");
1475                   b = 0;
1476                }
1477                else if(ch == '\t')
1478                {
1479                   buffer[b] = 0;
1480                   f.Puts(buffer);
1481                   f.Puts("\\t");
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 }