json-tree.c revision fcd3d6214ce1b8169b6481c78e02d9054901fed3
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch/* Copyright (c) 2015-2016 Dovecot authors, see the included COPYING file */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch#include "lib.h"
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch#include "json-tree.h"
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstruct json_tree {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch pool_t pool;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct json_tree_node *root, *cur, *cur_child;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch};
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstruct json_tree *
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_init_type(enum json_type container)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct json_tree *tree;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch pool_t pool;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch pool = pool_alloconly_create("json tree", 1024);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree = p_new(pool, struct json_tree, 1);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->pool = pool;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value_type = container == JSON_TYPE_ARRAY ? container : JSON_TYPE_OBJECT;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return tree;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschvoid json_tree_deinit(struct json_tree **_tree)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct json_tree *tree = *_tree;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch *_tree = NULL;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch pool_unref(&tree->pool);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
0afd9a9acab584e770ffcd6a0e1e02e2d18d360aJosef 'Jeff' Sipek
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstatic void
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_append_child(struct json_tree *tree, enum json_type type,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch const char *value)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch struct json_tree_node *node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch node = p_new(tree->pool, struct json_tree_node, 1);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch node->parent = tree->cur;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch node->value_type = type;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch node->value.str = p_strdup(tree->pool, value);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (tree->cur_child == NULL)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value.child = node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch else
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur_child->next = node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur_child = node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstatic void
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_set_cur(struct json_tree *tree, struct json_tree_node *node)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur = node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur_child = tree->cur->value.child;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (tree->cur_child != NULL) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch while (tree->cur_child->next != NULL)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur_child = tree->cur_child->next;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch }
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschstatic int
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_append_value(struct json_tree *tree, enum json_type type,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch const char *value)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch switch (tree->cur->value_type) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_OBJECT_KEY:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* "key": value - we already added the node and set its key,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch so now just set the value */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value_type = type;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value.str = p_strdup(tree->pool, value);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_set_cur(tree, tree->cur->parent);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_ARRAY:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch /* element in array - add a new node */
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_append_child(tree, type, value);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch default:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch }
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return 0;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschint json_tree_append(struct json_tree *tree, enum json_type type,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch const char *value)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch switch (type) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_OBJECT_KEY:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (tree->cur->value_type != JSON_TYPE_OBJECT)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_append_child(tree, type, NULL);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_set_cur(tree, tree->cur_child);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->key = p_strdup(tree->pool, value);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_ARRAY:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (json_tree_append_value(tree, type, NULL) < 0)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_set_cur(tree, tree->cur_child);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_OBJECT:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value_type = JSON_TYPE_OBJECT;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch else if (tree->cur->value_type == JSON_TYPE_ARRAY) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_append_child(tree, type, NULL);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_set_cur(tree, tree->cur_child);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch } else {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch }
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_OBJECT_END:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (tree->cur->parent == NULL ||
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value_type != JSON_TYPE_OBJECT)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_set_cur(tree, tree->cur->parent);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_ARRAY_END:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (tree->cur->parent == NULL ||
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch tree->cur->value_type != JSON_TYPE_ARRAY)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_set_cur(tree, tree->cur->parent);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_STRING:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_NUMBER:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_TRUE:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_FALSE:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch case JSON_TYPE_NULL:
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (json_tree_append_value(tree, type, value) < 0)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return -1;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch break;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch }
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return 0;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschconst struct json_tree_node *
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_root(const struct json_tree *tree)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
0afd9a9acab584e770ffcd6a0e1e02e2d18d360aJosef 'Jeff' Sipek return tree->root;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschconst struct json_tree_node *
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_find_key(const struct json_tree_node *node, const char *key)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch i_assert(node->value_type == JSON_TYPE_OBJECT);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch node = json_tree_get_child(node);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch for (; node != NULL; node = node->next) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (node->key != NULL && strcmp(node->key, key) == 0)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch }
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return NULL;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschconst struct json_tree_node *
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Boschjson_tree_find_child_with(const struct json_tree_node *node,
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch const char *key, const char *value)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch{
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch const struct json_tree_node *child;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch i_assert(node->value_type == JSON_TYPE_OBJECT ||
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch node->value_type == JSON_TYPE_ARRAY);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch for (node = json_tree_get_child(node); node != NULL; node = node->next) {
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (node->value_type != JSON_TYPE_OBJECT)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch continue;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch child = json_tree_find_key(node, key);
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch if (child != NULL &&
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch json_tree_get_value_str(child) != NULL &&
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch strcmp(json_tree_get_value_str(child), value) == 0)
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return node;
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch }
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch return NULL;
0afd9a9acab584e770ffcd6a0e1e02e2d18d360aJosef 'Jeff' Sipek}
2cbbe9b4829adb184c83dbf780316f4144559054Stephan Bosch