Api wrapper to communicate with EVRYTHNG's Engine.

Dependencies:   EthernetInterface mbed-rtos

Dependents:   EvrythngApiExample

JsonParser.cpp

Committer:
vladounet
Date:
2012-08-30
Revision:
0:d38d192c2f5f

File content as of revision 0:d38d192c2f5f:

/*
 * (c) Copyright 2012 EVRYTHNG Ltd London / Zurich
 * www.evrythng.com
 *
 * --- DISCLAIMER ---
 *
 * EVRYTHNG provides this source code "as is" and without warranty of any kind,
 * and hereby disclaims all express or implied warranties, including without
 * limitation warranties of merchantability, fitness for a particular purpose,
 * performance, accuracy, reliability, and non-infringement.
 *
 * Author: Michel Yerly
 *
 */
#include "JsonParser.h"
#include "string.h"
#include "stdlib.h"
#include "errno.h"
#include "util.h"

//#define DEBUG_JSONTOKENIZER
//#define DEBUG_JSONPARSER
//#define DEBUG_JSONGET

using namespace std;

bool isDigit(char c)
{
    return c >= '0' && c <= '9';
}

JsonParser::JsonParser()
{
    this->pDocument = NULL;
}

JsonParser::~JsonParser()
{
    if (this->pDocument) {
        delete this->pDocument;
    }
}

int JsonParser::parse(const char* json)
{
    this->json = json;
    this->json_len = strlen(json);

    if (this->pDocument) {
        delete this->pDocument;
        this->pDocument = NULL;
    }

    ctPos = 0;
    ctLen = 0;
    if (goToNextToken() != 0) return -1;
    bool ok = true;
    if (json[ctPos] == '{') {
        if (parseObject(&pDocument) != 0) {
            ok = false;
        }
    } else {
        if (parseArray(&pDocument) != 0) ok = false;
    }
    if (ct != TOKEN_EOS) {
        ok = false;
    }

    if (!ok) {
        delete pDocument;
        pDocument = NULL;
        return -1;
    }

    return 0;
}

JsonValue* JsonParser::getDocument()
{
    return pDocument;
}

int JsonParser::goToNextToken()
{

#ifdef DEBUG_JSONTOKENIZER

    dbg.printf("Token: ");

#endif

    ctPos += ctLen;

    // Skip whitespaces
    while (ctPos < json_len &&
            (json[ctPos] == ' ' || json[ctPos] == '\t' ||
             json[ctPos] == '\r' || json[ctPos] == '\n'))
        ++ctPos;

    if (ctPos < json_len) {
        if (json[ctPos] == '"') {
            ct = TOKEN_STRING;
            int i = ctPos+1;
            while (i < json_len && json[i] != '"') {
                if (json[i] == '\\') {
                    if (i+1 < json_len) {
                        switch (json[i+1]) {
                            case '\\':
                            case '"':
                            case '/':
                            case 'b':
                            case 'f':
                            case 'n':
                            case 'r':
                            case 't':
                                ++i;
                                break;
                            case 'u':
                                i+= 5;
                                break;
                            default:
                                return -1;
                        }
                    } else {
                        return -1;
                    }
                }
                ++i;
            }
            if (i >= json_len || json[i] != '"') return -1;
            ctLen = i - ctPos + 1;
        } else if (isDigit(json[ctPos]) || json[ctPos] == '-') {
            ct = TOKEN_NUMBER;
            char* e;
            errno = 0;
            ctNumberVal = strtod(json+ctPos, &e);
            if (errno || e - json <= 0) return -1;
            ctLen = (e - json) - ctPos;

        } else if (strncmp(json+ctPos,"true",4) == 0) {
            ct = TOKEN_TRUE;
            ctLen = 4;
        } else if (strncmp(json+ctPos,"false",5) == 0) {
            ct = TOKEN_FALSE;
            ctLen = 5;
        } else if (strncmp(json+ctPos,"null",4) == 0) {
            ct = TOKEN_NULL;
            ctLen = 4;
        } else {
            ct = TOKEN_DELIMITER;
            ctLen = 1;
        }
    } else {
        ct = TOKEN_EOS;
        ctLen = 0;
    }

#ifdef DEBUG_JSONTOKENIZER

    switch (ct) {
        case TOKEN_DELIMITER:
            dbg.printf("Delimtier - ");
            break;
        case TOKEN_EOS:
            dbg.printf("End of stream");
            break;
        case TOKEN_NUMBER:
            dbg.printf("Number %g - ", ctNumberVal);
            break;
        case TOKEN_STRING:
            dbg.printf("String - ");
            break;
        case TOKEN_FALSE:
            dbg.printf("False - ");
            break;
        case TOKEN_TRUE:
            dbg.printf("True - ");
            break;
        case TOKEN_NULL:
            dbg.printf("Null - ");
            break;
    }
    if (ct != TOKEN_EOS) {
        for (int i = 0; i < ctLen; ++i)
            dbg.printf("%c", json[ctPos+i]);
    }
    dbg.printf(" (%d,%d)\r\n", ctPos, ctLen);

#endif

    return 0;
}

