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