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