08b87d1b4812c658b60163f58d01a05e25ca90f5
[sdk] / ecere / src / sys / JSON.ec
1 namespace sys;
2
3 import "instance"
4 import "System"
5 import "Array"
6
7 default:
8 __attribute__((unused)) static void UnusedFunction()
9 {
10    int a;
11    a.OnGetDataFromString(null);
12    a.OnGetString(null, 0, 0);
13    a.OnFree();
14 }
15 extern int __ecereVMethodID_class_OnGetDataFromString;
16 extern int __ecereVMethodID_class_OnGetString;
17 extern int __ecereVMethodID_class_OnFree;
18 private:
19
20 public enum JSONResult { syntaxError, success, typeMismatch, noItem };
21
22 public enum SetBool : uint
23 {
24    unset, false, true;
25
26    /*public property bool     // NOT WORKING!
27    {
28       set { return value ? true : false; }
29       get { return (this == true); }
30    }*/
31 };
32
33
34 public class JSONParser
35 {
36 public:
37    File f;
38    char ch;
39
40    void SkipEmpty()
41    {
42       while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/'))
43       {
44          f.Getc(&ch);
45       }
46    }
47
48    JSONResult GetValue(Class type, DataValue value)
49    {
50       JSONResult result = syntaxError;
51       ch = 0;
52       SkipEmpty();
53       if(ch == '\"')
54       {
55          String string;
56          result = GetString(&string);
57          if(result)
58          {
59             Property prop;
60             if(type && (!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *")))
61             {
62                value.p = string;
63             }
64             else if(type && (type.type == enumClass || type.type == unitClass))
65             {
66                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, &value.i, string))
67                   result = success;
68                else
69                   result = typeMismatch;
70                delete string;
71             }
72             else if(type && (prop = eClass_FindProperty(type, "String", type.module)))
73             {
74                // TOFIX: Add more conversion property support... Expecting void * compatible here
75                value.p = ((void *(*)())(void *)prop.Set)(string);
76                result = success;
77                delete string;
78             }
79             else if(type && (type.type == structClass))
80             {
81                if(((bool (*)(void *, void *, const char *))(void *)type._vTbl[__ecereVMethodID_class_OnGetDataFromString])(type, value.p, string))
82                   result = success;
83                else
84                   result = typeMismatch;
85                delete string;
86             }
87             else
88             {
89                delete string;
90                result = typeMismatch;
91             }
92          }
93       }
94       else if(ch == '[')
95       {
96          Container array;
97          if(type && eClass_IsDerived(type, class(Map)))
98          {
99             result = GetMap(type, (Map *)&array);
100          }
101          else
102             result = GetArray(type, &array);
103
104          if(result == success && type && eClass_IsDerived(type, class(Container)))
105          {
106             value.p = array;
107          }
108          else
109          {
110             if(array)
111                array.Free();
112             delete array;
113             if(result != success)
114                result = typeMismatch;
115          }
116       }
117       else if(ch == '-' || isdigit(ch))
118       {
119          result = GetNumber(type, value);
120       }
121       else if(ch == '{')
122       {
123          void * object = value.p;
124          result = GetObject(type, &object);
125          if(result)
126          {
127             if(type && type.type == structClass);
128             else if(type && (type.type == normalClass || type.type == noHeadClass || type.type == bitClass))
129             {
130                value.p = object;
131             }
132             else
133             {
134                result = typeMismatch;
135                if(type)
136                   ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, object);
137             }
138          }
139       }
140       else if(isalpha(ch))
141       {
142          char buffer[256];
143          int c = 0;
144          while(c < sizeof(buffer)-1 && isalpha(ch))
145          {
146             buffer[c++] = ch;
147             if(!f.Getc(&ch)) break;
148          }
149          buffer[c] = 0;
150          result = success;
151
152          if(type)
153          {
154             if(!strcmp(type.name, "bool"))
155             {
156                if(!strcmpi(buffer, "false")) value.i = 0;
157                else if(!strcmpi(buffer, "true")) value.i = 1;
158                else
159                   result = typeMismatch;
160             }
161             else if(!strcmp(type.name, "SetBool"))
162             {
163                if(!strcmpi(buffer, "false")) value.i = SetBool::false;
164                else if(!strcmpi(buffer, "true")) value.i = SetBool::true;
165                else
166                   result = typeMismatch;
167             }
168             else if(!strcmpi(buffer, "null"))
169             {
170                if(type.type != structClass)
171                   value.p = 0;
172             }
173             else
174                result = typeMismatch;
175          }
176          else
177             result = typeMismatch;
178       }
179       else if(ch == '}' || ch == ']')
180          result = noItem;
181       return result;
182    }
183
184    JSONResult GetArray(Class type, Container * array)
185    {
186       JSONResult result = syntaxError;
187       SkipEmpty();
188       *array = null;
189       if(ch == '[')
190       {
191          *array = eInstance_New(type);
192          result = success;
193          while(result)
194          {
195             DataValue value { };
196             Class arrayType = null;
197             JSONResult itemResult;
198
199             if(eClass_IsDerived(type, class(Container)))
200             {
201                arrayType = type.templateArgs[0].dataTypeClass;
202             }
203
204             if(arrayType && arrayType.type == structClass)
205                value.p = new0 byte[arrayType.structSize];
206             itemResult = GetValue(arrayType, value);
207             if(itemResult == success)
208             {
209                // TODO: Verify the matching between template type and uint64
210                uint64 t;
211                if(arrayType.type == structClass)
212                {
213                   t = (uint64)(uintptr)value.p;
214                }
215                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
216                {
217                   t = value.ui64; //*(uint64 *)&value.d;
218                }
219                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
220                {
221                   t = value.ui; //f*(uint *)&value.f;
222                }
223                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
224                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
225                {
226                   t = value.ui64;
227                }
228                else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
229                   !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
230                {
231                   t = value.i;
232                }
233                else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
234                   !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
235                   !strcmp(arrayType.dataTypeString, "int16"))
236                {
237                   t = value.s;
238                }
239                else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
240                   !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
241                {
242                   t = value.c;
243                }
244                else
245                {
246                   t = (uint64)(uintptr)value.p;
247                }
248                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
249
250                if(arrayType && arrayType.type == structClass)
251                   delete value.p;
252             }
253             else
254             {
255                if(itemResult == typeMismatch)
256                {
257                   if(arrayType)
258                      PrintLn("Warning: Incompatible value for array value, expected ", (String)arrayType.name);
259                }
260                else if(itemResult == noItem)
261                   result = success;
262                else
263                   result = itemResult;
264             }
265
266             if(result != syntaxError)
267             {
268                if(ch != ']' && ch != ',')
269                {
270                   ch = 0;
271                   SkipEmpty();
272                }
273                if(ch == ']')
274                {
275                   break;
276                }
277                else if(ch != ',')
278                   result = syntaxError;
279             }
280          }
281       }
282       ch = 0;
283       return result;
284    }
285
286    JSONResult GetMap(Class type, Map * map)
287    {
288       JSONResult result = syntaxError;
289       SkipEmpty();
290       *map = null;
291       if(ch == '[')
292       {
293          Class mapNodeType = type.templateArgs[0].dataTypeClass;
294          Class keyType = mapNodeType.templateArgs[0].dataTypeClass;
295          Property keyProp = null;
296          if(keyType && !strcmp(keyType.dataTypeString, "char *"))
297             keyProp = eClass_FindProperty(mapNodeType, "key", mapNodeType.module);
298
299          *map = eInstance_New(type);
300          result = success;
301
302          while(result)
303          {
304             DataValue value { };
305
306             JSONResult itemResult;
307
308             itemResult = GetValue(mapNodeType, value);
309             if(itemResult == success)
310             {
311                String s = keyProp ? ((void * (*)(void *))(void *)keyProp.Get)(value.p) : null;
312                ((void *(*)(void *, uint64))(void *)map->Add)(*map, (uint64)(uintptr)value.p);
313                // Must free String keys here
314                delete s;
315             }
316             else
317             {
318                if(itemResult == typeMismatch)
319                {
320                   if(mapNodeType)
321                      PrintLn("Warning: Incompatible value for array value, expected ", (String)mapNodeType.name);
322                }
323                else if(itemResult == noItem)
324                   result = success;
325                else
326                   result = itemResult;
327             }
328
329             if(result != syntaxError)
330             {
331                if(ch != ']' && ch != ',')
332                {
333                   ch = 0;
334                   SkipEmpty();
335                }
336                if(ch == ']')
337                {
338                   break;
339                }
340                else if(ch != ',')
341                   result = syntaxError;
342             }
343          }
344       }
345       ch = 0;
346       return result;
347    }
348
349    JSONResult GetString(String * string)
350    {
351       JSONResult result = syntaxError;
352       Array<char> buffer { minAllocSize = 256 };
353       bool escaped = false;
354
355       *string = null;
356       SkipEmpty();
357       if(ch == '\"')
358       {
359          while(f.Getc(&ch))
360          {
361             if(ch == '\\' && !escaped)
362                escaped = true;
363             else
364             {
365                if(escaped)
366                {
367                   if(ch == 'b') ch = '\b';
368                   else if(ch == 'f') ch = '\f';
369                   else if(ch == 'n') ch = '\n';
370                   else if(ch == 'r') ch = '\r';
371                   else if(ch == 't') ch = '\t';
372                   else if(ch == 'u')
373                   {
374                      // SKIP FOR NOW...
375                      char unicode[4];
376                      f.Getc(&unicode[0]);
377                      f.Getc(&unicode[1]);
378                      f.Getc(&unicode[2]);
379                      f.Getc(&unicode[3]);
380                   }
381                   escaped = false;
382                }
383                else if(ch == '\"')
384                {
385                   break;
386                }
387                buffer.Add(ch);
388                if(buffer.minAllocSize < buffer.count)
389                   buffer.minAllocSize *= 2;
390             }
391          }
392          buffer.Add(0);
393          *string = CopyString(buffer.array);
394          result = success;
395       }
396       delete buffer;
397       if(ch != ',' && ch != '}')
398          ch = 0;
399       return result;
400    }
401
402    public JSONResult GetObject(Class objectType, void ** object)
403    {
404       JSONResult result = syntaxError;
405       if(!objectType || objectType.type != structClass)
406          *object = null;
407       SkipEmpty();
408       if(ch == '{')
409       {
410          Class mapKeyClass = null, mapDataClass = null;
411
412          if(objectType && objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
413          {
414             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
415             mapDataClass = objectType.templateArgs[2].dataTypeClass;
416          }
417
418          result = success;
419          if(objectType && (objectType.type == noHeadClass || objectType.type == normalClass))
420          {
421             *object = eInstance_New(objectType);
422          }
423          else if(objectType && objectType.type != structClass)
424          {
425             *object = eSystem_New(objectType.typeSize);
426          }
427
428          while(result)
429          {
430             String string;
431             ch = 0;
432             if(GetString(&string))
433             {
434                DataMember member = null;
435                Property prop = null;
436                Class type = null;
437                bool isKey = false;
438                uint offset = 0;
439
440                if(objectType)
441                {
442                   string[0] = (char)tolower(string[0]);
443                   if(mapKeyClass && !strcmp(string, "key"))
444                   {
445                      prop = eClass_FindProperty(objectType, "key", objectType.module);
446                      type = mapKeyClass;
447                      isKey = true;
448                   }
449                   else if(mapDataClass && !strcmp(string, "value"))
450                   {
451                      prop = eClass_FindProperty(objectType, "value", objectType.module);
452                      type = mapDataClass;
453                   }
454                   else
455                   {
456                      member = eClass_FindDataMember(objectType, string, objectType.module, null, null);
457                      if(member)
458                      {
459                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
460                         if(!type)
461                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
462
463                         offset = member._class.offset + member.offset;
464                      }
465                      else if(!member)
466                      {
467                         prop = eClass_FindProperty(objectType, string, objectType.module);
468                         if(prop)
469                         {
470                            type = eSystem_FindClass(__thisModule, prop.dataTypeString);
471                            if(!type)
472                               type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
473                         }
474                         else
475                            PrintLn("Warning: member ", string, " not found in class ", (String)objectType.name);
476                      }
477                   }
478                }
479                // Find Member in Object Class
480                {
481                   DataValue value { };
482
483                   if(type && type.type == structClass)
484                   {
485                      if(member)
486                      {
487                         value.p = (byte *)*object + offset;
488                         memset(value.p, 0, type.structSize);
489                      }
490                      else if(prop)
491                      {
492                         value.p = new0 byte[type.structSize];
493                      }
494                   }
495                   ch = 0;
496                   SkipEmpty();
497                   if(ch == ':')
498                   {
499                      JSONResult itemResult = GetValue(type, value);
500                      if(itemResult != syntaxError)
501                      {
502                         if(prop || member)
503                         {
504                            if(!type)
505                            {
506                               PrintLn("warning: Unresolved data type ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
507                            }
508                            else if(itemResult == success)
509                            {
510                               // Set value
511                               if(member)
512                               {
513                                  // TOFIX: How to swiftly handle classes with base data type?
514                                  if(type.type == structClass)
515                                     ;
516                                  else if(type.type == normalClass || type.type == noHeadClass)
517                                  {
518                                     void ** ptr = (void**)((byte *)*object + offset);
519                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
520                                     {
521                                        Container container = (Container)*ptr;
522                                        container.Free();
523                                        delete container;
524                                     }
525                                     *ptr = value.p;
526                                  }
527                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
528                                  {
529                                     *(double *)((byte *)*object + offset) = value.d;
530                                  }
531                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
532                                  {
533                                     *(float *)((byte *)*object + offset) = value.f;
534                                  }
535                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
536                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
537                                  {
538                                     *(uint64 *)((byte *)*object + offset) = value.ui64;
539                                  }
540                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
541                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
542                                  {
543                                     *(int *)((byte *)*object + offset) = value.i;
544                                  }
545                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
546                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
547                                     !strcmp(type.dataTypeString, "int16"))
548                                  {
549                                     *(short *)((byte *)*object + offset) = value.s;
550                                  }
551                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
552                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
553                                  {
554                                     *(char *)((byte *)*object + offset) = value.c;
555                                  }
556                                  else
557                                  {
558                                     *(void **)((byte *)*object + offset) = value.p;
559                                  }
560                               }
561                               else if(prop && prop.Set)
562                               {
563                                  if(!strcmp(type.dataTypeString, "char *"))
564                                  {
565                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
566                                     if(!isKey)
567                                        delete value.p;
568                                  }
569                                  else if(type.type == enumClass || type.type == bitClass || type.type == unitClass || type.type == systemClass)
570                                  {
571                                     // TOFIX: How to swiftly handle classes with base data type?
572                                     if(type == class(double) || !strcmp(type.dataTypeString, "double"))
573                                     {
574                                        ((void (*)(void *, double))(void *)prop.Set)(*object, value.d);
575                                     }
576                                     else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
577                                     {
578                                        ((void (*)(void *, float))(void *)prop.Set)(*object, value.f);
579                                     }
580                                     else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
581                                        !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
582                                     {
583                                        ((void (*)(void *, uint64))(void *)prop.Set)(*object, value.ui64);
584                                     }
585                                     else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
586                                        !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
587                                     {
588                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
589                                     }
590                                     else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
591                                        !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
592                                        !strcmp(type.dataTypeString, "int16"))
593                                     {
594                                        ((void (*)(void *, short))(void *)prop.Set)(*object, value.s);
595                                     }
596                                     else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
597                                        !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
598                                     {
599                                        ((void (*)(void *, char))(void *)prop.Set)(*object, value.c);
600                                     }
601                                     else
602                                     {
603                                        ((void (*)(void *, int))(void *)prop.Set)(*object, value.i);
604                                     }
605                                  }
606                                  else
607                                  {
608                                     ((void (*)(void *, void *))(void *)prop.Set)(*object, value.p);
609                                  }
610                               }
611                            }
612                            else
613                            {
614                               PrintLn("Warning: Incompatible value for ", member ? (String)member.name : (String)prop.name,
615                                  ", expected ", member ? (String)member.dataTypeString : (String)prop.dataTypeString);
616                            }
617                         }
618                      }
619                   }
620                   else
621                      result = syntaxError;
622
623                   if(prop && type.type == structClass)
624                   {
625                      delete value.p;
626                   }
627                }
628             }
629             else if(ch && ch != '}' && ch != ',')
630                result = syntaxError;
631             delete string;
632
633             if(result)
634             {
635                SkipEmpty();
636                if(ch == '}')
637                {
638                   break;
639                }
640                else if(ch != ',')
641                   result = syntaxError;
642             }
643          }
644       }
645       ch = 0;
646       return result;
647    }
648
649    JSONResult GetNumber(Class type, DataValue value)
650    {
651       JSONResult result = syntaxError;
652       char buffer[256];
653       int c = 0;
654       while(c < sizeof(buffer)-1 && (ch == '-' || ch == '.' || tolower(ch) == 'e' || ch == '+' || isdigit(ch)))
655       {
656          buffer[c++] = ch;
657          if(!f.Getc(&ch)) break;
658       }
659       buffer[c] = 0;
660       //if(strchr(buffer, '.'))
661       if(!type) return success;
662
663       // TOFIX: How to swiftly handle classes with base data type?
664       if(type == class(double) || !strcmp(type.dataTypeString, "double"))
665       {
666          value.d = strtod(buffer, null);
667          result = success;
668       }
669       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
670       {
671          value.f = (float)strtod(buffer, null);
672          result = success;
673       }
674       // TOFIX: int64 looks for class long long?
675       //else if(type == class(int64) || !strcmp(type.dataTypeString, "int64"))
676       else if(!strcmp(type.dataTypeString, "int64"))
677       {
678          value.i64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
679          result = success;
680       }
681       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
682       {
683          value.ui64 = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
684          result = success;
685       }
686       else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int"))
687       {
688          value.ui = (uint)strtoul(buffer, null, 10);  // TOFIX: 64 bit support
689          result = success;
690       }
691       else
692       {
693          value.i = (int)strtol(buffer, null, 10);
694          result = success;
695       }
696
697       if(result == success && type.type == unitClass)
698       {
699          // Convert to reference unit
700          Property prop;
701          for(prop = type.conversions.first; prop; prop = prop.next)
702          {
703             bool refProp = false;
704             Class c;
705             if(!strcmp(prop.name, type.base.fullName))
706                refProp = true;
707             else if( (c = eSystem_FindClass(type.module, prop.name) ) )
708             {
709                Property p;
710                for(p = c.conversions.first; p; p = p.next)
711                {
712                   if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
713                   {
714                      refProp = true;
715                      break;
716                   }
717                }
718             }
719             if(refProp)
720             {
721                if(prop.Set && prop.Get)
722                {
723                   const String dts = type.base.dataTypeString;
724                   if(!strcmp(dts, "double"))
725                      value.d = ((double(*)(double))(void *)prop.Get)(value.d);
726                   else if(!strcmp(dts, "float"))
727                      value.f = ((float(*)(float))(void *)prop.Get)(value.f);
728                   else if(!strcmp(dts, "int"))
729                      value.i = ((int(*)(int))(void *)prop.Get)(value.i);
730                   else if(!strcmp(dts, "int64"))
731                      value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
732                   else if(!strcmp(dts, "unsigned int"))
733                      value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
734                   else if(!strcmp(dts, "uint64"))
735                      value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
736                }
737                else
738                   break;
739             }
740          }
741       }
742       return result;
743    }
744 }
745
746 bool WriteMap(File f, Class type, Map map, int indent)
747 {
748    if(map)
749    {
750       int i;
751       bool isFirst = true;
752       MapIterator it { map = map };
753       Class mapNodeClass = map._class.templateArgs[0].dataTypeClass;
754       f.Puts("[\n");
755       indent++;
756
757       while(it.Next())
758       {
759          MapNode n = (MapNode)it.pointer;
760          if(!isFirst)
761             f.Puts(",\n");
762          else
763             isFirst = false;
764          for(i = 0; i<indent; i++) f.Puts("   ");
765          _WriteJSONObject(f, mapNodeClass, n, indent);
766       }
767       f.Puts("\n");
768       indent--;
769       for(i = 0; i<indent; i++) f.Puts("   ");
770       f.Puts("]");
771    }
772    else
773       f.Puts("null");
774    return true;
775 }
776
777 bool WriteArray(File f, Class type, Container array, int indent)
778 {
779    if(array)
780    {
781       int i;
782       bool isFirst = true;
783       Iterator it { array };
784       Class arrayType = type.templateArgs[0].dataTypeClass;
785       f.Puts("[\n");
786       indent++;
787
788       while(it.Next())
789       {
790          DataValue value { };
791          uint64 t = ((uint64(*)(void *, void *))(void *)array.GetData)(array, it.pointer);
792          if(!isFirst)
793             f.Puts(",\n");
794          else
795             isFirst = false;
796
797          // Add value
798          // TODO: Verify the matching between template type and uint64
799          if(arrayType.type == structClass)
800          {
801             value.p = (void *)(uintptr)t;
802          }
803          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
804          {
805             value.ui64 = t;
806             //value.d = *(double *)&t;
807          }
808          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
809          {
810             value.ui = (uint)t;
811             //value.f = *(float *)&t;
812          }
813          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
814             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
815          {
816             value.ui64 = t;
817          }
818          else if(arrayType.typeSize == sizeof(int) || !strcmp(arrayType.dataTypeString, "int") ||
819             !strcmp(arrayType.dataTypeString, "unsigned int") || !strcmp(arrayType.dataTypeString, "uint"))
820          {
821             value.i = (int)t;
822          }
823          else if(arrayType.typeSize == sizeof(short int) || !strcmp(arrayType.dataTypeString, "short") ||
824             !strcmp(arrayType.dataTypeString, "unsigned short") || !strcmp(arrayType.dataTypeString, "uint16") ||
825             !strcmp(arrayType.dataTypeString, "int16"))
826          {
827             value.s = (short)t;
828          }
829          else if(arrayType.typeSize == sizeof(byte) || !strcmp(arrayType.dataTypeString, "char") ||
830             !strcmp(arrayType.dataTypeString, "unsigned char") || !strcmp(arrayType.dataTypeString, "byte"))
831          {
832             value.c = (char)t;
833          }
834          else
835          {
836             value.p = (void *)(uintptr)t;
837          }
838          for(i = 0; i<indent; i++) f.Puts("   ");
839          WriteValue(f, arrayType, value, indent);
840       }
841       f.Puts("\n");
842       indent--;
843       for(i = 0; i<indent; i++) f.Puts("   ");
844       f.Puts("]");
845    }
846    else
847       f.Puts("null");
848    return true;
849 }
850
851 bool WriteNumber(File f, Class type, DataValue value, int indent)
852 {
853    char buffer[1024];
854    bool needClass = false;
855    bool quote;
856    buffer[0] = 0;
857    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
858       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
859    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
860       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.f, buffer, null, &needClass);
861    else if(!strcmp(type.dataTypeString, "int64"))
862       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i64, buffer, null, &needClass);
863    else if(!strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64") || type.typeSize == sizeof(int64))
864       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui64, buffer, null, &needClass);
865    else if(!strcmp(type.dataTypeString, "int"))
866       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.i, buffer, null, &needClass);
867    else if(!strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint") || type.typeSize == sizeof(int))
868       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.ui, buffer, null, &needClass);
869    else if(!strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "int16"))
870       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.s, buffer, null, &needClass);
871    else if(!strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") || type.typeSize == sizeof(short int))
872       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.us, buffer, null, &needClass);
873    else if(!strcmp(type.dataTypeString, "char"))
874       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
875    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
876       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
877
878    quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' ')));
879    if(quote) f.Puts("\"");
880    f.Puts(buffer);
881    if(quote) f.Puts("\"");
882    return true;
883 }
884
885 bool WriteValue(File f, Class type, DataValue value, int indent)
886 {
887    if(!strcmp(type.name, "String") || !strcmp(type.dataTypeString, "char *"))
888    {
889       if(!value.p)
890          f.Puts("null");
891       else
892       {
893          f.Puts("\"");
894          //if(strchr(value.p, '\"') || strchr(value.p, '\\'))
895          {
896             int c = 0;
897             int b = 0;
898             char buffer[1024];
899             char * string = value.p;
900             char ch;
901             while(true)
902             {
903                ch = string[c++];
904                if(ch == '\"')
905                {
906                   buffer[b] = 0;
907                   f.Puts(buffer);
908                   f.Puts("\\\"");
909                   b = 0;
910                }
911                else if(ch == '\\')
912                {
913                   buffer[b] = 0;
914                   f.Puts(buffer);
915                   f.Puts("\\\\");
916                   b = 0;
917                }
918                else if(b == sizeof(buffer)-2 || !ch)
919                {
920                   buffer[b++] = ch;
921                   if(ch) buffer[b] = 0;
922                   f.Puts(buffer);
923                   b = 0;
924                   if(!ch) break;
925                }
926                else
927                   buffer[b++] = ch;
928             }
929          }
930          /*else
931             f.Puts(value.p);*/
932          f.Puts("\"");
933       }
934    }
935    else if(!strcmp(type.name, "bool"))
936    {
937       if(value.i)
938          f.Puts("true");
939       else
940          f.Puts("false");
941    }
942    else if(!strcmp(type.name, "SetBool"))
943    {
944       if(value.i == SetBool::true)
945          f.Puts("true");
946       else if(value.i == SetBool::false)
947          f.Puts("false");
948       else
949          f.Puts("unset");
950    }
951    else if(type.type == enumClass)
952    {
953       f.Puts("\"");
954       WriteNumber(f, type, value, indent);
955       f.Puts("\"");
956    }
957    else if(eClass_IsDerived(type, class(Map)))
958    {
959       WriteMap(f, type, value.p, indent);
960    }
961    else if(eClass_IsDerived(type, class(Container)))
962    {
963       WriteArray(f, type, value.p, indent);
964    }
965    else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
966    {
967       _WriteJSONObject(f, type, value.p, indent);
968    }
969    else if(type.type == bitClass)
970    {
971       Class dataType;
972       dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
973       WriteNumber(f, dataType, value, indent);
974    }
975    else if(type.type == systemClass || type.type == unitClass)
976    {
977       WriteNumber(f, type, value, indent);
978    }
979    return true;
980 }
981
982 public bool WriteJSONObject(File f, Class objectType, void * object, int indent)
983 {
984    bool result = false;
985    if(object)
986    {
987       result = _WriteJSONObject(f, objectType, object, indent);
988       f.Puts("\n");
989    }
990    return result;
991 }
992
993 static bool _WriteJSONObject(File f, Class objectType, void * object, int indent)
994 {
995    if(object)
996    {
997       const char * string = null;
998
999       if(objectType._vTbl[__ecereVMethodID_class_OnGetString] != objectType.base._vTbl[__ecereVMethodID_class_OnGetString])
1000       {
1001          char buffer[1024];
1002          buffer[0] = 0;
1003          string = ((const char *(*)())(void *)objectType._vTbl[__ecereVMethodID_class_OnGetString])(objectType, object, buffer, null, null);
1004       }
1005       if(string)
1006       {
1007          // TOCHECK: ProjectNode.ec why do we add quotes in OnGetString there?
1008          if(string[0] == '\"')
1009             f.Puts(string);
1010          else
1011          {
1012             f.Puts("\"");
1013             f.Puts(string);
1014             f.Puts("\"");
1015          }
1016       }
1017       else
1018       {
1019          Property prop;
1020          int c;
1021          bool isFirst = true;
1022          Class mapKeyClass = null, mapDataClass = null;
1023          Class baseClass;
1024          List<Class> bases { };
1025
1026          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
1027          {
1028             mapKeyClass = objectType.templateArgs[0].dataTypeClass;
1029             mapDataClass = objectType.templateArgs[2].dataTypeClass;
1030          }
1031
1032          f.Puts("{\n");
1033          indent++;
1034
1035          for(baseClass = objectType; baseClass; baseClass = baseClass.base)
1036          {
1037             if(baseClass.isInstanceClass || !baseClass.base)
1038                break;
1039             bases.Insert(null, baseClass);
1040          }
1041
1042          for(baseClass : bases)
1043          {
1044             for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
1045             {
1046                if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
1047                if(prop.isProperty)
1048                {
1049                   if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
1050                   {
1051                      DataValue value { };
1052                      Class type;
1053
1054                      if(mapKeyClass && !strcmp(prop.name, "key"))
1055                         type = mapKeyClass;
1056                      else if(mapDataClass && !strcmp(prop.name, "value"))
1057                         type = mapDataClass;
1058                      else
1059                         type = eSystem_FindClass(__thisModule, prop.dataTypeString);
1060                      if(!type)
1061                         type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
1062                      if(!type)
1063                         PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
1064                      else
1065                      {
1066                         // TOFIX: How to swiftly handle classes with base data type?
1067                         if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1068                         {
1069                            value.d = ((double (*)(void *))(void *)prop.Get)(object);
1070                         }
1071                         else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1072                         {
1073                            value.f = ((float (*)(void *))(void *)prop.Get)(object);
1074                         }
1075                         else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1076                            !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1077                         {
1078                            value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
1079                         }
1080                         else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1081                            !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1082                         {
1083                            value.i = ((int (*)(void *))(void *)prop.Get)(object);
1084                         }
1085                         else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1086                            !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1087                            !strcmp(type.dataTypeString, "int16"))
1088                         {
1089                            value.s = ((short (*)(void *))(void *)prop.Get)(object);
1090                         }
1091                         else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1092                            !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1093                         {
1094                            value.c = ((char (*)(void *))(void *)prop.Get)(object);
1095                         }
1096                         else if(type.type == structClass)
1097                         {
1098                            value.p = new0 byte[type.structSize];
1099                            ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
1100                         }
1101                         else
1102                         {
1103                            value.p = ((void *(*)(void *))(void *)prop.Get)(object);
1104                         }
1105
1106                         if(!isFirst) f.Puts(",\n");
1107                         for(c = 0; c<indent; c++) f.Puts("   ");
1108
1109                         f.Puts("\"");
1110                         f.Putc((char)toupper(prop.name[0]));
1111                         f.Puts(prop.name+1);
1112                         f.Puts("\" : ");
1113                         WriteValue(f, type, value, indent);
1114                         isFirst = false;
1115                         if(type.type == structClass)
1116                            delete value.p;
1117                      }
1118                   }
1119                }
1120                else
1121                {
1122                   DataMember member = (DataMember)prop;
1123                   DataValue value { };
1124                   uint offset;
1125                   Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
1126                   if(!type)
1127                      type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
1128
1129                   offset = member._class.offset + member.offset;
1130
1131                   if(type)
1132                   {
1133                      if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
1134                      {
1135                         if(type.type == structClass)
1136                            value.p = (void *)((byte *)object + offset);
1137                         else
1138                            value.p = *(void **)((byte *)object + offset);
1139                         if(!value.p)
1140                            continue;
1141                      }
1142                      else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
1143                      {
1144                         value.d = *(double *)((byte *)object + offset);
1145                      }
1146                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
1147                      {
1148                         value.f = *(float *)((byte *)object + offset);
1149                      }
1150                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
1151                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
1152                      {
1153                         value.ui64 = *(uint64 *)((byte *)object + offset);
1154                      }
1155                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
1156                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
1157                      {
1158                         value.i = *(int *)((byte *)object + offset);
1159                         if(!strcmp(type.name, "bool") || type.type == enumClass)
1160                            if(!value.i)
1161                               continue;
1162                      }
1163                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
1164                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
1165                         !strcmp(type.dataTypeString, "int16"))
1166                      {
1167                         value.s = *(short *)((byte *)object + offset);
1168                      }
1169                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
1170                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
1171                      {
1172                         value.c = *(char *)((byte *)object + offset);
1173                      }
1174                      else
1175                      {
1176                         value.i = *(int *)((byte *)object + offset);
1177                      }
1178
1179                      if(!isFirst) f.Puts(",\n");
1180                      for(c = 0; c<indent; c++) f.Puts("   ");
1181
1182                      f.Puts("\"");
1183                      f.Putc((char)toupper(member.name[0]));
1184                      f.Puts(member.name+1);
1185                      f.Puts("\" : ");
1186                      WriteValue(f, type, value, indent);
1187                      isFirst = false;
1188                   }
1189                }
1190             }
1191          }
1192
1193          delete bases;
1194
1195          indent--;
1196          f.Puts("\n");
1197          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");
1198       }
1199    }
1200    else
1201       f.Puts("null");
1202    return true;
1203 }