ecere/sys/JSON: Fixed support for writing struct properties
[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             itemResult = GetValue(arrayType, value);
204             if(itemResult == success)
205             {
206                // TODO: Verify the matching between template type and uint64
207                uint64 t;
208                if(arrayType.type == structClass)
209                {
210                   t = (uint64)(uintptr)value.p;
211                }
212                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
213                {
214                   t = value.ui64; //*(uint64 *)&value.d;
215                }
216                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
217                {
218                   t = value.ui; //f*(uint *)&value.f;
219                }
220                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
221                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
222                {
223                   t = value.ui64;
224                }
225                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
226                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
227                {
228                   t = value.i;
229                }
230                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
231                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
232                   !strcmp(arrayType.dataTypeString, "int16"))
233                {
234                   t = value.s;
235                }
236                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
237                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
238                {
239                   t = value.c;
240                }
241                else
242                {
243                   t = (uint64)(uintptr)value.p;
244                }
245                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
246             }
247             else
248             {
249                if(itemResult == typeMismatch)
250                {
251                   if(arrayType)
252                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
253                }
254                else if(itemResult == noItem)
255                   result = success;
256                else
257                   result = itemResult;
258             }
259
260             if(result != syntaxError)
261             {
262                if(ch != ']' && ch != ',')
263                {
264                   ch = 0;
265                   SkipEmpty();
266                }
267                if(ch == ']')
268                {
269                   break;
270                }
271                else if(ch != ',')
272                   result = syntaxError;
273             }
274          }
275       }
276       ch = 0;
277       return result;
278    }
279
280    JSONResult GetMap(Class type, Map * map)
281    {
282       JSONResult result = syntaxError;
283       SkipEmpty();
284       *map = null;
285       if(ch == '[')
286       {
287          Class mapNodeType = type.templateArgs[0].dataTypeClass;
288          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
289          Property keyProp = null;
290          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
291             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
292
293          *map = eInstance_New(type);
294          result = success;
295
296          while(result)
297          {
298             DataValue value { };
299
300             JSONResult itemResult;
301
302             itemResult = GetValue(mapNodeType, value);
303             if(itemResult == success)
304             {
305                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
306                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
307                // Must free String keys here
308                delete s;
309             }
310             else
311             {
312                if(itemResult == typeMismatch)
313                {
314                   if(mapNodeType)
315                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
316                }
317                else if(itemResult == noItem)
318                   result = success;
319                else
320                   result = itemResult;
321             }
322
323             if(result != syntaxError)
324             {
325                if(ch != ']' && ch != ',')
326                {
327                   ch = 0;
328                   SkipEmpty();
329                }
330                if(ch == ']')
331                {
332                   break;
333                }
334                else if(ch != ',')
335                   result = syntaxError;
336             }
337          }
338       }
339       ch = 0;
340       return result;
341    }
342
343    JSONResult GetString(String * string)
344    {
345       JSONResult result = syntaxError;
346       Array<char> buffer { minAllocSize = 256 };
347       bool escaped = false;
348
349       *string = null;
350       SkipEmpty();
351       if(ch == '\"')
352       {
353          while(f.Getc(&ch))
354          {
355             if(ch == '\\' && !escaped)
356                escaped = true;
357             else
358             {
359                if(escaped)
360                {
361                   if(ch == 'b') ch = '\b';
362                   else if(ch == 'f') ch = '\f';
363                   else if(ch == 'n') ch = '\n';
364                   else if(ch == 'r') ch = '\r';
365                   else if(ch == 't') ch = '\t';
366                   else if(ch == 'u')
367                   {
368                      // SKIP FOR NOW...
369                      char unicode[4];
370                      f.Getc(&unicode[0]);
371                      f.Getc(&unicode[1]);
372                      f.Getc(&unicode[2]);
373                      f.Getc(&unicode[3]);
374                   }
375                   escaped = false;
376                }
377                else if(ch == '\"')
378                {
379                   break;
380                }
381                buffer.Add(ch);
382                if(buffer.minAllocSize < buffer.count)
383                   buffer.minAllocSize *= 2;
384             }
385          }
386          buffer.Add(0);
387          *string = CopyString(buffer.array);
388          result = success;
389       }
390       delete buffer;
391       if(ch != ',' && ch != '}')
392          ch = 0;
393       return result;
394    }
395
396    public JSONResult GetObject(Class objectType, void ** object)
397    {
398       JSONResult result = syntaxError;
399       if(!objectType || objectType.type != structClass)
400          *object = null;
401       SkipEmpty();
402       if(ch == '{')
403       {
404          Class mapKeyClass = null, mapDataClass = null;
405
406          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
407          {
408             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
409             mapDataClass = objectType.templateArgs[2].dataTypeClass;
410          }
411
412          result = success;
413          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
414          {
415             *object = eInstance_New(objectType);
416          }
417          else if(objectType && objectType.type != structClass)
418          {
419             *object = eSystem_New(objectType.typeSize);
420          }
421
422          while(result)
423          {
424             String string;
425             ch = 0;
426             if(GetString(&string))
427             {
428                DataMember member = null;
429                Property prop = null;
430                Class type = null;
431                bool isKey = false;
432                uint offset = 0;
433
434                if(objectType)
435                {
436                   string[0] = (char)tolower(string[0]);
437                   if(mapKeyClass && !strcmp(string, "key"))
438                   {
439                      prop = eClass_FindProperty(objectType, "key", objectType.module);
440                      type = mapKeyClass;
441                      isKey = true;
442                   }
443                   else if(mapDataClass && !strcmp(string, "value"))
444                   {
445                      prop = eClass_FindProperty(objectType, "value", objectType.module);
446                      type = mapDataClass;
447                   }
448                   else
449                   {
450                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
451                      if(member)
452                      {
453                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
454                         if(!type)
455                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
456
457                         offset = member._class.offset + member.offset;
458                      }
459                      else if(!member)
460                      {
461                         prop = eClass_FindProperty(objectType, string, objectType.module);
462                         if(prop)
463                         {
464                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
465                            if(!type)
466                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
467                         }
468                         else
469                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
470                      }
471                   }
472                }
473                // Find Member in Object Class
474                {
475                   DataValue value { };
476
477                   if(type && type.type == structClass)
478                   {
479                      value.p = (byte *)*object + offset;
480                   }
481                   ch = 0;
482                   SkipEmpty();
483                   if(ch == ':')
484                   {
485                      JSONResult itemResult = GetValue(type, value);
486                      if(itemResult != syntaxError)
487                      {
488                         if(prop || member)
489                         {
490                            if(!type)
491                            {
492                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
493                            }
494                            else if(itemResult == success)
495                            {
496                               // Set value
497                               if(member)
498                               {
499                                  // TOFIX: How to swiftly handle classes with base data type?
500                                  if(type.type == structClass)
501                                     ;
502                                  else if(type.type == normalClass || type.type == noHeadClass)
503                                  {
504                                     void ** ptr = (void**)((byte *)*object + offset);
505                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
506                                     {
507                                        Container container = (Container)*ptr;
508                                        container.Free();
509                                        delete container;
510                                     }
511                                     *ptr = value.p;
512                                  }
513                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
514                                  {
515                                     *(double *)((byte *)*object + offset) = value.d;
516                                  }
517                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
518                                  {
519                                     *(float *)((byte *)*object + offset) = value.f;
520                                  }
521                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
522                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
523                                  {
524                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
525                                  }
526                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
527                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
528                                  {
529                                     *(int *)((byte *)*object + offset) = value.i;
530                                  }
531                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
532                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
533                                     !strcmp(type.dataTypeString, "int16"))
534                                  {
535                                     *(short *)((byte *)*object + offset) = value.s;
536                                  }
537                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
538                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
539                                  {
540                                     *(char *)((byte *)*object + offset) = value.c;
541                                  }
542                                  else
543                                  {
544                                     *(void **)((byte *)*object + offset) = value.p;
545                                  }
546                               }
547                               else if(prop && prop.Set)
548                               {
549                                  if(!strcmp(type.dataTypeString, "char *"))
550                                  {
551                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
552                                     if(!isKey)
553                                        delete value.p;
554                                  }
555                                  // TOFIX: How to swiftly handle classes with base data type?
556                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
557                                  {
558                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
559                                  }
560                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
561                                  {
562                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
563                                  }
564                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
565                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
566                                  {
567                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
568                                  }
569                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
570                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
571                                  {
572                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
573                                  }
574                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
575                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
576                                     !strcmp(type.dataTypeString, "int16"))
577                                  {
578                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
579                                  }
580                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
581                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
582                                  {
583                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
584                                  }
585                                  else
586                                  {
587                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
588                                  }
589                               }
590                            }
591                            else
592                            {
593                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
594                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
595                            }
596                         }
597                      }
598                   }
599                   else
600                      result = syntaxError;
601                }
602             }
603             else if(ch && ch != '}' && ch != ',')
604                result = syntaxError;
605             delete string;
606
607             if(result)
608             {
609                SkipEmpty();
610                if(ch == '}')
611                {
612                   break;
613                }
614                else if(ch != ',')
615                   result = syntaxError;
616             }
617          }
618       }
619       ch = 0;
620       return result;
621    }
622
623    JSONResult GetNumber(Class type, DataValue value)
624    {
625       JSONResult result = syntaxError;
626       char buffer[256];
627       int c = 0;
628       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
629       {
630          buffer[c++] = ch;
631          if(!f.Getc(&ch)) break;
632       }
633       buffer[c] = 0;
634       //if(strchr(buffer, '.'))
635
636       // TOFIX: How to swiftly handle classes with base data type?
637       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
638       {
639          value.d = strtod(buffer, null);
640          result = success;
641       }
642       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
643       {
644          value.f = (float)strtod(buffer, null);
645          result = success;
646       }
647       // TOFIX: int64 looks for class long long?
648       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
649       else if(!strcmp(type.dataTypeString, "int64"))
650       {
651          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
652          result = success;
653       }
654       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
655       {
656          value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
657          result = success;
658       }
659       else
660       {
661          value.i = strtol(buffer, null, 10);
662          result = success;
663       }
664       return result;
665    }
666 }
667
668 bool WriteMap(File f, Class type, Map map, int indent)
669 {
670    if(map)
671    {
672       int i;
673       bool isFirst = true;
674       MapIterator it { map = map };
675       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
676       f.Puts("[\n");
677       indent++;
678
679       while(it.Next())
680       {
681          MapNode n = (MapNode)it.pointer;
682          if(!isFirst)
683             f.Puts(",\n");
684          else
685             isFirst = false;
686          for(i = 0; i<indent; i++) f.Puts("   ");
687          _WriteJSONObject(f, mapNodeClass, n, indent);
688       }
689       f.Puts("\n");
690       indent--;
691       for(i = 0; i<indent; i++) f.Puts("   ");
692       f.Puts("]");
693    }
694    else
695       f.Puts("null");
696    return true;
697 }
698
699 bool WriteArray(File f, Class type, Container array, int indent)
700 {
701    if(array)
702    {
703       int i;
704       bool isFirst = true;
705       Iterator it { array };
706       Class arrayType = type.templateArgs[0].dataTypeClass;
707       f.Puts("[\n");
708       indent++;
709
710       while(it.Next())
711       {
712          DataValue value { };
713          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
714          if(!isFirst)
715             f.Puts(",\n");
716          else
717             isFirst = false;
718
719          // Add value
720          // TODO: Verify the matching between template type and uint64
721          if(arrayType.type == structClass)
722          {
723             value.p = (void *)(uintptr)t;
724          }
725          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
726          {
727             value.ui64 = t;
728             //value.d = *(double *)&t;
729          }
730          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
731          {
732             value.ui = (uint)t;
733             //value.f = *(float *)&t;
734          }
735          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
736             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
737          {
738             value.ui64 = t;
739          }
740          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
741             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
742          {
743             value.i = (int)t;
744          }
745          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
746             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
747             !strcmp(arrayType.dataTypeString, "int16"))
748          {
749             value.s = (short)t;
750          }
751          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
752             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
753          {
754             value.c = (char)t;
755          }
756          else
757          {
758             value.p = (void *)(uintptr)t;
759          }
760          for(i = 0; i<indent; i++) f.Puts("   ");
761          WriteValue(f, arrayType, value, indent);
762       }
763       f.Puts("\n");
764       indent--;
765       for(i = 0; i<indent; i++) f.Puts("   ");
766       f.Puts("]");
767    }
768    else
769       f.Puts("null");
770    return true;
771 }
772
773 bool WriteNumber(File f, Class type, DataValue value, int indent)
774 {
775    char buffer[1024];
776    bool needClass = false;
777    buffer[0] = 0;
778    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
779       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
780    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
781       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
782    else if(!strcmp(type.dataTypeString, "int64"))
783       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
784    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
785       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
786    else if(!strcmp(type.dataTypeString, "int"))
787       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
788    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
789       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
790    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
791       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
792    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
793       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
794    else if(!strcmp(type.dataTypeString, "char"))
795       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
796    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
797       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
798    f.Puts(buffer);
799    return true;
800 }
801
802 bool WriteValue(File f, Class type, DataValue value, int indent)
803 {
804    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
805    {
806       if(!value.p)
807          f.Puts("null");
808       else
809       {
810          f.Puts("\"");
811          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
812          {
813             int c = 0;
814             int b = 0;
815             char buffer[1024];
816             char * string = value.p;
817             char ch;
818             while(true)
819             {
820                ch = string[c++];
821                if(ch == '\"')
822                {
823                   buffer[b] = 0;
824                   f.Puts(buffer);
825                   f.Puts("\\\"");
826                   b = 0;
827                }
828                else if(ch == '\\')
829                {
830                   buffer[b] = 0;
831                   f.Puts(buffer);
832                   f.Puts("\\\\");
833                   b = 0;
834                }
835                else if(b == sizeof(buffer)-2 || !ch)
836                {
837                   buffer[b++] = ch;
838                   if(ch) buffer[b] = 0;
839                   f.Puts(buffer);
840                   b = 0;
841                   if(!ch) break;
842                }
843                else
844                   buffer[b++] = ch;
845             }
846          }
847          /*else
848             f.Puts(value.p);*/
849          f.Puts("\"");
850       }
851    }
852    else if(!strcmp(type.name, "bool"))
853    {
854       if(value.i)
855          f.Puts("true");
856       else
857          f.Puts("false");
858    }
859    else if(!strcmp(type.name, "SetBool"))
860    {
861       if(value.i == SetBool::true)
862          f.Puts("true");
863       else if(value.i == SetBool::false)
864          f.Puts("false");
865       else
866          f.Puts("unset");
867    }
868    else if(type.type == enumClass || type.type == unitClass)
869    {
870       f.Puts("\"");
871       WriteNumber(f, type, value, indent);
872       f.Puts("\"");
873    }
874    else if(eClass_IsDerived(type, class(Map)))
875    {
876       WriteMap(f, type, value.p, indent);
877    }
878    else if(eClass_IsDerived(type, class(Container)))
879    {
880       WriteArray(f, type, value.p, indent);
881    }
882    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
883    {
884       _WriteJSONObject(f, type, value.p, indent);
885    }
886    else if(type.type == bitClass)
887    {
888       Class dataType;
889       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
890       WriteNumber(f, dataType, value, indent);
891    }
892    else if(type.type == systemClass)
893    {
894       WriteNumber(f, type, value, indent);
895    }
896    return true;
897 }
898
899 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
900 {
901    bool result = false;
902    if(object)
903    {
904       result = _WriteJSONObject(f, objectType, object, indent);
905       f.Puts("\n");
906    }
907    return result;
908 }
909
910 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
911 {
912    if(object)
913    {
914       const char * string = null;
915
916       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
917       {
918          char buffer[1024];
919          buffer[0] = 0;
920          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
921       }
922       if(string)
923       {
924          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
925          if(string[0] == '\"')
926             f.Puts(string);
927          else
928          {
929             f.Puts("\"");
930             f.Puts(string);
931             f.Puts("\"");
932          }
933       }
934       else
935       {
936          Property prop;
937          int c;
938          bool isFirst = true;
939          Class mapKeyClass = null, mapDataClass = null;
940          Class baseClass;
941          List<Class> bases { };
942
943          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
944          {
945             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
946             mapDataClass = objectType.templateArgs[2].dataTypeClass;
947          }
948
949          f.Puts("{\n");
950          indent++;
951
952          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
953          {
954             if(baseClass.isInstanceClass || !baseClass.base)
955                break;
956             bases.Insert(null, baseClass);
957          }
958
959          for(baseClass : bases)
960          {
961             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
962             {
963                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
964                if(prop.isProperty)
965                {
966                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
967                   {
968                      DataValue value { };
969                      Class type;
970
971                      if(mapKeyClass && !strcmp(prop.name, "key"))
972                         type = mapKeyClass;
973                      else if(mapDataClass && !strcmp(prop.name, "value"))
974                         type = mapDataClass;
975                      else
976                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
977                      if(!type)
978                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
979                      if(!type)
980                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
981                      else
982                      {
983                         // TOFIX: How to swiftly handle classes with base data type?
984                         if(type == class(double) || !strcmp(type.dataTypeString, "double"))
985                         {
986                            value.d = ((double (*)(void *))(void *)prop.Get)(object);
987                         }
988                         else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
989                         {
990                            value.f = ((float (*)(void *))(void *)prop.Get)(object);
991                         }
992                         else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
993                            !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
994                         {
995                            value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
996                         }
997                         else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
998                            !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
999                         {
1000                            value.i = ((int (*)(void *))(void *)prop.Get)(object);
1001                         }
1002                         else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1003                            !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1004                            !strcmp(type.dataTypeString, "int16"))
1005                         {
1006                            value.s = ((short (*)(void *))(void *)prop.Get)(object);
1007                         }
1008                         else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1009                            !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1010                         {
1011                            value.c = ((char (*)(void *))(void *)prop.Get)(object);
1012                         }
1013                         else if(type.type == structClass)
1014                         {
1015                            value.p = new byte[type.structSize];
1016                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1017                         }
1018                         else
1019                         {
1020                            value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1021                         }
1022
1023                         if(!isFirst) f.Puts(",\n");
1024                         for(c = 0; c<indent; c++) f.Puts("   ");
1025
1026                         f.Puts("\"");
1027                         f.Putc((char)toupper(prop.name[0]));
1028                         f.Puts(prop.name+1);
1029                         f.Puts("\" : ");
1030                         WriteValue(f, type, value, indent);
1031                         isFirst = false;
1032                         if(type.type == structClass)
1033                            delete value.p;
1034                      }
1035                   }
1036                }
1037                else
1038                {
1039                   DataMember member = (DataMember)prop;
1040                   DataValue value { };
1041                   uint offset;
1042                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1043                   if(!type)
1044                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1045
1046                   offset = member._class.offset + member.offset;
1047
1048                   if(type)
1049                   {
1050                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1051                      {
1052                         if(type.type == structClass)
1053                            value.p = (void *)((byte *)object + offset);
1054                         else
1055                            value.p = *(void **)((byte *)object + offset);
1056                         if(!value.p)
1057                            continue;
1058                      }
1059                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1060                      {
1061                         value.d = *(double *)((byte *)object + offset);
1062                      }
1063                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1064                      {
1065                         value.f = *(float *)((byte *)object + offset);
1066                      }
1067                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1068                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1069                      {
1070                         value.ui64 = *(uint64 *)((byte *)object + offset);
1071                      }
1072                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1073                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1074                      {
1075                         value.i = *(int *)((byte *)object + offset);
1076                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1077                            if(!value.i)
1078                               continue;
1079                      }
1080                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1081                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1082                         !strcmp(type.dataTypeString, "int16"))
1083                      {
1084                         value.s = *(short *)((byte *)object + offset);
1085                      }
1086                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1087                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1088                      {
1089                         value.c = *(char *)((byte *)object + offset);
1090                      }
1091                      else
1092                      {
1093                         value.i = *(int *)((byte *)object + offset);
1094                      }
1095
1096                      if(!isFirst) f.Puts(",\n");
1097                      for(c = 0; c<indent; c++) f.Puts("   ");
1098
1099                      f.Puts("\"");
1100                      f.Putc((char)toupper(member.name[0]));
1101                      f.Puts(member.name+1);
1102                      f.Puts("\" : ");
1103                      WriteValue(f, type, value, indent);
1104                      isFirst = false;
1105                   }
1106                }
1107             }
1108          }
1109
1110          delete bases;
1111
1112          indent--;
1113          f.Puts("\n");
1114          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1115       }
1116    }
1117    else
1118       f.Puts("null");
1119    return true;
1120 }