nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: PublicHeader: None nuclear@1: Filename : OVR_JSON.h nuclear@1: Content : JSON format reader and writer nuclear@1: Created : April 9, 2013 nuclear@1: Author : Brant Lewis nuclear@1: Notes : nuclear@1: The code is a derivative of the cJSON library written by Dave Gamble and subject nuclear@1: to the following permissive copyright. nuclear@1: nuclear@1: Copyright (c) 2009 Dave Gamble nuclear@1: nuclear@1: Permission is hereby granted, free of charge, to any person obtaining a copy nuclear@1: of this software and associated documentation files (the "Software"), to deal nuclear@1: in the Software without restriction, including without limitation the rights nuclear@1: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell nuclear@1: copies of the Software, and to permit persons to whom the Software is nuclear@1: furnished to do so, subject to the following conditions: nuclear@1: nuclear@1: The above copyright notice and this permission notice shall be included in nuclear@1: all copies or substantial portions of the Software. nuclear@1: nuclear@1: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR nuclear@1: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, nuclear@1: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE nuclear@1: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER nuclear@1: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, nuclear@1: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN nuclear@1: THE SOFTWARE. nuclear@1: nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: ************************************************************************************/ nuclear@1: nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include "OVR_JSON.h" nuclear@1: #include "Kernel/OVR_SysFile.h" nuclear@1: #include "Kernel/OVR_Log.h" nuclear@1: nuclear@1: namespace OVR { nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Create a new copy of a string nuclear@1: static char* JSON_strdup(const char* str) nuclear@1: { nuclear@1: UPInt len = OVR_strlen(str) + 1; nuclear@1: char* copy = (char*)OVR_ALLOC(len); nuclear@1: if (!copy) nuclear@1: return 0; nuclear@1: memcpy(copy, str, len); nuclear@1: return copy; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Render the number from the given item into a string. nuclear@1: static char* PrintNumber(double d) nuclear@1: { nuclear@1: char *str; nuclear@1: //double d=item->valuedouble; nuclear@1: int valueint = (int)d; nuclear@1: if (fabs(((double)valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) nuclear@1: { nuclear@1: str=(char*)OVR_ALLOC(21); // 2^64+1 can be represented in 21 chars. nuclear@1: if (str) nuclear@1: OVR_sprintf(str, 21, "%d", valueint); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: str=(char*)OVR_ALLOC(64); // This is a nice tradeoff. nuclear@1: if (str) nuclear@1: { nuclear@1: if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) nuclear@1: OVR_sprintf(str, 64, "%.0f", d); nuclear@1: else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) nuclear@1: OVR_sprintf(str, 64, "%e", d); nuclear@1: else nuclear@1: OVR_sprintf(str, 64, "%f", d); nuclear@1: } nuclear@1: } nuclear@1: return str; nuclear@1: } nuclear@1: nuclear@1: // Parse the input text into an un-escaped cstring, and populate item. nuclear@1: static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; nuclear@1: nuclear@1: // Helper to assign error sting and return 0. nuclear@1: const char* AssignError(const char** perror, const char *errorMessage) nuclear@1: { nuclear@1: if (perror) nuclear@1: *perror = errorMessage; nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // ***** JSON Node class nuclear@1: nuclear@1: JSON::JSON(JSONItemType itemType) nuclear@1: : Type(itemType), dValue(0.0) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: JSON::~JSON() nuclear@1: { nuclear@1: JSON* child = Children.GetFirst(); nuclear@1: while (!Children.IsNull(child)) nuclear@1: { nuclear@1: child->RemoveNode(); nuclear@1: child->Release(); nuclear@1: child = Children.GetFirst(); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Parse the input text to generate a number, and populate the result into item nuclear@1: // Returns the text position after the parsed number nuclear@1: const char* JSON::parseNumber(const char *num) nuclear@1: { nuclear@1: const char* num_start = num; nuclear@1: double n=0, sign=1, scale=0; nuclear@1: int subscale = 0, nuclear@1: signsubscale = 1; nuclear@1: nuclear@1: // Could use sscanf for this? nuclear@1: if (*num=='-') nuclear@1: sign=-1,num++; // Has sign? nuclear@1: if (*num=='0') nuclear@1: num++; // is zero nuclear@1: nuclear@1: if (*num>='1' && *num<='9') nuclear@1: { nuclear@1: do nuclear@1: { nuclear@1: n=(n*10.0)+(*num++ -'0'); nuclear@1: } nuclear@1: while (*num>='0' && *num<='9'); // Number? nuclear@1: } nuclear@1: nuclear@1: if (*num=='.' && num[1]>='0' && num[1]<='9') nuclear@1: { nuclear@1: num++; nuclear@1: do nuclear@1: { nuclear@1: n=(n*10.0)+(*num++ -'0'); nuclear@1: scale--; nuclear@1: } nuclear@1: while (*num>='0' && *num<='9'); // Fractional part? nuclear@1: } nuclear@1: nuclear@1: if (*num=='e' || *num=='E') // Exponent? nuclear@1: { nuclear@1: num++; nuclear@1: if (*num=='+') nuclear@1: num++; nuclear@1: else if (*num=='-') nuclear@1: { nuclear@1: signsubscale=-1; nuclear@1: num++; // With sign? nuclear@1: } nuclear@1: nuclear@1: while (*num>='0' && *num<='9') nuclear@1: subscale=(subscale*10)+(*num++ - '0'); // Number? nuclear@1: } nuclear@1: nuclear@1: // Number = +/- number.fraction * 10^+/- exponent nuclear@1: n = sign*n*pow(10.0,(scale+subscale*signsubscale)); nuclear@1: nuclear@1: // Assign parsed value. nuclear@1: Type = JSON_Number; nuclear@1: dValue = n; nuclear@1: Value.AssignString(num_start, num - num_start); nuclear@1: nuclear@1: return num; nuclear@1: } nuclear@1: nuclear@1: // Parses a hex string up to the specified number of digits. nuclear@1: // Returns the first character after the string. nuclear@1: const char* ParseHex(unsigned* val, unsigned digits, const char* str) nuclear@1: { nuclear@1: *val = 0; nuclear@1: nuclear@1: for(unsigned digitCount = 0; digitCount < digits; digitCount++, str++) nuclear@1: { nuclear@1: unsigned v = *str; nuclear@1: nuclear@1: if ((v >= '0') && (v <= '9')) nuclear@1: v -= '0'; nuclear@1: else if ((v >= 'a') && (v <= 'f')) nuclear@1: v = 10 + v - 'a'; nuclear@1: else if ((v >= 'A') && (v <= 'F')) nuclear@1: v = 10 + v - 'A'; nuclear@1: else nuclear@1: break; nuclear@1: nuclear@1: *val = *val * 16 + v; nuclear@1: } nuclear@1: nuclear@1: return str; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Parses the input text into a string item and returns the text position after nuclear@1: // the parsed string nuclear@1: const char* JSON::parseString(const char* str, const char** perror) nuclear@1: { nuclear@1: const char* ptr = str+1; nuclear@1: const char* p; nuclear@1: char* ptr2; nuclear@1: char* out; nuclear@1: int len=0; nuclear@1: unsigned uc, uc2; nuclear@1: nuclear@1: if (*str!='\"') nuclear@1: { nuclear@1: return AssignError(perror, "Syntax Error: Missing quote"); nuclear@1: } nuclear@1: nuclear@1: while (*ptr!='\"' && *ptr && ++len) nuclear@1: { nuclear@1: if (*ptr++ == '\\') ptr++; // Skip escaped quotes. nuclear@1: } nuclear@1: nuclear@1: // This is how long we need for the string, roughly. nuclear@1: out=(char*)OVR_ALLOC(len+1); nuclear@1: if (!out) nuclear@1: return 0; nuclear@1: nuclear@1: ptr = str+1; nuclear@1: ptr2= out; nuclear@1: nuclear@1: while (*ptr!='\"' && *ptr) nuclear@1: { nuclear@1: if (*ptr!='\\') nuclear@1: { nuclear@1: *ptr2++ = *ptr++; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: ptr++; nuclear@1: switch (*ptr) nuclear@1: { nuclear@1: case 'b': *ptr2++ = '\b'; break; nuclear@1: case 'f': *ptr2++ = '\f'; break; nuclear@1: case 'n': *ptr2++ = '\n'; break; nuclear@1: case 'r': *ptr2++ = '\r'; break; nuclear@1: case 't': *ptr2++ = '\t'; break; nuclear@1: nuclear@1: // Transcode utf16 to utf8. nuclear@1: case 'u': nuclear@1: nuclear@1: // Get the unicode char. nuclear@1: p = ParseHex(&uc, 4, ptr + 1); nuclear@1: if (ptr != p) nuclear@1: ptr = p - 1; nuclear@1: nuclear@1: if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) nuclear@1: break; // Check for invalid. nuclear@1: nuclear@1: // UTF16 surrogate pairs. nuclear@1: if (uc>=0xD800 && uc<=0xDBFF) nuclear@1: { nuclear@1: if (ptr[1]!='\\' || ptr[2]!='u') nuclear@1: break; // Missing second-half of surrogate. nuclear@1: nuclear@1: p= ParseHex(&uc2, 4, ptr + 3); nuclear@1: if (ptr != p) nuclear@1: ptr = p - 1; nuclear@1: nuclear@1: if (uc2<0xDC00 || uc2>0xDFFF) nuclear@1: break; // Invalid second-half of surrogate. nuclear@1: nuclear@1: uc = 0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); nuclear@1: } nuclear@1: nuclear@1: len=4; nuclear@1: nuclear@1: if (uc<0x80) nuclear@1: len=1; nuclear@1: else if (uc<0x800) nuclear@1: len=2; nuclear@1: else if (uc<0x10000) nuclear@1: len=3; nuclear@1: nuclear@1: ptr2+=len; nuclear@1: nuclear@1: switch (len) nuclear@1: { nuclear@1: case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; nuclear@1: case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; nuclear@1: case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; nuclear@1: case 1: *--ptr2 = (char)(uc | firstByteMark[len]); nuclear@1: } nuclear@1: ptr2+=len; nuclear@1: break; nuclear@1: nuclear@1: default: nuclear@1: *ptr2++ = *ptr; nuclear@1: break; nuclear@1: } nuclear@1: ptr++; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: *ptr2 = 0; nuclear@1: if (*ptr=='\"') nuclear@1: ptr++; nuclear@1: nuclear@1: // Make a copy of the string nuclear@1: Value=out; nuclear@1: OVR_FREE(out); nuclear@1: Type=JSON_String; nuclear@1: nuclear@1: return ptr; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Render the string provided to an escaped version that can be printed. nuclear@1: char* PrintString(const char* str) nuclear@1: { nuclear@1: const char *ptr; nuclear@1: char *ptr2,*out; nuclear@1: int len=0; nuclear@1: unsigned char token; nuclear@1: nuclear@1: if (!str) nuclear@1: return JSON_strdup(""); nuclear@1: ptr=str; nuclear@1: nuclear@1: token=*ptr; nuclear@1: while (token && ++len)\ nuclear@1: { nuclear@1: if (strchr("\"\\\b\f\n\r\t",token)) nuclear@1: len++; nuclear@1: else if (token<32) nuclear@1: len+=5; nuclear@1: ptr++; nuclear@1: token=*ptr; nuclear@1: } nuclear@1: nuclear@1: int buff_size = len+3; nuclear@1: out=(char*)OVR_ALLOC(buff_size); nuclear@1: if (!out) nuclear@1: return 0; nuclear@1: nuclear@1: ptr2 = out; nuclear@1: ptr = str; nuclear@1: *ptr2++ = '\"'; nuclear@1: nuclear@1: while (*ptr) nuclear@1: { nuclear@1: if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') nuclear@1: *ptr2++=*ptr++; nuclear@1: else nuclear@1: { nuclear@1: *ptr2++='\\'; nuclear@1: switch (token=*ptr++) nuclear@1: { nuclear@1: case '\\': *ptr2++='\\'; break; nuclear@1: case '\"': *ptr2++='\"'; break; nuclear@1: case '\b': *ptr2++='b'; break; nuclear@1: case '\f': *ptr2++='f'; break; nuclear@1: case '\n': *ptr2++='n'; break; nuclear@1: case '\r': *ptr2++='r'; break; nuclear@1: case '\t': *ptr2++='t'; break; nuclear@1: default: nuclear@1: OVR_sprintf(ptr2, buff_size - (ptr2-out), "u%04x",token); nuclear@1: ptr2+=5; nuclear@1: break; // Escape and print. nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: *ptr2++='\"'; nuclear@1: *ptr2++=0; nuclear@1: return out; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Utility to jump whitespace and cr/lf nuclear@1: static const char* skip(const char* in) nuclear@1: { nuclear@1: while (in && *in && (unsigned char)*in<=' ') nuclear@1: in++; nuclear@1: return in; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Parses the supplied buffer of JSON text and returns a JSON object tree nuclear@1: // The returned object must be Released after use nuclear@1: JSON* JSON::Parse(const char* buff, const char** perror) nuclear@1: { nuclear@1: const char* end = 0; nuclear@1: JSON* json = new JSON(); nuclear@1: nuclear@1: if (!json) nuclear@1: { nuclear@1: AssignError(perror, "Error: Failed to allocate memory"); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: end = json->parseValue(skip(buff), perror); nuclear@1: if (!end) nuclear@1: { nuclear@1: json->Release(); nuclear@1: return NULL; nuclear@1: } // parse failure. ep is set. nuclear@1: nuclear@1: return json; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Parser core - when encountering text, process appropriately. nuclear@1: const char* JSON::parseValue(const char* buff, const char** perror) nuclear@1: { nuclear@1: if (perror) nuclear@1: *perror = 0; nuclear@1: nuclear@1: if (!buff) nuclear@1: return NULL; // Fail on null. nuclear@1: nuclear@1: if (!strncmp(buff,"null",4)) nuclear@1: { nuclear@1: Type = JSON_Null; nuclear@1: return buff+4; nuclear@1: } nuclear@1: if (!strncmp(buff,"false",5)) nuclear@1: { nuclear@1: Type = JSON_Bool; nuclear@1: Value = "false"; nuclear@1: dValue = 0; nuclear@1: return buff+5; nuclear@1: } nuclear@1: if (!strncmp(buff,"true",4)) nuclear@1: { nuclear@1: Type = JSON_Bool; nuclear@1: Value = "true"; nuclear@1: dValue = 1; nuclear@1: return buff+4; nuclear@1: } nuclear@1: if (*buff=='\"') nuclear@1: { nuclear@1: return parseString(buff, perror); nuclear@1: } nuclear@1: if (*buff=='-' || (*buff>='0' && *buff<='9')) nuclear@1: { nuclear@1: return parseNumber(buff); nuclear@1: } nuclear@1: if (*buff=='[') nuclear@1: { nuclear@1: return parseArray(buff, perror); nuclear@1: } nuclear@1: if (*buff=='{') nuclear@1: { nuclear@1: return parseObject(buff, perror); nuclear@1: } nuclear@1: nuclear@1: return AssignError(perror, "Syntax Error: Invalid syntax"); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Render a value to text. nuclear@1: char* JSON::PrintValue(int depth, bool fmt) nuclear@1: { nuclear@1: char *out=0; nuclear@1: nuclear@1: switch (Type) nuclear@1: { nuclear@1: case JSON_Null: out = JSON_strdup("null"); break; nuclear@1: case JSON_Bool: nuclear@1: if (dValue == 0) nuclear@1: out = JSON_strdup("false"); nuclear@1: else nuclear@1: out = JSON_strdup("true"); nuclear@1: break; nuclear@1: case JSON_Number: out = PrintNumber(dValue); break; nuclear@1: case JSON_String: out = PrintString(Value); break; nuclear@1: case JSON_Array: out = PrintArray(depth, fmt); break; nuclear@1: case JSON_Object: out = PrintObject(depth, fmt); break; nuclear@1: case JSON_None: OVR_ASSERT_LOG(false, ("Bad JSON type.")); break; nuclear@1: } nuclear@1: return out; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Build an array object from input text and returns the text position after nuclear@1: // the parsed array nuclear@1: const char* JSON::parseArray(const char* buff, const char** perror) nuclear@1: { nuclear@1: JSON *child; nuclear@1: if (*buff!='[') nuclear@1: { nuclear@1: return AssignError(perror, "Syntax Error: Missing opening bracket"); nuclear@1: } nuclear@1: nuclear@1: Type=JSON_Array; nuclear@1: buff=skip(buff+1); nuclear@1: nuclear@1: if (*buff==']') nuclear@1: return buff+1; // empty array. nuclear@1: nuclear@1: child = new JSON(); nuclear@1: if (!child) nuclear@1: return 0; // memory fail nuclear@1: Children.PushBack(child); nuclear@1: nuclear@1: buff=skip(child->parseValue(skip(buff), perror)); // skip any spacing, get the buff. nuclear@1: if (!buff) nuclear@1: return 0; nuclear@1: nuclear@1: while (*buff==',') nuclear@1: { nuclear@1: JSON *new_item = new JSON(); nuclear@1: if (!new_item) nuclear@1: return AssignError(perror, "Error: Failed to allocate memory"); nuclear@1: nuclear@1: Children.PushBack(new_item); nuclear@1: nuclear@1: buff=skip(new_item->parseValue(skip(buff+1), perror)); nuclear@1: if (!buff) nuclear@1: return AssignError(perror, "Error: Failed to allocate memory"); nuclear@1: } nuclear@1: nuclear@1: if (*buff==']') nuclear@1: return buff+1; // end of array nuclear@1: nuclear@1: return AssignError(perror, "Syntax Error: Missing ending bracket"); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Render an array to text. The returned text must be freed nuclear@1: char* JSON::PrintArray(int depth, bool fmt) nuclear@1: { nuclear@1: char **entries; nuclear@1: char * out = 0,*ptr,*ret; nuclear@1: SPInt len = 5; nuclear@1: nuclear@1: bool fail = false; nuclear@1: nuclear@1: // How many entries in the array? nuclear@1: int numentries = GetItemCount(); nuclear@1: if (!numentries) nuclear@1: { nuclear@1: out=(char*)OVR_ALLOC(3); nuclear@1: if (out) nuclear@1: OVR_strcpy(out, 3, "[]"); nuclear@1: return out; nuclear@1: } nuclear@1: // Allocate an array to hold the values for each nuclear@1: entries=(char**)OVR_ALLOC(numentries*sizeof(char*)); nuclear@1: if (!entries) nuclear@1: return 0; nuclear@1: memset(entries,0,numentries*sizeof(char*)); nuclear@1: nuclear@1: //// Retrieve all the results: nuclear@1: JSON* child = Children.GetFirst(); nuclear@1: for (int i=0; iPrintValue(depth+1, fmt); nuclear@1: entries[i]=ret; nuclear@1: if (ret) nuclear@1: len+=OVR_strlen(ret)+2+(fmt?1:0); nuclear@1: else nuclear@1: { nuclear@1: fail = true; nuclear@1: break; nuclear@1: } nuclear@1: child = Children.GetNext(child); nuclear@1: } nuclear@1: nuclear@1: // If we didn't fail, try to malloc the output string nuclear@1: if (!fail) nuclear@1: out=(char*)OVR_ALLOC(len); nuclear@1: // If that fails, we fail. nuclear@1: if (!out) nuclear@1: fail = true; nuclear@1: nuclear@1: // Handle failure. nuclear@1: if (fail) nuclear@1: { nuclear@1: for (int i=0; iparseString(skip(buff), perror)); nuclear@1: if (!buff) nuclear@1: return 0; nuclear@1: child->Name = child->Value; nuclear@1: child->Value.Clear(); nuclear@1: nuclear@1: if (*buff!=':') nuclear@1: { nuclear@1: return AssignError(perror, "Syntax Error: Missing colon"); nuclear@1: } nuclear@1: nuclear@1: buff=skip(child->parseValue(skip(buff+1), perror)); // skip any spacing, get the value. nuclear@1: if (!buff) nuclear@1: return 0; nuclear@1: nuclear@1: while (*buff==',') nuclear@1: { nuclear@1: child = new JSON(); nuclear@1: if (!child) nuclear@1: return 0; // memory fail nuclear@1: nuclear@1: Children.PushBack(child); nuclear@1: nuclear@1: buff=skip(child->parseString(skip(buff+1), perror)); nuclear@1: if (!buff) nuclear@1: return 0; nuclear@1: nuclear@1: child->Name=child->Value; nuclear@1: child->Value.Clear(); nuclear@1: nuclear@1: if (*buff!=':') nuclear@1: { nuclear@1: return AssignError(perror, "Syntax Error: Missing colon"); nuclear@1: } // fail! nuclear@1: nuclear@1: // Skip any spacing, get the value. nuclear@1: buff=skip(child->parseValue(skip(buff+1), perror)); nuclear@1: if (!buff) nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: if (*buff=='}') nuclear@1: return buff+1; // end of array nuclear@1: nuclear@1: return AssignError(perror, "Syntax Error: Missing closing brace"); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Render an object to text. The returned string must be freed nuclear@1: char* JSON::PrintObject(int depth, bool fmt) nuclear@1: { nuclear@1: char** entries = 0, **names = 0; nuclear@1: char* out = 0; nuclear@1: char* ptr, *ret, *str; nuclear@1: SPInt len = 7, i = 0, j; nuclear@1: bool fail = false; nuclear@1: nuclear@1: // Count the number of entries. nuclear@1: int numentries = GetItemCount(); nuclear@1: nuclear@1: // Explicitly handle empty object case nuclear@1: if (numentries == 0) nuclear@1: { nuclear@1: out=(char*)OVR_ALLOC(fmt?depth+3:3); nuclear@1: if (!out) nuclear@1: return 0; nuclear@1: ptr=out; nuclear@1: *ptr++='{'; nuclear@1: nuclear@1: if (fmt) nuclear@1: { nuclear@1: *ptr++='\n'; nuclear@1: for (i=0;iName); nuclear@1: entries[i++] = ret = child->PrintValue(depth, fmt); nuclear@1: nuclear@1: if (str && ret) nuclear@1: { nuclear@1: len += OVR_strlen(ret)+OVR_strlen(str)+2+(fmt?2+depth:0); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: fail = true; nuclear@1: break; nuclear@1: } nuclear@1: nuclear@1: child = Children.GetNext(child); nuclear@1: } nuclear@1: nuclear@1: // Try to allocate the output string nuclear@1: if (!fail) nuclear@1: out=(char*)OVR_ALLOC(len); nuclear@1: if (!out) nuclear@1: fail=true; nuclear@1: nuclear@1: // Handle failure nuclear@1: if (fail) nuclear@1: { nuclear@1: for (i=0;ipNext) nuclear@1: count++; nuclear@1: return count; nuclear@1: } nuclear@1: nuclear@1: JSON* JSON::GetItemByIndex(unsigned index) nuclear@1: { nuclear@1: unsigned i = 0; nuclear@1: JSON* child = 0; nuclear@1: nuclear@1: if (!Children.IsEmpty()) nuclear@1: { nuclear@1: child = Children.GetFirst(); nuclear@1: nuclear@1: while (i < index) nuclear@1: { nuclear@1: if (Children.IsNull(child->pNext)) nuclear@1: { nuclear@1: child = 0; nuclear@1: break; nuclear@1: } nuclear@1: child = child->pNext; nuclear@1: i++; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: return child; nuclear@1: } nuclear@1: nuclear@1: // Returns the child item with the given name or NULL if not found nuclear@1: JSON* JSON::GetItemByName(const char* name) nuclear@1: { nuclear@1: JSON* child = 0; nuclear@1: nuclear@1: if (!Children.IsEmpty()) nuclear@1: { nuclear@1: child = Children.GetFirst(); nuclear@1: nuclear@1: while (OVR_strcmp(child->Name, name) != 0) nuclear@1: { nuclear@1: if (Children.IsNull(child->pNext)) nuclear@1: { nuclear@1: child = 0; nuclear@1: break; nuclear@1: } nuclear@1: child = child->pNext; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: return child; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Adds a new item to the end of the child list nuclear@1: void JSON::AddItem(const char *string, JSON *item) nuclear@1: { nuclear@1: if (!item) nuclear@1: return; nuclear@1: nuclear@1: item->Name = string; nuclear@1: Children.PushBack(item); nuclear@1: } nuclear@1: nuclear@1: /* nuclear@1: nuclear@1: // Removes and frees the items at the given index nuclear@1: void JSON::DeleteItem(unsigned int index) nuclear@1: { nuclear@1: unsigned int num_items = 0; nuclear@1: JSON* child = Children.GetFirst(); nuclear@1: while (!Children.IsNull(child) && num_items < index) nuclear@1: { nuclear@1: num_items++; nuclear@1: child = Children.GetNext(child); nuclear@1: } nuclear@1: nuclear@1: if (!Children.IsNull(child)) nuclear@1: nuclear@1: child->RemoveNode(); nuclear@1: child->Release(); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Replaces and frees the item at the give index with the new item nuclear@1: void JSON::ReplaceItem(unsigned int index, JSON* new_item) nuclear@1: { nuclear@1: unsigned int num_items = 0; nuclear@1: JSON* child = Children.GetFirst(); nuclear@1: while (!Children.IsNull(child) && num_items < index) nuclear@1: { nuclear@1: num_items++; nuclear@1: child = Children.GetNext(child); nuclear@1: } nuclear@1: nuclear@1: if (!Children.IsNull(child)) nuclear@1: { nuclear@1: child->ReplaceNodeWith(new_item); nuclear@1: child->Release(); nuclear@1: } nuclear@1: } nuclear@1: */ nuclear@1: nuclear@1: // Helper function to simplify creation of a typed object nuclear@1: JSON* JSON::createHelper(JSONItemType itemType, double dval, const char* strVal) nuclear@1: { nuclear@1: JSON *item = new JSON(itemType); nuclear@1: if (item) nuclear@1: { nuclear@1: item->dValue = dval; nuclear@1: if (strVal) nuclear@1: item->Value = strVal; nuclear@1: } nuclear@1: return item; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Adds an element to an array object type nuclear@1: void JSON::AddArrayElement(JSON *item) nuclear@1: { nuclear@1: if (!item) nuclear@1: return; nuclear@1: nuclear@1: Children.PushBack(item); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Returns the size of an array nuclear@1: int JSON::GetArraySize() nuclear@1: { nuclear@1: if (Type == JSON_Array) nuclear@1: return GetItemCount(); nuclear@1: else nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: // Returns the number value an the give array index nuclear@1: double JSON::GetArrayNumber(int index) nuclear@1: { nuclear@1: if (Type == JSON_Array) nuclear@1: { nuclear@1: JSON* number = GetItemByIndex(index); nuclear@1: return number ? number->dValue : 0.0; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return 0; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Returns the string value at the given array index nuclear@1: const char* JSON::GetArrayString(int index) nuclear@1: { nuclear@1: if (Type == JSON_Array) nuclear@1: { nuclear@1: JSON* number = GetItemByIndex(index); nuclear@1: return number ? number->Value : 0; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return 0; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Loads and parses the given JSON file pathname and returns a JSON object tree. nuclear@1: // The returned object must be Released after use. nuclear@1: JSON* JSON::Load(const char* path, const char** perror) nuclear@1: { nuclear@1: SysFile f; nuclear@1: if (!f.Open(path, File::Open_Read, File::Mode_Read)) nuclear@1: { nuclear@1: AssignError(perror, "Failed to open file"); nuclear@1: return NULL; nuclear@1: } nuclear@1: nuclear@1: int len = f.GetLength(); nuclear@1: UByte* buff = (UByte*)OVR_ALLOC(len); nuclear@1: int bytes = f.Read(buff, len); nuclear@1: f.Close(); nuclear@1: nuclear@1: if (bytes == 0 || bytes != len) nuclear@1: { nuclear@1: OVR_FREE(buff); nuclear@1: return NULL; nuclear@1: } nuclear@1: nuclear@1: JSON* json = JSON::Parse((char*)buff, perror); nuclear@1: OVR_FREE(buff); nuclear@1: return json; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // Serializes the JSON object and writes to the give file path nuclear@1: bool JSON::Save(const char* path) nuclear@1: { nuclear@1: SysFile f; nuclear@1: if (!f.Open(path, File::Open_Write | File::Open_Create | File::Open_Truncate, File::Mode_Write)) nuclear@1: return false; nuclear@1: nuclear@1: char* text = PrintValue(0, true); nuclear@1: if (text) nuclear@1: { nuclear@1: SPInt len = OVR_strlen(text); nuclear@1: OVR_ASSERT(len < (SPInt)(int)len); nuclear@1: nuclear@1: int bytes = f.Write((UByte*)text, (int)len); nuclear@1: f.Close(); nuclear@1: OVR_FREE(text); nuclear@1: return (bytes == len); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: }