diff options
Diffstat (limited to 'src/core/basetypes/MCJSONParser.cpp')
-rw-r--r-- | src/core/basetypes/MCJSONParser.cpp | 827 |
1 files changed, 827 insertions, 0 deletions
diff --git a/src/core/basetypes/MCJSONParser.cpp b/src/core/basetypes/MCJSONParser.cpp new file mode 100644 index 00000000..d9f68204 --- /dev/null +++ b/src/core/basetypes/MCJSONParser.cpp @@ -0,0 +1,827 @@ +// +// JSONParser.cpp +// mailcore2 +// +// Created by DINH Viêt Hoà on 4/17/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#include "MCJSONParser.h" + +#include <stdlib.h> + +#include "MCUtils.h" +#include "MCString.h" +#include "MCData.h" +#include "MCHashMap.h" +#include "MCArray.h" +#include "MCValue.h" +#include "MCNull.h" +#include "MCAutoreleasePool.h" +#include "MCAssert.h" + +using namespace mailcore; + +void JSONParser::init() +{ + mContent = NULL; + mResult = NULL; + mPosition = 0; +} + +JSONParser::JSONParser() +{ + init(); +} + +JSONParser::~JSONParser() +{ + MC_SAFE_RELEASE(mResult); + MC_SAFE_RELEASE(mContent); +} + +String * JSONParser::content() +{ + return mContent; +} + +void JSONParser::setContent(String * content) +{ + MC_SAFE_REPLACE_RETAIN(String, mContent, content); +} + +unsigned int JSONParser::position() +{ + return mPosition; +} + +void JSONParser::setPosition(unsigned int position) +{ + mPosition = position; +} + +Object * JSONParser::result() +{ + return mResult; +} + +bool JSONParser::parse() +{ + skipBlank(); + if (parseDictionary()) + return true; + if (parseArray()) + return true; + if (parseBoolean()) + return true; + if (parseNull()) + return true; + if (parseFloat()) + return true; + if (parseString()) + return true; + + return false; +} + +bool JSONParser::isEndOfExpression() +{ + if (mPosition == mContent->length()) + return true; + + if (mContent->characterAtIndex(mPosition) == ';') + return true; + + return false; +} + +void JSONParser::skipBlank() +{ + mPosition = skipBlank(mPosition); +} + +unsigned int JSONParser::skipBlank(unsigned int position) +{ + while (1) { + UChar ch; + + if (position >= mContent->length()) + break; + + ch = mContent->characterAtIndex(position); + if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n')) + position ++; + else + break; + } + + return position; +} + +bool JSONParser::parseDictionary() +{ + int position; + HashMap * dict; + + position = mPosition; + + if (!scanCharacter('{', position)) { + return false; + } + position ++; + position = skipBlank(position); + + dict = HashMap::hashMap(); + + if (scanCharacter('}', position)) { + position ++; + } + else { + while (1) { + JSONParser * keyParser; + JSONParser * valueParser; + String * key; + Object * value; + + if (position >= mContent->length()) + return false; + + key = NULL; + keyParser = new JSONParser(); + keyParser->autorelease(); + keyParser->setContent(mContent); + keyParser->setPosition(position); + if (!keyParser->parse()) + return false; + + if (!keyParser->result()->className()->isEqual(MCSTR("mailcore::String"))) + return false; + + key = (String *) keyParser->result(); + position = keyParser->position(); + + position = skipBlank(position); + + if (!scanCharacter(':', position)) + return false; + position ++; + + position = skipBlank(position); + + value = NULL; + valueParser = new JSONParser(); + valueParser->autorelease(); + valueParser->setContent(mContent); + valueParser->setPosition(position); + if (!valueParser->parse()) + return false; + + value = valueParser->result(); + position = valueParser->position(); + + dict->setObjectForKey(key, value); + + position = skipBlank(position); + + if (scanCharacter(',', position)) { + position ++; + position = skipBlank(position); + } + else if (scanCharacter('}', position)) { + position ++; + break; + } + else { + return false; + } + } + } + + mResult = dict->retain(); + mPosition = position; + + return true; +} + +bool JSONParser::scanCharacter(UChar ch, unsigned int position) +{ + if (position >= mContent->length()) + return false; + + if (mContent->characterAtIndex(position) != ch) + return false; + + return true; +} + +bool JSONParser::scanKeyword(String * str, unsigned int position) +{ + bool result; + AutoreleasePool * pool; + + if (position + str->length() >= mContent->length()) + return false; + + pool = new AutoreleasePool(); + + result = false; + if (mContent->substringWithRange(RangeMake(position, str->length()))->isEqual(str)) + result = true; + pool->release(); + + return result; +} + +bool JSONParser::parseArray() +{ + int position; + Array * array; + + position = mPosition; + + if (!scanCharacter('[', position)) { + return false; + } + position ++; + position = skipBlank(position); + + array = Array::array(); + + if (scanCharacter(']', position)) { + position ++; + } + else { + while (1) { + JSONParser * valueParser; + Object * value; + + if (position >= mContent->length()) + return false; + + value = NULL; + valueParser = new JSONParser(); + valueParser->setContent(mContent); + valueParser->setPosition(position); + valueParser->autorelease(); + if (!valueParser->parse()) + return false; + + value = valueParser->result(); + + position = valueParser->position(); + array->addObject(value); + + position = skipBlank(position); + + if (scanCharacter(',', position)) { + position ++; + position = skipBlank(position); + } + else if (scanCharacter(']', position)) { + position ++; + break; + } + else { + return false; + } + } + } + + mResult = array->retain(); + mPosition = position; + + return true; +} + +static inline int hexToInt(UChar inCharacter) +{ + if (inCharacter >= '0' && inCharacter <= '9') { + return inCharacter - '0'; + } + else if (inCharacter >= 'a' && inCharacter <= 'f') { + return inCharacter - 'a' + 10; + } + else if (inCharacter >= 'A' && inCharacter <= 'F') { + return inCharacter - 'A' + 10; + } + else { + return -1; + } +} + +bool JSONParser::parseString() +{ + unsigned int position; + String * str; + bool doubleQuote = false; + bool startString; + bool endOfString; + + position = mPosition; + + startString = false; + if (scanCharacter('\"', position)) { + doubleQuote = true; + startString = true; + } + else if (scanCharacter('\'', position)) { + startString = true; + } + + if (!startString) + return false; + + position ++; + + str = String::string(); + + endOfString = false; + while (1) { + UChar ch; + + if (position >= mContent->length()) + return false; + + ch = mContent->characterAtIndex(position); + switch (ch) { + case '\\': + { + position ++; + + if (position >= mContent->length()) + return false; + + ch = mContent->characterAtIndex(position); + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + UChar char_value; + unsigned int count; + + count = 0; + char_value = 0; + while ((ch >= '0') && (ch <= '7')) { + int value; + + value = hexToInt(ch); + if (value == -1) + break; + + char_value = char_value * 8 + value; + count ++; + position ++; + if (count >= 3) + break; + if (position >= mContent->length()) + break; + ch = mContent->characterAtIndex(position); + } + UChar characters[2]; + characters[0] = char_value; + characters[1] = 0; + str->appendCharactersLength(characters, 1); + break; + } + case 'b': + str->appendString(MCSTR("\b")); + position ++; + break; + case 't': + str->appendString(MCSTR("\t")); + position ++; + break; + case 'n': + str->appendString(MCSTR("\n")); + position ++; + break; + case 'r': + str->appendString(MCSTR("\r")); + position ++; + break; + case 'f': + str->appendString(MCSTR("\f")); + position ++; + break; + case 'v': + str->appendString(MCSTR("\v")); + position ++; + break; + case '\"': + str->appendString(MCSTR("\"")); + position ++; + break; + case '\'': + str->appendString(MCSTR("\'")); + position ++; + break; + case '\\': + str->appendString(MCSTR("\\")); + position ++; + break; + case '/': + str->appendString(MCSTR("/")); + position ++; + break; + case 'u': { + unsigned int i; + UChar char_value; + + position ++; + char_value = 0; + for(i = 0 ; i < 4 ; i ++) { + int value; + + if (position >= mContent->length()) + return false; + ch = mContent->characterAtIndex(position); + value = hexToInt(ch); + if (value == -1) + break; + + char_value = char_value * 16 + value; + position ++; + } + + UChar characters[2]; + characters[0] = char_value; + characters[1] = 0; + str->appendCharactersLength(characters, 1); + + break; + } + case 'x': { + unsigned int i; + UChar char_value; + + position ++; + char_value = 0; + for(i = 0 ; i < 2 ; i ++) { + int value; + + if (position >= mContent->length()) + return false; + ch = mContent->characterAtIndex(position); + value = hexToInt(ch); + if (value == -1) + break; + + char_value = char_value * 16 + value; + position ++; + } + + UChar characters[2]; + characters[0] = char_value; + characters[1] = 0; + str->appendCharactersLength(characters, 1); + + break; + } + default: { + UChar characters[3]; + characters[0] = '\\'; + characters[1] = ch; + characters[2] = 0; + str->appendCharactersLength(characters, 2); + + position ++; + break; + } + } + break; + } + + case '\'': + position ++; + if (!doubleQuote) + endOfString = true; + else + str->appendString(MCSTR("\'")); + break; + case '\"': + position ++; + if (doubleQuote) + endOfString = true; + else + str->appendString(MCSTR("\"")); + break; + default: { + position ++; + UChar characters[2]; + characters[0] = ch; + characters[1] = 0; + str->appendCharactersLength(characters, 1); + break; + } + } + + if (endOfString) + break; + } + + mResult = str->retain(); + mPosition = position; + + return true; +} + +bool JSONParser::parseBoolean() +{ + if (scanKeyword(MCSTR("true"), mPosition)) { + mPosition += MCSTR("true")->length(); + mResult = Value::valueWithBoolValue(true)->retain();; + return true; + } + + if (scanKeyword(MCSTR("false"), mPosition)) { + mPosition += MCSTR("false")->length(); + mResult = Value::valueWithBoolValue(false)->retain();; + return true; + } + + return false; +} + +bool JSONParser::parseNull() +{ + if (scanKeyword(MCSTR("null"), mPosition)) { + mPosition += MCSTR("null")->length(); + mResult = Null::null()->retain(); + return true; + } + + return false; +} + +bool JSONParser::parseFloat() +{ + const char * str; + char * endptr; + double value; + char * endptrInt; + long long valueInt; + + AutoreleasePool * pool; + + pool = new AutoreleasePool(); + if (mContent->length() > mPosition + 50) { + str = mContent->substringWithRange(RangeMake(mPosition, 50))->UTF8Characters(); + } + else { + str = mContent->substringFromIndex(mPosition)->UTF8Characters(); + } + value = strtod(str, &endptr); + if (endptr == str) { + pool->release(); + return false; + } + valueInt = strtoll(str, &endptrInt, 0); + if (endptr == endptrInt) { + mPosition += endptrInt - str; + mResult = Value::valueWithLongLongValue(valueInt)->retain(); + } + else { + mPosition += endptr - str; + mResult = Value::valueWithDoubleValue(value); + } + pool->release(); + + return true; +} + +Object * JSONParser::objectFromData(Data * data) +{ + String * str = NULL; + Object * result; + + str = String::stringWithData(data, "utf-8"); + if (str == NULL) + return NULL; + + result = objectFromString(str); + + return result; +} + +Object * JSONParser::objectFromString(String * str) +{ + Object * result; + JSONParser * parser; + + parser = new JSONParser(); + parser->setContent(str); + parser->parse(); + result = parser->result(); + result->retain()->autorelease(); + parser->release(); + + return result; +} + +Data * JSONParser::dataFromObject(Object * object) +{ + return stringFromObject(object)->dataUsingEncoding("utf-8"); +} + +String * JSONParser::stringFromObject(Object * object) +{ + String * result; + + result = String::string(); + appendStringFromObject(object, result); + + return result; +} + + +String * JSONParser::JSStringFromString(String * str) +{ + unsigned int i; + String * result; + + result = String::string(); + for(i = 0 ; i < str->length() ; i ++) { + UChar ch; + + ch = str->characterAtIndex(i); + if ((ch >= '0') && (ch <= '9')) { + UChar characters[2]; + characters[0] = ch; + characters[1] = 0; + result->appendCharactersLength(characters, 1); + } + else if ((ch >= 'a') && (ch <= 'z')) { + UChar characters[2]; + characters[0] = ch; + characters[1] = 0; + result->appendCharactersLength(characters, 1); + } + else if ((ch >= 'A') && (ch <= 'Z')) { + UChar characters[2]; + characters[0] = ch; + characters[1] = 0; + result->appendCharactersLength(characters, 1); + } + else { + switch (ch) { + case ' ': + case '.': + case ',': + case '<': + case '>': + case '(': + case ')': + case '!': + case '@': + case '#': + case '$': + case '%': + case '^': + case '&': + case '*': + case '-': + case '_': + case '+': + case '=': + case '?': + case '{': + case '}': + case '[': + case ']': + case ':': + case ';': + case '|': + case '~': + case '\'': { + UChar characters[2]; + characters[0] = ch; + characters[1] = 0; + result->appendCharactersLength(characters, 1); + break; + } + case '"': + case '/': + case '\\': { + UChar characters[3]; + characters[0] = '\\'; + characters[1] = ch; + characters[2] = 0; + result->appendCharactersLength(characters, 2); + break; + } + default: { + result->appendUTF8Format("\\u%04x", (unsigned int) ch); + break; + } + } + } + } + + return result; +} + +void JSONParser::appendStringFromObject(Object * object, String * string) +{ + if (object->className()->isEqual(MCSTR("mailcore::String"))) { + string->appendString(MCSTR("\"")); + string->appendString(JSStringFromString((String *) object)); + string->appendString(MCSTR("\"")); + } + else if (object->className()->isEqual(MCSTR("mailcore::Value"))) { + Value * value = (Value *) object; + switch (value->type()) { + case ValueTypeBool: + string->appendString(value->boolValue() ? MCSTR("true") : MCSTR("false")); + break; + case ValueTypeChar: + string->appendUTF8Format("%i", (int) value->charValue()); + break; + case ValueTypeUnsignedChar: + string->appendUTF8Format("%i", (int) value->unsignedCharValue()); + break; + case ValueTypeShort: + string->appendUTF8Format("%i", (int) value->shortValue()); + break; + case ValueTypeUnsignedShort: + string->appendUTF8Format("%i", (int) value->unsignedShortValue()); + break; + case ValueTypeInt: + string->appendUTF8Format("%i", (int) value->intValue()); + break; + case ValueTypeUnsignedInt: + string->appendUTF8Format("%u", (int) value->unsignedIntValue()); + break; + case ValueTypeLong: + string->appendUTF8Format("%ld", value->longValue()); + break; + case ValueTypeUnsignedLong: + string->appendUTF8Format("%lu", value->unsignedLongValue()); + break; + case ValueTypeLongLong: + string->appendUTF8Format("%lld", value->longLongValue()); + break; + case ValueTypeUnsignedLongLong: + string->appendUTF8Format("%llu", value->unsignedLongLongValue()); + break; + case ValueTypeFloat: + string->appendUTF8Format("%g", value->floatValue()); + break; + case ValueTypeDouble: + string->appendUTF8Format("%lg", value->doubleValue()); + break; + default: + MCAssert(0); + break; + } + } + else if (object->className()->isEqual(MCSTR("mailcore::Null"))) { + string->appendString(MCSTR("null")); + } + else if (object->className()->isEqual(MCSTR("mailcore::Array"))) { + Array * array; + bool first; + + first = true; + array = (Array *) object; + string->appendString(MCSTR("[")); + for(unsigned int i = 0 ; i < array->count() ; i ++) { + Object * arrayElement = array->objectAtIndex(i); + if (first) { + first = false; + } + else { + string->appendString(MCSTR(",")); + } + appendStringFromObject(arrayElement, string); + } + string->appendString(MCSTR("]")); + } + else if (object->className()->isEqual(MCSTR("mailcore::HashMap"))) { + HashMap * dict; + bool first; + + first = true; + dict = (HashMap *) object; + string->appendString(MCSTR("{")); + Array * keys = dict->allKeys(); + for(unsigned int i = 0 ; i < keys->count() ; i ++) { + String * key = (String *) keys->objectAtIndex(i); + + if (first) { + first = false; + } + else { + string->appendString(MCSTR(",")); + } + appendStringFromObject(key, string); + string->appendString(MCSTR(":")); + appendStringFromObject(dict->objectForKey(key), string); + } + string->appendString(MCSTR("}")); + } +} |