compiler; ecere; ide; eda: Fixed 32 bit warnings
[sdk] / ecere / src / sys / JSON.ec
1 namespace sys;
2
3 import "System"
4 import "Array"
5
6 default:
7 __attribute__((unused)) static void UnusedFunction()
8 {
9    int a;
10    a.OnGetDataFromString(null);
11    a.OnGetString(null, 0, 0);
12    a.OnFree();
13 }
14 extern int __ecereVMethodID_class_OnGetDataFromString;
15 extern int __ecereVMethodID_class_OnGetString;
16 extern int __ecereVMethodID_class_OnFree;
17 private:
18
19 public enum JSONResult { syntaxError, success, typeMismatch, noItem };
20
21 public enum SetBool : uint
22 {
23    unset, false, true /*; // Syntax error! */
24
25    /*public property bool     // NOT WORKING!
26    {
27       set { return value ? true : false; }
28       get { return (this == true); }
29    }*/
30 };
31
32
33 public class JSONParser
34 {
35 public:
36    File f;
37    char ch;
38
39    void SkipEmpty()
40    {
41       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'))
42       {
43          f.Getc(&ch);
44       }
45    }
46
47    JSONResult GetValue(Class type, DataValue value)
48    {
49       JSONResult result = syntaxError;
50       ch = 0;
51       SkipEmpty();
52       if(ch == '\"')
53       {
54          String string;
55          result = GetString(&string);
56          if(result)
57          {
58             Property prop;
59             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
60             {
61                value.p = string;
62             }
63             else if(type && type.type == enumClass)
64             {
65                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
66                   result = success;
67                else
68                   result = typeMismatch;
69                delete string;
70             }
71             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
72             {
73                // TOFIX: Add more conversion property support... Expecting void * compatible here
74                value.p = ((void *(*)())(void *)prop.Set)(string);
75                result = success;
76                delete string;
77             }
78             else if(type && (type.type == structClass))
79             {
80                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
81                   result = success;
82                else
83                   result = typeMismatch;
84                delete string;
85             }
86             else
87             {
88                delete string;
89                result = typeMismatch;
90             }
91          }
92       }
93       else if(ch == '[')
94       {
95          Container array;
96          result = GetArray(type, &array);
97
98          if(type && eClass_IsDerived(type, class(Container)))
99          {
100             value.p = array;
101          }
102          else
103          {
104             if(array)
105                array.Free();
106             delete array;
107             result = typeMismatch;
108          }
109       }
110       else if(ch == '-' || isdigit(ch))
111       {
112          result = GetNumber(type, value);
113       }
114       else if(ch == '{')
115       {
116          void * object = value.p;
117          result = GetObject(type, &object);
118          if(result)
119          {
120             if(type.type == structClass);
121             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
122             {
123                value.p = object;
124             }
125             else
126             {
127                result = typeMismatch;
128                ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
129             }
130          }
131       }
132       else if(isalpha(ch))
133       {
134          char buffer[256];
135          int c = 0;
136          while(c < sizeof(buffer)-1 && isalpha(ch))
137          {
138             buffer[c++] = ch;
139             if(!f.Getc(&ch)) break;
140          }
141          buffer[c] = 0;
142          result = success;
143
144          if(type)
145          {
146             if(!strcmp(type.name, "bool"))
147             {
148                if(!strcmpi(buffer, "false")) value.i = 0;
149                else if(!strcmpi(buffer, "true")) value.i = 1;
150                else
151                   result = typeMismatch;
152             }
153             else if(!strcmp(type.name, "SetBool"))
154             {
155                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
156                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
157                else
158                   result = typeMismatch;
159             }
160             else if(!strcmpi(buffer, "null"))
161             {
162                value.p = 0;
163             }
164             else
165                result = typeMismatch;
166          }
167          else
168             result = typeMismatch;
169       }
170       else if(ch == '}' || ch == ']')
171          result = noItem;
172       return result;
173    }
174
175    JSONResult GetArray(Class type, Container * array)
176    {
177       JSONResult result = syntaxError;
178       SkipEmpty();
179       *array = null;
180       if(ch == '[')
181       {
182          *array = eInstance_New(type);
183          result = success;
184          while(result)
185          {
186             DataValue value { };
187             Class arrayType = null;
188             JSONResult itemResult;
189
190             if(eClass_IsDerived(type, class(Container)))
191             {
192                arrayType = type.templateArgs[0].dataTypeClass;
193             }
194
195             itemResult = GetValue(arrayType, value);
196             if(itemResult == success)
197             {
198                // TODO: Verify the matching between template type and uint64
199                uint64 t;
200                if(arrayType.type == structClass)
201                {
202                   t = (uint64) value.p;
203                }
204                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
205                {
206                   t = *(uint64 *)&value.d;
207                }
208                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
209                {
210                   t = *(uint *)&value.f;
211                }
212                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
213                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
214                {
215                   t = value.ui64;
216                }
217                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
218                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
219                {
220                   t = value.i;
221                }
222                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
223                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
224                   !strcmp(arrayType.dataTypeString, "int16"))
225                {
226                   t = value.s;
227                }
228                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
229                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
230                {
231                   t = value.c;
232                }
233                else
234                {
235                   t = (uint64)value.p;
236                }
237                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
238             }
239             else
240             {
241                if(itemResult == typeMismatch)
242                {
243                   if(arrayType)
244                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
245                }
246                else if(itemResult == noItem)
247                   result = success;
248                else
249                   result = itemResult;
250             }
251
252             if(result != syntaxError)
253             {
254                if(ch != ']' && ch != ',')
255                {
256                   ch = 0;
257                   SkipEmpty();
258                }
259                if(ch == ']')
260                {
261                   break;
262                }
263                else if(ch != ',')
264                   result = syntaxError;
265             }
266          }
267       }
268       ch = 0;
269       return result;
270    }
271
272    JSONResult GetString(String * string)
273    {
274       JSONResult result = syntaxError;
275       Array<char> buffer { minAllocSize = 256 };
276       bool escaped = false;
277
278       *string = null;
279       SkipEmpty();
280       if(ch == '\"')
281       {
282          while(f.Getc(&ch))
283          {
284             if(ch == '\\' && !escaped)
285                escaped = true;
286             else
287             {
288                if(escaped)
289                {
290                   if(ch == 'b') ch = '\b';
291                   else if(ch == 'f') ch = '\f';
292                   else if(ch == 'n') ch = '\n';
293                   else if(ch == 'r') ch = '\r';
294                   else if(ch == 't') ch = '\t';
295                   else if(ch == 'u')
296                   {
297                      // SKIP FOR NOW...
298                      char unicode[4];
299                      f.Getc(&unicode[0]);
300                      f.Getc(&unicode[1]);
301                      f.Getc(&unicode[2]);
302                      f.Getc(&unicode[3]);
303                   }
304                   escaped = false;
305                }
306                else if(ch == '\"')
307                {
308                   break;
309                }
310                buffer.Add(ch);
311                if(buffer.minAllocSize < buffer.count)
312                   buffer.minAllocSize *= 2;
313             }
314          }
315          buffer.Add(0);
316          *string = CopyString(buffer.array);
317          result = success;
318       }
319       delete buffer;
320       ch = 0;
321       return result;
322    }
323
324    public JSONResult GetObject(Class objectType, void ** object)
325    {
326       JSONResult result = syntaxError;
327       if(objectType.type != structClass)
328          *object = null;
329       SkipEmpty();
330       if(ch == '{')
331       {
332          result = success;
333          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
334          {
335             *object = eInstance_New(objectType);
336          }
337          else if(objectType && objectType.type != structClass)
338          {
339             *object = eSystem_New(objectType.typeSize);
340          }
341
342          while(result)
343          {
344             String string;
345             ch = 0;
346             if(GetString(&string))
347             {
348                DataMember member = null;
349                Property prop = null;
350                Class type = null;
351
352                if(objectType)
353                {
354                   string[0] = (char)tolower(string[0]);
355                   member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
356                   if(member)
357                   {
358                      type = eSystem_FindClass(__thisModule, member.dataTypeString);
359                      if(!type)
360                         type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
361                   }
362                   else if(!member)
363                   {
364                      prop = eClass_FindProperty(objectType, string, objectType.module);
365                      if(prop)
366                      {
367                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
368                         if(!type)
369                            type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
370                      }
371                      else
372                         PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
373                   }
374                }
375                // Find Member in Object Class
376                {
377                   DataValue value { };
378
379                   if(type && type.type == structClass)
380                   {
381                      value.p = (byte *)*object + member._class.offset + member.offset;
382                   }
383                   ch = 0;
384                   SkipEmpty();
385                   if(ch == ':')
386                   {
387                      JSONResult itemResult = GetValue(type, value);
388                      if(itemResult != syntaxError)
389                      {
390                         if(prop || member)
391                         {
392                            if(!type)
393                            {
394                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
395                            }
396                            else if(itemResult == success)
397                            {
398                               // Set value
399                               if(member)
400                               {
401                                  // TOFIX: How to swiftly handle classes with base data type?
402                                  if(type.type == structClass)
403                                     ; // Unhandled
404                                  else if(type.type == normalClass || type.type == noHeadClass)
405                                  {
406                                     void ** ptr = (void**)((byte *)*object + member._class.offset + member.offset);
407                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
408                                     {
409                                        Container container = (Container)*ptr;
410                                        container.Free();
411                                        delete container;
412                                     }
413                                     *ptr = value.p;
414                                  }
415                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
416                                  {
417                                     *(double *)((byte *)*object + member._class.offset + member.offset) = value.d;
418                                  }
419                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
420                                  {
421                                     *(float *)((byte *)*object + member._class.offset + member.offset) = value.f;
422                                  }
423                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
424                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
425                                  {
426                                     *(uint64 *)((byte *)*object + member._class.offset + member.offset) = value.ui64;
427                                  }
428                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
429                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
430                                  {
431                                     *(int *)((byte *)*object + member._class.offset + member.offset) = value.i;
432                                  }
433                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
434                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
435                                     !strcmp(type.dataTypeString, "int16"))
436                                  {
437                                     *(short *)((byte *)*object + member._class.offset + member.offset) = value.s;
438                                  }
439                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
440                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
441                                  {
442                                     *(char *)((byte *)*object + member._class.offset + member.offset) = value.c;
443                                  }
444                                  else
445                                  {
446                                     *(void **)((byte *)*object + member._class.offset + member.offset) = value.p;
447                                  }
448                               }
449                               else if(prop && prop.Set)
450                               {
451                                  if(!strcmp(type.dataTypeString, "char *"))
452                                  {
453                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
454                                     delete value.p;
455                                  }
456                                  // TOFIX: How to swiftly handle classes with base data type?
457                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
458                                  {
459                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
460                                  }
461                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
462                                  {
463                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
464                                  }
465                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
466                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
467                                  {
468                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
469                                  }
470                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
471                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
472                                  {
473                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
474                                  }
475                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
476                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
477                                     !strcmp(type.dataTypeString, "int16"))
478                                  {
479                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
480                                  }
481                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
482                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
483                                  {
484                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
485                                  }
486                                  else
487                                  {
488                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
489                                  }
490                               }
491                            }
492                            else
493                            {
494                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
495                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
496                            }
497                         }
498                      }
499                   }
500                   else
501                      result = syntaxError;
502                }
503             }
504             else if(ch)
505                result = syntaxError;
506             delete string;
507
508             if(result)
509             {
510                SkipEmpty();
511                if(ch == '}')
512                {
513                   break;
514                }
515                else if(ch != ',')
516                   result = syntaxError;
517             }
518          }
519       }
520       ch = 0;
521       return result;
522    }
523
524    JSONResult GetNumber(Class type, DataValue value)
525    {
526       JSONResult result = syntaxError;
527       char buffer[256];
528       int c = 0;
529       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
530       {
531          buffer[c++] = ch;
532          if(!f.Getc(&ch)) break;
533       }
534       buffer[c] = 0;
535       //if(strchr(buffer, '.'))
536
537       // TOFIX: How to swiftly handle classes with base data type?
538       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
539       {
540          value.d = strtod(buffer, null);
541          result = success;
542       }
543       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
544       {
545          value.f = (float)strtod(buffer, null);
546          result = success;
547       }
548       // TOFIX: int64 looks for class long long?
549       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
550       else if(!strcmp(type.dataTypeString, "int64"))
551       {
552          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
553          result = success;
554       }
555       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
556       {
557          value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
558          result = success;
559       }
560       else
561       {
562          value.i = strtol(buffer, null, 10);
563          result = success;
564       }
565       return result;
566    }
567 }
568
569 bool WriteArray(File f, Class type, Container array, int indent)
570 {
571    if(array)
572    {
573       int i;
574       bool isFirst = true;
575       Iterator it { array };
576       Class arrayType = type.templateArgs[0].dataTypeClass;
577       f.Puts("[\n");
578       indent++;
579
580       while(it.Next())
581       {
582          DataValue value { };
583          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
584          if(!isFirst)
585             f.Puts(",\n");
586          else
587             isFirst = false;
588
589          // Add value
590          // TODO: Verify the matching between template type and uint64
591          if(arrayType.type == structClass)
592          {
593             value.p = (void *)t;
594          }
595          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
596          {
597             value.d = *(double *)&t;
598          }
599          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
600          {
601             value.f = *(float *)&t;
602          }
603          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
604             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
605          {
606             value.ui64 = t;
607          }
608          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
609             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
610          {
611             value.i = (int)t;
612          }
613          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
614             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
615             !strcmp(arrayType.dataTypeString, "int16"))
616          {
617             value.s = (short)t;
618          }
619          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
620             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
621          {
622             value.c = (char)t;
623          }
624          else
625          {
626             value.p = (void *)t;
627          }
628          for(i = 0; i<indent; i++) f.Puts("   ");
629          WriteValue(f, arrayType, value, indent);
630       }
631       f.Puts("\n");
632       indent--;
633       for(i = 0; i<indent; i++) f.Puts("   ");
634       f.Puts("]");
635    }
636    else
637       f.Puts("null");
638    return true;
639 }
640
641 bool WriteNumber(File f, Class type, DataValue value, int indent)
642 {
643    char buffer[1024];
644    bool needClass = false;
645    buffer[0] = 0;
646    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
647       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
648    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
649       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
650    else if(!strcmp(type.dataTypeString, "int64"))
651       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
652    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
653       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
654    else if(!strcmp(type.dataTypeString, "int"))
655       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
656    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
657       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
658    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
659       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
660    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
661       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
662    else if(!strcmp(type.dataTypeString, "char"))
663       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
664    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
665       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
666    f.Puts(buffer);
667    return true;
668 }
669
670 bool WriteValue(File f, Class type, DataValue value, int indent)
671 {
672    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
673    {
674       f.Puts("\"");
675       //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
676       {
677          int c = 0;
678          int b = 0;
679          char buffer[1024];
680          char * string = value.p;
681          char ch;
682          while(true)
683          {
684             ch = string[c++];
685             if(ch == '\"')
686             {
687                buffer[b] = 0;
688                f.Puts(buffer);
689                f.Puts("\\\"");
690                b = 0;
691             }
692             else if(ch == '\\')
693             {
694                buffer[b] = 0;
695                f.Puts(buffer);
696                f.Puts("\\\\");
697                b = 0;
698             }
699             else if(b == sizeof(buffer)-2 || !ch)
700             {
701                buffer[b++] = ch;
702                if(ch) buffer[b] = 0;
703                f.Puts(buffer);
704                b = 0;
705                if(!ch) break;
706             }
707             else
708                buffer[b++] = ch;
709          }
710       }
711       /*else
712          f.Puts(value.p);*/
713       f.Puts("\"");
714    }
715    else if(!strcmp(type.name, "bool"))
716    {
717       if(value.i)
718          f.Puts("true");
719       else
720          f.Puts("false");
721    }
722    else if(!strcmp(type.name, "SetBool"))
723    {
724       if(value.i == SetBool::true)
725          f.Puts("true");
726       else if(value.i == SetBool::false)
727          f.Puts("false");
728       else
729          f.Puts("unset");
730    }
731    else if(type.type == enumClass)
732    {
733       f.Puts("\"");
734       WriteNumber(f, type, value, indent);
735       f.Puts("\"");
736    }
737    else if(eClass_IsDerived(type, class(Container)))
738    {
739       WriteArray(f, type, value.p, indent);
740    }
741    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || type.type == bitClass)
742    {
743       _WriteJSONObject(f, type, value.p, indent);
744    }
745    else if(type.type == systemClass)
746    {
747       WriteNumber(f, type, value, indent);
748    }
749    return true;
750 }
751
752 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
753 {
754    bool result = false;
755    if(object)
756    {
757       result = _WriteJSONObject(f, objectType, object, indent);
758       f.Puts("\n");
759    }
760    return result;
761 }
762
763 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
764 {
765    if(object)
766    {
767       const char * string = null;
768
769       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
770       {
771          char buffer[1024];
772          buffer[0] = 0;
773          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
774       }
775       if(string)
776       {
777          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
778          if(string[0] == '\"')
779             f.Puts(string);
780          else
781          {
782             f.Puts("\"");
783             f.Puts(string);
784             f.Puts("\"");
785          }
786       }
787       else
788       {
789          Property prop;
790          int c;
791          bool isFirst = true;
792
793          f.Puts("{\n");
794          indent++;
795
796          for(prop = objectType.membersAndProperties.first; prop; prop = prop.next)
797          {
798             if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
799             if(prop.isProperty)
800             {
801                if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
802                {
803                   DataValue value { };
804                   Class type = eSystem_FindClass(__thisModule, prop.dataTypeString);
805                   if(!type)
806                      type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
807                   if(!type)
808                      PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
809                   else
810                   {
811                      // TOFIX: How to swiftly handle classes with base data type?
812                      if(type == class(double) || !strcmp(type.dataTypeString, "double"))
813                      {
814                         value.d = ((double (*)(void *))(void *)prop.Get)(object);
815                      }
816                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
817                      {
818                         value.f = ((float (*)(void *))(void *)prop.Get)(object);
819                      }
820                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
821                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
822                      {
823                         value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
824                      }
825                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
826                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
827                      {
828                         value.i = ((int (*)(void *))(void *)prop.Get)(object);
829                      }
830                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
831                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
832                         !strcmp(type.dataTypeString, "int16"))
833                      {
834                         value.s = ((short (*)(void *))(void *)prop.Get)(object);
835                      }
836                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
837                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
838                      {
839                         value.c = ((char (*)(void *))(void *)prop.Get)(object);
840                      }
841                      else
842                      {
843                         value.p = ((void *(*)(void *))(void *)prop.Get)(object);
844                      }
845
846                      if(!isFirst) f.Puts(",\n");
847                      for(c = 0; c<indent; c++) f.Puts("   ");
848
849                      f.Puts("\"");
850                      f.Putc((char)toupper(prop.name[0]));
851                      f.Puts(prop.name+1);
852                      f.Puts("\" : ");
853                      WriteValue(f, type, value, indent);
854                      isFirst = false;
855                   }
856                }
857             }
858             else
859             {
860                DataMember member = (DataMember)prop;
861                DataValue value { };
862                Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
863                if(!type)
864                   type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
865
866                if(type)
867                {
868                   if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
869                   {
870                      if(type.type == structClass)
871                         value.p = (void *)((byte *)object + member._class.offset + member.offset);
872                      else
873                         value.p = *(void **)((byte *)object + member._class.offset + member.offset);
874                      if(!value.p)
875                         continue;
876                   }
877                   else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
878                   {
879                      value.d = *(double *)((byte *)object + member._class.offset + member.offset);
880                   }
881                   else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
882                   {
883                      value.f = *(float *)((byte *)object + member._class.offset + member.offset);
884                   }
885                   else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
886                      !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
887                   {
888                      value.ui64 = *(uint64 *)((byte *)object + member._class.offset + member.offset);
889                   }
890                   else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
891                      !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
892                   {
893                      value.i = *(int *)((byte *)object + member._class.offset + member.offset);
894                      if(!strcmp(type.name, "bool") || type.type == enumClass)
895                         if(!value.i)
896                            continue;
897                   }
898                   else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
899                      !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
900                      !strcmp(type.dataTypeString, "int16"))
901                   {
902                      value.s = *(short *)((byte *)object + member._class.offset + member.offset);
903                   }
904                   else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
905                      !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
906                   {
907                      value.c = *(char *)((byte *)object + member._class.offset + member.offset);
908                   }
909                   else
910                   {
911                      value.i = *(int *)((byte *)object + member._class.offset + member.offset);
912                   }
913
914                   if(!isFirst) f.Puts(",\n");
915                   for(c = 0; c<indent; c++) f.Puts("   ");
916
917                   f.Puts("\"");
918                   f.Putc((char)toupper(member.name[0]));
919                   f.Puts(member.name+1);
920                   f.Puts("\" : ");
921                   WriteValue(f, type, value, indent);
922                   isFirst = false;
923                }
924             }
925          }
926
927          indent--;
928          f.Puts("\n");
929          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
930       }
931    }
932    else
933       f.Puts("null");
934    return true;
935 }