ecere, ide: Proper prototypes for invoking methods directly from virtual table
[sdk] / ecere / src / sys / JSON.ec
1 namespace sys;
2
3 import "System"
4 import "Array"
5
6 default:
7 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;
117          result = GetObject(type, &object);
118          if(result)
119          {
120             if(type && (type.type == normalClass || type.type == structClass || type.type == noHeadClass || type.type == bitClass))
121             {
122                value.p = object;
123             }
124             else
125             {
126                result = typeMismatch;
127                ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
128             }
129          }
130       }
131       else if(isalpha(ch))
132       {
133          char buffer[256];
134          int c = 0;
135          while(c < sizeof(buffer)-1 && isalpha(ch))
136          {
137             buffer[c++] = ch;
138             if(!f.Getc(&ch)) break;
139          }
140          buffer[c] = 0;
141          result = success;
142          
143          if(type)
144          { 
145             if(!strcmp(type.name, "bool"))
146             {
147                if(!strcmpi(buffer, "false")) value.i = 0;
148                else if(!strcmpi(buffer, "true")) value.i = 1;
149                else
150                   result = typeMismatch;
151             }
152             else if(!strcmp(type.name, "SetBool"))
153             {
154                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
155                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
156                else
157                   result = typeMismatch;
158             }
159             else if(!strcmpi(buffer, "null")) 
160             {
161                value.p = 0;
162             }
163             else
164                result = typeMismatch;
165          }
166          else
167             result = typeMismatch;
168       }
169       else if(ch == '}' || ch == ']')
170          result = noItem;
171       return result;
172    }
173
174    JSONResult GetArray(Class type, Container * array)
175    {
176       JSONResult result = syntaxError;
177       SkipEmpty();
178       *array = null;
179       if(ch == '[')
180       {
181          int count = 0;
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       *object = null;
328       SkipEmpty();
329       if(ch == '{')
330       {
331          result = success;
332          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
333          {
334             *object = eInstance_New(objectType);
335          }
336          else if(objectType)
337          {
338             *object = eSystem_New(objectType.typeSize);
339          }
340
341          while(result)
342          {
343             String string;
344             ch = 0;
345             if(GetString(&string))
346             {
347                bool found = true;
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 == class(double) || !strcmp(type.dataTypeString, "double"))
403                                  {
404                                     *(double *)((byte *)*object + member._class.offset + member.offset) = value.d;
405                                  }
406                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
407                                  {
408                                     *(float *)((byte *)*object + member._class.offset + member.offset) = value.f;
409                                  }
410                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
411                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
412                                  {
413                                     *(uint64 *)((byte *)*object + member._class.offset + member.offset) = value.ui64;
414                                  }
415                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
416                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
417                                  {
418                                     int * ptr = (int *)((byte *)*object + member._class.offset + member.offset);
419                                     // TODO: Fix Me Up 64 bit pointer here
420                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
421                                     {
422                                        Container container = (Container)*ptr;
423                                        container.Free();
424                                        delete container;
425                                     }
426                                     *ptr = value.i;
427                                  }
428                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
429                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
430                                     !strcmp(type.dataTypeString, "int16"))
431                                  {
432                                     *(short *)((byte *)*object + member._class.offset + member.offset) = value.s;
433                                  }
434                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
435                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
436                                  {
437                                     *(char *)((byte *)*object + member._class.offset + member.offset) = value.c;
438                                  }
439                                  else if(type.type != structClass)
440                                  {
441                                     *(void **)((byte *)*object + member._class.offset + member.offset) = value.p;
442                                  }
443                               }
444                               else if(prop && prop.Set)
445                               {
446                                  if(!strcmp(type.dataTypeString, "char *"))
447                                  {
448                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
449                                     delete value.p;
450                                  }
451                                  // TOFIX: How to swiftly handle classes with base data type?
452                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
453                                  {
454                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
455                                  }
456                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
457                                  {
458                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
459                                  }
460                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
461                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
462                                  {
463                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
464                                  }
465                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
466                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
467                                  {
468                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
469                                  }
470                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
471                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
472                                     !strcmp(type.dataTypeString, "int16"))
473                                  {
474                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
475                                  }
476                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
477                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
478                                  {
479                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
480                                  }
481                                  else
482                                  {
483                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
484                                  }
485                               }
486                            }
487                            else
488                            {
489                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
490                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
491                            }
492                         }
493                      }
494                   }
495                   else
496                      result = syntaxError;
497                }
498             }
499             else if(ch)
500                result = syntaxError;
501             delete string;
502
503             if(result)
504             {
505                SkipEmpty();
506                if(ch == '}')
507                {
508                   break;
509                }
510                else if(ch != ',')
511                   result = syntaxError;
512             }
513          }
514       }
515       ch = 0;
516       return result;
517    }
518
519    JSONResult GetNumber(Class type, DataValue value)
520    {
521       JSONResult result = syntaxError;
522       char buffer[256];
523       int c = 0;
524       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
525       {
526          buffer[c++] = ch;
527          if(!f.Getc(&ch)) break;
528       }
529       buffer[c] = 0;
530       //if(strchr(buffer, '.'))
531
532       // TOFIX: How to swiftly handle classes with base data type?
533       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
534       {
535          value.d = strtod(buffer, null);
536          result = success;
537       }
538       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
539       {
540          value.f = (float)strtod(buffer, null);
541          result = success;
542       }
543       // TOFIX: int64 looks for class long long?
544       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
545       else if(!strcmp(type.dataTypeString, "int64"))
546       {
547          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
548          result = success;
549       }
550       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
551       {
552          value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
553          result = success;
554       }
555       else
556       {
557          value.i = strtol(buffer, null, 10);
558          result = success;
559       }
560       return result;
561    }
562 }
563
564 bool WriteArray(File f, Class type, Container array, int indent)
565 {
566    if(array)
567    {
568       int c;
569       int i;
570       bool isFirst = true;
571       Iterator it { array };
572       Class arrayType = type.templateArgs[0].dataTypeClass;
573       f.Puts("[\n");
574       indent++;
575       
576       while(it.Next())
577       {
578          byte * pointer;
579          DataValue value { };
580          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
581          if(!isFirst)
582             f.Puts(",\n");
583          else
584             isFirst = false;         
585          
586          // Add value
587          // TODO: Verify the matching between template type and uint64
588          if(arrayType.type == structClass)
589          {
590             value.p = (void *)t;
591          }
592          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
593          {
594             value.d = *(double *)&t;
595          }
596          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
597          {
598             value.f = *(float *)&t;
599          }
600          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64")
601             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
602          {
603             value.ui64 = t;
604          }
605          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") || 
606             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
607          {
608             value.i = (int)t;
609          }
610          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") || 
611             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") || 
612             !strcmp(arrayType.dataTypeString, "int16"))
613          {
614             value.s = (short)t;
615          }
616          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") || 
617             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
618          {
619             value.c = (char)t;
620          }
621          else
622          {
623             value.p = (void *)t;
624          }
625          for(i = 0; i<indent; i++) f.Puts("   ");
626          WriteValue(f, arrayType, value, indent);
627       }      
628       f.Puts("\n");
629       indent--;
630       for(i = 0; i<indent; i++) f.Puts("   ");
631       f.Puts("]");      
632    }
633    else
634       f.Puts("null");
635    return true;
636 }
637
638 bool WriteNumber(File f, Class type, DataValue value, int indent)
639 {
640    char buffer[1024];
641    bool needClass = false;
642    buffer[0] = 0;
643    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
644       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
645    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
646       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
647    else if(!strcmp(type.dataTypeString, "int64"))
648       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
649    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
650       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
651    else if(!strcmp(type.dataTypeString, "int"))
652       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
653    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
654       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
655    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
656       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
657    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
658       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
659    else if(!strcmp(type.dataTypeString, "char"))
660       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
661    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
662       ((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
663    f.Puts(buffer);
664    return true;
665 }
666
667 bool WriteValue(File f, Class type, DataValue value, int indent)
668 {
669    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
670    {
671       f.Puts("\"");
672       //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
673       {
674          int c = 0;
675          int b = 0;
676          char buffer[1024];
677          char * string = value.p;
678          char ch;
679          while(true)
680          {
681             ch = string[c++];
682             if(ch == '\"')
683             {
684                buffer[b] = 0;
685                f.Puts(buffer);
686                f.Puts("\\\"");
687                b = 0;
688             }
689             else if(ch == '\\')
690             {
691                buffer[b] = 0;
692                f.Puts(buffer);
693                f.Puts("\\\\");
694                b = 0;
695             }
696             else if(b == sizeof(buffer)-2 || !ch)
697             {
698                buffer[b++] = ch;
699                if(ch) buffer[b] = 0;
700                f.Puts(buffer);
701                b = 0;
702                if(!ch) break;
703             }
704             else
705                buffer[b++] = ch;
706          }
707       }
708       /*else
709          f.Puts(value.p);*/
710       f.Puts("\"");
711    }
712    else if(!strcmp(type.name, "bool"))
713    {
714       if(value.i)
715          f.Puts("true");
716       else
717          f.Puts("false");
718    }
719    else if(!strcmp(type.name, "SetBool"))
720    {
721       if(value.i == SetBool::true)
722          f.Puts("true");
723       else if(value.i == SetBool::false)
724          f.Puts("false");
725       else
726          f.Puts("unset");
727    }
728    else if(type.type == enumClass)
729    {
730       f.Puts("\"");
731       WriteNumber(f, type, value, indent);      
732       f.Puts("\"");
733    }
734    else if(eClass_IsDerived(type, class(Container)))
735    {
736       WriteArray(f, type, value.p, indent);
737    }
738    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || type.type == bitClass)
739    {
740       _WriteJSONObject(f, type, value.p, indent);
741    }
742    else if(type.type == systemClass)
743    {
744       WriteNumber(f, type, value, indent);
745    }
746    return true;
747 }
748
749 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
750 {
751    bool result = false;
752    if(object)
753    {
754       result = _WriteJSONObject(f, objectType, object, indent);
755       f.Puts("\n");
756    }
757    return result;
758 }
759
760 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
761 {
762    if(object)
763    {
764       char * string = null;
765
766       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
767       {
768          char buffer[1024];
769          buffer[0] = 0;
770          string = ((char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
771       }
772       if(string)
773       {
774          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
775          if(string[0] == '\"')
776             f.Puts(string);
777          else
778          {
779             f.Puts("\"");
780             f.Puts(string);
781             f.Puts("\"");
782          }
783       }
784       else
785       {
786          Property prop;
787          int c;
788          bool isFirst = true;
789          
790          f.Puts("{\n");
791          indent++;
792
793          for(prop = objectType.membersAndProperties.first; prop; prop = prop.next)
794          {
795             if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
796             if(prop.isProperty)
797             {
798                if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
799                {
800                   DataValue value { };
801                   Class type = eSystem_FindClass(__thisModule, prop.dataTypeString);
802                   if(!type)
803                      type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
804
805                   // TOFIX: How to swiftly handle classes with base data type?
806                   if(type == class(double) || !strcmp(type.dataTypeString, "double"))
807                   {
808                      value.d = ((double (*)(void *))(void *)prop.Get)(object);
809                   }
810                   else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
811                   {
812                      value.f = ((float (*)(void *))(void *)prop.Get)(object);
813                   }
814                   else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
815                      !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
816                   {
817                      value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
818                   }
819                   else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
820                      !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
821                   {
822                      value.i = ((int (*)(void *))(void *)prop.Get)(object);
823                   }
824                   else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
825                      !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
826                      !strcmp(type.dataTypeString, "int16"))
827                   {
828                      value.s = ((short (*)(void *))(void *)prop.Get)(object);
829                   }
830                   else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
831                      !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
832                   {
833                      value.c = ((char (*)(void *))(void *)prop.Get)(object);
834                   }
835                   else
836                   {
837                      value.p = ((void *(*)(void *))(void *)prop.Get)(object);
838                   }
839
840                   if(!isFirst) f.Puts(",\n");
841                   for(c = 0; c<indent; c++) f.Puts("   ");
842
843                   f.Puts("\"");
844                   f.Putc((char)toupper(prop.name[0]));
845                   f.Puts(prop.name+1);
846                   f.Puts("\" : ");
847                   WriteValue(f, type, value, indent);
848                   isFirst = false;
849                }
850             }
851             else
852             {
853                DataMember member = (DataMember)prop;
854                DataValue value { };
855                Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
856                if(!type)
857                   type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
858
859                if(type)
860                {
861                   if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
862                   {
863                      if(type.type == structClass)
864                         value.p = (void *)((byte *)object + member._class.offset + member.offset);
865                      else
866                         value.p = *(void **)((byte *)object + member._class.offset + member.offset);
867                      if(!value.p)
868                         continue;
869                   }
870                   else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
871                   {
872                      value.d = *(double *)((byte *)object + member._class.offset + member.offset);
873                   }
874                   else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
875                   {
876                      value.f = *(float *)((byte *)object + member._class.offset + member.offset);
877                   }
878                   else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
879                      !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
880                   {
881                      value.ui64 = *(uint64 *)((byte *)object + member._class.offset + member.offset);
882                   }
883                   else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
884                      !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
885                   {
886                      value.i = *(int *)((byte *)object + member._class.offset + member.offset);
887                      if(!strcmp(type.name, "bool") || type.type == enumClass)
888                         if(!value.i)
889                            continue;
890                   }
891                   else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
892                      !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
893                      !strcmp(type.dataTypeString, "int16"))
894                   {
895                      value.s = *(short *)((byte *)object + member._class.offset + member.offset);
896                   }
897                   else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
898                      !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
899                   {
900                      value.c = *(char *)((byte *)object + member._class.offset + member.offset);
901                   }
902                   else
903                   {
904                      value.i = *(int *)((byte *)object + member._class.offset + member.offset);  
905                   }
906
907                   if(!isFirst) f.Puts(",\n");
908                   for(c = 0; c<indent; c++) f.Puts("   ");
909
910                   f.Puts("\"");
911                   f.Putc((char)toupper(member.name[0]));
912                   f.Puts(member.name+1);
913                   f.Puts("\" : ");
914                   WriteValue(f, type, value, indent);
915                   isFirst = false;
916                }
917             }
918          }   
919
920          indent--;
921          f.Puts("\n");
922          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
923       }
924    }
925    else
926       f.Puts("null");
927    return true;
928 }