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