installer: Fixed crash on configuration by setting the SettingsContainer's dataClass...
[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(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(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                type._vTbl[__ecereVMethodID_class_OnFree](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                   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                bool found = true;
347                DataMember member = null;
348                Property prop = null;
349                Class type = null;
350
351                if(objectType)
352                {
353                   string[0] = (char)tolower(string[0]);
354                   member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
355                   if(member)
356                   {
357                      type = eSystem_FindClass(__thisModule, member.dataTypeString);
358                      if(!type)
359                         type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
360                   }
361                   else if(!member)
362                   {
363                      prop = eClass_FindProperty(objectType, string, objectType.module);
364                      if(prop)
365                      {
366                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
367                         if(!type)
368                            type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
369                      }
370                      else
371                         PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
372                   }                    
373                }
374                // Find Member in Object Class
375                {
376                   DataValue value { };
377
378                   if(type && type.type == structClass)
379                   {
380                      value.p = (byte *)*object + member._class.offset + member.offset;
381                   }
382                   ch = 0;
383                   SkipEmpty();
384                   if(ch == ':')
385                   {
386                      JSONResult itemResult = GetValue(type, value);
387                      if(itemResult != syntaxError)
388                      {
389                         if(prop || member)
390                         {
391                            if(!type)
392                            {
393                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
394                            }
395                            else if(itemResult == success)
396                            {
397                               // Set value
398                               if(member)
399                               {
400                                  // TOFIX: How to swiftly handle classes with base data type?
401                                  if(type == class(double) || !strcmp(type.dataTypeString, "double"))
402                                  {
403                                     *(double *)((byte *)*object + member._class.offset + member.offset) = value.d;
404                                  }
405                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
406                                  {
407                                     *(float *)((byte *)*object + member._class.offset + member.offset) = value.f;
408                                  }
409                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
410                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
411                                  {
412                                     *(uint64 *)((byte *)*object + member._class.offset + member.offset) = value.ui64;
413                                  }
414                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
415                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
416                                  {
417                                     int * ptr = (int *)((byte *)*object + member._class.offset + member.offset);
418                                     // TODO: Fix Me Up 64 bit pointer here
419                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
420                                     {
421                                        Container container = (Container)*ptr;
422                                        container.Free();
423                                        delete container;
424                                     }
425                                     *ptr = value.i;
426                                  }
427                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
428                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
429                                     !strcmp(type.dataTypeString, "int16"))
430                                  {
431                                     *(short *)((byte *)*object + member._class.offset + member.offset) = value.s;
432                                  }
433                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
434                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
435                                  {
436                                     *(char *)((byte *)*object + member._class.offset + member.offset) = value.c;
437                                  }
438                                  else if(type.type != structClass)
439                                  {
440                                     *(void **)((byte *)*object + member._class.offset + member.offset) = value.p;
441                                  }
442                               }
443                               else if(prop && prop.Set)
444                               {
445                                  if(!strcmp(type.dataTypeString, "char *"))
446                                  {
447                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
448                                     delete value.p;
449                                  }
450                                  // TOFIX: How to swiftly handle classes with base data type?
451                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
452                                  {
453                                     ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
454                                  }
455                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
456                                  {
457                                     ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
458                                  }
459                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
460                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
461                                  {
462                                     ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
463                                  }
464                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
465                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
466                                  {
467                                     ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
468                                  }
469                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
470                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
471                                     !strcmp(type.dataTypeString, "int16"))
472                                  {
473                                     ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
474                                  }
475                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
476                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
477                                  {
478                                     ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
479                                  }
480                                  else
481                                  {
482                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
483                                  }
484                               }
485                            }
486                            else
487                            {
488                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
489                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
490                            }
491                         }
492                      }
493                   }
494                   else
495                      result = syntaxError;
496                }
497             }
498             else if(ch)
499                result = syntaxError;
500             delete string;
501
502             if(result)
503             {
504                SkipEmpty();
505                if(ch == '}')
506                {
507                   break;
508                }
509                else if(ch != ',')
510                   result = syntaxError;
511             }
512          }
513       }
514       ch = 0;
515       return result;
516    }
517
518    JSONResult GetNumber(Class type, DataValue value)
519    {
520       JSONResult result = syntaxError;
521       char buffer[256];
522       int c = 0;
523       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
524       {
525          buffer[c++] = ch;
526          if(!f.Getc(&ch)) break;
527       }
528       buffer[c] = 0;
529       //if(strchr(buffer, '.'))
530
531       // TOFIX: How to swiftly handle classes with base data type?
532       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
533       {
534          value.d = strtod(buffer, null);
535          result = success;
536       }
537       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
538       {
539          value.f = (float)strtod(buffer, null);
540          result = success;
541       }
542       // TOFIX: int64 looks for class long long?
543       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
544       else if(!strcmp(type.dataTypeString, "int64"))
545       {
546          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
547          result = success;
548       }
549       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
550       {
551          value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
552          result = success;
553       }
554       else
555       {
556          value.i = strtol(buffer, null, 10);
557          result = success;
558       }
559       return result;
560    }
561 }
562
563 bool WriteArray(File f, Class type, Container array, int indent)
564 {
565    if(array)
566    {
567       int c;
568       int i;
569       bool isFirst = true;
570       Iterator it { array };
571       Class arrayType = type.templateArgs[0].dataTypeClass;
572       f.Puts("[\n");
573       indent++;
574       
575       while(it.Next())
576       {
577          byte * pointer;
578          DataValue value { };
579          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
580          if(!isFirst)
581             f.Puts(",\n");
582          else
583             isFirst = false;         
584          
585          // Add value
586          // TODO: Verify the matching between template type and uint64
587          if(arrayType.type == structClass)
588          {
589             value.p = (void *)t;
590          }
591          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
592          {
593             value.d = *(double *)&t;
594          }
595          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
596          {
597             value.f = *(float *)&t;
598          }
599          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64")
600             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
601          {
602             value.ui64 = t;
603          }
604          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") || 
605             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
606          {
607             value.i = (int)t;
608          }
609          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") || 
610             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") || 
611             !strcmp(arrayType.dataTypeString, "int16"))
612          {
613             value.s = (short)t;
614          }
615          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") || 
616             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
617          {
618             value.c = (char)t;
619          }
620          else
621          {
622             value.p = (void *)t;
623          }
624          for(i = 0; i<indent; i++) f.Puts("   ");
625          WriteValue(f, arrayType, value, indent);
626       }      
627       f.Puts("\n");
628       indent--;
629       for(i = 0; i<indent; i++) f.Puts("   ");
630       f.Puts("]");      
631    }
632    else
633       f.Puts("null");
634    return true;
635 }
636
637 bool WriteNumber(File f, Class type, DataValue value, int indent)
638 {
639    char buffer[1024];
640    bool needClass = false;
641    buffer[0] = 0;
642    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
643       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.d, buffer, 0, &needClass);
644    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
645       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.f, buffer, null, &needClass);
646    else if(!strcmp(type.dataTypeString, "int64"))
647       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.i64, buffer, null, &needClass);
648    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
649       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.ui64, buffer, null, &needClass);
650    else if(!strcmp(type.dataTypeString, "int"))
651       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.i, buffer, null, &needClass);
652    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
653       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.ui, buffer, null, &needClass);
654    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
655       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.s, buffer, null, &needClass);
656    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
657       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.us, buffer, null, &needClass);
658    else if(!strcmp(type.dataTypeString, "char"))
659       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.c, buffer, null, &needClass);
660    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
661       type._vTbl[__ecereVMethodID_class_OnGetString](type, &value.uc, buffer, null, &needClass);
662    f.Puts(buffer);
663    return true;
664 }
665
666 bool WriteValue(File f, Class type, DataValue value, int indent)
667 {
668    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
669    {
670       f.Puts("\"");
671       //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
672       {
673          int c = 0;
674          int b = 0;
675          char buffer[1024];
676          char * string = value.p;
677          char ch;
678          while(true)
679          {
680             ch = string[c++];
681             if(ch == '\"')
682             {
683                buffer[b] = 0;
684                f.Puts(buffer);
685                f.Puts("\\\"");
686                b = 0;
687             }
688             else if(ch == '\\')
689             {
690                buffer[b] = 0;
691                f.Puts(buffer);
692                f.Puts("\\\\");
693                b = 0;
694             }
695             else if(b == sizeof(buffer)-2 || !ch)
696             {
697                buffer[b++] = ch;
698                if(ch) buffer[b] = 0;
699                f.Puts(buffer);
700                b = 0;
701                if(!ch) break;
702             }
703             else
704                buffer[b++] = ch;
705          }
706       }
707       /*else
708          f.Puts(value.p);*/
709       f.Puts("\"");
710    }
711    else if(!strcmp(type.name, "bool"))
712    {
713       if(value.i)
714          f.Puts("true");
715       else
716          f.Puts("false");
717    }
718    else if(!strcmp(type.name, "SetBool"))
719    {
720       if(value.i == SetBool::true)
721          f.Puts("true");
722       else if(value.i == SetBool::false)
723          f.Puts("false");
724       else
725          f.Puts("unset");
726    }
727    else if(type.type == enumClass)
728    {
729       f.Puts("\"");
730       WriteNumber(f, type, value, indent);      
731       f.Puts("\"");
732    }
733    else if(eClass_IsDerived(type, class(Container)))
734    {
735       WriteArray(f, type, value.p, indent);
736    }
737    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || type.type == bitClass)
738    {
739       _WriteJSONObject(f, type, value.p, indent);
740    }
741    else if(type.type == systemClass)
742    {
743       WriteNumber(f, type, value, indent);
744    }
745    return true;
746 }
747
748 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
749 {
750    bool result = false;
751    if(object)
752    {
753       result = _WriteJSONObject(f, objectType, object, indent);
754       f.Puts("\n");
755    }
756    return result;
757 }
758
759 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
760 {
761    if(object)
762    {
763       char * string = null;
764
765       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
766       {
767          char buffer[1024];
768          buffer[0] = 0;
769          string = ((char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
770       }
771       if(string)
772       {
773          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
774          if(string[0] == '\"')
775             f.Puts(string);
776          else
777          {
778             f.Puts("\"");
779             f.Puts(string);
780             f.Puts("\"");
781          }
782       }
783       else
784       {
785          Property prop;
786          int c;
787          bool isFirst = true;
788          
789          f.Puts("{\n");
790          indent++;
791
792          for(prop = objectType.membersAndProperties.first; prop; prop = prop.next)
793          {
794             if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
795             if(prop.isProperty)
796             {
797                if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
798                {
799                   DataValue value { };
800                   Class type = eSystem_FindClass(__thisModule, prop.dataTypeString);
801                   if(!type)
802                      type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
803
804                   // TOFIX: How to swiftly handle classes with base data type?
805                   if(type == class(double) || !strcmp(type.dataTypeString, "double"))
806                   {
807                      value.d = ((double (*)(void *))(void *)prop.Get)(object);
808                   }
809                   else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
810                   {
811                      value.f = ((float (*)(void *))(void *)prop.Get)(object);
812                   }
813                   else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
814                      !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
815                   {
816                      value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
817                   }
818                   else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
819                      !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
820                   {
821                      value.i = ((int (*)(void *))(void *)prop.Get)(object);
822                   }
823                   else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
824                      !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
825                      !strcmp(type.dataTypeString, "int16"))
826                   {
827                      value.s = ((short (*)(void *))(void *)prop.Get)(object);
828                   }
829                   else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
830                      !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
831                   {
832                      value.c = ((char (*)(void *))(void *)prop.Get)(object);
833                   }
834                   else
835                   {
836                      value.p = ((void *(*)(void *))(void *)prop.Get)(object);
837                   }
838
839                   if(!isFirst) f.Puts(",\n");
840                   for(c = 0; c<indent; c++) f.Puts("   ");
841
842                   f.Puts("\"");
843                   f.Putc((char)toupper(prop.name[0]));
844                   f.Puts(prop.name+1);
845                   f.Puts("\" : ");
846                   WriteValue(f, type, value, indent);
847                   isFirst = false;
848                }
849             }
850             else
851             {
852                DataMember member = (DataMember)prop;
853                DataValue value { };
854                Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
855                if(!type)
856                   type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
857
858                if(type)
859                {
860                   if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
861                   {
862                      if(type.type == structClass)
863                         value.p = (void *)((byte *)object + member._class.offset + member.offset);
864                      else
865                         value.p = *(void **)((byte *)object + member._class.offset + member.offset);
866                      if(!value.p)
867                         continue;
868                   }
869                   else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
870                   {
871                      value.d = *(double *)((byte *)object + member._class.offset + member.offset);
872                   }
873                   else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
874                   {
875                      value.f = *(float *)((byte *)object + member._class.offset + member.offset);
876                   }
877                   else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64")
878                      !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
879                   {
880                      value.ui64 = *(uint64 *)((byte *)object + member._class.offset + member.offset);
881                   }
882                   else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") || 
883                      !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
884                   {
885                      value.i = *(int *)((byte *)object + member._class.offset + member.offset);
886                      if(!strcmp(type.name, "bool") || type.type == enumClass)
887                         if(!value.i)
888                            continue;
889                   }
890                   else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") || 
891                      !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || 
892                      !strcmp(type.dataTypeString, "int16"))
893                   {
894                      value.s = *(short *)((byte *)object + member._class.offset + member.offset);
895                   }
896                   else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") || 
897                      !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
898                   {
899                      value.c = *(char *)((byte *)object + member._class.offset + member.offset);
900                   }
901                   else
902                   {
903                      value.i = *(int *)((byte *)object + member._class.offset + member.offset);  
904                   }
905
906                   if(!isFirst) f.Puts(",\n");
907                   for(c = 0; c<indent; c++) f.Puts("   ");
908
909                   f.Puts("\"");
910                   f.Putc((char)toupper(member.name[0]));
911                   f.Puts(member.name+1);
912                   f.Puts("\" : ");
913                   WriteValue(f, type, value, indent);
914                   isFirst = false;
915                }
916             }
917          }   
918
919          indent--;
920          f.Puts("\n");
921          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
922       }
923    }
924    else
925       f.Puts("null");
926    return true;
927 }