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