ecere/sys/JSON: Fixed broken TimeStamp text serialization
[sdk] / ecere / src / sys / JSON.ec
index 06fed42..e9f9f1b 100644 (file)
@@ -1,5 +1,6 @@
 namespace sys;
 
+import "instance"
 import "System"
 import "Array"
 
@@ -20,7 +21,7 @@ public enum JSONResult { syntaxError, success, typeMismatch, noItem };
 
 public enum SetBool : uint
 {
-   unset, false, true /*; // Syntax error! */
+   unset, false, true;
 
    /*public property bool     // NOT WORKING!
    {
@@ -38,7 +39,7 @@ public:
 
    void SkipEmpty()
    {
-      while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'))
+      while(!f.Eof() && (!ch || ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '/'))
       {
          f.Getc(&ch);
       }
@@ -94,7 +95,9 @@ public:
       {
          Container array;
          if(type && eClass_IsDerived(type, class(Map)))
+         {
             result = GetMap(type, (Map *)&array);
+         }
          else
             result = GetArray(type, &array);
 
@@ -197,6 +200,8 @@ public:
                arrayType = type.templateArgs[0].dataTypeClass;
             }
 
+            if(arrayType && arrayType.type == structClass)
+               value.p = new0 byte[arrayType.structSize];
             itemResult = GetValue(arrayType, value);
             if(itemResult == success)
             {
@@ -208,11 +213,11 @@ public:
                }
                else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
                {
-                  t = *(uint64 *)&value.d;
+                  t = value.ui64; //*(uint64 *)&value.d;
                }
                else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
                {
-                  t = *(uint *)&value.f;
+                  t = value.ui; //f*(uint *)&value.f;
                }
                else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
                   !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
@@ -240,6 +245,9 @@ public:
                   t = (uint64)(uintptr)value.p;
                }
                ((void *(*)(void *, uint64))(void *)array->Add)(*array, t);
+
+               if(arrayType && arrayType.type == structClass)
+                  delete value.p;
             }
             else
             {
@@ -426,6 +434,7 @@ public:
                Property prop = null;
                Class type = null;
                bool isKey = false;
+               uint offset = 0;
 
                if(objectType)
                {
@@ -449,6 +458,8 @@ public:
                         type = eSystem_FindClass(__thisModule, member.dataTypeString);
                         if(!type)
                            type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
+
+                        offset = member._class.offset + member.offset;
                      }
                      else if(!member)
                      {
@@ -470,7 +481,15 @@ public:
 
                   if(type && type.type == structClass)
                   {
-                     value.p = (byte *)*object + member._class.offset + member.offset;
+                     if(member)
+                     {
+                        value.p = (byte *)*object + offset;
+                        memset(value.p, 0, type.structSize);
+                     }
+                     else if(prop)
+                     {
+                        value.p = new0 byte[type.structSize];
+                     }
                   }
                   ch = 0;
                   SkipEmpty();
@@ -495,7 +514,7 @@ public:
                                     ;
                                  else if(type.type == normalClass || type.type == noHeadClass)
                                  {
-                                    void ** ptr = (void**)((byte *)*object + member._class.offset + member.offset);
+                                    void ** ptr = (void**)((byte *)*object + offset);
                                     if(eClass_IsDerived(type, class(Container)) && *ptr)
                                     {
                                        Container container = (Container)*ptr;
@@ -506,36 +525,36 @@ public:
                                  }
                                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
                                  {
-                                    *(double *)((byte *)*object + member._class.offset + member.offset) = value.d;
+                                    *(double *)((byte *)*object + offset) = value.d;
                                  }
                                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
                                  {
-                                    *(float *)((byte *)*object + member._class.offset + member.offset) = value.f;
+                                    *(float *)((byte *)*object + offset) = value.f;
                                  }
                                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
                                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
                                  {
-                                    *(uint64 *)((byte *)*object + member._class.offset + member.offset) = value.ui64;
+                                    *(uint64 *)((byte *)*object + offset) = value.ui64;
                                  }
                                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
                                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
                                  {
-                                    *(int *)((byte *)*object + member._class.offset + member.offset) = value.i;
+                                    *(int *)((byte *)*object + offset) = value.i;
                                  }
                                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
                                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
                                     !strcmp(type.dataTypeString, "int16"))
                                  {
-                                    *(short *)((byte *)*object + member._class.offset + member.offset) = value.s;
+                                    *(short *)((byte *)*object + offset) = value.s;
                                  }
                                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
                                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
                                  {
-                                    *(char *)((byte *)*object + member._class.offset + member.offset) = value.c;
+                                    *(char *)((byte *)*object + offset) = value.c;
                                  }
                                  else
                                  {
-                                    *(void **)((byte *)*object + member._class.offset + member.offset) = value.p;
+                                    *(void **)((byte *)*object + offset) = value.p;
                                  }
                               }
                               else if(prop && prop.Set)
@@ -592,6 +611,11 @@ public:
                   }
                   else
                      result = syntaxError;
+
+                  if(prop && type.type == structClass)
+                  {
+                     delete value.p;
+                  }
                }
             }
             else if(ch && ch != '}' && ch != ',')
@@ -647,7 +671,12 @@ public:
       }
       else if(type == class(uint64) || !strcmp(type.dataTypeString, "uint64"))
       {
-         value.ui64 = strtol(buffer, null, 10);  // TOFIX: 64 bit support
+         value.ui64 = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
+         result = success;
+      }
+      else if(type == class(uint) || !strcmp(type.dataTypeString, "unsigned int"))
+      {
+         value.ui = strtoul(buffer, null, 10);  // TOFIX: 64 bit support
          result = success;
       }
       else
@@ -655,6 +684,52 @@ public:
          value.i = strtol(buffer, null, 10);
          result = success;
       }
+
+      if(result == success && type.type == unitClass)
+      {
+         // Convert to reference unit
+         Property prop;
+         for(prop = type.conversions.first; prop; prop = prop.next)
+         {
+            bool refProp = false;
+            Class c;
+            if(!strcmp(prop.name, type.base.fullName))
+               refProp = true;
+            else if( (c = eSystem_FindClass(type.module, prop.name) ) )
+            {
+               Property p;
+               for(p = c.conversions.first; p; p = p.next)
+               {
+                  if(!strcmp(p.name, type.base.fullName) && !p.Set && !p.Get)
+                  {
+                     refProp = true;
+                     break;
+                  }
+               }
+            }
+            if(refProp)
+            {
+               if(prop.Set && prop.Get)
+               {
+                  const String dts = type.base.dataTypeString;
+                  if(!strcmp(dts, "double"))
+                     value.d = ((double(*)(double))(void *)prop.Get)(value.d);
+                  else if(!strcmp(dts, "float"))
+                     value.f = ((float(*)(float))(void *)prop.Get)(value.f);
+                  else if(!strcmp(dts, "int"))
+                     value.i = ((int(*)(int))(void *)prop.Get)(value.i);
+                  else if(!strcmp(dts, "int64"))
+                     value.i64 = ((int64(*)(int64))(void *)prop.Get)(value.i64);
+                  else if(!strcmp(dts, "unsigned int"))
+                     value.ui = ((uint(*)(uint))(void *)prop.Get)(value.ui);
+                  else if(!strcmp(dts, "uint64"))
+                     value.ui64 = ((uint64(*)(uint64))(void *)prop.Get)(value.ui64);
+               }
+               else
+                  break;
+            }
+         }
+      }
       return result;
    }
 }
@@ -718,11 +793,13 @@ bool WriteArray(File f, Class type, Container array, int indent)
          }
          else if(arrayType == class(double) || !strcmp(arrayType.dataTypeString, "double"))
          {
-            value.d = *(double *)&t;
+            value.ui64 = t;
+            //value.d = *(double *)&t;
          }
          else if(arrayType == class(float) || !strcmp(arrayType.dataTypeString, "float"))
          {
-            value.f = *(float *)&t;
+            value.ui = (uint)t;
+            //value.f = *(float *)&t;
          }
          else if(arrayType.typeSize == sizeof(int64) || !strcmp(arrayType.dataTypeString, "int64") ||
             !strcmp(arrayType.dataTypeString, "unsigned int64") || !strcmp(arrayType.dataTypeString, "uint64"))
@@ -766,6 +843,7 @@ bool WriteNumber(File f, Class type, DataValue value, int indent)
 {
    char buffer[1024];
    bool needClass = false;
+   bool quote;
    buffer[0] = 0;
    if(type == class(double) || !strcmp(type.dataTypeString, "double"))
       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.d, buffer, 0, &needClass);
@@ -787,7 +865,11 @@ bool WriteNumber(File f, Class type, DataValue value, int indent)
       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.c, buffer, null, &needClass);
    else if(!strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte") || type.typeSize == sizeof(byte))
       ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &value.uc, buffer, null, &needClass);
+
+   quote = (type.type == unitClass && ((buffer[0] != '.' && !isdigit(buffer[0])) || strchr(buffer, ' ')));
+   if(quote) f.Puts("\"");
    f.Puts(buffer);
+   if(quote) f.Puts("\"");
    return true;
 }
 
@@ -857,7 +939,7 @@ bool WriteValue(File f, Class type, DataValue value, int indent)
       else
          f.Puts("unset");
    }
-   else if(type.type == enumClass || type.type == unitClass)
+   else if(type.type == enumClass)
    {
       f.Puts("\"");
       WriteNumber(f, type, value, indent);
@@ -871,11 +953,17 @@ bool WriteValue(File f, Class type, DataValue value, int indent)
    {
       WriteArray(f, type, value.p, indent);
    }
-   else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || type.type == bitClass)
+   else if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
    {
       _WriteJSONObject(f, type, value.p, indent);
    }
-   else if(type.type == systemClass)
+   else if(type.type == bitClass)
+   {
+      Class dataType;
+      dataType = eSystem_FindClass(__thisModule, type.dataTypeString);
+      WriteNumber(f, dataType, value, indent);
+   }
+   else if(type.type == systemClass || type.type == unitClass)
    {
       WriteNumber(f, type, value, indent);
    }
@@ -923,6 +1011,8 @@ static bool _WriteJSONObject(File f, Class objectType, void * object, int indent
          int c;
          bool isFirst = true;
          Class mapKeyClass = null, mapDataClass = null;
+         Class baseClass;
+         List<Class> bases { };
 
          if(objectType.templateClass && eClass_IsDerived(objectType.templateClass, class(MapNode)))
          {
@@ -933,144 +1023,166 @@ static bool _WriteJSONObject(File f, Class objectType, void * object, int indent
          f.Puts("{\n");
          indent++;
 
-         for(prop = objectType.membersAndProperties.first; prop; prop = prop.next)
+         for(baseClass = objectType; baseClass; baseClass = baseClass.base)
          {
-            if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
-            if(prop.isProperty)
+            if(baseClass.isInstanceClass || !baseClass.base)
+               break;
+            bases.Insert(null, baseClass);
+         }
+
+         for(baseClass : bases)
+         {
+            for(prop = baseClass.membersAndProperties.first; prop; prop = prop.next)
             {
-               if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
+               if(prop.memberAccess != publicAccess || (prop.isProperty && (!prop.Set || !prop.Get))) continue;
+               if(prop.isProperty)
                {
-                  DataValue value { };
-                  Class type;
+                  if(!prop.conversion && (!prop.IsSet || prop.IsSet(object)))
+                  {
+                     DataValue value { };
+                     Class type;
 
-                  if(mapKeyClass && !strcmp(prop.name, "key"))
-                     type = mapKeyClass;
-                  else if(mapDataClass && !strcmp(prop.name, "value"))
-                     type = mapDataClass;
-                  else
-                     type = eSystem_FindClass(__thisModule, prop.dataTypeString);
-                  if(!type)
-                     type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
+                     if(mapKeyClass && !strcmp(prop.name, "key"))
+                        type = mapKeyClass;
+                     else if(mapDataClass && !strcmp(prop.name, "value"))
+                        type = mapDataClass;
+                     else
+                        type = eSystem_FindClass(__thisModule, prop.dataTypeString);
+                     if(!type)
+                        type = eSystem_FindClass(__thisModule.application, prop.dataTypeString);
+                     if(!type)
+                        PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
+                     else
+                     {
+                        // TOFIX: How to swiftly handle classes with base data type?
+                        if(type == class(double) || !strcmp(type.dataTypeString, "double"))
+                        {
+                           value.d = ((double (*)(void *))(void *)prop.Get)(object);
+                        }
+                        else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
+                        {
+                           value.f = ((float (*)(void *))(void *)prop.Get)(object);
+                        }
+                        else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
+                           !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
+                        {
+                           value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
+                        }
+                        else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
+                           !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
+                        {
+                           value.i = ((int (*)(void *))(void *)prop.Get)(object);
+                        }
+                        else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
+                           !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
+                           !strcmp(type.dataTypeString, "int16"))
+                        {
+                           value.s = ((short (*)(void *))(void *)prop.Get)(object);
+                        }
+                        else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
+                           !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
+                        {
+                           value.c = ((char (*)(void *))(void *)prop.Get)(object);
+                        }
+                        else if(type.type == structClass)
+                        {
+                           value.p = new0 byte[type.structSize];
+                           ((void (*)(void *, void *))(void *)prop.Get)(object, value.p);
+                        }
+                        else
+                        {
+                           value.p = ((void *(*)(void *))(void *)prop.Get)(object);
+                        }
+
+                        if(!isFirst) f.Puts(",\n");
+                        for(c = 0; c<indent; c++) f.Puts("   ");
+
+                        f.Puts("\"");
+                        f.Putc((char)toupper(prop.name[0]));
+                        f.Puts(prop.name+1);
+                        f.Puts("\" : ");
+                        WriteValue(f, type, value, indent);
+                        isFirst = false;
+                        if(type.type == structClass)
+                           delete value.p;
+                     }
+                  }
+               }
+               else
+               {
+                  DataMember member = (DataMember)prop;
+                  DataValue value { };
+                  uint offset;
+                  Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
                   if(!type)
-                     PrintLn("warning: Unresolved data type ", (String)prop.dataTypeString);
-                  else
+                     type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
+
+                  offset = member._class.offset + member.offset;
+
+                  if(type)
                   {
-                     // TOFIX: How to swiftly handle classes with base data type?
-                     if(type == class(double) || !strcmp(type.dataTypeString, "double"))
+                     if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
                      {
-                        value.d = ((double (*)(void *))(void *)prop.Get)(object);
+                        if(type.type == structClass)
+                           value.p = (void *)((byte *)object + offset);
+                        else
+                           value.p = *(void **)((byte *)object + offset);
+                        if(!value.p)
+                           continue;
+                     }
+                     else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
+                     {
+                        value.d = *(double *)((byte *)object + offset);
                      }
                      else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
                      {
-                        value.f = ((float (*)(void *))(void *)prop.Get)(object);
+                        value.f = *(float *)((byte *)object + offset);
                      }
                      else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
                         !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
                      {
-                        value.ui64 = ((uint64 (*)(void *))(void *)prop.Get)(object);
+                        value.ui64 = *(uint64 *)((byte *)object + offset);
                      }
                      else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
                         !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
                      {
-                        value.i = ((int (*)(void *))(void *)prop.Get)(object);
+                        value.i = *(int *)((byte *)object + offset);
+                        if(!strcmp(type.name, "bool") || type.type == enumClass)
+                           if(!value.i)
+                              continue;
                      }
                      else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
                         !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
                         !strcmp(type.dataTypeString, "int16"))
                      {
-                        value.s = ((short (*)(void *))(void *)prop.Get)(object);
+                        value.s = *(short *)((byte *)object + offset);
                      }
                      else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
                         !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
                      {
-                        value.c = ((char (*)(void *))(void *)prop.Get)(object);
+                        value.c = *(char *)((byte *)object + offset);
                      }
                      else
                      {
-                        value.p = ((void *(*)(void *))(void *)prop.Get)(object);
+                        value.i = *(int *)((byte *)object + offset);
                      }
 
                      if(!isFirst) f.Puts(",\n");
                      for(c = 0; c<indent; c++) f.Puts("   ");
 
                      f.Puts("\"");
-                     f.Putc((char)toupper(prop.name[0]));
-                     f.Puts(prop.name+1);
+                     f.Putc((char)toupper(member.name[0]));
+                     f.Puts(member.name+1);
                      f.Puts("\" : ");
                      WriteValue(f, type, value, indent);
                      isFirst = false;
                   }
                }
             }
-            else
-            {
-               DataMember member = (DataMember)prop;
-               DataValue value { };
-               Class type = eSystem_FindClass(__thisModule, member.dataTypeString);
-               if(!type)
-                  type = eSystem_FindClass(__thisModule.application, member.dataTypeString);
-
-               if(type)
-               {
-                  if(type.type == normalClass || type.type == noHeadClass || type.type == structClass || !strcmp(type.name, "String"))
-                  {
-                     if(type.type == structClass)
-                        value.p = (void *)((byte *)object + member._class.offset + member.offset);
-                     else
-                        value.p = *(void **)((byte *)object + member._class.offset + member.offset);
-                     if(!value.p)
-                        continue;
-                  }
-                  else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
-                  {
-                     value.d = *(double *)((byte *)object + member._class.offset + member.offset);
-                  }
-                  else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
-                  {
-                     value.f = *(float *)((byte *)object + member._class.offset + member.offset);
-                  }
-                  else if(type.typeSize == sizeof(int64) || !strcmp(type.dataTypeString, "int64") ||
-                     !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
-                  {
-                     value.ui64 = *(uint64 *)((byte *)object + member._class.offset + member.offset);
-                  }
-                  else if(type.typeSize == sizeof(int) || !strcmp(type.dataTypeString, "int") ||
-                     !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
-                  {
-                     value.i = *(int *)((byte *)object + member._class.offset + member.offset);
-                     if(!strcmp(type.name, "bool") || type.type == enumClass)
-                        if(!value.i)
-                           continue;
-                  }
-                  else if(type.typeSize == sizeof(short int) || !strcmp(type.dataTypeString, "short") ||
-                     !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||
-                     !strcmp(type.dataTypeString, "int16"))
-                  {
-                     value.s = *(short *)((byte *)object + member._class.offset + member.offset);
-                  }
-                  else if(type.typeSize == sizeof(byte) || !strcmp(type.dataTypeString, "char") ||
-                     !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
-                  {
-                     value.c = *(char *)((byte *)object + member._class.offset + member.offset);
-                  }
-                  else
-                  {
-                     value.i = *(int *)((byte *)object + member._class.offset + member.offset);
-                  }
-
-                  if(!isFirst) f.Puts(",\n");
-                  for(c = 0; c<indent; c++) f.Puts("   ");
-
-                  f.Puts("\"");
-                  f.Putc((char)toupper(member.name[0]));
-                  f.Puts(member.name+1);
-                  f.Puts("\" : ");
-                  WriteValue(f, type, value, indent);
-                  isFirst = false;
-               }
-            }
          }
 
+         delete bases;
+
          indent--;
          f.Puts("\n");
          for(c = 0; c<indent; c++) f.Puts("   "); f.Puts("}");