int JsonParser::parseObject(JsonValue** object)
{

#ifdef DEBUG_JSONPARSER
    dbg.printf("Enter parseObject\r\n");
#endif

    *object = JsonValue::createMap();
    map<string,JsonValue*>* m = (*object)->value.map;

    if (ct != TOKEN_DELIMITER || json[ctPos] != '{') return -1;
    if (goToNextToken() != 0) return -1;

    if (ct == TOKEN_STRING) {

        string key;
        key.assign(json+ctPos+1, ctLen-2);

        if (goToNextToken() != 0) return -1;

        if (ct != TOKEN_DELIMITER || json[ctPos] != ':') return -1;
        if (goToNextToken() != 0) return -1;

        JsonValue* pValue;
        if (parseValue(&pValue) != 0) {
            delete pValue;
            return -1;
        }
        (*m)[key] = pValue;

        while (ct == TOKEN_DELIMITER && json[ctPos] == ',') {
            if (goToNextToken() != 0) return -1;

            if (ct != TOKEN_STRING) return -1;
            key.assign(json+ctPos+1, ctLen-2);
            if (goToNextToken() != 0) return -1;

            if (ct != TOKEN_DELIMITER || json[ctPos] != ':') return -1;
            if (goToNextToken() != 0) return -1;

            if (parseValue(&pValue) != 0) {
                delete pValue;
                return -1;
            }
            (*m)[key] = pValue;
        }
    }

    if (ct != TOKEN_DELIMITER || json[ctPos] != '}') return -1;
    if (goToNextToken() != 0) return -1;

#ifdef DEBUG_JSONPARSER
    dbg.printf("Exit parseObject\r\n");
#endif
    return 0;
}

int JsonParser::parseValue(JsonValue** value)
{

#ifdef DEBUG_JSONPARSER
    dbg.printf("Enter parseValue\r\n");
#endif

    switch (ct) {
        case TOKEN_STRING:
            *value = JsonValue::createString(json+ctPos+1,ctLen-2);
            if (goToNextToken() != 0) return -1;
            break;
        case TOKEN_NUMBER:
            *value = JsonValue::createDouble(ctNumberVal);
            if (goToNextToken() != 0) return -1;
            break;
        case TOKEN_NULL:
            *value = JsonValue::createNull();
            if (goToNextToken() != 0) return -1;
            break;
        case TOKEN_FALSE:
            *value = JsonValue::createBoolean(false);
            if (goToNextToken() != 0) return -1;
            break;
        case TOKEN_TRUE:
            *value = JsonValue::createBoolean(true);
            if (goToNextToken() != 0) return -1;
            break;
        case TOKEN_DELIMITER:
            if (json[ctPos] == '{') {
                if (parseObject(value) != 0) return -1;
            } else if (json[ctPos] == '[') {
                if (parseArray(value) != 0) return -1;
            }
            break;
        default:
            *value = JsonValue::createNull();
            return -1;
    }

#ifdef DEBUG_JSONPARSER
    dbg.printf("Exit parseValue\r\n");
#endif

    return 0;
}

int JsonParser::parseArray(JsonValue** array)
{

#ifdef DEBUG_JSONPARSER
    dbg.printf("Enter parseArray\r\n");
#endif

    *array = JsonValue::createVector();
    vector<JsonValue*>* vec = (*array)->value.vec;

    if (ct != TOKEN_DELIMITER || json[ctPos] != '[') return -1;
    if (goToNextToken() != 0) return -1;

    if (ct != TOKEN_DELIMITER || json[ctPos] != ']') {

        JsonValue* pValue;
        if (parseValue(&pValue) != 0) {
            delete pValue;
            return -1;
        };
        vec->push_back(pValue);

        while (ct == TOKEN_DELIMITER && json[ctPos] == ',') {
            if (goToNextToken() != 0) return -1;

            if (parseValue(&pValue) != 0) {
                delete pValue;
                return -1;
            };
            vec->push_back(pValue);
        }
    }

    if (ct != TOKEN_DELIMITER || json[ctPos] != ']') return -1;
    if (goToNextToken() != 0) return -1;

#ifdef DEBUG_JSONPARSER
    dbg.printf("Exit parseArray\r\n");
#endif

    return 0;
}

int inst = 0;

JsonValue::JsonValue(const char* buffer, int len)
{
    type = VT_CHAR_PTR;
    value.s = new char[len+1];
    strncpy(value.s, buffer, len);
    value.s[len] = '\0';
}

JsonValue::JsonValue(double d)
{
    type = VT_DOUBLE;
    value.d = d;
}

JsonValue::JsonValue(bool b)
{
    type = b ? VT_CST_TRUE : VT_CST_FALSE;

}

JsonValue::JsonValue()
{
}

JsonValue::~JsonValue()
{
    switch (type) {
        case VT_CHAR_PTR:
            delete[] value.s;
            break;
        case VT_MAP_PTR:
            for (map<string,JsonValue*>::iterator itr = value.map->begin(); itr != value.map->end(); itr++)
                delete ((*itr).second);
            delete value.map;
            break;
        case VT_VEC_PTR:
            for (vector<JsonValue*>::iterator itr = value.vec->begin(); itr != value.vec->end(); itr++)
                delete (*itr);
            delete value.vec;
            break;
        default:
            break;
    }
}

