/* $Id$ */ /* * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "util.hpp" #define THIS_FILE "json.cpp" using namespace pj; using namespace std; /* Json node operations */ static bool jsonNode_hasUnread(const ContainerNode*); static string jsonNode_unreadName(const ContainerNode*n) PJSUA2_THROW(Error); static float jsonNode_readNumber(const ContainerNode*, const string&) PJSUA2_THROW(Error); static bool jsonNode_readBool(const ContainerNode*, const string&) PJSUA2_THROW(Error); static string jsonNode_readString(const ContainerNode*, const string&) PJSUA2_THROW(Error); static StringVector jsonNode_readStringVector(const ContainerNode*, const string&) PJSUA2_THROW(Error); static ContainerNode jsonNode_readContainer(const ContainerNode*, const string &) PJSUA2_THROW(Error); static ContainerNode jsonNode_readArray(const ContainerNode*, const string &) PJSUA2_THROW(Error); static void jsonNode_writeNumber(ContainerNode*, const string &name, float num) PJSUA2_THROW(Error); static void jsonNode_writeBool(ContainerNode*, const string &name, bool value) PJSUA2_THROW(Error); static void jsonNode_writeString(ContainerNode*, const string &name, const string &value) PJSUA2_THROW(Error); static void jsonNode_writeStringVector(ContainerNode*, const string &name, const StringVector &value) PJSUA2_THROW(Error); static ContainerNode jsonNode_writeNewContainer(ContainerNode*, const string &name) PJSUA2_THROW(Error); static ContainerNode jsonNode_writeNewArray(ContainerNode*, const string &name) PJSUA2_THROW(Error); static container_node_op json_op = { &jsonNode_hasUnread, &jsonNode_unreadName, &jsonNode_readNumber, &jsonNode_readBool, &jsonNode_readString, &jsonNode_readStringVector, &jsonNode_readContainer, &jsonNode_readArray, &jsonNode_writeNumber, &jsonNode_writeBool, &jsonNode_writeString, &jsonNode_writeStringVector, &jsonNode_writeNewContainer, &jsonNode_writeNewArray }; /////////////////////////////////////////////////////////////////////////////// JsonDocument::JsonDocument() : root(NULL) { pj_caching_pool_init(&cp, NULL, 0); pool = pj_pool_create(&cp.factory, "jsondoc", 512, 512, NULL); if (!pool) PJSUA2_RAISE_ERROR(PJ_ENOMEM); } JsonDocument::~JsonDocument() { if (pool) pj_pool_release(pool); pj_caching_pool_destroy(&cp); } void JsonDocument::initRoot() const { rootNode.op = &json_op; rootNode.data.doc = (void*)this; rootNode.data.data1 = (void*)root; rootNode.data.data2 = root->value.children.next; } void JsonDocument::loadFile(const string &filename) PJSUA2_THROW(Error) { if (root) PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()", "Document already initialized"); if (!pj_file_exists(filename.c_str())) PJSUA2_RAISE_ERROR(PJ_ENOTFOUND); pj_ssize_t size = (pj_ssize_t)pj_file_size(filename.c_str()); pj_status_t status; char *buffer = (char*)pj_pool_alloc(pool, size+1); pj_oshandle_t fd = 0; unsigned parse_size; char err_msg[120]; pj_json_err_info err_info; err_msg[0] = '\0'; status = pj_file_open(pool, filename.c_str(), PJ_O_RDONLY, &fd); if (status != PJ_SUCCESS) goto on_error; status = pj_file_read(fd, buffer, &size); if (status != PJ_SUCCESS) goto on_error; pj_file_close(fd); fd = NULL; if (size <= 0) { status = PJ_EEOF; goto on_error; } parse_size = (unsigned)size; root = pj_json_parse(pool, buffer, &parse_size, &err_info); if (root == NULL) { pj_ansi_snprintf(err_msg, sizeof(err_msg), "JSON parsing failed: syntax error in file '%s' at " "line %d column %d", filename.c_str(), err_info.line, err_info.col); PJ_LOG(1,(THIS_FILE, err_msg)); status = PJLIB_UTIL_EINJSON; goto on_error; } initRoot(); return; on_error: if (fd) pj_file_close(fd); if (err_msg[0]) PJSUA2_RAISE_ERROR3(status, "loadFile()", err_msg); else PJSUA2_RAISE_ERROR(status); } void JsonDocument::loadString(const string &input) PJSUA2_THROW(Error) { if (root) PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()", "Document already initialized"); unsigned size = (unsigned)input.size(); char *buffer = (char*)pj_pool_alloc(pool, size+1); unsigned parse_size = (unsigned)size; pj_json_err_info err_info; pj_memcpy(buffer, input.c_str(), size); root = pj_json_parse(pool, buffer, &parse_size, &err_info); if (root == NULL) { char err_msg[80]; pj_ansi_snprintf(err_msg, sizeof(err_msg), "JSON parsing failed at line %d column %d", err_info.line, err_info.col); PJ_LOG(1,(THIS_FILE, err_msg)); PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "loadString()", err_msg); } initRoot(); } struct save_file_data { pj_oshandle_t fd; }; static pj_status_t json_file_writer(const char *s, unsigned size, void *user_data) { save_file_data *sd = (save_file_data*)user_data; pj_ssize_t ssize = (pj_ssize_t)size; return pj_file_write(sd->fd, s, &ssize); } void JsonDocument::saveFile(const string &filename) PJSUA2_THROW(Error) { struct save_file_data sd; pj_status_t status; /* Make sure root container has been created */ getRootContainer(); status = pj_file_open(pool, filename.c_str(), PJ_O_WRONLY, &sd.fd); if (status != PJ_SUCCESS) PJSUA2_RAISE_ERROR(status); status = pj_json_writef(root, &json_file_writer, &sd.fd); pj_file_close(sd.fd); if (status != PJ_SUCCESS) PJSUA2_RAISE_ERROR(status); } struct save_string_data { string output; }; static pj_status_t json_string_writer(const char *s, unsigned size, void *user_data) { save_string_data *sd = (save_string_data*)user_data; sd->output.append(s, size); return PJ_SUCCESS; } string JsonDocument::saveString() PJSUA2_THROW(Error) { struct save_string_data sd; pj_status_t status; /* Make sure root container has been created */ getRootContainer(); status = pj_json_writef(root, &json_string_writer, &sd); if (status != PJ_SUCCESS) PJSUA2_RAISE_ERROR(status); return sd.output; } ContainerNode & JsonDocument::getRootContainer() const { if (!root) { root = allocElement(); pj_json_elem_obj(root, NULL); initRoot(); } return rootNode; } pj_json_elem* JsonDocument::allocElement() const { return (pj_json_elem*)pj_pool_alloc(pool, sizeof(pj_json_elem)); } pj_pool_t *JsonDocument::getPool() { return pool; } /////////////////////////////////////////////////////////////////////////////// struct json_node_data { JsonDocument *doc; pj_json_elem *jnode; pj_json_elem *childPtr; }; static bool jsonNode_hasUnread(const ContainerNode *node) { json_node_data *jdat = (json_node_data*)&node->data; return jdat->childPtr != (pj_json_elem*)&jdat->jnode->value.children; } static void json_verify(struct json_node_data *jdat, const char *op, const string &name, pj_json_val_type type) { if (jdat->childPtr == (pj_json_elem*)&jdat->jnode->value.children) PJSUA2_RAISE_ERROR3(PJ_EEOF, op, "No unread element"); /* If name is specified, then check if the names match, except * when the node name itself is empty and the parent node is * an array, then ignore the checking (JSON doesn't allow array * elements to have name). */ if (jdat->jnode->type != PJ_JSON_VAL_ARRAY && name.size() && jdat->childPtr->name.slen && name.compare(0, name.size(), jdat->childPtr->name.ptr, jdat->childPtr->name.slen)) { char err_msg[80]; pj_ansi_snprintf(err_msg, sizeof(err_msg), "Name mismatch: expecting '%s' got '%.*s'", name.c_str(), (int)jdat->childPtr->name.slen, jdat->childPtr->name.ptr); PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg); } if (type != PJ_JSON_VAL_NULL && jdat->childPtr->type != type) { char err_msg[80]; pj_ansi_snprintf(err_msg, sizeof(err_msg), "Type mismatch: expecting %d got %d", type, jdat->childPtr->type); PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, op, err_msg); } } static string jsonNode_unreadName(const ContainerNode *node) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "unreadName()", "", PJ_JSON_VAL_NULL); return pj2Str(jdat->childPtr->name); } static float jsonNode_readNumber(const ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "readNumber()", name, PJ_JSON_VAL_NUMBER); jdat->childPtr = jdat->childPtr->next; return jdat->childPtr->prev->value.num; } static bool jsonNode_readBool(const ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "readBool()", name, PJ_JSON_VAL_BOOL); jdat->childPtr = jdat->childPtr->next; return PJ2BOOL(jdat->childPtr->prev->value.is_true); } static string jsonNode_readString(const ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "readString()", name, PJ_JSON_VAL_STRING); jdat->childPtr = jdat->childPtr->next; return pj2Str(jdat->childPtr->prev->value.str); } static StringVector jsonNode_readStringVector(const ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "readStringVector()", name, PJ_JSON_VAL_ARRAY); StringVector result; pj_json_elem *child = jdat->childPtr->value.children.next; while (child != (pj_json_elem*)&jdat->childPtr->value.children) { if (child->type != PJ_JSON_VAL_STRING) { char err_msg[80]; pj_ansi_snprintf(err_msg, sizeof(err_msg), "Elements not string but type %d", child->type); PJSUA2_RAISE_ERROR3(PJLIB_UTIL_EINJSON, "readStringVector()", err_msg); } result.push_back(pj2Str(child->value.str)); child = child->next; } jdat->childPtr = jdat->childPtr->next; return result; } static ContainerNode jsonNode_readContainer(const ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "readContainer()", name, PJ_JSON_VAL_OBJ); ContainerNode json_node; json_node.op = &json_op; json_node.data.doc = (void*)jdat->doc; json_node.data.data1 = (void*)jdat->childPtr; json_node.data.data2 = (void*)jdat->childPtr->value.children.next; jdat->childPtr = jdat->childPtr->next; return json_node; } static ContainerNode jsonNode_readArray(const ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; json_verify(jdat, "readArray()", name, PJ_JSON_VAL_ARRAY); ContainerNode json_node; json_node.op = &json_op; json_node.data.doc = (void*)jdat->doc; json_node.data.data1 = (void*)jdat->childPtr; json_node.data.data2 = (void*)jdat->childPtr->value.children.next; jdat->childPtr = jdat->childPtr->next; return json_node; } static pj_str_t alloc_name(JsonDocument *doc, const string &name) { pj_str_t new_name; pj_strdup2(doc->getPool(), &new_name, name.c_str()); return new_name; } static void jsonNode_writeNumber(ContainerNode *node, const string &name, float num) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_json_elem_number(el, &nm, num); pj_json_elem_add(jdat->jnode, el); } static void jsonNode_writeBool(ContainerNode *node, const string &name, bool value) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_json_elem_bool(el, &nm, value); pj_json_elem_add(jdat->jnode, el); } static void jsonNode_writeString(ContainerNode *node, const string &name, const string &value) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_str_t new_val; pj_strdup2(jdat->doc->getPool(), &new_val, value.c_str()); pj_json_elem_string(el, &nm, &new_val); pj_json_elem_add(jdat->jnode, el); } static void jsonNode_writeStringVector(ContainerNode *node, const string &name, const StringVector &value) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_json_elem_array(el, &nm); for (unsigned i=0; idoc->getPool(), &new_val, value[i].c_str()); pj_json_elem *child = jdat->doc->allocElement(); pj_json_elem_string(child, NULL, &new_val); pj_json_elem_add(el, child); } pj_json_elem_add(jdat->jnode, el); } static ContainerNode jsonNode_writeNewContainer(ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_json_elem_obj(el, &nm); pj_json_elem_add(jdat->jnode, el); ContainerNode json_node; json_node.op = &json_op; json_node.data.doc = (void*)jdat->doc; json_node.data.data1 = (void*)el; json_node.data.data2 = (void*)el->value.children.next; return json_node; } static ContainerNode jsonNode_writeNewArray(ContainerNode *node, const string &name) PJSUA2_THROW(Error) { json_node_data *jdat = (json_node_data*)&node->data; pj_json_elem *el = jdat->doc->allocElement(); pj_str_t nm = alloc_name(jdat->doc, name); pj_json_elem_array(el, &nm); pj_json_elem_add(jdat->jnode, el); ContainerNode json_node; json_node.op = &json_op; json_node.data.doc = (void*)jdat->doc; json_node.data.data1 = (void*)el; json_node.data.data2 = (void*)el->value.children.next; return json_node; }