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