wip: json
[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 ch;
39
40    void SkipEmpty()
41    {
42       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/'))
43       {
44          f.Getc(&ch);
45       }
46    }
47
48    JSONResult GetValue(Class type, DataValue value)
49    {
50       JSONResult result = syntaxError;
51       ch = 0;
52       SkipEmpty();
53       if(ch == '\"')
54       {
55          String string;
56          result = GetString(&string);
57          if(result)
58          {
59             Property prop;
60             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
61             {
62                value.p = string;
63             }
64             else if(type && (type.type == enumClass /*|| type.type == unitClass*/))
65             {
66                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
67                   result = success;
68                else
69                   result = typeMismatch;
70                delete string;
71             }
72             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
73             {
74                // TOFIX: Add more conversion property support... Expecting void * compatible here
75                value.p = ((void *(*)())(void *)prop.Set)(string);
76                result = success;
77                delete string;
78             }
79             else if(type && (type.type == structClass))
80             {
81                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
82                   result = success;
83                else
84                   result = typeMismatch;
85                delete string;
86             }
87             else
88             {
89                delete string;
90                result = typeMismatch;
91             }
92          }
93       }
94       else if(ch == '[')
95       {
96          Container array;
97          if(type && eClass_IsDerived(type, class(Map)))
98          {
99             result = GetMap(type, (Map *)&array);
100          }
101          else
102             result = GetArray(type, &array);
103
104          if(result == success && type && eClass_IsDerived(type, class(Container)))
105          {
106             value.p = array;
107          }
108          else
109          {
110             if(array)
111                array.Free();
112             delete array;
113             if(result != success)
114                result = typeMismatch;
115          }
116       }
117       else if(ch == '-' || isdigit(ch))
118       {
119          result = GetNumber(type, value);
120       }
121       else if(ch == '{')
122       {
123          void * object = value.p;
124          result = GetObject(type, &object);
125          if(result)
126          {
127             if(type && type.type == structClass);
128             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
129             {
130                value.p = object;
131             }
132             else
133             {
134                result = typeMismatch;
135                if(type)
136                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
137             }
138          }
139       }
140       else if(isalpha(ch))
141       {
142          char buffer[256];
143          int c = 0;
144          while(c < sizeof(buffer)-1 && isalpha(ch))
145          {
146             buffer[c++] = ch;
147             if(!f.Getc(&ch)) break;
148          }
149          buffer[c] = 0;
150          result = success;
151
152          if(type)
153          {
154             if(!strcmp(type.name, "bool"))
155             {
156                if(!strcmpi(buffer, "false")) value.i = 0;
157                else if(!strcmpi(buffer, "true")) value.i = 1;
158                else
159                   result = typeMismatch;
160             }
161             else if(!strcmp(type.name, "SetBool"))
162             {
163                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
164                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
165                else
166                   result = typeMismatch;
167             }
168             else if(!strcmpi(buffer, "null"))
169             {
170                value.p = 0;
171             }
172             else
173                result = typeMismatch;
174          }
175          else
176             result = typeMismatch;
177       }
178       else if(ch == '}' || ch == ']')
179          result = noItem;
180       return result;
181    }
182
183    JSONResult GetArray(Class type, Container * array)
184    {
185       JSONResult result = syntaxError;
186       SkipEmpty();
187       *array = null;
188       if(ch == '[')
189       {
190          *array = eInstance_New(type);
191          result = success;
192          while(result)
193          {
194             DataValue value { };
195             Class arrayType = null;
196             JSONResult itemResult;
197
198             if(eClass_IsDerived(type, class(Container)))
199             {
200                arrayType = type.templateArgs[0].dataTypeClass;
201             }
202
203             if(arrayType && arrayType.type == structClass)
204                value.p = new byte[arrayType.structSize];
205             itemResult = GetValue(arrayType, value);
206             if(itemResult == success)
207             {
208                // TODO: Verify the matching between template type and uint64
209                uint64 t;
210                if(arrayType.type == structClass)
211                {
212                   t = (uint64)(uintptr)value.p;
213                }
214                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
215                {
216                   t = value.ui64; //*(uint64 *)&value.d;
217                }
218                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
219                {
220                   t = value.ui; //f*(uint *)&value.f;
221                }
222                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
223                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
224                {
225                   t = value.ui64;
226                }
227                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
228                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
229                {
230                   t = value.i;
231                }
232                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
233                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
234                   !strcmp(arrayType.dataTypeString, "int16"))
235                {
236                   t = value.s;
237                }
238                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
239                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
240                {
241                   t = value.c;
242                }
243                else
244                {
245                   t = (uint64)(uintptr)value.p;
246                }
247                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
248
249                if(arrayType && arrayType.type == structClass)
250                   delete value.p;
251             }
252             else
253             {
254                if(itemResult == typeMismatch)
255                {
256                   if(arrayType)
257                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
258                }
259                else if(itemResult == noItem)
260                   result = success;
261                else
262                   result = itemResult;
263             }
264
265             if(result != syntaxError)
266             {
267                if(ch != ']' && ch != ',')
268                {
269                   ch = 0;
270                   SkipEmpty();
271                }
272                if(ch == ']')
273                {
274                   break;
275                }
276                else if(ch != ',')
277                   result = syntaxError;
278             }
279          }
280       }
281       ch = 0;
282       return result;
283    }
284
285    JSONResult GetMap(Class type, Map * map)
286    {
287       JSONResult result = syntaxError;
288       SkipEmpty();
289       *map = null;
290       if(ch == '[')
291       {
292          Class mapNodeType = type.templateArgs[0].dataTypeClass;
293          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
294          Property keyProp = null;
295          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
296             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
297
298          *map = eInstance_New(type);
299          result = success;
300
301          while(result)
302          {
303             DataValue value { };
304
305             JSONResult itemResult;
306
307             itemResult = GetValue(mapNodeType, value);
308             if(itemResult == success)
309             {
310                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
311                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
312                // Must free String keys here
313                delete s;
314             }
315             else
316             {
317                if(itemResult == typeMismatch)
318                {
319                   if(mapNodeType)
320                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
321                }
322                else if(itemResult == noItem)
323                   result = success;
324                else
325                   result = itemResult;
326             }
327
328             if(result != syntaxError)
329             {
330                if(ch != ']' && ch != ',')
331                {
332                   ch = 0;
333                   SkipEmpty();
334                }
335                if(ch == ']')
336                {
337                   break;
338                }
339                else if(ch != ',')
340                   result = syntaxError;
341             }
342          }
343       }
344       ch = 0;
345       return result;
346    }
347
348    JSONResult GetString(String * string)
349    {
350       JSONResult result = syntaxError;
351       Array<char> buffer { minAllocSize = 256 };
352       bool escaped = false;
353
354       *string = null;
355       SkipEmpty();
356       if(ch == '\"')
357       {
358          while(f.Getc(&ch))
359          {
360             if(ch == '\\' && !escaped)
361                escaped = true;
362             else
363             {
364                if(escaped)
365                {
366                   if(ch == 'b') ch = '\b';
367                   else if(ch == 'f') ch = '\f';
368                   else if(ch == 'n') ch = '\n';
369                   else if(ch == 'r') ch = '\r';
370                   else if(ch == 't') ch = '\t';
371                   else if(ch == 'u')
372                   {
373                      // SKIP FOR NOW...
374                      char unicode[4];
375                      f.Getc(&unicode[0]);
376                      f.Getc(&unicode[1]);
377                      f.Getc(&unicode[2]);
378                      f.Getc(&unicode[3]);
379                   }
380                   escaped = false;
381                }
382                else if(ch == '\"')
383                {
384                   break;
385                }
386                buffer.Add(ch);
387                if(buffer.minAllocSize < buffer.count)
388                   buffer.minAllocSize *= 2;
389             }
390          }
391          buffer.Add(0);
392          *string = CopyString(buffer.array);
393          result = success;
394       }
395       delete buffer;
396       if(ch != ',' && ch != '}')
397          ch = 0;
398       return result;
399    }
400
401    public JSONResult GetObject(Class objectType, void ** object)
402    {
403       JSONResult result = syntaxError;
404       if(!objectType || objectType.type != structClass)
405          *object = null;
406       SkipEmpty();
407       if(ch == '{')
408       {
409          Class mapKeyClass = null, mapDataClass = null;
410
411          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
412          {
413             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
414             mapDataClass = objectType.templateArgs[2].dataTypeClass;
415          }
416
417          result = success;
418          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
419          {
420             *object = eInstance_New(objectType);
421          }
422          else if(objectType && objectType.type != structClass)
423          {
424             *object = eSystem_New(objectType.typeSize);
425          }
426
427          while(result)
428          {
429             String string;
430             ch = 0;
431             if(GetString(&string))
432             {
433                DataMember member = null;
434                Property prop = null;
435                Class type = null;
436                bool isKey = false;
437                uint offset = 0;
438
439                if(objectType)
440                {
441                   string[0] = (char)tolower(string[0]);
442                   if(mapKeyClass && !strcmp(string, "key"))
443                   {
444                      prop = eClass_FindProperty(objectType, "key", objectType.module);
445                      type = mapKeyClass;
446                      isKey = true;
447                   }
448                   else if(mapDataClass && !strcmp(string, "value"))
449                   {
450                      prop = eClass_FindProperty(objectType, "value", objectType.module);
451                      type = mapDataClass;
452                   }
453                   else
454                   {
455                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
456                      if(member)
457                      {
458                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
459                         if(!type)
460                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
461
462                         offset = member._class.offset + member.offset;
463                      }
464                      else if(!member)
465                      {
466                         prop = eClass_FindProperty(objectType, string, objectType.module);
467                         if(prop)
468                         {
469                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
470                            if(!type)
471                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
472                         }
473                         else
474                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
475                      }
476                   }
477                }
478                // Find Member in Object Class
479                {
480                   DataValue value { };
481
482                   if(type && type.type == structClass)
483                   {
484                      value.p = (byte *)*object + offset;
485                   }
486                   ch = 0;
487                   SkipEmpty();
488                   if(ch == ':')
489                   {
490                      JSONResult itemResult = GetValue(type, value);
491                      if(itemResult != syntaxError)
492                      {
493                         if(prop || member)
494                         {
495                            if(!type)
496                            {
497                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
498                            }
499                            else if(itemResult == success)
500                            {
501                               // Set value
502                               if(member)
503                               {
504                                  // TOFIX: How to swiftly handle classes with base data type?
505                                  if(type.type == structClass)
506                                     ;
507                                  else if(type.type == normalClass || type.type == noHeadClass)
508                                  {
509                                     void ** ptr = (void**)((byte *)*object + offset);
510                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
511                                     {
512                                        Container container = (Container)*ptr;
513                                        container.Free();
514                                        delete container;
515                                     }
516                                     *ptr = value.p;
517                                  }
518                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
519                                  {
520                                     *(double *)((byte *)*object + offset) = value.d;
521                                  }
522                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
523                                  {
524                                     *(float *)((byte *)*object + offset) = value.f;
525                                  }
526                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
527                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
528                                  {
529                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
530                                  }
531                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
532                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
533                                  {
534                                     *(int *)((byte *)*object + offset) = value.i;
535                                  }
536                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
537                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
538                                     !strcmp(type.dataTypeString, "int16"))
539                                  {
540                                     *(short *)((byte *)*object + offset) = value.s;
541                                  }
542                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
543                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
544                                  {
545                                     *(char *)((byte *)*object + offset) = value.c;
546                                  }
547                                  else
548                                  {
549                                     *(void **)((byte *)*object + offset) = value.p;
550                                  }
551                               }
552                               else if(prop && prop.Set)
553                               {
554                                  if(!strcmp(type.dataTypeString, "char *"))
555                                  {
556                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
557                                     if(!isKey)
558                                        delete value.p;
559                                  }
560                                  // TOFIX: How to swiftly handle classes with base data type?
561                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
562                                  {
563                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
564                                  }
565                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
566                                  {
567                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
568                                  }
569                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
570                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
571                                  {
572                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
573                                  }
574                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
575                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
576                                  {
577                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
578                                  }
579                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
580                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
581                                     !strcmp(type.dataTypeString, "int16"))
582                                  {
583                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
584                                  }
585                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
586                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
587                                  {
588                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
589                                  }
590                                  else
591                                  {
592                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
593                                  }
594                               }
595                            }
596                            else
597                            {
598                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
599                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
600                            }
601                         }
602                      }
603                   }
604                   else
605                      result = syntaxError;
606                }
607             }
608             else if(ch && ch != '}' && ch != ',')
609                result = syntaxError;
610             delete string;
611
612             if(result)
613             {
614                SkipEmpty();
615                if(ch == '}')
616                {
617                   break;
618                }
619                else if(ch != ',')
620                   result = syntaxError;
621             }
622          }
623       }
624       ch = 0;
625       return result;
626    }
627
628    JSONResult GetNumber(Class type, DataValue value)
629    {
630       JSONResult result = syntaxError;
631       char buffer[256];
632       int c = 0;
633       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
634       {
635          buffer[c++] = ch;
636          if(!f.Getc(&ch)) break;
637       }
638       buffer[c] = 0;
639       //if(strchr(buffer, '.'))
640
641       // TOFIX: How to swiftly handle classes with base data type?
642       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
643       {
644          value.d = strtod(buffer, null);
645          result = success;
646       }
647       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
648       {
649          value.f = (float)strtod(buffer, null);
650          result = success;
651       }
652       // TOFIX: int64 looks for class long long?
653       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
654       else if(!strcmp(type.dataTypeString, "int64"))
655       {
656          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
657          result = success;
658       }
659       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
660       {
661          value.ui64 = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
662          result = success;
663       }
664       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int"))
665       {
666          value.ui = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
667          result = success;
668       }
669       else
670       {
671          value.i = strtol(buffer, null, 10);
672          result = success;
673       }
674
675       if(result == success && type.type == unitClass)
676       {
677          // Convert to reference unit
678          Class dataType;
679          Property prop;
680          for(prop = type.conversions.first; prop; prop = prop.next)
681          {
682             bool refProp = false;
683             Class c;
684             if(!strcmp(prop.name, type.base.fullName))
685                refProp = true;
686             else if( (c = eSystem_FindClass(type.module, prop.name) ) )
687             {
688                Property p;
689                for(p = c.conversions.first; p; p = p.next)
690                {
691                   if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
692                   {
693                      refProp = true;
694                      break;
695                   }
696                }
697             }
698             if(refProp)
699             {
700                if(prop.Set && prop.Get)
701                {
702                   const String dts = type.base.dataTypeString;
703                   if(!strcmp(dts, "double"))
704                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
705                   else if(!strcmp(dts, "float"))
706                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
707                   else if(!strcmp(dts, "int"))
708                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
709                   else if(!strcmp(dts, "int64"))
710                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
711                }
712                else
713                   break;
714             }
715          }
716       }
717       return result;
718    }
719 }
720
721 bool WriteMap(File f, Class type, Map map, int indent)
722 {
723    if(map)
724    {
725       int i;
726       bool isFirst = true;
727       MapIterator it { map = map };
728       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
729       f.Puts("[\n");
730       indent++;
731
732       while(it.Next())
733       {
734          MapNode n = (MapNode)it.pointer;
735          if(!isFirst)
736             f.Puts(",\n");
737          else
738             isFirst = false;
739          for(i = 0; i<indent; i++) f.Puts("   ");
740          _WriteJSONObject(f, mapNodeClass, n, indent);
741       }
742       f.Puts("\n");
743       indent--;
744       for(i = 0; i<indent; i++) f.Puts("   ");
745       f.Puts("]");
746    }
747    else
748       f.Puts("null");
749    return true;
750 }
751
752 bool WriteArray(File f, Class type, Container array, int indent)
753 {
754    if(array)
755    {
756       int i;
757       bool isFirst = true;
758       Iterator it { array };
759       Class arrayType = type.templateArgs[0].dataTypeClass;
760       f.Puts("[\n");
761       indent++;
762
763       while(it.Next())
764       {
765          DataValue value { };
766          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
767          if(!isFirst)
768             f.Puts(",\n");
769          else
770             isFirst = false;
771
772          // Add value
773          // TODO: Verify the matching between template type and uint64
774          if(arrayType.type == structClass)
775          {
776             value.p = (void *)(uintptr)t;
777          }
778          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
779          {
780             value.ui64 = t;
781             //value.d = *(double *)&t;
782          }
783          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
784          {
785             value.ui = (uint)t;
786             //value.f = *(float *)&t;
787          }
788          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
789             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
790          {
791             value.ui64 = t;
792          }
793          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
794             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
795          {
796             value.i = (int)t;
797          }
798          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
799             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
800             !strcmp(arrayType.dataTypeString, "int16"))
801          {
802             value.s = (short)t;
803          }
804          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
805             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
806          {
807             value.c = (char)t;
808          }
809          else
810          {
811             value.p = (void *)(uintptr)t;
812          }
813          for(i = 0; i<indent; i++) f.Puts("   ");
814          WriteValue(f, arrayType, value, indent);
815       }
816       f.Puts("\n");
817       indent--;
818       for(i = 0; i<indent; i++) f.Puts("   ");
819       f.Puts("]");
820    }
821    else
822       f.Puts("null");
823    return true;
824 }
825
826 bool WriteNumber(File f, Class type, DataValue value, int indent)
827 {
828    char buffer[1024];
829    bool needClass = false;
830    buffer[0] = 0;
831    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
832       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
833    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
834       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
835    else if(!strcmp(type.dataTypeString, "int64"))
836       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
837    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
838       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
839    else if(!strcmp(type.dataTypeString, "int"))
840       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
841    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
842       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
843    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
844       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
845    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
846       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
847    else if(!strcmp(type.dataTypeString, "char"))
848       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
849    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
850       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
851    f.Puts(buffer);
852    return true;
853 }
854
855 bool WriteValue(File f, Class type, DataValue value, int indent)
856 {
857    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
858    {
859       if(!value.p)
860          f.Puts("null");
861       else
862       {
863          f.Puts("\"");
864          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
865          {
866             int c = 0;
867             int b = 0;
868             char buffer[1024];
869             char * string = value.p;
870             char ch;
871             while(true)
872             {
873                ch = string[c++];
874                if(ch == '\"')
875                {
876                   buffer[b] = 0;
877                   f.Puts(buffer);
878                   f.Puts("\\\"");
879                   b = 0;
880                }
881                else if(ch == '\\')
882                {
883                   buffer[b] = 0;
884                   f.Puts(buffer);
885                   f.Puts("\\\\");
886                   b = 0;
887                }
888                else if(b == sizeof(buffer)-2 || !ch)
889                {
890                   buffer[b++] = ch;
891                   if(ch) buffer[b] = 0;
892                   f.Puts(buffer);
893                   b = 0;
894                   if(!ch) break;
895                }
896                else
897                   buffer[b++] = ch;
898             }
899          }
900          /*else
901             f.Puts(value.p);*/
902          f.Puts("\"");
903       }
904    }
905    else if(!strcmp(type.name, "bool"))
906    {
907       if(value.i)
908          f.Puts("true");
909       else
910          f.Puts("false");
911    }
912    else if(!strcmp(type.name, "SetBool"))
913    {
914       if(value.i == SetBool::true)
915          f.Puts("true");
916       else if(value.i == SetBool::false)
917          f.Puts("false");
918       else
919          f.Puts("unset");
920    }
921    else if(type.type == enumClass) // || type.type == unitClass)
922    {
923       f.Puts("\"");
924       WriteNumber(f, type, value, indent);
925       f.Puts("\"");
926    }
927    else if(eClass_IsDerived(type, class(Map)))
928    {
929       WriteMap(f, type, value.p, indent);
930    }
931    else if(eClass_IsDerived(type, class(Container)))
932    {
933       WriteArray(f, type, value.p, indent);
934    }
935    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
936    {
937       _WriteJSONObject(f, type, value.p, indent);
938    }
939    else if(type.type == bitClass)
940    {
941       Class dataType;
942       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
943       WriteNumber(f, dataType, value, indent);
944    }
945    else if(type.type == systemClass || type.type == unitClass)
946    {
947       WriteNumber(f, type, value, indent);
948    }
949    else if(type.type == bitClass)
950    {
951       Class dataType;
952       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
953       WriteNumber(f, dataType, value, indent);
954    }
955    return true;
956 }
957
958 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
959 {
960    bool result = false;
961    if(object)
962    {
963       result = _WriteJSONObject(f, objectType, object, indent);
964       f.Puts("\n");
965    }
966    return result;
967 }
968
969 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
970 {
971    if(object)
972    {
973       const char * string = null;
974
975       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
976       {
977          char buffer[1024];
978          buffer[0] = 0;
979          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
980       }
981       if(string)
982       {
983          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
984          if(string[0] == '\"')
985             f.Puts(string);
986          else
987          {
988             f.Puts("\"");
989             f.Puts(string);
990             f.Puts("\"");
991          }
992       }
993       else
994       {
995          Property prop;
996          int c;
997          bool isFirst = true;
998          Class mapKeyClass = null, mapDataClass = null;
999          Class baseClass;
1000          List<Class> bases { };
1001
1002          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
1003          {
1004             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
1005             mapDataClass = objectType.templateArgs[2].dataTypeClass;
1006          }
1007
1008          f.Puts("{\n");
1009          indent++;
1010
1011          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
1012          {
1013             if(baseClass.isInstanceClass || !baseClass.base)
1014                break;
1015             bases.Insert(null, baseClass);
1016          }
1017
1018          for(baseClass : bases)
1019          {
1020             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
1021             {
1022                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
1023                if(prop.isProperty)
1024                {
1025                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
1026                   {
1027                      DataValue value { };
1028                      Class type;
1029
1030                      if(mapKeyClass && !strcmp(prop.name, "key"))
1031                         type = mapKeyClass;
1032                      else if(mapDataClass && !strcmp(prop.name, "value"))
1033                         type = mapDataClass;
1034                      else
1035                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
1036                      if(!type)
1037                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
1038                      if(!type)
1039                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
1040                      else
1041                      {
1042                         // TOFIX: How to swiftly handle classes with base data type?
1043                         if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1044                         {
1045                            value.d = ((double (*)(void *))(void *)prop.Get)(object);
1046                         }
1047                         else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1048                         {
1049                            value.f = ((float (*)(void *))(void *)prop.Get)(object);
1050                         }
1051                         else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1052                            !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1053                         {
1054                            value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
1055                         }
1056                         else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1057                            !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1058                         {
1059                            value.i = ((int (*)(void *))(void *)prop.Get)(object);
1060                         }
1061                         else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1062                            !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1063                            !strcmp(type.dataTypeString, "int16"))
1064                         {
1065                            value.s = ((short (*)(void *))(void *)prop.Get)(object);
1066                         }
1067                         else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1068                            !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1069                         {
1070                            value.c = ((char (*)(void *))(void *)prop.Get)(object);
1071                         }
1072                         else if(type.type == structClass)
1073                         {
1074                            value.p = new byte[type.structSize];
1075                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1076                         }
1077                         else
1078                         {
1079                            value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1080                         }
1081
1082                         if(!isFirst) f.Puts(",\n");
1083                         for(c = 0; c<indent; c++) f.Puts("   ");
1084
1085                         f.Puts("\"");
1086                         f.Putc((char)toupper(prop.name[0]));
1087                         f.Puts(prop.name+1);
1088                         f.Puts("\" : ");
1089                         WriteValue(f, type, value, indent);
1090                         isFirst = false;
1091                         if(type.type == structClass)
1092                            delete value.p;
1093                      }
1094                   }
1095                }
1096                else
1097                {
1098                   DataMember member = (DataMember)prop;
1099                   DataValue value { };
1100                   uint offset;
1101                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1102                   if(!type)
1103                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1104
1105                   offset = member._class.offset + member.offset;
1106
1107                   if(type)
1108                   {
1109                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1110                      {
1111                         if(type.type == structClass)
1112                            value.p = (void *)((byte *)object + offset);
1113                         else
1114                            value.p = *(void **)((byte *)object + offset);
1115                         if(!value.p)
1116                            continue;
1117                      }
1118                      else if(type.type == bitClass)
1119                      {
1120                         value.ui64 = *(uint64 *)((byte *)object);
1121 #if 0
1122                         uint64 bits = 0;
1123                         uint64 part = 0;
1124                         BitMember bitMember = (BitMember)member;
1125                         /*if(!bitMember.dataType)
1126                            bitMember.dataType = ProcessTypeString(bitMember.dataTypeString, false);*/
1127                         /*type = bitMember.dataType;
1128                         if(type.kind == classType && type._class && type._class.registered)
1129                         {
1130                            if(!type._class.registered.dataType)
1131                               type._class.registered.dataType = ProcessTypeString(type._class.registered.dataTypeString, false);
1132                            type = type._class.registered.dataType;
1133                         }*/
1134                         PrintLn(type.name);
1135                         PrintLn(type.dataTypeString);
1136 #if 0
1137                         switch(bitMember.dataType.kind)
1138                         {
1139                            case _BoolType:
1140                            case charType:       { byte v; type.isSigned ? GetChar(value, (char *)&v) : GetUChar(value, &v); part = (uint64)v; break; }
1141                            case shortType:      { uint16 v; type.isSigned ? GetShort(value, (short *)&v) : GetUShort(value, &v); part = (uint64)v; break; }
1142                            case intType:
1143                            case longType:       { uint v; type.isSigned ? GetInt(value, (int *)&v) : GetUInt(value, &v); part = (uint64)v; break; }
1144                            case int64Type:      { uint64 v; type.isSigned ? GetInt64(value, (int64 *)&v) : GetUInt64(value, &v); part = (uint64)v; break; }
1145                            case intPtrType:     { uintptr v; type.isSigned ? GetIntPtr(value, (intptr *)&v) : GetUIntPtr(value, &v); part = (uint64)v; break; }
1146                            case intSizeType:    { uintsize v; type.isSigned ? GetIntSize(value, (intsize *)&v) : GetUIntSize(value, &v); part = (uint64)v; break; }
1147                         }
1148 #endif
1149                         bits |= part << bitMember.pos;
1150 #endif
1151                      }
1152                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1153                      {
1154                         value.d = *(double *)((byte *)object + offset);
1155                      }
1156                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1157                      {
1158                         value.f = *(float *)((byte *)object + offset);
1159                      }
1160                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1161                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1162                      {
1163                         value.ui64 = *(uint64 *)((byte *)object + offset);
1164                      }
1165                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1166                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1167                      {
1168                         value.i = *(int *)((byte *)object + offset);
1169                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1170                            if(!value.i)
1171                               continue;
1172                      }
1173                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1174                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1175                         !strcmp(type.dataTypeString, "int16"))
1176                      {
1177                         value.s = *(short *)((byte *)object + offset);
1178                      }
1179                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1180                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1181                      {
1182                         value.c = *(char *)((byte *)object + offset);
1183                      }
1184                      else
1185                      {
1186                         value.i = *(int *)((byte *)object + offset);
1187                      }
1188
1189                      if(!isFirst) f.Puts(",\n");
1190                      for(c = 0; c<indent; c++) f.Puts("   ");
1191
1192                      f.Puts("\"");
1193                      f.Putc((char)toupper(member.name[0]));
1194                      f.Puts(member.name+1);
1195                      f.Puts("\" : ");
1196                      WriteValue(f, type, value, indent);
1197                      isFirst = false;
1198                   }
1199                }
1200             }
1201          }
1202
1203          delete bases;
1204
1205          indent--;
1206          f.Puts("\n");
1207          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1208       }
1209    }
1210    else
1211       f.Puts("null");
1212    return true;
1213 }