diff options
author | Liong Sauw Ming <ming@teluu.com> | 2014-01-16 05:30:46 +0000 |
---|---|---|
committer | Liong Sauw Ming <ming@teluu.com> | 2014-01-16 05:30:46 +0000 |
commit | e56ea14ab8531ee3cec375460577d1b89bf62e26 (patch) | |
tree | df77c3acb961514b2022ee9e030071b691145920 /pjsip/src/pjsua2/json.cpp | |
parent | bd1c47e995a3a844868f1d4dcc8f77f163ae721b (diff) |
Closed #1723: Merging pjsua2 branch into trunk
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4704 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src/pjsua2/json.cpp')
-rw-r--r-- | pjsip/src/pjsua2/json.cpp | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/pjsip/src/pjsua2/json.cpp b/pjsip/src/pjsua2/json.cpp new file mode 100644 index 00000000..afa25989 --- /dev/null +++ b/pjsip/src/pjsua2/json.cpp @@ -0,0 +1,544 @@ +/* $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 <pjsua2/json.hpp> +#include <pjlib-util/errno.h> +#include <pj/file_io.h> +#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) + throw(Error); +static float jsonNode_readNumber(const ContainerNode*, + const string&) + throw(Error); +static bool jsonNode_readBool(const ContainerNode*, + const string&) + throw(Error); +static string jsonNode_readString(const ContainerNode*, + const string&) + throw(Error); +static StringVector jsonNode_readStringVector(const ContainerNode*, + const string&) + throw(Error); +static ContainerNode jsonNode_readContainer(const ContainerNode*, + const string &) + throw(Error); +static ContainerNode jsonNode_readArray(const ContainerNode*, + const string &) + throw(Error); +static void jsonNode_writeNumber(ContainerNode*, + const string &name, + float num) + throw(Error); +static void jsonNode_writeBool(ContainerNode*, + const string &name, + bool value) + throw(Error); +static void jsonNode_writeString(ContainerNode*, + const string &name, + const string &value) + throw(Error); +static void jsonNode_writeStringVector(ContainerNode*, + const string &name, + const StringVector &value) + throw(Error); +static ContainerNode jsonNode_writeNewContainer(ContainerNode*, + const string &name) + throw(Error); +static ContainerNode jsonNode_writeNewArray(ContainerNode*, + const string &name) + 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) 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) throw(Error) +{ + if (root) + PJSUA2_RAISE_ERROR3(PJ_EINVALIDOP, "JsonDocument.loadString()", + "Document already initialized"); + + unsigned size = 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) 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() 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 (name.size() && name.compare(0, name.size(), + jdat->childPtr->name.ptr, + jdat->childPtr->name.slen) && + jdat->childPtr->name.slen && + jdat->jnode->type != PJ_JSON_VAL_ARRAY) + { + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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; i<value.size(); ++i) { + pj_str_t new_val; + + pj_strdup2(jdat->doc->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) + 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) + 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; +} |