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