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