JsonValue* JsonValue::createString(const char* buffer, int len)
{
    return new JsonValue(buffer, len);
}

JsonValue* JsonValue::createDouble(double d)
{
    return new JsonValue(d);
}

JsonValue* JsonValue::createBoolean(bool b)
{
    return new JsonValue(b);
}

JsonValue* JsonValue::createMap()
{
    JsonValue* ret = new JsonValue();
    ret->type = VT_MAP_PTR;
    ret->value.map = new map<string,JsonValue*>();
    return ret;
}

JsonValue* JsonValue::createVector()
{
    JsonValue* ret = new JsonValue();
    ret->type = VT_VEC_PTR;
    ret->value.vec = new vector<JsonValue*>();
    return ret;
}

JsonValue* JsonValue::createNull()
{
    JsonValue* ret = new JsonValue();
    ret->type = VT_CST_NULL;
    return ret;
}

void JsonValue::print()
{
    bool c = false;
    switch (type) {
        case VT_CHAR_PTR:
            dbg.printf("\"%s\"", value.s);
            break;
        case VT_MAP_PTR:
            dbg.printf("{");
            for (map<string,JsonValue*>::iterator itr = value.map->begin(); itr != value.map->end(); itr++) {
                if (c) dbg.printf(",");
                else c = true;
                dbg.printf("\"%s\":",(*itr).first.c_str());
                (*itr).second->print();
            }
            dbg.printf("}");
            break;
        case VT_VEC_PTR:
            dbg.printf("[");
            for (vector<JsonValue*>::iterator itr = value.vec->begin(); itr != value.vec->end(); itr++) {
                if (c) dbg.printf(",");
                else c = true;
                (*itr)->print();
            }
            dbg.printf("]");
            break;
        case VT_DOUBLE:
            dbg.printf("%g", value.d);
            break;
        case VT_CST_TRUE:
            dbg.printf("true");
            break;
        case VT_CST_FALSE:
            dbg.printf("false");
            break;
        case VT_CST_NULL:
            dbg.printf("null");
            break;
        default:
            break;
    }
}

JsonValue* JsonValue::get(const char* path)
{
    JsonValue* pValue = this;
    int pos = 0;
    while (path != NULL && path[pos] != '\0') {

#ifdef DEBUG_JSONGET
        dbg.printf("::get ");
        pValue->print();
        dbg.printf("\r\n");
#endif

        const char* start = path+pos;
        const char* pSl = strchr(start, '/');
        int len = pSl == NULL ? strlen(start) : pSl - start;
        if (len <= 0) return NULL;
        if (pValue->type == VT_VEC_PTR) {
            int v = atoi(start);
            if (v == 0 && path[pos] != '0') {
                return NULL;
            }
            if (v < 0 || v >= pValue->value.vec->size()) {
                return NULL;
            }
            pValue = (*pValue->value.vec)[v];
        } else if (pValue->type == VT_MAP_PTR) {
            char* pKey = new char[len+1];
            strncpy(pKey, start, len);
            pKey[len] = '\0';
            pValue = (*pValue->value.map)[pKey];
            delete[] pKey;
            if (pValue == NULL) {
                return NULL;
            }
        } else {
            return NULL;
        }
        pos += len;
        if (pSl) ++pos;
    }

#ifdef DEBUG_JSONGET
        dbg.printf("::get ");
        pValue->print();
        dbg.printf("\r\n");
#endif

    return pValue;
}

const double* JsonValue::getDouble(const char* path)
{
    JsonValue* pV = get(path);
    if (pV != NULL && pV->type == VT_DOUBLE) {
        return &(pV->value.d);
    } else {
        return NULL;
    }
}

const char* JsonValue::getString(const char* path)
{
    JsonValue* pV = get(path);
    if (pV != NULL && pV->type == VT_CHAR_PTR) {
        return pV->value.s;
    } else {
        return NULL;
    }
}
const std::vector<JsonValue*>* JsonValue::getVector(const char* path)
{
    JsonValue* pV = get(path);
    if (pV != NULL && pV->type == VT_VEC_PTR) {
        return pV->value.vec;
    } else {
        return NULL;
    }
}
const std::map<std::string,JsonValue*>* JsonValue::getMap(const char* path)
{
    JsonValue* pV = get(path);
    if (pV != NULL && pV->type == VT_MAP_PTR) {
        return pV->value.map;
    } else {
        return NULL;
    }
}

static const bool FALSE = false;
static const bool TRUE = true;

const bool* JsonValue::getBoolean(const char* path)
{
    JsonValue* pV = get(path);
    if (pV != NULL && (pV->type == VT_CST_TRUE || pV->type == VT_CST_FALSE)) {
        return (pV->type == VT_CST_TRUE) ? &TRUE : &FALSE;
    } else {
        return NULL;
    }
}
bool JsonValue::isNull(const char* path)
{
    JsonValue* pV = get(path);
    return (pV != NULL) && (pV->type == VT_CST_NULL);
}