summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYossi Kantor <yossi.kantor@samsung.com>2013-04-03 15:35:50 +0300
committerDaniel Zaoui <daniel.zaoui@samsung.com>2013-09-10 08:44:26 +0300
commit44304381a1fc3303ffff3d605728ba68ad1b2cb6 (patch)
treee29b05b9dfde963637536286fe7dae4f12901f90
parent4ece1a1f9f0f58235fb92ed8cfb0bafe5ee6ef45 (diff)
downloadefl-devs/jackdanielz/json_parser.tar.gz
-rw-r--r--src/Makefile_Eina.am9
-rw-r--r--src/benchmarks/eina/Makefile.am1
-rw-r--r--src/benchmarks/eina/eina_bench.c1
-rw-r--r--src/benchmarks/eina/eina_bench.h1
-rw-r--r--src/benchmarks/eina/eina_bench_json.c59
-rw-r--r--src/examples/eina/eina_json_01.c59
-rw-r--r--src/examples/eina/eina_json_02.c68
-rw-r--r--src/examples/eina/eina_json_03.c100
-rw-r--r--src/lib/eina/Eina.h3
-rw-r--r--src/lib/eina/eina_json.c1294
-rw-r--r--src/lib/eina/eina_json.h899
-rw-r--r--src/tests/eina/eina_suite.c1
-rw-r--r--src/tests/eina/eina_suite.h1
-rw-r--r--src/tests/eina/eina_test_json.c613
14 files changed, 3105 insertions, 4 deletions
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 7e40749d1f..72b2ea5726 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -80,7 +80,8 @@ lib/eina/eina_inline_lock_barrier.x \
lib/eina/eina_tmpstr.h \
lib/eina/eina_alloca.h \
lib/eina/eina_cow.h \
-lib/eina/eina_inline_unicode.x
+lib/eina/eina_inline_unicode.x \
+lib/eina/eina_json.h
# Will be back for developper after 1.2.
# lib/eina/eina_model.h
@@ -147,7 +148,8 @@ lib/eina/eina_value.c \
lib/eina/eina_xattr.c \
lib/eina/eina_share_common.h \
lib/eina/eina_private.h \
-lib/eina/eina_strbuf_common.h
+lib/eina/eina_strbuf_common.h \
+lib/eina/eina_json.c
# Will be back for developper after 1.2
# lib/eina/eina_model.c \
@@ -267,7 +269,8 @@ tests/eina/eina_test_simple_xml_parser.c \
tests/eina/eina_test_value.c \
tests/eina/eina_test_cow.c \
tests/eina/eina_test_barrier.c \
-tests/eina/eina_test_tmpstr.c
+tests/eina/eina_test_tmpstr.c \
+tests/eina/eina_test_json.c
# tests/eina/eina_test_model.c
tests_eina_eina_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
diff --git a/src/benchmarks/eina/Makefile.am b/src/benchmarks/eina/Makefile.am
index 63cf39f8d0..3c8ae681a3 100644
--- a/src/benchmarks/eina/Makefile.am
+++ b/src/benchmarks/eina/Makefile.am
@@ -24,6 +24,7 @@ eina_bench_mempool.c \
eina_bench_stringshare_e17.c \
eina_bench_array.c \
eina_bench_rectangle_pool.c \
+eina_bench_json.c \
ecore_list.c \
ecore_strings.c \
ecore_hash.c \
diff --git a/src/benchmarks/eina/eina_bench.c b/src/benchmarks/eina/eina_bench.c
index 5bd35f819b..5bb98d99e9 100644
--- a/src/benchmarks/eina/eina_bench.c
+++ b/src/benchmarks/eina/eina_bench.c
@@ -36,6 +36,7 @@ struct _Eina_Benchmark_Case
static const Eina_Benchmark_Case etc[] = {
{ "Hash", eina_bench_hash },
+ /* { "JSON", eina_bench_json }, */
/* { "Array vs List vs Inlist", eina_bench_array }, */
/* { "Stringshare", eina_bench_stringshare }, */
/* { "Convert", eina_bench_convert }, */
diff --git a/src/benchmarks/eina/eina_bench.h b/src/benchmarks/eina/eina_bench.h
index d575822b54..6c8b12526e 100644
--- a/src/benchmarks/eina/eina_bench.h
+++ b/src/benchmarks/eina/eina_bench.h
@@ -29,6 +29,7 @@ void eina_bench_sort(Eina_Benchmark *bench);
void eina_bench_mempool(Eina_Benchmark *bench);
void eina_bench_rectangle_pool(Eina_Benchmark *bench);
void eina_bench_quadtree(Eina_Benchmark *bench);
+void eina_bench_json(Eina_Benchmark *bench);
/* Specific benchmark. */
void eina_bench_e17(void);
diff --git a/src/benchmarks/eina/eina_bench_json.c b/src/benchmarks/eina/eina_bench_json.c
new file mode 100644
index 0000000000..e18eab8f7e
--- /dev/null
+++ b/src/benchmarks/eina/eina_bench_json.c
@@ -0,0 +1,59 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2013 Yossi Kantor
+ Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#ifdef EINA_BENCH_HAVE_GLIB
+# include <glib.h>
+#endif
+
+#include "Evas_Data.h"
+#include "Ecore_Data.h"
+
+#include "eina_json.h"
+#include "eina_main.h"
+#include "eina_bench.h"
+
+static void
+eina_bench_json_parse(int request)
+{
+ int i;
+ eina_init();
+
+ Eina_Json_Context *jctx = eina_json_context_dom_new();
+ eina_json_context_parse(jctx, "{");
+ for (i = 0; i < request; i++)
+ eina_json_context_parse(jctx, "\"Bench\":[\"Json\", 34.5, true, null],");
+ eina_json_context_parse(jctx, "\"End\":null }");
+
+ eina_shutdown();
+}
+
+void eina_bench_json(Eina_Benchmark *bench)
+{
+ eina_benchmark_register(bench, "json",
+ EINA_BENCHMARK(eina_bench_json_parse),
+ 100, 100000, 500);
+}
diff --git a/src/examples/eina/eina_json_01.c b/src/examples/eina/eina_json_01.c
new file mode 100644
index 0000000000..036fbae5f8
--- /dev/null
+++ b/src/examples/eina/eina_json_01.c
@@ -0,0 +1,59 @@
+//Compile with:
+//gcc -g eina_json_01.c -o eina_json_01 `pkg-config --cflags --libs eina`
+
+// This example demonstrates a simple use of json context parsing of a complete string with example
+// of a result's diagnostics and output.
+
+#include "Eina.h"
+
+#define DOERROR(x,args...) { printf(x,## args); return 1; }
+#define BUFFSIZE 100
+
+char *json_err_name[] = {"No error","Lexical error","Syntax Error","Input Past End"};
+char my_json[] =
+"\
+{\n\
+\"String\":\"MyJSON\",\n \
+\"Array\":[1,2,3,true,5,false,\"\",{},null,[78,\"Hello\"],\"World\"],\n \
+\"Object\": { \"Subobj\":{ \"Sub1\":null,\"Sub2\":56} }\n \
+}\
+";
+
+int
+main(int argc,void **argv)
+{
+ eina_init();
+
+ // Its a DOM parsing. When its done without errors, we'll have a json tree to take.
+ Eina_Json_Context *ctx = eina_json_context_dom_new();
+
+ //Now parse
+ eina_json_context_parse(ctx, my_json);
+
+ //Analize and report results
+ Eina_Json_Error jsnerr = eina_json_context_error_get(ctx);
+ if (jsnerr)
+ {
+ printf ("Parsing failed\n");
+ printf ("Error %d:%d %s\n", eina_json_context_line_get(ctx),
+ eina_json_context_column_get(ctx),
+ json_err_name[jsnerr]);
+ }
+ else if (eina_json_context_completed_get(ctx))
+ {
+ // If parsing was successful - take the json tree and print it
+ printf ("Successfully parsed\n");
+ Eina_Json_Value *jsnval = eina_json_context_dom_tree_take(ctx);
+ char *json_output = eina_json_format_string_get(jsnval, EINA_JSON_FORMAT_BASIC);
+ eina_json_value_free(jsnval);
+ printf("%s\n", json_output);
+ free(json_output);
+ }
+ else
+ printf ("Parsing was not completed\n");
+
+ eina_json_context_free(ctx);
+ eina_shutdown();
+
+ return 0;
+}
diff --git a/src/examples/eina/eina_json_02.c b/src/examples/eina/eina_json_02.c
new file mode 100644
index 0000000000..546a219393
--- /dev/null
+++ b/src/examples/eina/eina_json_02.c
@@ -0,0 +1,68 @@
+//Compile with:
+//gcc -g eina_json_02.c -o eina_json_02 `pkg-config --cflags --libs eina`
+
+// This example reads and parses json text gradually from file by BUFFSIZE chunks.
+// Usage: eina_json_02 jsonfile
+
+#include "Eina.h"
+
+#define DOERROR(x,args...) { printf(x,## args); return 1; }
+#define BUFFSIZE 100
+
+char *json_err_name[]={"No error","Lexical error","Syntax Error","Input Past End"};
+char rbuff[BUFFSIZE] = {0};
+
+int
+main(int argc, void **argv)
+{
+ size_t rd;
+
+ eina_init();
+
+ if (argc < 2) DOERROR("Usage: eina_json_02 filename \n");
+
+ FILE* fp = fopen(argv[1], "r");
+ if (!fp) DOERROR("Error openning file file %s\n", (char*)argv[1]);
+
+ // Its a DOM parsing. When its done without errors, we'll have a json tree to take.
+ Eina_Json_Context *ctx = eina_json_context_dom_new();
+
+ // Read and parse json file by BUFFSIZE chunks
+ while (eina_json_context_unfinished_get(ctx))
+ {
+ rd = fread(rbuff, 1, BUFFSIZE,fp);
+ eina_json_context_parse_n(ctx, rbuff, rd);
+
+ //End of file reached
+ if (rd != BUFFSIZE) break;
+ }
+
+ //Report results
+ Eina_Json_Error jsnerr = eina_json_context_error_get(ctx);
+ if (jsnerr)
+ {
+ printf ("Parsing failed\n");
+ printf ("Error %d:%d %s\n",eina_json_context_line_get(ctx),
+ eina_json_context_column_get(ctx),
+ json_err_name[jsnerr]);
+ }
+ else if (eina_json_context_completed_get(ctx))
+ {
+ // If parsing was successful - take the json tree and print it
+ printf ("File %s was successfully parsed \n\n", (char*)argv[1]);
+ Eina_Json_Value *jsnval = eina_json_context_dom_tree_take(ctx);
+ char *json_output = eina_json_format_string_get(jsnval, EINA_JSON_FORMAT_BASIC);
+ eina_json_value_free(jsnval);
+ printf("%s\n", json_output);
+ free(json_output);
+ }
+ else
+ printf ("Parsing was not completed - file is incomplete\n");
+
+ eina_json_context_free(ctx);
+ fclose(fp);
+
+ eina_shutdown();
+
+ return 0;
+}
diff --git a/src/examples/eina/eina_json_03.c b/src/examples/eina/eina_json_03.c
new file mode 100644
index 0000000000..9b5a0e113f
--- /dev/null
+++ b/src/examples/eina/eina_json_03.c
@@ -0,0 +1,100 @@
+//Compile with:
+//gcc -g eina_json_03.c -o eina_json_03 `pkg-config --cflags --libs eina`
+
+// This example demonstrates a simple use of a json context SAX parsing
+// with callback function which prints indented json objects
+
+#include "Eina.h"
+
+#define DOERROR(x,args...) { printf(x,## args); return 1; }
+#define BUFFSIZE 100
+
+typedef struct
+{
+ Eina_Strbuf *text;
+ unsigned long parent_idx;
+} Sax_Parser_Data;
+
+char *json_err_name[]={"No error","Lexical error","Syntax Error","Input Past End"};
+
+char *json_type_string[]={"NULL","NUMBER","STRING","BOOLEAN","PAIR","OBJECT","ARRAY"};
+
+char my_json[] =
+"{\n\
+\"String\":\"MyJSON\",\n \
+\"Array\":[1,2,3,true,5,false,\"\",{},null,[78,\"Hello\"],\"World\"],\n \
+\"Object\": { \"Subobj\":{ \"Sub1\":null,\"Sub2\":56} }\n \
+}";
+
+void *
+sax_parser_cb(Eina_Json_Type type, void *parent, const char *text, void *data)
+{
+ Sax_Parser_Data *saxdata = (Sax_Parser_Data *)data;
+ const char* strval = NULL;
+
+ switch(type)
+ {
+ case EINA_JSON_TYPE_NUMBER:
+ case EINA_JSON_TYPE_STRING:
+ case EINA_JSON_TYPE_PAIR:
+ strval = text;
+ break;
+ case EINA_JSON_TYPE_BOOLEAN:
+ strval = (*text == 't') ? "true" : "false";
+ break;
+ }
+
+ saxdata->parent_idx++;
+ eina_strbuf_append_printf(saxdata->text,
+ "(%p) PARENT(%p) TYPE:%s",
+ (void*)saxdata->parent_idx,
+ parent,
+ json_type_string[type]);
+
+ if (strval) eina_strbuf_append_printf(saxdata->text, " = \"%s\"", strval);
+ eina_strbuf_append_char(saxdata->text, '\n');
+
+ return (void*)saxdata->parent_idx;
+}
+
+int
+main(int argc,void **argv)
+{
+ Sax_Parser_Data mysax;
+
+ eina_init();
+
+ mysax.text = eina_strbuf_new();
+ mysax.parent_idx = 1;
+
+ // Its a SAX parsing. We initilize it with our our callback function
+ // and the data to be used by the callback.
+ Eina_Json_Context *ctx = eina_json_context_sax_new(sax_parser_cb, &mysax);
+
+ //Now parse
+ eina_json_context_parse(ctx, my_json);
+
+ //Analize and report results
+ Eina_Json_Error jsnerr = eina_json_context_error_get(ctx);
+ if (jsnerr)
+ {
+ printf ("Parsing failed\n");
+ printf ("Error %d:%d %s\n", eina_json_context_line_get(ctx),
+ eina_json_context_column_get(ctx),
+ json_err_name[jsnerr]);
+ }
+ else if (eina_json_context_completed_get(ctx))
+ {
+ // If parsing was successful - print the text that our callback produced.
+ printf ("File was successfully parsed\n\n");
+ printf("%s\n", eina_strbuf_string_get(mysax.text));
+ }
+ else
+ printf ("Parsing was not completed\n");
+
+ eina_strbuf_free(mysax.text);
+ eina_json_context_free(ctx);
+
+ eina_shutdown();
+ return 0;
+}
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index e22d6e2060..37a9d5a7c0 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -203,7 +203,7 @@
*
* Eina tools aims to help application development, providing ways to
* make it safer, log errors, manage memory more efficiently and more.
- *
+ *
*/
#ifdef _WIN32
@@ -263,6 +263,7 @@ extern "C" {
#include "eina_xattr.h"
#include "eina_value.h"
#include "eina_cow.h"
+#include "eina_json.h"
#ifdef __cplusplus
}
diff --git a/src/lib/eina/eina_json.c b/src/lib/eina/eina_json.c
new file mode 100644
index 0000000000..4288249082
--- /dev/null
+++ b/src/lib/eina/eina_json.c
@@ -0,0 +1,1294 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2013 Yossi Kantor
+ * Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "eina_json.h"
+#include <string.h>
+#include <math.h>
+
+/*============================================================================*
+ * Local *
+ *============================================================================*/
+
+#define _RETURN_SW(st, retr) { Stm_Switch _sw; _sw.state = st;\
+ _sw.retrans = retr; return _sw; }
+
+#define _CASE_DIGIT case '0':case '1':case '2':\
+ case '3':case '4':case '5':\
+ case '6':case '7':case '8':\
+ case '9'
+
+#define _ISDIGIT(a) (a >= '0' && a <= '9')
+
+#define JSON_GLUE_BUFF_STEP 64
+
+static Eina_Json_Value *_eina_json_type_new(Eina_Json_Type type);
+
+typedef struct _Stm_State Stm_State;
+typedef struct _Stm_Machine Stm_Machine;
+typedef struct _Stm_switch Stm_Switch;
+typedef unsigned int Stm_Val;
+
+typedef Stm_Switch(*Stm_State_Cb)(Stm_State *state, Stm_Val token, void* data);
+
+struct _Stm_switch
+{
+ Stm_State *state;
+ Eina_Bool retrans;
+};
+
+struct _Stm_State
+{
+ Stm_State_Cb state_cb;
+ void *param_ptr1;
+ int param_int1;
+};
+
+struct _Stm_Machine
+{
+ Stm_State *initial_state;
+ Stm_State *current_state;
+ void *data;
+};
+
+struct _lex_keyword_param
+{
+ char* str;
+ int len;
+ Stm_Val token;
+};
+
+struct _Eina_Json_Value
+{
+ EINA_INLIST;
+ Eina_Json_Type type;
+ Eina_Json_Value *parent;
+ union
+ {
+ double number;
+ Eina_Strbuf *string;
+ Eina_Bool boolean;
+ Eina_Inlist *lst;
+ struct _Pair
+ {
+ Eina_Strbuf *name;
+ Eina_Json_Value *val;
+ } pair;
+ };
+};
+
+struct _Eina_Json_Context
+{
+ Stm_Machine lex_machine;
+ Stm_Machine syntax_machine;
+
+ Eina_Json_Error latest_error;
+
+ Eina_Json_Value *jobj;
+
+ Eina_Array *jstack;
+ void *jparent;
+ Eina_Json_Type jparent_type;
+ Eina_Json_Parser_Cb parser_cb;
+ void *cb_data;
+
+ const char *str;
+ const char *head;
+
+ Eina_Bool glue_on;
+ char *glue_buf;
+ unsigned glue_buf_size;
+ unsigned glue_len;
+
+ unsigned line;
+ unsigned col;
+};
+
+enum _Eina_Json_Token
+{
+ JSON_STRING = 1,
+ JSON_NUMBER,
+ JSON_TRUE,
+ JSON_FALSE,
+ JSON_NULL,
+ JSON_OBJ_OPEN = '{',
+ JSON_OBJ_CLOSE = '}',
+ JSON_ARR_OPEN = '[',
+ JSON_ARR_CLOSE = ']',
+ JSON_COMMA = ',',
+ JSON_COLON = ':'
+};
+
+// Syntax and Lexical parsing state's callbacks forward declaration
+static Stm_Switch _syntx_entry_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_value_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_new_object_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_object_name_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_object_colon_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_object_next_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_array_next_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_end_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _syntx_unexpected_cb(Stm_State *state, Stm_Val token, void *data);
+
+static Stm_Switch _lex_initial_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_int_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_frac_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_digit_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_exp_sign_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_exp_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_string_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_string_esc_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_keyword_cb(Stm_State *state, Stm_Val token, void *data);
+static Stm_Switch _lex_unexpected_cb(Stm_State *state, Stm_Val token, void *data);
+
+// Syntax states
+static Stm_State syntx_entry = { _syntx_entry_cb };
+static Stm_State syntx_end = { _syntx_end_cb };
+static Stm_State syntx_new_object = { _syntx_new_object_cb };
+static Stm_State syntx_value = { _syntx_value_cb };
+static Stm_State syntx_object_name = { _syntx_object_name_cb };
+static Stm_State syntx_object_colon = { _syntx_object_colon_cb };
+static Stm_State syntx_next_object = { _syntx_object_next_cb };
+static Stm_State syntx_next_array = { _syntx_array_next_cb };
+static Stm_State syntx_unexpected = { _syntx_unexpected_cb };
+static const Stm_Switch switch_syntax_unexpected = { &syntx_unexpected, EINA_TRUE };
+
+// Lexical states
+static struct _lex_keyword_param lex_keyword_true = {"true", 4, JSON_TRUE};
+static struct _lex_keyword_param lex_keyword_false = {"false", 5, JSON_FALSE};
+static struct _lex_keyword_param lex_keyword_null = {"null", 4, JSON_NULL};
+
+static Stm_State lex_initial = {_lex_initial_cb };
+static Stm_State lex_int = { _lex_int_cb };
+static Stm_State lex_int_entry = {_lex_digit_cb, (void*)&lex_int };
+static Stm_State lex_fraction = {_lex_frac_cb };
+static Stm_State lex_fraction_entry = {_lex_digit_cb, (void*)&lex_fraction };
+static Stm_State lex_exp_sign = { _lex_exp_sign_cb };
+static Stm_State lex_exp = { _lex_exp_cb };
+static Stm_State lex_exp_entry = {_lex_digit_cb, (void*)&lex_exp };
+static Stm_State lex_string = {_lex_string_cb };
+static Stm_State lex_string_esc = {_lex_string_esc_cb };
+static Stm_State lex_unexpected = { _lex_unexpected_cb };
+static Stm_State lex_true = {_lex_keyword_cb, (void*)&lex_keyword_true };
+static Stm_State lex_false = {_lex_keyword_cb, (void*)&lex_keyword_false };
+static Stm_State lex_null = {_lex_keyword_cb, (void*)&lex_keyword_null };
+static const Stm_Switch switch_lex_unexpected = { &lex_unexpected, EINA_TRUE };
+
+
+void *
+_eina_json_parser_dom_cb(Eina_Json_Type type, void *parent, const char *text, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ Eina_Json_Value *newjval = NULL;
+
+ switch(type)
+ {
+ case EINA_JSON_TYPE_NULL:
+ newjval = eina_json_null_new();
+ break;
+ case EINA_JSON_TYPE_NUMBER:
+ newjval = eina_json_number_new(atof(text));
+ break;
+ case EINA_JSON_TYPE_STRING:
+ newjval = eina_json_string_new(text);
+ break;
+ case EINA_JSON_TYPE_BOOLEAN:
+ newjval = eina_json_boolean_new((*text == 't') ? EINA_TRUE : EINA_FALSE );
+ break;
+ case EINA_JSON_TYPE_PAIR:
+ newjval = _eina_json_type_new(EINA_JSON_TYPE_PAIR);
+ if (newjval)
+ {
+ newjval->pair.name = eina_strbuf_new();
+ eina_strbuf_append(newjval->pair.name, text);
+ }
+ break;
+ case EINA_JSON_TYPE_OBJECT:
+ newjval = eina_json_object_new();
+ break;
+ case EINA_JSON_TYPE_ARRAY:
+ newjval = eina_json_array_new();
+ break;
+ }
+
+ if (!newjval) return NULL;
+
+ if (parent)
+ {
+ Eina_Json_Value *jcontainer = (Eina_Json_Value*)parent;
+ switch(jcontainer->type)
+ {
+ case EINA_JSON_TYPE_PAIR:
+ jcontainer->pair.val = newjval;
+ break;
+ case EINA_JSON_TYPE_OBJECT:
+ jcontainer->lst = eina_inlist_append(jcontainer->lst,
+ EINA_INLIST_GET(newjval));
+ break;
+ case EINA_JSON_TYPE_ARRAY:
+ eina_json_array_append(jcontainer, newjval);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ jsctx->jobj = newjval;
+ }
+ return newjval;
+}
+
+void
+_state_machine_feed(Stm_Machine *machine, Stm_Val token)
+{
+ while (machine->current_state)
+ {
+ Stm_Switch sw =
+ machine->current_state->state_cb
+ (machine->current_state, token, machine->data);
+
+ machine->current_state = sw.state;
+ if (!sw.retrans) break;
+ }
+}
+
+inline static void
+_syntax_token_process(Stm_Val token, Eina_Json_Context *data)
+{
+ _state_machine_feed(&(data->syntax_machine), token);
+}
+
+static Stm_Switch
+_syntx_entry_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_OBJ_OPEN:
+ case JSON_ARR_OPEN:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_value_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ Stm_State* nextstate = NULL;
+ Eina_Json_Type jtype;
+ Eina_Bool call_cb = EINA_TRUE;
+ void* valptr = NULL;
+
+ switch (token)
+ {
+ case JSON_OBJ_CLOSE:
+ case JSON_ARR_CLOSE:
+ if (eina_array_count(jsctx->jstack) < 1)
+ _RETURN_SW(&syntx_end, EINA_FALSE);
+ jsctx->jparent = eina_array_pop(jsctx->jstack);
+ jsctx->jparent_type = (Eina_Json_Type)eina_array_pop(jsctx->jstack);
+ call_cb = EINA_FALSE;
+ break;
+
+ case JSON_OBJ_OPEN:
+ nextstate = &syntx_new_object;
+ jtype = EINA_JSON_TYPE_OBJECT;
+ break;
+
+ case JSON_ARR_OPEN:
+ nextstate = &syntx_value;
+ jtype = EINA_JSON_TYPE_ARRAY;
+ break;
+
+ case JSON_NUMBER: jtype = EINA_JSON_TYPE_NUMBER; break;
+ case JSON_STRING: jtype = EINA_JSON_TYPE_STRING; break;
+ case JSON_TRUE: case JSON_FALSE: jtype = EINA_JSON_TYPE_BOOLEAN; break;
+ case JSON_NULL: jtype = EINA_JSON_TYPE_NULL; break;
+
+ default:
+ return switch_syntax_unexpected;
+ }
+
+ if (call_cb)
+ {
+ valptr = jsctx->parser_cb(jtype, jsctx->jparent, jsctx->glue_buf,
+ jsctx->cb_data);
+
+ if (!valptr)
+ return switch_syntax_unexpected;
+ }
+
+ if (nextstate)
+ {
+ if (jsctx->jparent)
+ {
+ eina_array_push(jsctx->jstack, (void*)jsctx->jparent_type);
+ eina_array_push(jsctx->jstack, jsctx->jparent);
+ }
+ jsctx->jparent = valptr;
+ jsctx->jparent_type = jtype;
+ } else
+ {
+ if (jsctx->jparent_type == EINA_JSON_TYPE_PAIR)
+ {
+ jsctx->jparent = eina_array_pop(jsctx->jstack);
+ jsctx->jparent_type = (Eina_Json_Type)eina_array_pop(jsctx->jstack);
+ }
+ nextstate = (jsctx->jparent_type == EINA_JSON_TYPE_OBJECT) ?
+ &syntx_next_object :
+ &syntx_next_array;
+ }
+
+ _RETURN_SW(nextstate, EINA_FALSE);
+}
+
+static Stm_Switch
+_syntx_new_object_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_OBJ_CLOSE:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ case JSON_STRING:
+ _RETURN_SW(&syntx_object_name, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static
+Stm_Switch _syntx_object_name_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+
+ if (token == JSON_STRING)
+ {
+ eina_array_push(jsctx->jstack, (void*)jsctx->jparent_type);
+ eina_array_push(jsctx->jstack, jsctx->jparent);
+
+ jsctx->jparent_type = EINA_JSON_TYPE_PAIR;
+ jsctx->jparent = jsctx->parser_cb(EINA_JSON_TYPE_PAIR, jsctx->jparent,
+ jsctx->glue_buf, jsctx->cb_data);
+ if (jsctx->jparent) _RETURN_SW(&syntx_object_colon, EINA_FALSE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_object_colon_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ if (token == JSON_COLON)
+ _RETURN_SW(&syntx_value, EINA_FALSE);
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_object_next_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_COMMA:
+ _RETURN_SW(&syntx_object_name, EINA_FALSE);
+ case JSON_OBJ_CLOSE:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_array_next_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ case JSON_COMMA:
+ _RETURN_SW(&syntx_value, EINA_FALSE);
+ case JSON_ARR_CLOSE:
+ _RETURN_SW(&syntx_value, EINA_TRUE);
+ }
+ return switch_syntax_unexpected;
+}
+
+static Stm_Switch
+_syntx_end_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ jsctx->latest_error = EINA_JSON_ERROR_PAST_END;
+ _RETURN_SW(NULL, EINA_FALSE);
+}
+
+static Stm_Switch
+_syntx_unexpected_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ if (jsctx->latest_error == EINA_JSON_ERROR_NONE)
+ jsctx->latest_error = EINA_JSON_ERROR_SYNTAX_TOKEN;
+ _RETURN_SW(NULL, EINA_FALSE);
+}
+
+Stm_Switch
+_lex_initial_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+
+ if (jsctx->glue_on)
+ {
+ jsctx->glue_buf[0]='\0';
+ jsctx->glue_len = 0;
+ jsctx->glue_on = EINA_FALSE;
+ }
+
+ switch (token)
+ {
+ case '}': case '{': case ',':
+ case ':': case '[': case ']':
+ _syntax_token_process(token, data);
+ _RETURN_SW (state, EINA_FALSE);
+
+ case '\n':
+ jsctx->line ++;
+ jsctx->col = 0;
+ case '\r': case '\t': case ' ':
+ _RETURN_SW (state, EINA_FALSE);
+
+ case '\"':
+ _RETURN_SW(&lex_string, EINA_FALSE);
+
+ _CASE_DIGIT:
+ _RETURN_SW(&lex_int_entry, EINA_TRUE);
+ case '-':
+ jsctx->glue_on = EINA_TRUE;
+ _RETURN_SW(&lex_int_entry, EINA_FALSE);
+
+ case 't':
+ _RETURN_SW(&lex_true, EINA_TRUE);
+ case 'f':
+ _RETURN_SW(&lex_false, EINA_TRUE);
+ case 'n':
+ _RETURN_SW(&lex_null, EINA_TRUE);
+ }
+ return switch_lex_unexpected;
+}
+
+static Stm_Switch
+_lex_digit_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ jsctx->glue_on = EINA_TRUE;
+
+ Stm_State* next_state = (Stm_State*)(state->param_ptr1);
+ if (_ISDIGIT(token))
+ _RETURN_SW(next_state, EINA_FALSE);
+
+ return switch_lex_unexpected;
+}
+
+static Stm_Switch
+_lex_int_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW(state, EINA_FALSE);
+ case 'e':
+ case 'E':
+ _RETURN_SW(&lex_exp_sign, EINA_FALSE);
+ case '.':
+ _RETURN_SW(&lex_fraction_entry, EINA_FALSE);
+ }
+ _syntax_token_process(JSON_NUMBER, data);
+ _RETURN_SW(&lex_initial, EINA_TRUE);
+}
+
+static Stm_Switch
+_lex_frac_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW(state, EINA_FALSE);
+ case 'e':
+ case 'E':
+ _RETURN_SW(&lex_exp_sign, EINA_FALSE);
+ }
+ _syntax_token_process(JSON_NUMBER, data);
+ _RETURN_SW (&lex_initial, EINA_TRUE);
+}
+
+static Stm_Switch
+_lex_exp_sign_cb(Stm_State *state EINA_UNUSED, Stm_Val token, void *data EINA_UNUSED)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW (&lex_exp_entry, EINA_TRUE);
+ case '-':
+ case '+':
+ _RETURN_SW (&lex_exp_entry, EINA_FALSE);
+ }
+ return switch_lex_unexpected;
+}
+
+static Stm_Switch
+_lex_exp_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ switch (token)
+ {
+ _CASE_DIGIT:
+ _RETURN_SW(state, EINA_FALSE);
+ }
+ _syntax_token_process(JSON_NUMBER, data);
+ _RETURN_SW(&lex_initial, EINA_TRUE);
+}
+
+static Stm_Switch
+_lex_string_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ jsctx->glue_on = EINA_TRUE;
+
+ switch (token)
+ {
+ case '\\':
+ _RETURN_SW (&lex_string_esc, EINA_FALSE);
+ case '\"':
+ _syntax_token_process(JSON_STRING, data);
+ _RETURN_SW (&lex_initial, EINA_FALSE);
+ }
+ _RETURN_SW(state, EINA_FALSE);
+}
+
+static Stm_Switch
+_lex_string_esc_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data EINA_UNUSED)
+{
+ _RETURN_SW(&lex_string, EINA_FALSE);
+}
+
+static Stm_Switch
+_lex_keyword_cb(Stm_State *state, Stm_Val token, void *data)
+{
+ struct _lex_keyword_param *param = state->param_ptr1;
+
+ char* cmpstr = param->str;
+ unsigned maxlen = param->len - 1;
+
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+
+ jsctx->glue_on = EINA_TRUE;
+
+ if ((jsctx->glue_len > maxlen) || (cmpstr[jsctx->glue_len] != (char)token))
+ return switch_lex_unexpected;
+
+ if (jsctx->glue_len == maxlen)
+ {
+ _syntax_token_process(param->token, data);
+ _RETURN_SW(&lex_initial, EINA_FALSE);
+ }
+ _RETURN_SW(state, EINA_FALSE);
+}
+
+static Stm_Switch
+_lex_unexpected_cb(Stm_State *state EINA_UNUSED, Stm_Val token EINA_UNUSED, void *data)
+{
+ Eina_Json_Context *jsctx = (Eina_Json_Context *)data;
+ if (jsctx->latest_error == EINA_JSON_ERROR_NONE)
+ jsctx->latest_error = EINA_JSON_ERROR_LEX_TOKEN;
+ _RETURN_SW(NULL, EINA_FALSE);
+}
+
+static Eina_Json_Context *
+_eina_json_context_new(Eina_Json_Parser_Cb cb, void *data)
+{
+ Eina_Json_Context *ret = calloc(1, sizeof(Eina_Json_Context));
+
+ if (ret)
+ {
+ ret->jstack = eina_array_new(10);
+
+ ret->glue_buf_size = JSON_GLUE_BUFF_STEP;
+ ret->glue_buf = malloc(ret->glue_buf_size);
+
+ ret->parser_cb = (cb) ? cb : _eina_json_parser_dom_cb;
+ ret->cb_data = (cb) ? data : ret;
+
+ if (!(ret->jstack && ret->glue_buf))
+ {
+ if (ret->jstack) free(ret->jstack);
+ if (ret->glue_buf) free(ret->glue_buf);
+ free(ret);
+ return NULL;
+ }
+
+ eina_json_context_reset(ret);
+ }
+ return ret;
+}
+
+static Eina_Bool
+_eina_json_context_parse(Eina_Json_Context *ctx, const char *text, unsigned text_len)
+{
+ unsigned lencount = (text_len) ? text_len : 1;
+
+ ctx->head = ctx->str = text;
+
+ if (ctx->latest_error)
+ return EINA_FALSE;
+
+ while (*ctx->head && lencount)
+ {
+ _state_machine_feed(&(ctx->lex_machine), *ctx->head);
+
+ if (ctx->glue_on)
+ {
+ if (ctx->glue_buf_size - ctx->glue_len < 2)
+ {
+ ctx->glue_buf_size += JSON_GLUE_BUFF_STEP;
+ ctx->glue_buf = realloc(ctx->glue_buf, ctx->glue_buf_size);
+ }
+ ctx->glue_buf[ctx->glue_len++] = *ctx->head;
+ ctx->glue_buf[ctx->glue_len] = '\0';
+ }
+
+ if (ctx->latest_error)
+ return EINA_FALSE;
+
+ ctx->head++;
+ ctx->col++;
+
+ if (text_len) lencount--;
+ }
+ return EINA_TRUE;
+}
+
+static Eina_Json_Value *
+_eina_json_parse(const char *text, unsigned size)
+{
+ Eina_Json_Value *ret = NULL;
+ Eina_Json_Context *ctx = eina_json_context_dom_new();
+ _eina_json_context_parse(ctx, text, size);
+ if (eina_json_context_completed_get(ctx))
+ ret = eina_json_context_dom_tree_take(ctx);
+ eina_json_context_free(ctx);
+ return ret;
+}
+
+static Eina_Json_Value *
+_eina_json_type_new(Eina_Json_Type type)
+{
+ Eina_Json_Value *ret = calloc(1, sizeof(Eina_Json_Value));
+ if (ret)
+ ret->type = type;
+ return ret;
+}
+
+static unsigned
+_eina_json_gen_count_get(Eina_Json_Value *obj)
+{
+ return eina_inlist_count(obj->lst);
+}
+
+static Eina_Json_Value *
+_eina_json_gen_nth_get(Eina_Json_Value *obj, unsigned idx)
+{
+ unsigned count = 0;
+ Eina_Inlist *l = NULL;
+ for (l = obj->lst; l; l = l->next)
+ if ((count++) == idx) break;
+ return EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+}
+
+static Eina_Json_Value *
+_eina_json_gen_append(Eina_Json_Value *obj, Eina_Json_Value *objadd)
+{
+ if (objadd->parent)
+ {
+ EINA_LOG_ERR("Can't append json object %p. "
+ "Its already belongs to another json object %p.\n",
+ objadd, objadd->parent);
+ return NULL;
+ }
+ obj->lst = eina_inlist_append(obj->lst, EINA_INLIST_GET(objadd));
+ objadd->parent = obj;
+ return objadd;
+}
+
+static Eina_Json_Value *
+_eina_json_gen_insert(Eina_Json_Value *obj, unsigned ind, Eina_Json_Value *jsnval)
+{
+ Eina_Json_Value *o = _eina_json_gen_nth_get(obj, ind);
+ if (!o && ind) return NULL;
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't insert json object %p. "
+ "Its already belongs to another json object %p.\n",
+ jsnval, jsnval->parent);
+ return NULL;
+ }
+
+ obj->lst = eina_inlist_prepend_relative(obj->lst,
+ EINA_INLIST_GET(jsnval),
+ EINA_INLIST_GET(o));
+ jsnval->parent = obj;
+ return jsnval;
+}
+
+static Eina_Bool
+_eina_json_gen_nth_remove(Eina_Json_Value *obj, unsigned ind)
+{
+ Eina_Json_Value *o = _eina_json_gen_nth_get(obj, ind);
+ if (!o) return EINA_FALSE;
+ obj->lst = eina_inlist_remove(obj->lst, EINA_INLIST_GET(o));
+ o->parent = NULL;
+ eina_json_value_free(o);
+ return EINA_TRUE;
+}
+
+static inline Eina_Iterator *
+_eina_json_gen_iterator_new(Eina_Json_Value *obj)
+{
+ return eina_inlist_iterator_new(obj->lst);
+}
+
+static void
+_eina_json_delim_print(Eina_Json_Value *jsnval, Eina_Strbuf *jtext,
+ unsigned ident0, unsigned identV, Eina_Bool objbreak)
+{
+ int newident;
+ Eina_Json_Value *jval;
+ Eina_Inlist *l = NULL;
+
+ switch (jsnval->type)
+ {
+ case EINA_JSON_TYPE_NULL:
+ eina_strbuf_append(jtext, "null");
+ break;
+
+ case EINA_JSON_TYPE_NUMBER:
+ if (ceil(jsnval->number) == jsnval->number)
+ eina_strbuf_append_printf(jtext, "%ld", (long)(jsnval->number));
+ else
+ eina_strbuf_append_printf(jtext, "%.2f", jsnval->number);
+ break;
+
+ case EINA_JSON_TYPE_STRING:
+ eina_strbuf_append_printf(jtext, "\"%s\"",
+ eina_strbuf_string_get(jsnval->string));
+ break;
+
+ case EINA_JSON_TYPE_BOOLEAN:
+ eina_strbuf_append(jtext, (jsnval->boolean) ? "true" : "false");
+ break;
+
+ case EINA_JSON_TYPE_PAIR:
+ eina_strbuf_append_printf(jtext, "\"%s\"",
+ eina_strbuf_string_get(jsnval->pair.name));
+ eina_strbuf_append(jtext, ":");
+ if (objbreak)
+ if (jsnval->pair.val->type == EINA_JSON_TYPE_OBJECT)
+ eina_strbuf_append_printf(jtext, "\n%*s", ident0, " ");
+ _eina_json_delim_print(jsnval->pair.val, jtext, ident0, identV, objbreak);
+ break;
+
+ case EINA_JSON_TYPE_ARRAY:
+ eina_strbuf_append_char(jtext, '[');
+
+ for (l = jsnval->lst; l; l = l->next)
+ {
+ jval = EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+ _eina_json_delim_print(jval, jtext, ident0, identV, objbreak);
+ if (l->next) eina_strbuf_append(jtext, (objbreak) ? ", " : ",");
+ }
+ eina_strbuf_append_char(jtext, ']');
+ break;
+
+ case EINA_JSON_TYPE_OBJECT:
+ if (!jsnval->lst)
+ {
+ eina_strbuf_append(jtext, "{}");
+ break;
+ }
+ newident = ident0 + identV;
+ eina_strbuf_append_char(jtext, '{');
+
+ for (l = jsnval->lst; l;l = l->next)
+ {
+ if (objbreak)
+ eina_strbuf_append_printf(jtext, "\n%*s", newident, " ");
+ jval = EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+ _eina_json_delim_print(jval, jtext, newident, identV, objbreak);
+ if (l->next) eina_strbuf_append(jtext, ",");
+ }
+ if (objbreak)
+ eina_strbuf_append_printf(jtext, "\n%*s", ident0, " ");
+
+ eina_strbuf_append_char(jtext, '}');
+ break;
+ }
+}
+
+/*============================================================================*
+ * JSON Parser API *
+ *============================================================================*/
+
+EAPI void
+eina_json_context_reset(Eina_Json_Context* ctx)
+{
+ Stm_Machine * lexm = &(ctx->lex_machine);
+ Stm_Machine * stxm = &(ctx->syntax_machine);
+
+ lexm->current_state = lexm->initial_state = &lex_initial;
+ stxm->current_state = stxm->initial_state = &syntx_entry;
+ stxm->data = lexm->data = ctx;
+
+ ctx->latest_error = EINA_JSON_ERROR_NONE;
+
+ ctx->glue_len = 0;
+ ctx->glue_buf[0] = '\0';
+ ctx->glue_on = EINA_FALSE;
+
+ eina_array_clean(ctx->jstack);
+ eina_json_value_free(ctx->jobj);
+ ctx->jobj = NULL;
+ ctx->jparent=NULL;
+
+ ctx->str = ctx->head = NULL;
+ ctx->line = ctx->col = 1;
+}
+
+EAPI Eina_Json_Context *
+eina_json_context_dom_new()
+{
+ return _eina_json_context_new(NULL, NULL);
+}
+
+EAPI Eina_Json_Context *
+eina_json_context_sax_new(Eina_Json_Parser_Cb cb, void* data)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
+ return _eina_json_context_new(cb, data);
+}
+
+EAPI void
+eina_json_context_free(Eina_Json_Context* ctx)
+{
+ if (!ctx) return;
+
+ eina_json_context_reset(ctx);
+ free(ctx->glue_buf);
+ eina_array_free(ctx->jstack);
+ free(ctx);
+}
+
+EAPI inline unsigned
+eina_json_context_line_get(Eina_Json_Context* ctx)
+{
+ return ctx->line;
+}
+
+EAPI inline unsigned
+eina_json_context_column_get(Eina_Json_Context* ctx)
+{
+ return ctx->col;
+}
+
+EAPI inline Eina_Json_Error
+eina_json_context_error_get(Eina_Json_Context* ctx)
+{
+ return ctx->latest_error;
+}
+
+EAPI inline Eina_Bool
+eina_json_context_completed_get(Eina_Json_Context* ctx)
+{
+ return (ctx->syntax_machine.current_state == &syntx_end);
+}
+
+EAPI Eina_Bool
+eina_json_context_unfinished_get(Eina_Json_Context* ctx)
+{
+ return (Eina_Bool)(!eina_json_context_completed_get(ctx) &&
+ !eina_json_context_error_get(ctx));
+}
+
+EAPI Eina_Json_Value *
+eina_json_context_dom_tree_take(Eina_Json_Context* ctx)
+{
+ if (!eina_json_context_completed_get(ctx))
+ {
+ EINA_LOG_ERR("Taking json tree from erroneous or uncompleted json context");
+ return NULL;
+ }
+ if (!ctx->jobj)
+ {
+ EINA_LOG_ERR("Json tree already taken for this json context");
+ return NULL;
+ }
+ Eina_Json_Value* ret = ctx->jobj;
+ ctx->jobj = NULL;
+ return ret;
+}
+
+EAPI inline Eina_Json_Value *
+eina_json_parse(const char *text)
+{
+ return _eina_json_parse(text, 0);
+}
+
+EAPI Eina_Json_Value *
+eina_json_parse_n(const char *text, unsigned text_len)
+{
+ if (!text_len) return NULL;
+ return _eina_json_parse(text, text_len);
+}
+
+EAPI Eina_Bool
+eina_json_context_parse(Eina_Json_Context *ctx, const char *text)
+{
+ return _eina_json_context_parse(ctx, text, 0);
+}
+
+EAPI Eina_Bool
+eina_json_context_parse_n(Eina_Json_Context *ctx, const char *text, unsigned text_len)
+{
+ if (!text_len) return !!(ctx->latest_error);
+ return _eina_json_context_parse(ctx, text, text_len);
+}
+
+/*============================================================================*
+ * JSON Value manipulation API *
+ *============================================================================*/
+
+EAPI Eina_Json_Value *
+eina_json_number_new(double num)
+{
+ Eina_Json_Value *ret = _eina_json_type_new(EINA_JSON_TYPE_NUMBER);
+ if (ret) ret->number = num;
+ return ret;
+}
+
+EAPI Eina_Json_Value *
+eina_json_string_new(const char* string)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(string, NULL);
+ Eina_Json_Value *ret = _eina_json_type_new(EINA_JSON_TYPE_STRING);
+ if (ret)
+ {
+ ret->string = eina_strbuf_new();
+ eina_strbuf_append(ret->string, string);
+ }
+ return ret;
+}
+
+EAPI Eina_Json_Value *
+eina_json_boolean_new(Eina_Bool boolval)
+{
+ Eina_Json_Value *ret = _eina_json_type_new(EINA_JSON_TYPE_BOOLEAN);
+ if (ret) ret->boolean = boolval;
+ return ret;
+}
+
+EAPI inline Eina_Json_Value *
+eina_json_null_new()
+{
+ return _eina_json_type_new(EINA_JSON_TYPE_NULL);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_new()
+{
+ return _eina_json_type_new(EINA_JSON_TYPE_OBJECT);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_new()
+{
+ return _eina_json_type_new(EINA_JSON_TYPE_ARRAY);
+}
+
+EAPI void
+eina_json_value_free(Eina_Json_Value *jsnval)
+{
+ if (!jsnval) return;
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't free json object %p. It belongs to %p.\n",
+ jsnval, jsnval->parent);
+ return;
+ }
+
+ switch (jsnval->type)
+ {
+ case EINA_JSON_TYPE_STRING:
+ eina_strbuf_free(jsnval->string);
+ break;
+ case EINA_JSON_TYPE_PAIR:
+ eina_strbuf_free(jsnval->pair.name);
+ if (jsnval->pair.val) jsnval->pair.val->parent = NULL;
+ eina_json_value_free(jsnval->pair.val);
+ break;
+ case EINA_JSON_TYPE_OBJECT:
+ case EINA_JSON_TYPE_ARRAY:
+ while (jsnval->lst)
+ {
+ Eina_Json_Value *aux = EINA_INLIST_CONTAINER_GET(jsnval->lst,
+ Eina_Json_Value);
+ jsnval->lst = eina_inlist_remove(jsnval->lst, jsnval->lst);
+ aux->parent = NULL;
+ eina_json_value_free(aux);
+ }
+ default:
+ break;
+ }
+ free(jsnval);
+}
+
+EAPI inline
+Eina_Json_Type eina_json_type_get(Eina_Json_Value *jsnval)
+{
+ return jsnval->type;
+}
+
+EAPI Eina_Bool
+eina_json_number_set(Eina_Json_Value *jsnval, double num)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_NUMBER), EINA_FALSE);
+ jsnval->number = num;
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_json_string_set(Eina_Json_Value *jsnval, const char* string)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_STRING), EINA_FALSE);
+ eina_strbuf_reset(jsnval->string);
+ eina_strbuf_append(jsnval->string, string);
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+eina_json_boolean_set(Eina_Json_Value *jsnval, Eina_Bool boolval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_BOOLEAN), EINA_FALSE);
+ jsnval->boolean = boolval;
+ return EINA_TRUE;
+}
+
+EAPI double
+eina_json_number_get(Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_NUMBER), 0.0);
+ return jsnval->number;
+}
+
+EAPI const char *
+eina_json_string_get(Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_STRING), NULL);
+ return eina_strbuf_string_get(jsnval->string);
+}
+
+EAPI Eina_Bool
+eina_json_boolean_get(Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type != EINA_JSON_TYPE_BOOLEAN), EINA_FALSE);
+ return jsnval->boolean;
+}
+
+EAPI const char *
+eina_json_pair_name_get(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_PAIR), NULL);
+ return eina_strbuf_string_get(obj->pair.name);
+}
+
+EAPI Eina_Json_Value *
+eina_json_pair_value_get(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_PAIR), NULL);
+ return obj->pair.val;
+}
+
+EAPI unsigned
+eina_json_object_count_get(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), 0);
+ return _eina_json_gen_count_get(obj);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_nth_get(Eina_Json_Value *obj, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ return _eina_json_gen_nth_get(obj, idx);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_append(Eina_Json_Value *obj, const char* keyname, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't append json object %p.Its already "
+ "belongs to another json object %p.\n",
+ jsnval, jsnval->parent);
+ return NULL;
+ }
+
+ Eina_Json_Value *pr = _eina_json_type_new(EINA_JSON_TYPE_PAIR);
+ pr->pair.name = eina_strbuf_new();
+ eina_strbuf_append(pr->pair.name, keyname);
+ pr->pair.val = jsnval;
+
+ Eina_Json_Value *ins = _eina_json_gen_append(obj, pr);
+ return (ins) ? pr : NULL;
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_insert(Eina_Json_Value *obj, unsigned idx, const char* keyname, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+
+ Eina_Json_Value *o = _eina_json_gen_nth_get(obj, idx);
+ if (!o && idx) return NULL;
+
+ if (jsnval->parent)
+ {
+ EINA_LOG_ERR("Can't insert json object %p.Its already "
+ "belongs to another json object %p.\n",
+ jsnval, jsnval->parent);
+ return NULL;
+ }
+
+ Eina_Json_Value *pr = _eina_json_type_new(EINA_JSON_TYPE_PAIR);
+ pr->pair.name = eina_strbuf_new();
+ eina_strbuf_append(pr->pair.name, keyname);
+ pr->pair.val = jsnval;
+ jsnval->parent = pr;
+
+ Eina_Json_Value *ins = _eina_json_gen_insert(obj, idx, pr);
+ return (ins) ? pr : NULL;
+}
+
+EAPI Eina_Bool
+eina_json_object_nth_remove(Eina_Json_Value *obj, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), EINA_FALSE);
+ return _eina_json_gen_nth_remove(obj, idx);
+}
+
+EAPI Eina_Iterator *
+eina_json_object_iterator_new(Eina_Json_Value *obj)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+ return _eina_json_gen_iterator_new(obj);
+}
+
+EAPI Eina_Json_Value *
+eina_json_object_value_get_internal(Eina_Json_Value *obj, ...)
+{
+ char* jkey;
+ va_list vl;
+ Eina_Json_Value *jval;
+ Eina_Inlist *l;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((obj->type != EINA_JSON_TYPE_OBJECT), NULL);
+
+ jval = obj;
+ va_start(vl, obj);
+ while ((jkey = va_arg(vl, char*)))
+ {
+ if (jval->type != EINA_JSON_TYPE_OBJECT) return NULL;
+ for (l = jval->lst; l; l = l->next)
+ {
+ jval = EINA_INLIST_CONTAINER_GET(l, Eina_Json_Value);
+ if (!strcmp(eina_strbuf_string_get(jval->pair.name), jkey)) break;
+ jval = NULL;
+ }
+ if (!jval) break;
+ jval = jval->pair.val;
+ }
+ va_end(vl);
+
+ if (jval == obj) jval = NULL;
+
+ return jval;
+}
+
+EAPI unsigned
+eina_json_array_count_get(Eina_Json_Value *arr)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), 0);
+ return _eina_json_gen_count_get(arr);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_nth_get(Eina_Json_Value *arr, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ return _eina_json_gen_nth_get(arr, idx);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_append(Eina_Json_Value *arr, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+ return _eina_json_gen_append(arr, jsnval);
+}
+
+EAPI Eina_Json_Value *
+eina_json_array_insert(Eina_Json_Value *arr, unsigned idx, Eina_Json_Value *jsnval)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((jsnval->type == EINA_JSON_TYPE_PAIR), NULL);
+ return _eina_json_gen_insert(arr, idx, jsnval);
+}
+
+EAPI Eina_Bool
+eina_json_array_nth_remove(Eina_Json_Value *arr, unsigned idx)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), EINA_FALSE);
+ return _eina_json_gen_nth_remove(arr, idx);
+}
+
+EAPI Eina_Iterator *
+eina_json_array_iterator_new(Eina_Json_Value *arr)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((arr->type != EINA_JSON_TYPE_ARRAY), NULL);
+ return _eina_json_gen_iterator_new(arr);
+}
+
+EAPI char *
+eina_json_format_string_get(Eina_Json_Value *jsnval, Eina_Json_Format format)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL((format > EINA_JSON_FORMAT_BASIC), NULL);
+
+ int ident0 = 0;
+ int identV = (format) ? 2 : 0;
+ int objbreak = (format) ? EINA_TRUE : EINA_FALSE;
+
+ Eina_Strbuf *jtext = eina_strbuf_new();
+ _eina_json_delim_print(jsnval, jtext, ident0, identV, objbreak);
+ char *ret = eina_strbuf_string_steal(jtext);
+ eina_strbuf_free(jtext);
+ return ret;
+}
diff --git a/src/lib/eina/eina_json.h b/src/lib/eina/eina_json.h
new file mode 100644
index 0000000000..aa9fe2a250
--- /dev/null
+++ b/src/lib/eina/eina_json.h
@@ -0,0 +1,899 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2013 Yossi Kantor
+ * Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_JSON_H_
+#define EINA_JSON_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "eina_config.h"
+
+#include "eina_types.h"
+#include "eina_iterator.h"
+#include "eina_inlist.h"
+#include "eina_array.h"
+#include "eina_strbuf.h"
+#include "eina_safety_checks.h"
+
+/**
+ * @page eina_json_example_01
+ * @include eina_json_01.c
+ * @example eina_json_01.c
+ */
+
+ /**
+ * @page eina_json_example_02
+ * @include eina_json_02.c
+ * @example eina_json_02.c
+ */
+
+ /**
+ * @page eina_json_example_03
+ * @include eina_json_03.c
+ * @example eina_json_03.c
+ */
+
+/**
+ * @page eina_json_dom_example_page @b Dom @b Parsing
+ * @dontinclude eina_json_01.c
+ * @n
+ * DOM parsing is a method of parsing the whole text document into a convinient
+ * data structure. In our case we'll have a @c Eina_Json_Value* pointer to a
+ * root json object after we done.
+ *
+ * So, we define a json string @c my_json
+ * @skip my_json
+ * @until ;
+ *
+ * Next we initilize parse context instance @c ctx
+ * @skipline Eina_Json_Context
+ *
+ * and now we can parse @c my_json string
+ * @skipline eina_json_context_parse
+ *
+ * Lets see if error occured
+ * @skipline Eina_Json_Error
+ *
+ * Since no error occurred @ref eina_json_context_error_get() will return
+ * EINA_JSON_ERROR_NONE. In our case the parsing was completed successfully so
+ * @ref eina_json_context_completed_get() will return true and the next code
+ * will be executed
+ * @skip eina_json_context_completed_get
+ * @until }
+ *
+ * Since DOM parsing was successful we can now use the function
+ * @ref eina_json_context_dom_tree_take in order to get the pointer to a root
+ * object. Once we "took" the tree, we explicitly own it and its our
+ * responsibility to free it with @ref eina_json_value_free(). We can
+ * now traverse, and fully manipulate the tree pointed by @c jsnval with
+ * @ref Eina_Json_Tree_Group "Eina_Json_Value API". In this example we
+ * just print the tree to screen.
+ *
+ * And after we done we free the @c ctx instance
+ * @skipline eina_json_context_free(ctx);
+ *
+ * You can see the full source code
+ * @ref eina_json_example_01 "here".
+ */
+
+/**
+ * @page eina_json_sax_example_page @b Sax @b Parsing
+ * @dontinclude eina_json_03.c
+ * @n
+ * Unlike DOM parser, SAX parser doesn't parses the text document into a
+ * defined data structure, but instead notifies via callback function about each
+ * element and node it encounters. In general,API-wise it works pretty much the
+ * same as the @ref eina_json_dom_example_page "DOM" , the main difference that
+ * you don't get to @ref eina_json_context_dom_tree_take at the end of successful
+ * parsing. What your callback function produces is what you get.
+ *
+ * First we define a @c struct to use it as data structure for the callback
+ * @skip typedef
+ * @until Sax_Parser_Data
+ *
+ * Again, we define a json string @c my_json
+ * @skip my_json
+ * @until ;
+ *
+ * Now we define the callback function ( @ref Eina_Json_Parser_Cb )
+ * @skip void
+ * @until {
+ *
+ * The callback function takes 4 parameters:
+ * @li @c type - the type of json object that was currently parsed
+ * @li @c parent - a unique parent's id (usually pointer) representing
+ * the parent of this object. If NULL then this is a root object.
+ * (Explained further below).
+ * @li @c text - a text data of the object. Depends on @c type as follows:
+ * @par
+ * @c type = @c EINA_JSON_TYPE_STRING @c text holds the string @n
+ * @c type = @c EINA_JSON_TYPE_PAIR @c text holds the name of key value @n
+ * @c type = @c EINA_JSON_TYPE_NUMBER @c text holds the string of
+ * a number (always legal) @n
+ * @c type = @c EINA_JSON_TYPE_BOOLEAN first letter of @c text
+ * (@c *text) @c 'f' = false, @c 't' = true @n
+ *
+ * @li @c data - data that is passed to the callback
+ *
+ * The return value of this callback function is a void* parent id. So as
+ * you might have guessed, the @c parent parameter that you receive in the
+ * callback is the one that was returned from that callback earlier. If you
+ * parsing a json array with 2 numbers @a '[2,3]' it will look like that:
+ * You get first call to callback identifying array with parameters
+ * @c type = @c EINA_JSON_TYPE_ARRAY and @c parent = Some previous Id... .
+ * Your callback allocates the array in memory with address 0x001EDFC,
+ * (you save this address your private @c data) and return it. Next
+ * call for first element in the array which is number 2,
+ * with parameters: @c type = @c EINA_JSON_TYPE_NUMBER
+ * and @c parent = 0x001EDFC (right,from before) @c text = "2". The
+ * following call for a second element will be @c type =
+ @c EINA_JSON_TYPE_NUMBER and @c parent = 0x001EDFC
+ * @c text = "3". @n
+ * While its makes most sense that @ parent id will be a (void*) pointer,
+ * it doesn't necessary have to be one. As long as you have means to
+ * identify parents for their elements and common language between your
+ * callback and the parser stack its all good. Another thing about
+ * the return value, is that its only meaningful when processing
+ * parent types:
+ * EINA_JSON_TYPE_PAIR/EINA_JSON_TYPE_ARRAY/EINA_JSON_TYPE_OBJECT.
+ * Any other types can return any value (like (void*)1) except NULL.
+ * Returning NULL from the callback function will signal the parser
+ * that and error had occurred and @c EINA_JSON_ERROR_SYNTAX_TOKEN will
+ * be raised and the parser will stop.
+
+ * Now for the rest of our callback function:
+ * @until return
+ * @until }
+
+ * As you can see, our callback basically creates a string inside
+ * @c data->text which represents the elements it parses and the parent
+ * id is an increment of data->parent_idx.
+
+ * Now for the main function. First we define a variable to be passed as
+ * @c *data to our callback.
+ * @until Sax_Parser_Data
+
+ * Then we initialize it.
+ * @skip eina
+ * @until parent_idx
+ *
+ * Now create a SAX parse context instance and pass it our callback
+ * function and @c sax_parser_cb and reference to @c mysax as data.
+ * @skipline Eina_Json_Context
+ *
+ * Now we parse the the json text:
+ * @skipline eina_json_context_parse
+ *
+ * Upon successful completion we print our string in @c mysax
+ * @skip eina_json_context_completed_get
+ * @until }
+ *
+ * Not to forget to free the context
+ * @skipline eina_json_context_free
+ *
+ * You can see the full source code
+ * @ref eina_json_example_02 "here".
+ */
+
+/**
+ * @defgroup Eina_Json_Group Json
+ * Json format parser and data structure
+ *
+ * This module provides a complete set of tools for a Json format.
+ * It offers json @ref eina_json_sax_example_page "SAX" and
+ * @ref eina_json_dom_example_page "DOM" parser and json tree
+ * creation, traversal, manipulation and textual output abilities.
+ *
+ * @b Parsing @n
+ * While still offering a plain run-of-a-mill @ref eina_json_parse "eina_json_parse("
+ * @ref eina_json_parse_n "_n )" which takes a @a complete json text,parses it and
+ * then returns a json tree on success or NULL on failure,the main feature of this
+ * module is the context parsing function(s) @ref eina_json_context_parse
+ * "eina_json_context_parse(" @ref eina_json_context_parse_n "_n )". Context parsing is
+ * using a @ref Eina_Json_Context instance to parse a single json root object.
+ * The instance is serving as a state keeper of this parsing and this gives us
+ * extended abilities to:
+ * @li Stream parsing - parsing a single json root object not necessarily in one piece
+ * but as a series of sequential parts.
+ * @li Configuration for a specific parsing.
+ * @li Extended diagnostics and probing for a state of a parsing.
+ * @li Easy to add any future API and properties for @ref Eina_Json_Context.
+ *
+ * A small example. Lets say we have a small json we want to parse:
+ * @code
+ * {
+ * "Json1":"Hello",
+ * "Json2":"World",
+ * }
+ * @endcode
+ *
+ * We create new @c Eina_Json_Context instance @c called ctx
+ * @code
+ * Eina_Json_Context *ctx = eina_json_context_dom_new();
+ * @endcode
+ * (Note: We choose @ref eina_json_context_dom_new for this example,
+ * but we could've initialized @c ctx with
+ * @ref eina_json_context_sax_new just as well.)
+ *
+ * Then we can parse this json as whole:
+ * @code
+ * eina_json_context_parse(ctx, "{\"Json1\":\"Hello\",\"Json2\":\"World\"}");
+ * @endcode
+ *
+ * Or as a stream of sequential parts:
+ * @code
+ * eina_json_context_parse(ctx, "{\"Json1\":\"H");
+ * eina_json_context_parse(ctx, "ell");
+ * eina_json_context_parse(ctx, "o\",\"Json2\":\"World\"}");
+ * @endcode
+ *
+ * (The examples above parses null-terminated strings.
+ * If it isn't your case, use @ref eina_json_context_parse_n
+ * with a length of a string)
+ *
+ * At any time, @c ctx can be probed for its status. Test If parsing was
+ * completed successfully, an error occurred or neither (waits further input)
+ * and so on with @ref eina_json_context_error_get,
+ * @ref eina_json_context_completed_get etc. This @ref eina_json_example_02
+ * "example" shows the use of sequential parsing when reading json from file
+ * and parsing it in X-sized chunks.
+ *
+ * Please refer to these examples for detailed explanation:
+ * @li @ref eina_json_dom_example_page "DOM parsing"
+ * @li @ref eina_json_sax_example_page "SAX parsing"
+ */
+
+/**
+ * @addtogroup Eina_Tools_Group Tools
+ *
+ * @{
+ */
+
+/**
+ * @defgroup Eina_Json_Group Json
+ *
+ * @{
+ */
+
+typedef struct _Eina_Json_Context Eina_Json_Context;
+typedef struct _Eina_Json_Value Eina_Json_Value;
+typedef struct _Eina_Json_Output_Options Eina_Json_Output_Options;
+
+/**
+ * @typedef Eina_Json_Format
+ * Determinates how a json tree will be converted to text
+ */
+typedef enum _Eina_Json_Format
+{
+ EINA_JSON_FORMAT_PACKED,/**< Prints json in one line no spaces between delimiters */
+ EINA_JSON_FORMAT_BASIC/**< Nice, delimitered and indented human readable format */
+} Eina_Json_Format;
+
+typedef enum _Eina_Json_Type
+{
+ EINA_JSON_TYPE_NULL,
+ EINA_JSON_TYPE_NUMBER,
+ EINA_JSON_TYPE_STRING,
+ EINA_JSON_TYPE_BOOLEAN,
+ EINA_JSON_TYPE_PAIR,
+ EINA_JSON_TYPE_OBJECT,
+ EINA_JSON_TYPE_ARRAY
+} Eina_Json_Type;
+
+/**
+ * @typedef Eina_Json_Error
+ * Json parser possible error states
+ */
+typedef enum _Eina_Json_Error
+{
+ EINA_JSON_ERROR_NONE,/**< No error */
+ EINA_JSON_ERROR_LEX_TOKEN,/**< Error in lexical analysis */
+ EINA_JSON_ERROR_SYNTAX_TOKEN,/**< Error in syntax analysis */
+ EINA_JSON_ERROR_PAST_END/**< Input was entered to the parser after its already successfully parsed a json root */
+} Eina_Json_Error;
+
+typedef void*(*Eina_Json_Parser_Cb)(Eina_Json_Type type, void *parent, const char *text, void*data);
+
+/**
+ * @section Eina_Json_Parser_Group "Json Parser API"
+ *
+ * @{
+ */
+
+
+/**
+ * @brief Create new json DOM parser context
+ *
+ * @return Newly created json parser context on success or @c NULL on failure
+ *
+ */
+EAPI Eina_Json_Context *eina_json_context_dom_new() EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief @brief Create new json DOM parser context.
+ *
+ * @param cb The SAX parser @ref eina_json_sax_example_page "callback".
+ * Must not be NULL
+ * @param data The data to be passed to the callback (@p cb)
+ * @return Newly created json parser context on success or @c NULL on failure
+ *
+ */
+EAPI Eina_Json_Context *eina_json_context_sax_new(Eina_Json_Parser_Cb cb, void *data) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Reset the parser context to its initial state.
+ *
+ * @param ctx The json parser context to be reset
+ *
+ * Resets the paser context to the state it was when it was just created
+ * with @c eina_json_context_dom_new or @c eina_json_context_sax_new and
+ * makes it ready to parse a new json text.
+ * Use this function when you want to recycle an already existing json
+ * context instead of allocating a new one.
+ */
+EAPI void eina_json_context_reset(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief Free the jason parser context.
+ *
+ * @param ctx The json parser context to be freed
+ *
+ * Will free the allocated context with and all of its resourses
+ * no matter on what parsing stage it is.
+ */
+EAPI void eina_json_context_free(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief Returns the current line in a json text document being parsed
+ *
+ * @param ctx The json parser context with which the text is parsed
+ * @return Line number
+ *
+ * Returns the current location inside a parced text line-wise.
+ * As any text document the line count starts from 1.
+ */
+EAPI unsigned eina_json_context_line_get(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief Returns the current column in a json text document being parsed
+ *
+ * @param ctx The json parser context with which the text is parsed
+ * @return Column number
+ *
+ * Returns the current location inside a parced text column-wise.
+ * As any text document the column count starts from 1.
+ */
+EAPI unsigned eina_json_context_column_get(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief Returns the error state of parse context
+ *
+ * @param ctx The json parser context to be probed for error state.
+ * @return Error type of @ref Eina_Json_Error.
+ *
+ * Probe the json parser context for error. If last @c eina_json_context_parse(_n)
+ * resulted in an error, it will be returned by this function. If no error occured
+ * @c EINA_JSON_ERROR_NONE will be returned.
+ *
+ */
+EAPI Eina_Json_Error eina_json_context_error_get(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief Returns whether a context parsing was succesefully completed
+ *
+ * @param ctx The json parser context to be probed for complete state.
+ * @return @c EINA_TRUE if completed, @c EINA_FALSE is not.
+ *
+ * Returns true when a single json root was parsed without errors.
+ * @note Any input of non blank characters after parsing was completed
+ * will result in an @c EINA_ERROR_PAST_END error.
+ *
+ */
+EAPI Eina_Bool eina_json_context_completed_get(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief Returns whether a context parsing awaits further input to complete.
+ *
+ * @param ctx The json parser context to be probed for unfinished state.
+ * @return @c EINA_TRUE if awaits further input or @c EINA_FALSE if not.
+ *
+ * Basically, this function return true when context parser in neither completed
+ * and neither errorneous. Usefull in read loops.
+ *
+ */
+EAPI Eina_Bool eina_json_context_unfinished_get(Eina_Json_Context *ctx);
+
+
+/**
+ * @brief "Takes" parsed json DOM tree from parsing context and returns it.
+ *
+ * @param ctx The sucessefully parsed DOM parser context.
+ * @return Json tree on sucess or @c NULL on error.
+ *
+ * Once a DOM context parsing was succesefully completed, use this function to
+ * obtain its json tree. After the tree is returned and recived into your variable,
+ * it will be unreferenced by the json parse context. It will be your responsibility to
+ * free it with @ref eina_json_value_free once you "took" it. This function will return
+ * @c NULL and will issue an appropriate error message if the @p ctx is not a dom context,
+ * parseing is incomplete or errorneous, or if the tree was already "taken".
+ *
+ */
+EAPI Eina_Json_Value *eina_json_context_dom_tree_take(Eina_Json_Context *ctx) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Parse json text into a a json tree.
+ *
+ * @param text Null terminated string of complete json root object
+ * @return A json tree on sucesses, @c NULL otherwise
+ *
+ * A simple json parsing function. On sucess returns a newly allocated json tree.
+ * If parsing fails for whatsoverver reason, @c NULL will be returned.
+ * @note Its your resonsibility to free the returned tree with
+ * @ref eina_json_value_free
+ *
+ */
+EAPI Eina_Json_Value *eina_json_parse(const char *text);
+
+
+/**
+ * @brief Parse json text of a maximum specified length into a a json tree.
+ *
+ * @param text String of complete json root object
+ * @param text_len The length of @p text
+ * @return A json tree on sucesses, @c NULL otherwise
+ *
+ * This function is same as @ref eina_json_parse. Use it when you have strings
+ * of specified length and not necceserelly null terminated. If however a
+ * null-terminating character will occur earlier than @p text_len, it will be
+ * the end of string.
+ *
+ * @note Its your resonsibility to free the returned tree with
+ * @ref eina_json_value_free
+ *
+ */
+EAPI Eina_Json_Value *eina_json_parse_n(const char *text, unsigned text_len);
+
+
+/**
+ * @brief Parse json text using a parse context
+ *
+ * @param ctx Json parsing context
+ * @param text Null terminated string of complete or sequantial part of json root object
+ * @return @c EINA_FALSE if error occured during parsing, otherwise @c EINA_TRUE
+ *
+ * This function is the core of this module and its usage is documented in the genearal
+ * documentation and examples.
+ *
+ */
+EAPI Eina_Bool eina_json_context_parse(Eina_Json_Context *ctx, const char *text);
+
+
+/**
+ * @brief Parse json text of a maximum specified length using a parse context
+ *
+ * @param ctx Json parsing context
+ * @param text String of complete or sequantial part of json root object
+ * @return @c EINA_FALSE if error occured during parsing, otherwise @c EINA_TRUE
+ *
+ * This function is same as @ref eina_json_context_parse .Use it when you have strings
+ * of specified length and not necceserelly null terminated. If however a
+ * null-terminating character will occur earlier than @p text_len, it will be
+ * the end of string.
+ *
+ */
+EAPI Eina_Bool eina_json_context_parse_n(Eina_Json_Context *ctx, const char *text, unsigned text_len);
+
+/**
+ * @brief Turn json tree into json text
+ *
+ * @param jsnval Json tree
+ * @param format A text format you wish your json text to be. See @ref Eina_Json_Format
+ * @return Null terminated string of json text
+ *
+ * Takes a json value object and returns a null terminated string reperesenting the text
+ * in a format specified by @p format. Its your responsibility to free the string.
+ *
+ */
+EAPI char * eina_json_format_string_get(Eina_Json_Value *jsnval, Eina_Json_Format format) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @}
+ */
+
+/**
+ * @section Eina_Json_Tree_Group "Json Value API"
+ *
+ * @{
+ *
+ */
+
+/**
+ * @brief Creates json number value object
+ *
+ * @param num Number to initilize json value object with.
+ * @return Newly allocated json object of type @c EINA_JSON_TYPE_NUMBER
+ *
+ */
+EAPI Eina_Json_Value *eina_json_number_new(double num) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Creates json string value object
+ *
+ * @param string String to initilize json value object with.
+ * @return Newly allocated json object of type @c EINA_JSON_TYPE_STRING
+ *
+ */
+EAPI Eina_Json_Value *eina_json_string_new(const char *string) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Creates json boolean value object
+ *
+ * @param boolval Boolean value to initilize json value object with.
+ * @return Newly allocated json object of type @c EINA_JSON_TYPE_BOOLEAN
+ *
+ */
+EAPI Eina_Json_Value *eina_json_boolean_new(Eina_Bool boolval) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Creates json null value object
+ *
+ * @return Newly allocated json object of type @c EINA_JSON_TYPE_NULL
+ *
+ */
+EAPI Eina_Json_Value *eina_json_null_new() EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Creates json object container value object
+ *
+ * @return Newly allocated json object of type @c EINA_JSON_TYPE_OBJECT
+ *
+ */
+EAPI Eina_Json_Value *eina_json_object_new() EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Creates json array value object
+ *
+ * @return Newly allocated json object of type @c EINA_JSON_TYPE_ARRAY
+ *
+ */
+EAPI Eina_Json_Value *eina_json_array_new() EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Free json value object
+ *
+ * Frees allocated json value and its childern (if any) recursevly.
+ * @warning If a json object is a child of another object, freeing will fail
+ * and appropriate message will be logged.
+ *
+ */
+EAPI void eina_json_value_free(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Get the type of json value object
+ *
+ * @param jsnval Json value object
+ * @return @p jsnval type
+ *
+ */
+EAPI Eina_Json_Type eina_json_type_get(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Get number value of a json number value object
+ *
+ * @param jsnval Json value object of type @c EINA_JSON_TYPE_NUMBER
+ * @return Number value of a @p jsnval
+ *
+ */
+EAPI double eina_json_number_get(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Get string value of a json string value object
+ *
+ * @param jsnval Json value object of type @c EINA_JSON_TYPE_STRING
+ * @return String value of a @p jsnval
+ *
+ */
+EAPI const char *eina_json_string_get(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Get boolean value of a json boolean value object
+ *
+ * @param jsnval Json value object of type @c EINA_JSON_TYPE_BOOLEAN
+ * @return Boolean value of a @p jsnval
+ *
+ */
+EAPI Eina_Bool eina_json_boolean_get(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Get key name part of a key-value pair
+ *
+ * @param jsnval Json value object of type @c EINA_JSON_TYPE_PAIR
+ * @return Key name string
+ *
+ */
+EAPI const char *eina_json_pair_name_get(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Get value part of a key-value pair
+ *
+ * @param jsnval Json value object of type @c EINA_JSON_TYPE_PAIR
+ * @return Json value object reperesenting the value part of key-value pair
+ *
+ */
+EAPI Eina_Json_Value *eina_json_pair_value_get(Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Set number value to a json number value object
+ *
+ * @param num Json value object of type @c EINA_JSON_TYPE_NUMBER
+ * @param num Number to set json value object to.
+ * @return @c EINA_TRUE if setting sucesseded, otherwise EINA_FALSE
+ *
+ */
+EAPI Eina_Bool eina_json_number_set(Eina_Json_Value *jsnval, double num);
+
+
+/**
+ * @brief Set string value to a json string value object
+ *
+ * @param num Json value object of type @c EINA_JSON_TYPE_STRING
+ * @param string String to set json value object to.
+ * @return @c EINA_TRUE if setting sucesseded, otherwise EINA_FALSE
+ *
+ */
+EAPI Eina_Bool eina_json_string_set(Eina_Json_Value *jsnval, const char* string);
+
+
+/**
+ * @brief Set boolean value to a json string value object
+ *
+ * @param num Json value object of type @c EINA_JSON_TYPE_BOOLEAN
+ * @param boolval Boolean value to set json value object to.
+ * @return @c EINA_TRUE if setting sucesseded, otherwise EINA_FALSE
+ *
+ */
+EAPI Eina_Bool eina_json_boolean_set(Eina_Json_Value *jsnval, Eina_Bool boolval);
+
+
+/**
+ * @brief Get number of key-value pairs inside a json object
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @return Number of elements (key-value pairs) inside @p obj
+ *
+ */
+EAPI unsigned eina_json_object_count_get(Eina_Json_Value *obj);
+
+
+/**
+ * @brief Get the nth element of json object
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @param idx Index inside the @p obj
+ * @return Jason value object of @c EINA_JSON_TYPE_PAIR or @c NULL if @p idx
+ * out of bounds or other possible error
+ *
+ */
+EAPI Eina_Json_Value *eina_json_object_nth_get(Eina_Json_Value *obj, unsigned idx);
+
+
+/**
+ * @brief Append new key-value pair to the end of json object
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @param keyname Name of key in key-value pair
+ * @param jsnval Json value object as a value part of key-value pair. Must NOT be a @c EINA_JSON_TYPE_PAIR
+ * @return Reference of newly appended key-value pair inside @p obj, or @c NULL on failure
+ *
+ * @warning Once json value is appended or inserted into another json object or array its
+ * fully belongs to it. It can be freed only by that container and cannot be appended or
+ * inserted to any other. Doing so will triger a log message and error value will be returned.
+ */
+EAPI Eina_Json_Value *eina_json_object_append(Eina_Json_Value *obj, const char *keyname, Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Insert new key-value pair at nth place inside the json object
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @param idx Index inside the @p obj to insert the element at
+ * @param keyname Name of key in key-value pair
+ * @param jsnval Json value object as a value part of key-value pair. Must NOT be
+ * a @c EINA_JSON_TYPE_PAIR
+ * @return Reference of newly inserted key-value pair inside @p obj, or @c NULL on failure
+ *
+ * @note Insertion at index 0 on empty object will append the pair. Insertion when index is out of bounds
+ * will return NULL.
+
+ * @warning Once json value is appended or inserted into another json object or array its
+ * fully belongs to it. It can be freed only by that container and cannot be appended or
+ * inserted to any other. Doing so will triger a log message and error value will be returned.
+ *
+ */
+EAPI Eina_Json_Value *eina_json_object_insert(Eina_Json_Value *obj, unsigned idx, const char *keyname, Eina_Json_Value *jsnval);
+
+/**
+ * @brief Remove and free a key-value pair at nth place from the json object
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @param idx Index inside the @p obj to remove the element at.
+ * @return @c EINA_TRUE if removal sucedeed, @c EINA_FALSE otherwise
+ *
+ * The remove element will be freed with @ref eina_json_value_free - thus all of its
+ * children will be freed too recursevly.
+ *
+ */
+EAPI Eina_Bool eina_json_object_nth_remove(Eina_Json_Value *obj, unsigned idx);
+
+
+/**
+ * @brief Get @ref Eina_Iterator to iterate over objects key-value elements.
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @return Json obect's @ref Eina_Iterator
+ *
+ * This is the fastest way to iterate over the jason object's elements. Use it
+ * like any other Eina containers itterator, dont forgewt to free it in the end.
+ * All elements of object are of @c EINA_JSON_TYPE_PAIR type
+ *
+ */
+EAPI Eina_Iterator *eina_json_object_iterator_new(Eina_Json_Value *obj) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @brief Get value assosiated with a given key in json object recursively
+ *
+ * @param obj Json value object of type @c EINA_JSON_TYPE_OBJECT
+ * @param ... List of char* key names leading to the desired object. Must end with @c NULL
+ * @return Json value object if found, @c NULL otherwise.
+ *
+ * Finds the corresponding value in a key-value pair inside json object. This a recursive
+ * function so you can specify a number of keys and traverse into the children objects as well.
+ * If a key occurs more than once in the same object, only its first occurrence will be returned.
+ *
+ */
+EAPI Eina_Json_Value *eina_json_object_value_get_internal(Eina_Json_Value *obj, ...);
+
+
+/**
+ * @def eina_json_object_value_get
+ * A convenience wrapper around eina_json_object_value_get_internal()
+ * No @c NULL required at the end of paramenters
+ * @see eina_json_object_value_get_internal
+ */
+#define eina_json_object_value_get(obj, args...) eina_json_object_value_get_internal(obj, ## args, NULL)
+
+
+/**
+ * @brief Get number of elements inside a json array
+ *
+ * @param arr Json value object of type @c EINA_JSON_TYPE_ARRAY
+ * @return Number of elements inside @p arr
+ *
+ */
+EAPI unsigned eina_json_array_count_get(Eina_Json_Value *arr);
+
+
+/**
+ * @brief Get the nth element of json array
+ *
+ * @param arr Json value object of type @c EINA_JSON_TYPE_ARRAY
+ * @param idx Index inside the @p arr
+ * @return Json value object or @c NULL if @p idx out of bounds or other possible error
+ *
+ */
+EAPI Eina_Json_Value *eina_json_array_nth_get(Eina_Json_Value *arr, unsigned idx);
+
+
+/**
+ * @brief Append new element to the end of json array
+ *
+ * @param arr Json value object of type @c EINA_JSON_TYPE_ARRAY
+ * @param jsnval Json value object to be appended. Must NOT be a @c EINA_JSON_TYPE_PAIR
+ * @return Reference of newly appended elemnt inside @p arr or @c NULL on failure
+ *
+ * @warning Once json value is appended or inserted into another json object or array its
+ * fully belongs to it. It can be freed only by that container and cannot be appended or
+ * inserted to any other. Doing so will triger a log message and error value will be returned.
+ */
+EAPI Eina_Json_Value *eina_json_array_append(Eina_Json_Value *arr, Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Insert new element at nth place inside the json array
+ *
+ * @param arr Json value object of type @c EINA_JSON_TYPE_ARRAY
+ * @param idx Index inside the @p arr to insert the element at
+ * @param jsnval Json value object to be inserted. Must NOT be a @c EINA_JSON_TYPE_PAIR
+ * @return Reference of newly inserted element @p arr or @c NULL on failure
+ *
+ * @note Insertion at index 0 on empty array will append the element. Insertion when index is out of bounds
+ * will return NULL.
+
+ * @warning Once json value is appended or inserted into another json object or array its
+ * fully belongs to it. It can be freed only by that container and cannot be appended or
+ * inserted to any other. Doing so will triger a log message and error value will be returned.
+ *
+ */
+EAPI Eina_Json_Value *eina_json_array_insert(Eina_Json_Value *arr, unsigned idx, Eina_Json_Value *jsnval);
+
+
+/**
+ * @brief Remove and free an element at nth place from the json object
+ *
+ * @param arr Json value object of type @c EINA_JSON_TYPE_ARRAY
+ * @param idx Index inside the @p arr to remove the element at.
+ * @return @c EINA_TRUE if removal sucedeed, @c EINA_FALSE otherwise
+ *
+ * The remove element will be freed with @ref eina_json_value_free - thus all of its
+ * children will be freed too, recursevly.
+ *
+ */
+EAPI Eina_Bool eina_json_array_nth_remove(Eina_Json_Value *arr, unsigned idx);
+
+
+/**
+ * @brief Get @ref Eina_Iterator to iterate over array elements.
+ *
+ * @param arr Json value object of type @c EINA_JSON_TYPE_ARRAY
+ * @return Json obect's @ref Eina_Iterator
+ *
+ * This is the fastest way to iterate over the jason array's elements. Use it
+ * like any other Eina containers itterator, dont forget to free it in the end.
+ *
+ */
+EAPI Eina_Iterator *eina_json_array_iterator_new(Eina_Json_Value *arr) EINA_WARN_UNUSED_RESULT;
+
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif
diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c
index 1577b3aebb..12a14f2de0 100644
--- a/src/tests/eina/eina_suite.c
+++ b/src/tests/eina/eina_suite.c
@@ -73,6 +73,7 @@ static const Eina_Test_Case etc[] = {
// { "Model", eina_test_model },
{ "Barrier", eina_test_barrier },
{ "Tmp String", eina_test_tmpstr },
+ { "JSON", eina_test_json },
{ NULL, NULL }
};
diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h
index 85a32cf556..76b3f29818 100644
--- a/src/tests/eina/eina_suite.h
+++ b/src/tests/eina/eina_suite.h
@@ -60,5 +60,6 @@ void eina_test_model(TCase *tc);
void eina_test_cow(TCase *tc);
void eina_test_barrier(TCase *tc);
void eina_test_tmpstr(TCase *tc);
+void eina_test_json(TCase *tc);
#endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina/eina_test_json.c b/src/tests/eina/eina_test_json.c
new file mode 100644
index 0000000000..815de2603c
--- /dev/null
+++ b/src/tests/eina/eina_test_json.c
@@ -0,0 +1,613 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2013 Yossi Kantor
+ Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include "eina_suite.h"
+#include "Eina.h"
+
+typedef struct
+{
+ unsigned line;
+ unsigned col;
+} TextPos;
+
+typedef struct
+{
+ Eina_Strbuf *text;
+ unsigned long parent_idx;
+} Sax_Parser_Data;
+
+static const char *json_type_string[] = {"NL","NR","STR","BN","PAR","OBJ","ARR"};
+
+static const char jstr_array_before[] =
+"{\
+\"Array1\":[0,1,2,3,4,5,6],\n\
+\"Array2\":[56,\"He\",true,\"Hello\",false,null],\n\
+\"Array3\":[\"\",\"He\",true,\"Hello\",false,null],\n\
+\"Array4\":[]\
+}";
+
+static const char jstr_array_after[] =
+"{\"Array1\":[0,1,2,3,4,5,6],\"Array2\":[57,56,156,\"new\",\
+false,\"Bye\",true,null],\"Array3\":[],\"Array4\":[1]}";
+
+static const char jstr_object_before[] =
+"{\
+\"Object1\":{\"0\":0,\"1\":1,\"2\":2,\"3\":3,\"4\":4,\"5\":5,\"6\":6},\n\
+\"Object2\":{\"Num1\":56,\"Str1\":\"Str1\",\"Bool1\":true,\"Str2\":\"Hello\",\"Null\":null},\n\
+\"Object3\":{\"Num1\":56,\"Bool1\":true,\"String1\":\"String\",\"Null\":null},\
+\"Object4\":{}\
+}";
+
+static const char jstr_object_after[] =
+"{\"Object1\":{\"0\":0,\"1\":1,\"2\":2,\"3\":3,\"4\":4,\"5\":5,\"6\":6},\
+\"Object2\":{\"Num3\":57,\"Num2\":56,\"Num1\":156,\"Str3\":\"new\",\"Bool1\":false,\
+\"Str2\":\"HelloStr2\",\"Null\":null},\"Object3\":{},\"Object4\":{\"NumberOne\":1},\
+\"Object5\":true}";
+
+static const char jstr_object_tree[] =
+"{ \"Obj1\":{ \"Obj1_1\":11, \"Obj1_2\":12 },\"Obj2\":2 }";
+
+static const TextPos pos_full = {32,2};
+static const char jstr_full[] =
+"{\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\":\"Smith\",\n\
+ \"Type3\":25,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true,\n\
+ \"Type6\":false,\n\
+ \"Type7\":\n\
+ {\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\":\"Smith\",\n\
+ \"Type3\":25,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true,\n\
+ \"Type6\":false\n\
+ },\n\
+ \"Type8\":[\"John\",\"Smith\",\" Escaped \\\" \",25,null,true,false],\n\
+ \"TypeNum\":[0,-1,1,2.0,3.45,-4.67e2,5e-1,6e3,5e+1,-5.6e+2],\n\
+ \"TypeMix\":\n\
+ [\n\
+ 67,null,[],{\"Hello\":[true]},false,\"Bye\",\n\
+ {\n\
+ \"Type21\":\"John\",\n\
+ \"Type22\":\"Smith\",\n\
+ \"Type23\":{}\n\
+ },\n\
+ {\n\
+ \"type\":\"fax\",\n\
+ \"number\":\"646 555-4567\"\n\
+ }\n\
+ ]\n\
+}";
+
+static const char jstr_double_root[] =
+"{\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\":\"Smith\",\n\
+ \"Type3\":25,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true,\n\
+ \"Type6\":false,\n\
+ \"Type7\":\n\
+ {\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\":\"Smith\",\n\
+ \"Type3\":25,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true,\n\
+ \"Type6\":false\n\
+ },\n\
+ \"Type8\":[\"John\",\"Smith\",\" Escaped \\\" \",25,null,true,false],\n\
+ \"TypeNum\":[0,-1,1,2.0,3.45,-4.67e2,5e-1,6e3,5e+1,-5.6e+2],\n\
+ \"TypeMix\":\n\
+ [\n\
+ 67,null,[],{\"Hello\":[true]},false,\"Bye\",\n\
+ {\n\
+ \"Type21\":\"John\",\n\
+ \"Type22\":\"Smith\",\n\
+ \"Type23\":{}\n\
+ },\n\
+ {\n\
+ \"type\":\"fax\",\n\
+ \"number\":\"646 555-4567\"\n\
+ }\n\
+ ]\n\
+} \n\
+{ \"DoubleRoot\":null }\n\
+";
+
+static const TextPos pos_lex_error = {4,13};
+char jstr_lex_error[] =
+"{\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\":\"Smith\",\n\
+ \"Type3\":25a,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true,\n\
+}";
+
+static const TextPos pos_syntax_error = {3,17};
+char jstr_syntax_error[] =
+"{\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\" \"Smith\",\n\
+ \"Type3\":25,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true,\n\
+}";
+
+static const TextPos pos_incomplete = {6,15};
+char jstr_incomplete[]=
+"{\n\
+ \"Type1\":\"John\",\n\
+ \"Type2\":\"Smith\",\n\
+ \"Type3\":25,\n\
+ \"Type4\":null,\n\
+ \"Type5\":true";
+
+
+static const char jstr_full_packed[] =
+"\
+{\"Type1\":\"John\",\"Type2\":\"Smith\",\"Type3\":25,\"Type4\":null,\
+\"Type5\":true,\"Type6\":false,\"Type7\":{\"Type1\":\"John\",\
+\"Type2\":\"Smith\",\"Type3\":25,\"Type4\":null,\"Type5\":true,\
+\"Type6\":false},\"Type8\":[\"John\",\"Smith\",\
+\" Escaped \\\" \",25,null,true,false],\
+\"TypeNum\":[0,-1,1,2,3.45,-467,0.50,6000,50,-560],\
+\"TypeMix\":[67,null,[],{\"Hello\":[true]},false,\"Bye\",\
+{\"Type21\":\"John\",\"Type22\":\"Smith\",\"Type23\":{}},\
+{\"type\":\"fax\",\"number\":\"646 555-4567\"}]}\
+";
+
+static const char jstr_sax_result[] =
+"\
+(0x2)PR((nil)):OBJ(0x3)PR(0x2):PAR(\"Type1\")(0x4)PR(0x3):STR(\"John\")\
+(0x5)PR(0x2):PAR(\"Type2\")(0x6)PR(0x5):STR(\"Smith\")(0x7)PR(0x2):\
+PAR(\"Type3\")(0x8)PR(0x7):NR(\"25\")(0x9)PR(0x2):PAR(\"Type4\")(0xa)PR\
+(0x9):NL(0xb)PR(0x2):PAR(\"Type5\")(0xc)PR(0xb):BN(\"true\")(0xd)PR(0x2):\
+PAR(\"Type6\")(0xe)PR(0xd):BN(\"false\")(0xf)PR(0x2):PAR(\"Type7\")\
+(0x10)PR(0xf):OBJ(0x11)PR(0x10):PAR(\"Type1\")(0x12)PR(0x11):\
+STR(\"John\")(0x13)PR(0x10):PAR(\"Type2\")(0x14)PR(0x13):STR(\"Smith\")\
+(0x15)PR(0x10):PAR(\"Type3\")(0x16)PR(0x15):NR(\"25\")(0x17)PR(0x10):\
+PAR(\"Type4\")(0x18)PR(0x17):NL(0x19)PR(0x10):PAR(\"Type5\")(0x1a)PR(0x19)\
+:BN(\"true\")(0x1b)PR(0x10):PAR(\"Type6\")(0x1c)PR(0x1b):BN(\"false\")\
+(0x1d)PR(0x2):PAR(\"Type8\")(0x1e)PR(0x1d):ARR(0x1f)PR(0x1e):STR(\"John\")\
+(0x20)PR(0x1e):STR(\"Smith\")(0x21)PR(0x1e):STR(\" Escaped \\\" \")\
+(0x22)PR(0x1e):NR(\"25\")(0x23)PR(0x1e):NL(0x24)PR(0x1e):BN(\"true\")\
+(0x25)PR(0x1e):BN(\"false\")(0x26)PR(0x2):PAR(\"TypeNum\")(0x27)PR(0x26)\
+:ARR(0x28)PR(0x27):NR(\"0\")(0x29)PR(0x27):NR(\"-1\")(0x2a)PR(0x27):\
+NR(\"1\")(0x2b)PR(0x27):NR(\"2.0\")(0x2c)PR(0x27):NR(\"3.45\")(0x2d)\
+PR(0x27):NR(\"-4.67e2\")(0x2e)PR(0x27):NR(\"5e-1\")(0x2f)PR(0x27):\
+NR(\"6e3\")(0x30)PR(0x27):NR(\"5e+1\")(0x31)PR(0x27):NR(\"-5.6e+2\")\
+(0x32)PR(0x2):PAR(\"TypeMix\")(0x33)PR(0x32):ARR(0x34)PR(0x33):\
+NR(\"67\")(0x35)PR(0x33):NL(0x36)PR(0x33):ARR(0x37)PR(0x33):OBJ(0x38)\
+PR(0x37):PAR(\"Hello\")(0x39)PR(0x38):ARR(0x3a)PR(0x39):BN(\"true\")\
+(0x3b)PR(0x33):BN(\"false\")(0x3c)PR(0x33):STR(\"Bye\")(0x3d)PR(0x33):\
+OBJ(0x3e)PR(0x3d):PAR(\"Type21\")(0x3f)PR(0x3e):STR(\"John\")(0x40)\
+PR(0x3d):PAR(\"Type22\")(0x41)PR(0x40):STR(\"Smith\")(0x42)PR(0x3d)\
+:PAR(\"Type23\")(0x43)PR(0x42):OBJ(0x44)PR(0x33):OBJ(0x45)PR(0x44)\
+:PAR(\"type\")(0x46)PR(0x45):STR(\"fax\")(0x47)PR(0x44):PAR(\"number\")\
+(0x48)PR(0x47):STR(\"646 555-4567\")\
+";
+
+static void *
+sax_parser_cb(Eina_Json_Type type, void *parent, const char *text, void *data)
+{
+ Sax_Parser_Data *saxdata = (Sax_Parser_Data *)data;
+ const char* strval = NULL;
+
+ switch(type)
+ {
+ case EINA_JSON_TYPE_NUMBER:
+ case EINA_JSON_TYPE_STRING:
+ case EINA_JSON_TYPE_PAIR:
+ strval = text;
+ break;
+ case EINA_JSON_TYPE_BOOLEAN:
+ strval = (*text == 't') ? "true" : "false";
+ break;
+ default:
+ break;
+ }
+
+ saxdata->parent_idx++;
+ eina_strbuf_append_printf(saxdata->text,
+ "(%p)PR(%p):%s",
+ (void*)saxdata->parent_idx,
+ parent,
+ json_type_string[type]);
+
+ if (strval) eina_strbuf_append_printf(saxdata->text, "(\"%s\")", strval);
+
+ return (void*)saxdata->parent_idx;
+}
+
+START_TEST(eina_json_parse_test)
+{
+ eina_init();
+
+ Eina_Json_Value* jval = eina_json_parse(jstr_full);
+ fail_unless(jval != NULL);
+ char* jval_str = eina_json_format_string_get(jval, EINA_JSON_FORMAT_PACKED);
+ fail_if(strcmp(jstr_full_packed, jval_str));
+ free(jval_str);
+ eina_json_value_free(jval);
+
+ fail_if(eina_json_parse(jstr_lex_error));
+ fail_if(eina_json_parse(jstr_syntax_error));
+ fail_if(eina_json_parse(jstr_incomplete));
+ fail_if(eina_json_parse(jstr_double_root));
+
+ int json_full_len = strlen(jstr_full);
+
+ jval = eina_json_parse_n(jstr_double_root, json_full_len);
+ fail_unless(jval != NULL);
+ jval_str = eina_json_format_string_get(jval, EINA_JSON_FORMAT_PACKED);
+ fail_if(strcmp(jstr_full_packed, jval_str));
+ free(jval_str);
+ eina_json_value_free(jval);
+
+ jval = eina_json_parse_n(jstr_full, 0xFFFF);
+ fail_unless(jval != NULL);
+ jval_str = eina_json_format_string_get(jval, EINA_JSON_FORMAT_PACKED);
+ fail_if(strcmp(jstr_full_packed, jval_str));
+ free(jval_str);
+ eina_json_value_free(jval);
+
+ fail_if(eina_json_parse_n(jstr_full, json_full_len / 2));
+ fail_if(eina_json_parse_n(jstr_full, 0));
+
+ eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_json_context_dom_parse_test)
+{
+ eina_init();
+
+ Eina_Bool res;
+ Eina_Json_Context *ctx = eina_json_context_dom_new();
+ fail_if(eina_json_context_dom_tree_take(ctx));
+ res = eina_json_context_parse(ctx, jstr_full);
+ fail_unless(res);
+ fail_unless(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_unfinished_get(ctx));
+ fail_if(eina_json_context_line_get(ctx) != pos_full.line);
+ fail_if(eina_json_context_column_get(ctx) != pos_full.col);
+ fail_unless(eina_json_context_error_get(ctx) == EINA_JSON_ERROR_NONE);
+
+ res = eina_json_context_parse(ctx, "{");
+ fail_if(res);
+ fail_if(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_dom_tree_take(ctx));
+ fail_if(eina_json_context_unfinished_get(ctx));
+ fail_if(eina_json_context_line_get(ctx) != pos_full.line);
+ fail_if(eina_json_context_column_get(ctx) != pos_full.col);
+ fail_unless(eina_json_context_error_get(ctx) == EINA_JSON_ERROR_PAST_END);
+
+ eina_json_context_reset(ctx);
+
+ res = eina_json_context_parse(ctx, jstr_incomplete);
+ fail_unless(res);
+ fail_if(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_line_get(ctx) != pos_incomplete.line);
+ fail_if(eina_json_context_column_get(ctx) != pos_incomplete.col);
+ fail_unless(eina_json_context_error_get(ctx) == EINA_JSON_ERROR_NONE);
+
+ res = eina_json_context_parse(ctx, "}");
+ fail_unless(res);
+ fail_unless(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_line_get(ctx) != pos_incomplete.line);
+ fail_if(eina_json_context_column_get(ctx) != pos_incomplete.col+1);
+ fail_unless(eina_json_context_error_get(ctx) == EINA_JSON_ERROR_NONE);
+
+ eina_json_context_reset(ctx);
+
+ res = eina_json_context_parse(ctx, jstr_lex_error);
+ fail_if(res);
+ fail_if(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_line_get(ctx) != pos_lex_error.line);
+ fail_if(eina_json_context_column_get(ctx) != pos_lex_error.col);
+ fail_unless(eina_json_context_error_get(ctx) == EINA_JSON_ERROR_LEX_TOKEN);
+
+ eina_json_context_reset(ctx);
+
+ res = eina_json_context_parse(ctx, jstr_syntax_error);
+ fail_if(res);
+ fail_if(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_line_get(ctx) != pos_syntax_error.line);
+ fail_if(eina_json_context_column_get(ctx) != pos_syntax_error.col);
+ fail_unless(eina_json_context_error_get(ctx) == EINA_JSON_ERROR_SYNTAX_TOKEN);
+
+ eina_json_context_reset(ctx);
+
+ int len = strlen(jstr_double_root);
+ const int bsize = 3;
+ int head = 0;
+
+ while (eina_json_context_unfinished_get(ctx))
+ {
+ eina_json_context_parse_n(ctx, &jstr_double_root[head], bsize);
+ head += bsize;
+ fail_if(len-head < bsize);
+ }
+
+ Eina_Json_Value *jval = eina_json_context_dom_tree_take(ctx);
+ fail_unless(jval != NULL);
+ fail_if(eina_json_context_dom_tree_take(ctx));
+ char *jval_str = eina_json_format_string_get(jval, EINA_JSON_FORMAT_PACKED);
+ fail_if(strcmp(jstr_full_packed, jval_str));
+ free(jval_str);
+ eina_json_value_free(jval);
+
+ eina_json_context_free(ctx);
+ eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_json_context_sax_parse_test)
+{
+ eina_init();
+
+ Sax_Parser_Data testsax;
+
+ testsax.text = eina_strbuf_new();
+ testsax.parent_idx = 1;
+
+ Eina_Json_Context *ctx = eina_json_context_sax_new(sax_parser_cb, &testsax);
+
+ Eina_Bool res = eina_json_context_parse(ctx, jstr_full);
+ fail_unless(res);
+ fail_unless(eina_json_context_completed_get(ctx));
+ fail_if(eina_json_context_dom_tree_take(ctx));
+
+ fail_if(strcmp(eina_strbuf_string_get(testsax.text), jstr_sax_result));
+
+ eina_strbuf_free(testsax.text);
+ eina_json_context_free(ctx);
+ eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_json_object_test)
+{
+ eina_init();
+
+ Eina_Json_Value * jobj = eina_json_parse(jstr_object_before);
+ fail_if(!jobj);
+
+ Eina_Json_Value * tmp;
+
+ Eina_Json_Value *obj1 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 0));
+ Eina_Json_Value *obj2 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 1));
+ Eina_Json_Value *obj3 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 2));
+ Eina_Json_Value *obj4 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 3));
+
+ fail_if(eina_json_object_nth_get(obj4, 0));
+
+ tmp = eina_json_number_new(1);
+ fail_if(eina_json_object_insert(obj4, 1, "tmp", tmp));
+ eina_json_value_free(tmp);
+
+ fail_unless(eina_json_object_insert(obj4, 0, "NumberOne",
+ eina_json_number_new(1)) != NULL);
+
+ fail_unless(eina_json_object_nth_get(obj4, 0) != NULL );
+
+ tmp = eina_json_number_new(1);
+ fail_if(eina_json_object_insert(obj4, 1, "tmp", tmp));
+ eina_json_value_free(tmp);
+
+ fail_if(eina_json_object_nth_get(obj3, 11));
+ fail_if(eina_json_object_nth_remove(obj3, 11));
+ fail_if(eina_json_object_nth_remove(obj3, 7));
+ fail_if(eina_json_object_count_get(obj3) != 4);
+ while(eina_json_object_count_get(obj3))
+ fail_unless(eina_json_object_nth_remove(obj3, 0));
+
+ fail_if(eina_json_object_nth_remove(obj3, 0));
+ fail_if(eina_json_object_nth_get(obj3, 0));
+
+ tmp = eina_json_pair_value_get(eina_json_object_nth_get(obj2, 4));
+ fail_unless(eina_json_type_get(tmp) == EINA_JSON_TYPE_NULL);
+
+ fail_unless(eina_json_object_nth_remove(obj2, 1));
+
+ tmp = eina_json_pair_value_get(eina_json_object_nth_get(obj2, 3));
+ fail_unless(eina_json_type_get(tmp) == EINA_JSON_TYPE_NULL);
+
+ tmp = eina_json_pair_value_get(eina_json_object_nth_get(obj2, 1));
+ fail_unless(eina_json_boolean_set(tmp, !eina_json_boolean_get(tmp)));
+
+ tmp = eina_json_pair_value_get(eina_json_object_nth_get(obj2, 0));
+ fail_unless(eina_json_number_set(tmp, eina_json_number_get(tmp) + 100));
+
+ tmp = eina_json_object_nth_get(obj2, 2);
+ Eina_Strbuf *joinstr = eina_strbuf_new();
+ eina_strbuf_append(joinstr,
+ eina_json_string_get(eina_json_pair_value_get(tmp)));
+
+ eina_strbuf_append(joinstr, eina_json_pair_name_get(tmp));
+ fail_unless(eina_json_string_set(eina_json_pair_value_get(tmp),
+ eina_strbuf_string_get(joinstr)));
+
+ eina_strbuf_free(joinstr);
+
+ fail_unless(eina_json_object_insert(obj2, 0, "Num2",
+ eina_json_number_new(56)) != NULL );
+ fail_unless(eina_json_object_insert(obj2, 0, "Num3",
+ eina_json_number_new(57)) != NULL);
+ fail_unless(eina_json_object_insert(obj2, 3, "Str3",
+ eina_json_string_new("new")) != NULL);
+
+ tmp = eina_json_string_new("fail");
+ fail_if(eina_json_object_insert(obj2, 10, "fail", tmp));
+ eina_json_value_free(tmp);
+
+ int serial;
+ fail_unless(eina_json_object_count_get(obj1) == 7);
+ for (serial=0;serial<(int)eina_json_object_count_get(obj1);serial++)
+ {
+ tmp = eina_json_object_nth_get(obj1, serial);
+ fail_unless(eina_json_number_get(eina_json_pair_value_get(tmp)) == serial);
+ fail_unless((int)atof(eina_json_pair_name_get(tmp)) == serial);
+ }
+
+ serial = 0;
+ Eina_Iterator *it = eina_json_object_iterator_new(obj1);
+ Eina_Json_Value * data;
+ EINA_ITERATOR_FOREACH(it, data)
+ {
+ fail_unless(eina_json_number_get(eina_json_pair_value_get(data)) == serial);
+ fail_unless((int)atof(eina_json_pair_name_get(data)) == serial);
+ serial++;
+ }
+ eina_iterator_free(it);
+
+ Eina_Json_Value* japd;
+ japd = eina_json_object_append(jobj, "Object5", eina_json_boolean_new(EINA_TRUE));
+ fail_unless(eina_json_type_get(japd) == EINA_JSON_TYPE_PAIR);
+ fail_unless(eina_json_boolean_get(eina_json_pair_value_get(japd)));
+
+ char* fstr = eina_json_format_string_get(jobj, EINA_JSON_FORMAT_PACKED);
+ fail_if(strcmp(fstr, jstr_object_after));
+ free(fstr);
+
+ Eina_Json_Value *treeobj = eina_json_parse(jstr_object_tree);
+ fail_if(eina_json_object_value_get(treeobj));
+ fail_if(eina_json_object_value_get(treeobj, "Obj"));
+ fail_if(eina_json_object_value_get(treeobj, "Obj1", "Obj"));
+ tmp = eina_json_object_value_get(treeobj, "Obj1", "Obj1_2");
+ fail_unless((tmp != NULL));
+ fail_unless((eina_json_number_get(tmp) == 12));
+ tmp = eina_json_object_value_get(treeobj, "Obj2" );
+ fail_unless((tmp != NULL));
+ fail_unless((eina_json_number_get(tmp) == 2));
+ tmp = eina_json_null_new();
+ eina_json_object_insert(treeobj, 0, "Ent", tmp);
+ fail_if(eina_json_object_insert(jobj, 0, "Ent", tmp));
+ fail_if(eina_json_object_append(jobj, "Ent", tmp));
+ fail_if(eina_json_string_new(NULL));
+ eina_json_value_free(treeobj);
+
+ eina_json_value_free(jobj);
+ eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_json_array_test)
+{
+ eina_init();
+
+ Eina_Json_Value * jobj = eina_json_parse(jstr_array_before);
+ fail_if(!jobj);
+
+ Eina_Json_Value * tmp;
+
+ Eina_Json_Value *arr1 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 0));
+ Eina_Json_Value *arr2 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 1));
+ Eina_Json_Value *arr3 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 2));
+ Eina_Json_Value *arr4 = eina_json_pair_value_get(eina_json_object_nth_get(jobj, 3));
+
+ fail_if(eina_json_array_nth_get(arr4, 0));
+
+ tmp = eina_json_number_new(1);
+ fail_if(eina_json_array_insert(arr4, 1, tmp));
+ eina_json_value_free(tmp);
+
+ fail_unless(eina_json_array_insert(arr4, 0, eina_json_number_new(1)) != NULL);
+ fail_unless(eina_json_array_nth_get(arr4, 0) != NULL);
+
+ tmp = eina_json_number_new(1);
+ fail_if(eina_json_array_insert(arr4, 1, tmp));
+ eina_json_value_free(tmp);
+
+ fail_if(eina_json_array_nth_get(arr3, 11));
+ fail_if(eina_json_array_nth_remove(arr3, 11));
+ fail_if(eina_json_array_nth_remove(arr3, 7));
+ fail_if(eina_json_array_count_get(arr3)!=6);
+ while(eina_json_array_count_get(arr3))
+ fail_unless(eina_json_array_nth_remove(arr3, 0));
+
+ fail_if(eina_json_array_nth_remove(arr3, 0));
+ fail_if(eina_json_array_nth_get(arr3, 0));
+
+ fail_if(eina_json_type_get(eina_json_array_nth_get(arr2, 5)) != EINA_JSON_TYPE_NULL);
+ fail_unless(eina_json_array_nth_remove(arr2, 1));
+ fail_if(eina_json_type_get(eina_json_array_nth_get(arr2, 4)) != EINA_JSON_TYPE_NULL);
+
+ tmp = eina_json_array_nth_get(arr2, 1);
+ fail_unless(eina_json_boolean_set(tmp, !eina_json_boolean_get(tmp)));
+
+ tmp = eina_json_array_nth_get(arr2, 3);
+ fail_unless(eina_json_boolean_set(tmp, !eina_json_boolean_get(tmp)));
+
+ tmp = eina_json_array_nth_get(arr2, 0);
+ fail_unless(eina_json_number_set(tmp, eina_json_number_get(tmp)+100));
+
+ tmp = eina_json_array_nth_get(arr2, 2);
+ fail_if(strcmp(eina_json_string_get(tmp), "Hello"));
+ fail_unless(eina_json_string_set(tmp, "Bye"));
+
+ fail_unless(eina_json_array_insert(arr2, 0, eina_json_number_new(56)) != NULL);
+ fail_unless(eina_json_array_insert(arr2, 0, eina_json_number_new(57)) != NULL);
+ fail_unless(eina_json_array_insert(arr2, 3, eina_json_string_new("new")) != NULL);
+
+ tmp = eina_json_string_new("fail");
+ fail_if(eina_json_array_insert(arr2, 10, tmp));
+ eina_json_value_free(tmp);
+
+ int serial;
+ fail_unless(eina_json_array_count_get(arr1) == 7);
+ for (serial=0;serial<(int)eina_json_array_count_get(arr1);serial++)
+ fail_if(eina_json_number_get(eina_json_array_nth_get(arr1, serial)) != serial);
+
+ serial = 0;
+ Eina_Iterator *it = eina_json_array_iterator_new(arr1);
+ Eina_Json_Value * data;
+ EINA_ITERATOR_FOREACH(it, data)
+ fail_if(eina_json_number_get(data) != serial++);
+
+ eina_iterator_free(it);
+ char* fstr = eina_json_format_string_get(jobj, EINA_JSON_FORMAT_PACKED);
+ fail_if(strcmp(fstr, jstr_array_after));
+ free(fstr);
+
+ eina_json_value_free(jobj);
+ eina_shutdown();
+}
+END_TEST
+
+void
+eina_test_json(TCase *tc)
+{
+ tcase_add_test(tc, eina_json_parse_test);
+ tcase_add_test(tc, eina_json_context_dom_parse_test);
+ tcase_add_test(tc, eina_json_context_sax_parse_test);
+ tcase_add_test(tc, eina_json_object_test);
+ tcase_add_test(tc, eina_json_array_test);
+}