summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuilherme Lepsch <lepsch@expertisesolutions.com.br>2015-07-30 15:51:02 -0300
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>2015-08-31 16:45:45 -0300
commit6a1025c09e7c2f0489c64a3d09e48fd4197213fc (patch)
tree590343aa9b193327637d8ffa5a25c5947c986316
parentcb2cea98cdad2a76098139b1cf40b2b61dcbe4f0 (diff)
downloadefl-6a1025c09e7c2f0489c64a3d09e48fd4197213fc.tar.gz
ejson: Json Efl.Model implementation
-rw-r--r--cmakeconfig/EjsonConfig.cmake.in32
-rw-r--r--cmakeconfig/EjsonCxxConfig.cmake.in30
-rw-r--r--configure.ac54
-rw-r--r--pc/ejson-cxx.pc.in12
-rw-r--r--pc/ejson.pc.in12
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile_Ejson.am86
-rw-r--r--src/Makefile_Ejson_Cxx.am22
-rw-r--r--src/lib/ejson/Ejson.h67
-rw-r--r--src/lib/ejson/ejson_main.c65
-rw-r--r--src/lib/ejson/ejson_model.c1117
-rw-r--r--src/lib/ejson/ejson_model.eo50
-rw-r--r--src/lib/ejson/ejson_model_private.h31
-rw-r--r--src/lib/ejson/ejson_private.h15
-rw-r--r--src/tests/ejson/Makefile.am11
-rw-r--r--src/tests/ejson/ejson_suite.c121
-rw-r--r--src/tests/ejson/ejson_suite.h16
-rw-r--r--src/tests/ejson/ejson_test_ejson_model.c782
-rw-r--r--src/tests/ejson/ejson_test_ejson_model.h6
19 files changed, 2530 insertions, 1 deletions
diff --git a/cmakeconfig/EjsonConfig.cmake.in b/cmakeconfig/EjsonConfig.cmake.in
new file mode 100644
index 0000000000..0d603b48d4
--- /dev/null
+++ b/cmakeconfig/EjsonConfig.cmake.in
@@ -0,0 +1,32 @@
+# - Try to find ejson
+# Once done this will define
+# EJSON_FOUND - System has ejson
+# EJSON_INCLUDE_DIRS - The ejson include directories
+# EJSON_LIBRARIES - The libraries needed to use ejson
+# EJSON_DEFINITIONS - Compiler switches required for using ejson
+
+set(MY_PKG ejson)
+
+find_package(PkgConfig)
+if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER "2.8.1")
+ # "QUIET" was introduced in 2.8.2
+ set(_QUIET QUIET)
+endif ()
+pkg_check_modules(PC_LIBEJSON ${_QUIET} ${MY_PKG})
+
+find_library(EJSON_LIBRARY
+ NAMES ${PC_LIBEJSON_LIBRARIES}
+ HINTS ${PC_LIBEJSON_LIBDIR} ${PC_LIBEJSON_LIBRARY_DIRS} )
+
+set(EJSON_DEFINITIONS ${PC_LIBEJSON_CFLAGS_OTHER})
+set(EJSON_LIBRARIES ${EJSON_LIBRARY})
+set(EJSON_INCLUDE_DIRS ${PC_LIBEJSON_INCLUDE_DIRS})
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set EJSON_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(${MY_PKG} DEFAULT_MSG
+ EJSON_LIBRARIES EJSON_INCLUDE_DIRS)
+
+mark_as_advanced(EJSON_INCLUDE_DIRS EJSON_LIBRARY EJSON_LIBRARIES EJSON_DEFINITIONS)
+
diff --git a/cmakeconfig/EjsonCxxConfig.cmake.in b/cmakeconfig/EjsonCxxConfig.cmake.in
new file mode 100644
index 0000000000..e3fcf1ac28
--- /dev/null
+++ b/cmakeconfig/EjsonCxxConfig.cmake.in
@@ -0,0 +1,30 @@
+# - Try to find ejson
+# Once done this will define
+# EJSON_CXX_FOUND - System has ejson
+# EJSON_CXX_INCLUDE_DIRS - The ejson include directories
+# EJSON_CXX_LIBRARIES - The libraries needed to use ejson
+# EJSON_CXX_DEFINITIONS - Compiler switches required for using ejson
+
+set(MY_PKG ejson_cxx)
+
+find_package(PkgConfig)
+if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER "2.8.1")
+ # "QUIET" was introduced in 2.8.2
+ set(_QUIET QUIET)
+endif ()
+pkg_check_modules(PC_EJSON_CXX ${_QUIET} ${MY_PKG})
+find_library(EJSON_CXX_LIBRARY
+ NAMES ${PC_EJSON_CXX_LIBRARIES}
+ HINTS ${PC_EJSON_CXX_LIBDIR} ${PC_EJSON_CXX_LIBRARY_DIRS} )
+
+set(EJSON_CXX_DEFINITIONS ${PC_EJSON_CXX_CFLAGS_OTHER})
+set(EJSON_CXX_LIBRARIES ${EJSON_CXX_LIBRARY})
+set(EJSON_CXX_INCLUDE_DIRS ${PC_EJSON_CXX_INCLUDE_DIRS})
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set EJSON_CXX_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(${MY_PKG} DEFAULT_MSG
+ EJSON_CXX_LIBRARIES EJSON_CXX_INCLUDE_DIRS)
+
+mark_as_advanced(EJSON_CXX_INCLUDE_DIRS EJSON_CXX_LIBRARY EJSON_CXX_LIBRARIES EJSON_CXX_DEFINITIONS)
diff --git a/configure.ac b/configure.ac
index 01c7850290..442d7f399c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3911,7 +3911,7 @@ EFL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [emile])
ECORE_EVAS_MODULE([extn], [${want_ecore_evas_extn}])
ECORE_EVAS_MODULE([ews], [yes])
ECORE_EVAS_MODULE([fb], [${want_fb}])
-ECORE_EVAS_MODULE([drm], [${want_drm}],
+ECORE_EVAS_MODULE([drm], [${want_drm}],
[EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_drm}], [ecore-drm])])
ECORE_EVAS_MODULE([gl-drm], [${want_gl_drm}],
[EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_gl_drm}], [ecore-drm])])
@@ -4560,6 +4560,51 @@ EFL_ADD_LIBS([ELOCATION], [-lm])
EFL_LIB_END([Elocation])
#### End of Elocation
+
+
+
+#### Ejson
+EFL_LIB_START([Ejson])
+
+### Additional options to configure
+
+### Default values
+
+### Checks for programs
+
+### Checks for libraries
+EFL_INTERNAL_DEPEND_PKG([EJSON], [eo])
+EFL_INTERNAL_DEPEND_PKG([EJSON], [efl])
+EFL_INTERNAL_DEPEND_PKG([EJSON], [ecore])
+
+EFL_DEPEND_PKG([EJSON], [JSONC], [json-c >= 0.11])
+
+EFL_EVAL_PKGS([EJSON])
+
+### Checks for header files
+
+### Checks for types
+
+### Checks for structures
+
+### Checks for compiler characteristics
+
+### Checks for linker characteristics
+
+### Checks for library functions
+
+EFL_LIB_END([Ejson])
+#### End of Ejson
+
+#### Ejson CXX
+EFL_LIB_START([Ejson_Cxx])
+
+EFL_EVAL_PKGS([EJSON_CXX])
+
+EFL_LIB_END([Ejson_Cxx])
+#### End of Ejson CXX
+
+
### Add Wayland server library if test is enabled
if test "x${want_tests}" = "xyes" -a "x${want_wayland}" = "xyes"; then
EFL_DEPEND_PKG([ECORE_WAYLAND_SRV], [WAYLAND], [wayland-server >= 1.8.0])
@@ -4748,6 +4793,8 @@ pc/ethumb.pc
pc/ethumb_client.pc
pc/elocation.pc
pc/elua.pc
+pc/ejson.pc
+pc/ejson-cxx.pc
dbus-services/org.enlightenment.Ethumb.service
systemd-services/ethumb.service
$po_makefile_in
@@ -4797,6 +4844,10 @@ cmakeconfig/EluaConfig.cmake
cmakeconfig/EluaConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in
cmakeconfig/EmileConfig.cmake
cmakeconfig/EmileConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in
+cmakeconfig/EjsonConfig.cmake
+cmakeconfig/EjsonConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in
+cmakeconfig/EjsonCxxConfig.cmake
+cmakeconfig/EjsonCxxConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in
])
AC_OUTPUT
@@ -4910,6 +4961,7 @@ echo "Emotion.........: yes (${features_emotion})"
echo "Ethumb..........: yes"
echo "Ethumb_Client...: yes"
echo "Elua............: $have_elua"
+echo "Ejson...........: yes"
if test "${build_tests}" = "none"; then
echo "Tests...........: no"
elif test "${build_tests}" = "auto"; then
diff --git a/pc/ejson-cxx.pc.in b/pc/ejson-cxx.pc.in
new file mode 100644
index 0000000000..81ae839011
--- /dev/null
+++ b/pc/ejson-cxx.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Ejson C++
+Description: Json convenience library for EFL
+Requires.private: @requirements_pc_ejson@ @requirements_pc_eo@
+Version: @VERSION@
+Libs: -L${libdir} -lejson @requirements_public_libs_ejson@ @requirements_public_libs_eo@
+Libs.private: @requirements_libs_ejson@ @requirements_libs_eo@
+Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/eo-@VMAJ@ -I${includedir}/ejson-@VMAJ@ -I${includedir}/ejson-cxx-@VMAJ@
diff --git a/pc/ejson.pc.in b/pc/ejson.pc.in
new file mode 100644
index 0000000000..15d8f81bda
--- /dev/null
+++ b/pc/ejson.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: Ejson
+Description: Json convenience library for EFL
+Requires.private: @requirements_pc_ejson@
+Version: @VERSION@
+Libs: -L${libdir} -lejson @requirements_public_libs_ejson@
+Libs.private: @requirements_libs_ejson@
+Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/ejson-@VMAJ@
diff --git a/src/Makefile.am b/src/Makefile.am
index dccc538af2..0054ef7553 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,6 +60,7 @@ include Makefile_Edje.am
include Makefile_Emotion.am
include Makefile_Ethumb.am
include Makefile_Ethumb_Client.am
+include Makefile_Ejson.am
include Makefile_Eina_Cxx.am
include Makefile_Ecore_Cxx.am
@@ -71,6 +72,7 @@ include Makefile_Efl_Cxx.am
include Makefile_Edje_Cxx.am
include Makefile_Evas_Cxx.am
include Makefile_Eio_Cxx.am
+include Makefile_Ejson_Cxx.am
include Makefile_Elua.am
include Makefile_Elocation.am
diff --git a/src/Makefile_Ejson.am b/src/Makefile_Ejson.am
new file mode 100644
index 0000000000..6caa9d5469
--- /dev/null
+++ b/src/Makefile_Ejson.am
@@ -0,0 +1,86 @@
+### Library
+
+ejson_eolian_files = \
+lib/ejson/ejson_model.eo
+
+ejson_eolian_c = $(ejson_eolian_files:%.eo=%.eo.c)
+ejson_eolian_h = $(ejson_eolian_files:%.eo=%.eo.h)
+
+BUILT_SOURCES += \
+ $(ejson_eolian_c) \
+ $(ejson_eolian_h)
+
+CLEANFILES += \
+ $(ejson_eolian_c) \
+ $(ejson_eolian_h)
+
+ejsoneolianfilesdir = $(datadir)/eolian/include/ejson-@VMAJ@
+ejsoneolianfiles_DATA = \
+ $(ejson_eolian_files)
+
+EXTRA_DIST += \
+ ${ejsoneolianfiles_DATA}
+
+lib_LTLIBRARIES += lib/ejson/libejson.la
+
+installed_ejsonmainheadersdir = $(includedir)/ejson-@VMAJ@
+dist_installed_ejsonmainheaders_DATA = \
+lib/ejson/Ejson.h
+
+nodist_installed_ejsonmainheaders_DATA = \
+ $(ejson_eolian_h)
+
+lib_ejson_libejson_la_SOURCES = \
+lib/ejson/ejson_main.c \
+lib/ejson/ejson_private.h \
+lib/ejson/ejson_model.c \
+lib/ejson/ejson_model_private.h
+
+lib_ejson_libejson_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EJSON_CFLAGS@ @EFL_CFLAGS@
+lib_ejson_libejson_la_LIBADD = @EJSON_LIBS@ @EFL_LIBS@
+lib_ejson_libejson_la_DEPENDENCIES = @EJSON_INTERNAL_LIBS@ @EFL_INTERNAL_LIBS@
+lib_ejson_libejson_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@
+
+### Unit tests
+
+if EFL_ENABLE_TESTS
+
+check_PROGRAMS += tests/ejson/ejson_suite
+TESTS += tests/ejson/ejson_suite
+
+tests_ejson_ejson_suite_SOURCES = \
+tests/ejson/ejson_suite.c \
+tests/ejson/ejson_suite.h \
+tests/ejson/ejson_test_ejson_model.c \
+tests/ejson/ejson_test_ejson_model.h
+
+tests_ejson_ejson_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
+-DTESTS_SRC_DIR=\"$(abs_top_srcdir)/src/tests/ejson\" \
+-DTESTS_BUILD_DIR=\"$(abs_top_builddir)/src/tests/ejson\" \
+@CHECK_CFLAGS@ \
+@EJSON_CFLAGS@ @EFL_CFLAGS@
+
+tests_ejson_ejson_suite_LDADD = \
+@CHECK_LIBS@ \
+@USE_EJSON_LIBS@ \
+@USE_EFL_LIBS@
+
+tests_ejson_ejson_suite_DEPENDENCIES = \
+@USE_EJSON_INTERNAL_LIBS@
+
+endif
+
+EXTRA_DIST += $(EJSON_DATA_FILES)
+
+if HAVE_ELUA
+
+ejson_eolian_lua = $(ejson_eolian_files:%.eo=%.eo.lua)
+
+generated_ejson_lua_all = $(ejson_eolian_lua)
+
+CLEANFILES += $(generated_ejson_lua_all)
+
+installed_ejsonluadir = $(datadir)/elua/modules/ejson
+nodist_installed_ejsonlua_DATA = $(generated_ejson_lua_all)
+
+endif
diff --git a/src/Makefile_Ejson_Cxx.am b/src/Makefile_Ejson_Cxx.am
new file mode 100644
index 0000000000..5503676edf
--- /dev/null
+++ b/src/Makefile_Ejson_Cxx.am
@@ -0,0 +1,22 @@
+if HAVE_CXX11
+
+### Generated headers
+
+generated_ejson_cxx_bindings = $(ejson_eolian_files:%.eo=%.eo.hh)
+
+lib/ejson/Ejson.hh: $(generated_ejson_cxx_bindings)
+ @echo @ECHO_E@ "#ifndef EFL_CXX_EJSON_HH\n#define EFL_CXX_EJSON_HH\n" > $(top_builddir)/src/lib/ejson/Ejson.hh
+ @echo @ECHO_E@ "#ifdef EFL_BETA_API_SUPPORT" >> $(top_builddir)/src/lib/ejson/Ejson.hh
+ @for i in $(generated_ejson_cxx_bindings); do echo "#include <$$(basename $$i)>" >> $(top_builddir)/src/lib/ejson/Ejson.hh; done
+ @echo @ECHO_E@ "#endif\n\n#endif\n" >> $(top_builddir)/src/lib/ejson/Ejson.hh
+
+generated_ejson_cxx_all = \
+ $(generated_ejson_cxx_bindings) \
+ lib/ejson/Ejson.hh
+
+CLEANFILES += $(generated_ejson_cxx_all)
+
+installed_ejsoncxxmainheadersdir = $(includedir)/ejson-cxx-@VMAJ@/
+nodist_installed_ejsoncxxmainheaders_DATA = $(generated_ejson_cxx_all)
+
+endif
diff --git a/src/lib/ejson/Ejson.h b/src/lib/ejson/Ejson.h
new file mode 100644
index 0000000000..6e5657c668
--- /dev/null
+++ b/src/lib/ejson/Ejson.h
@@ -0,0 +1,67 @@
+#ifndef EJSON_H
+#define EJSON_H
+
+#include <Ecore.h>
+#include <Efl.h>
+#include <Efl_Config.h>
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EJSON_BUILD
+# ifdef DLL_EXPORT
+# define EAPI __declspec(dllexport)
+# else
+# define EAPI
+# endif /* ! DLL_EXPORT */
+# else
+# define EAPI __declspec(dllimport)
+# endif /* ! EFL_EJSON_BUILD */
+#else
+# ifdef __GNUC__
+# if __GNUC__ >= 4
+# define EAPI __attribute__ ((visibility("default")))
+# else
+# define EAPI
+# endif
+# else
+# define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EJSON_MODEL_NAME_PROPERTY "name"
+#define EJSON_MODEL_VALUE_PROPERTY "value"
+#define EJSON_MODEL_JSON_PROPERTY "json"
+
+/**
+ * @brief Initialize ejson.
+ *
+ * @return 1 or greater on success, 0 otherwise
+ */
+EAPI int ejson_init(void);
+
+/**
+ * @brief Shutdown ejson.
+ *
+ * @return 0 if ejson shuts down, greater than 0 otherwise.
+ */
+EAPI int ejson_shutdown(void);
+
+#ifdef EFL_EO_API_SUPPORT
+# include <ejson_model.eo.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#undef EAPI
+#define EAPI
+
+#endif
diff --git a/src/lib/ejson/ejson_main.c b/src/lib/ejson/ejson_main.c
new file mode 100644
index 0000000000..7ce2374c7c
--- /dev/null
+++ b/src/lib/ejson/ejson_main.c
@@ -0,0 +1,65 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "ejson_private.h"
+
+static int _ejson_init_count = 0;
+int _ejson_log_dom = -1;
+
+EAPI int
+ejson_init(void)
+{
+ if (_ejson_init_count++ > 0)
+ return _ejson_init_count;
+
+ if (!eina_init())
+ {
+ fputs("Ejson: Unable to initialize eina\n", stderr);
+ return 0;
+ }
+
+ _ejson_log_dom = eina_log_domain_register("ejson_model", EINA_COLOR_CYAN);
+ if (_ejson_log_dom < 0)
+ {
+ EINA_LOG_ERR("Unable to create an 'ejson_model' log domain");
+ _ejson_log_dom = -1;
+ eina_shutdown();
+ return 0;
+ }
+
+ if (!ecore_init())
+ {
+ ERR("Unable to initialize ecore");
+ eina_log_domain_unregister(_ejson_log_dom);
+ _ejson_log_dom = -1;
+ eina_shutdown();
+ return 0;
+ }
+
+ // ...
+
+ return _ejson_init_count;
+}
+
+EAPI int
+ejson_shutdown(void)
+{
+ if (_ejson_init_count <= 0)
+ {
+ ERR("Init count not greater than 0 in shutdown.");
+ _ejson_init_count = 0;
+ return 0;
+ }
+
+ if (--_ejson_init_count)
+ return _ejson_init_count;
+
+ // ...
+
+ ecore_shutdown();
+ eina_log_domain_unregister(_ejson_log_dom);
+ _ejson_log_dom = -1;
+ eina_shutdown();
+ return 0;
+}
diff --git a/src/lib/ejson/ejson_model.c b/src/lib/ejson/ejson_model.c
new file mode 100644
index 0000000000..6c1d1e8db2
--- /dev/null
+++ b/src/lib/ejson/ejson_model.c
@@ -0,0 +1,1117 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ejson_model_private.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define MY_CLASS EJSON_MODEL_CLASS
+#define MY_CLASS_NAME "Ejson_Model"
+
+typedef struct _Ejson_Model_Child_Data
+{
+ Eina_Stringshare *name;
+ int index;
+} Ejson_Model_Child_Data;
+
+static void _properties_load(Ejson_Model_Data *);
+static void _children_load(Ejson_Model_Data *);
+static bool _init(Ejson_Model_Data *);
+static void _clear(Ejson_Model_Data *);
+static void _properties_setup(Ejson_Model_Data *);
+static Ejson_Model_Type _parent_type_get(Ejson_Model_Data *);
+static void _children_hash_free(Ejson_Model_Child_Data *);
+static void _properties_hash_free(Eina_Value *);
+static bool _name_property_set(Ejson_Model_Data *, const Eina_Value *);
+static bool _value_property_set(Ejson_Model_Data *, const Eina_Value *);
+static bool _json_property_set(Ejson_Model_Data *, const Eina_Value *);
+static void _invalidate(Ejson_Model_Data *);
+static bool _stream_load(Ejson_Model_Data *);
+static Eina_Bool _eina_value_as(const Eina_Value *, const Eina_Value_Type *, void *);
+static Ejson_Model_Type _type_from_json_object(json_object *json);
+static bool _object_child_add(Ejson_Model_Data *, Ejson_Model_Data *, const char *);
+static bool _array_child_add(Ejson_Model_Data *, Ejson_Model_Data *, int);
+static bool _child_add(Ejson_Model_Data *, Ejson_Model_Data *, Ejson_Model_Child_Data *);
+static Ejson_Model_Child_Data *_object_child_data_new(const char *);
+static Ejson_Model_Child_Data *_array_child_data_new(int*);
+typedef Ejson_Model_Child_Data *(*Ejson_Model_Child_Data_New)(void *);
+static void _child_new(Ejson_Model_Data *, json_object *, void *, Ejson_Model_Child_Data_New);
+
+static Eo_Base *
+_ejson_model_eo_base_constructor(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ pd->obj = obj;
+ pd->load.status = EFL_MODEL_LOAD_STATUS_UNLOADED;
+ pd->json = NULL;
+ pd->properties_array = NULL;
+ pd->properties_hash = eina_hash_string_superfast_new(EINA_FREE_CB(_properties_hash_free));
+ pd->children_list = NULL;
+ pd->children_hash = eina_hash_pointer_new(EINA_FREE_CB(_children_hash_free));
+ pd->stream = NULL;
+ pd->type = EJSON_MODEL_TYPE_NULL;
+ pd->properties_calc = eina_array_new(1);
+ eina_array_push(pd->properties_calc, EJSON_MODEL_JSON_PROPERTY);
+ pd->json_property_valid = false;
+
+ return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor());
+}
+
+static void
+_ejson_model_constructor(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd,
+ Ejson_Model_Type type)
+{
+ DBG("(%p)", obj);
+ pd->type = type;
+}
+
+static void
+_ejson_model_eo_base_destructor(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ _clear(pd);
+
+ eina_hash_free(pd->children_hash);
+ eina_hash_free(pd->properties_hash);
+ eina_array_free(pd->properties_calc);
+
+ eo_do_super(obj, MY_CLASS, eo_destructor());
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_properties_get(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd,
+ Eina_Array * const* properties_array)
+{
+ DBG("(%p)", obj);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EFL_MODEL_LOAD_STATUS_ERROR);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->obj, EFL_MODEL_LOAD_STATUS_ERROR);
+
+ *(Eina_Array**)properties_array = pd->properties_array;
+ return pd->load.status;
+}
+
+static void
+_ejson_model_efl_model_base_properties_load(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)
+ return;
+
+ if (!_init(pd))
+ return;
+
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES);
+
+ _properties_load(pd);
+
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES);
+}
+
+static void
+_properties_load(Ejson_Model_Data *pd)
+{
+ _properties_setup(pd);
+ if (!pd->json)
+ return;
+
+ Eina_Array *changed_properties = eina_array_new(1);
+ Eina_Value *value = NULL;
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_NULL:
+ break;
+ case EJSON_MODEL_TYPE_BOOLEAN:
+ {
+ value = eina_hash_find(pd->properties_hash, EJSON_MODEL_VALUE_PROPERTY);
+ EINA_SAFETY_ON_NULL_GOTO(value, on_error);
+ json_bool v = json_object_get_boolean(pd->json);
+ Eina_Bool ret = eina_value_set(value, v);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+ break;
+ }
+ case EJSON_MODEL_TYPE_DOUBLE:
+ {
+ value = eina_hash_find(pd->properties_hash, EJSON_MODEL_VALUE_PROPERTY);
+ EINA_SAFETY_ON_NULL_GOTO(value, on_error);
+ double v = json_object_get_boolean(pd->json);
+ Eina_Bool ret = eina_value_set(value, v);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+ break;
+ }
+ case EJSON_MODEL_TYPE_INT:
+ {
+ value = eina_hash_find(pd->properties_hash, EJSON_MODEL_VALUE_PROPERTY);
+ EINA_SAFETY_ON_NULL_GOTO(value, on_error);
+ int64_t v = json_object_get_int64(pd->json);
+ Eina_Bool ret = eina_value_set(value, v);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+ break;
+ }
+ case EJSON_MODEL_TYPE_STRING:
+ {
+ value = eina_hash_find(pd->properties_hash, EJSON_MODEL_VALUE_PROPERTY);
+ EINA_SAFETY_ON_NULL_GOTO(value, on_error);
+ const char *v = json_object_get_string(pd->json);
+ Eina_Bool ret = eina_value_set(value, v);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+ break;
+ }
+ case EJSON_MODEL_TYPE_OBJECT:
+ break;
+ case EJSON_MODEL_TYPE_ARRAY:
+ break;
+ }
+
+ if (value)
+ eina_array_push(changed_properties, EJSON_MODEL_VALUE_PROPERTY);
+
+ if (EJSON_MODEL_TYPE_OBJECT == _parent_type_get(pd))
+ {
+ Eo *parent = eo_do_ret(pd->obj, parent, eo_parent_get());
+
+ Ejson_Model_Data *parent_pd = eo_data_scope_get(parent, EJSON_MODEL_CLASS);
+ EINA_SAFETY_ON_NULL_GOTO(parent_pd, on_error);
+
+ Ejson_Model_Child_Data *child_data = eina_hash_find(parent_pd->children_hash,
+ &pd->obj);
+ EINA_SAFETY_ON_NULL_GOTO(child_data, on_error);
+
+ value = eina_hash_find(pd->properties_hash, EJSON_MODEL_NAME_PROPERTY);
+ EINA_SAFETY_ON_NULL_GOTO(value, on_error);
+
+ Eina_Bool ret = eina_value_set(value, child_data->name);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+
+ ret = eina_array_push(changed_properties, EJSON_MODEL_NAME_PROPERTY);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+ }
+
+ Efl_Model_Property_Event evt = {
+ .changed_properties = changed_properties,
+ .invalidated_properties = pd->properties_calc
+ };
+ eo_do(pd->obj,
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt));
+
+on_error:
+ eina_array_free(changed_properties);
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_property_set(Eo *obj,
+ Ejson_Model_Data *pd,
+ const char *property,
+ const Eina_Value *value)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR);
+ DBG("(%p): property=%s", obj, property);
+
+ Eina_Array *changed_properties = eina_array_new(1);
+ const bool is_json_property = strcmp(EJSON_MODEL_JSON_PROPERTY, property) == 0;
+ if (is_json_property)
+ {
+ if (!_json_property_set(pd, value))
+ goto on_error;
+
+ // only notify properties_changed if loaded
+ if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED)
+ eina_array_push(changed_properties, EJSON_MODEL_JSON_PROPERTY);
+ }
+ else
+ {
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES))
+ goto on_error;
+
+ const bool is_name_property = strcmp(EJSON_MODEL_NAME_PROPERTY, property) == 0;
+ const bool is_value_property = strcmp(EJSON_MODEL_VALUE_PROPERTY, property) == 0;
+
+ if (!is_name_property && !is_value_property)
+ {
+ ERR("Unrecognized property: %s", property);
+ goto on_error;
+ }
+
+ if (is_name_property)
+ {
+ if (!_name_property_set(pd, value))
+ goto on_error;
+ eina_array_push(changed_properties, EJSON_MODEL_NAME_PROPERTY);
+ }
+ else if (is_value_property)
+ {
+ if (!_value_property_set(pd, value))
+ goto on_error;
+ eina_array_push(changed_properties, EJSON_MODEL_VALUE_PROPERTY);
+ }
+ }
+
+ if (eina_array_count(changed_properties))
+ {
+ pd->json_property_valid = false;
+ Efl_Model_Property_Event evt = {
+ .changed_properties = changed_properties,
+ .invalidated_properties = pd->properties_calc
+ };
+ eo_do(pd->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt));
+ }
+
+ eina_array_free(changed_properties);
+ return pd->load.status;
+
+on_error:
+ eina_array_free(changed_properties);
+ return EFL_MODEL_LOAD_STATUS_ERROR;
+}
+
+static bool
+_name_property_set(Ejson_Model_Data *pd, const Eina_Value *value)
+{
+ DBG("(%p)", pd->obj);
+ Ejson_Model_Type parent_type = _parent_type_get(pd);
+ if (EJSON_MODEL_TYPE_OBJECT != parent_type)
+ {
+ ERR("Only object types have the '" EJSON_MODEL_NAME_PROPERTY "' property");
+ return false;
+ }
+
+ Eina_Value *prop_value = eina_hash_find(pd->properties_hash, EJSON_MODEL_NAME_PROPERTY);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(prop_value, false);
+
+ eina_value_flush(prop_value);
+ Eina_Bool ret = eina_value_copy(value, prop_value);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false);
+
+ return true;
+}
+
+static bool
+_value_property_set(Ejson_Model_Data *pd, const Eina_Value *value)
+{
+ DBG("(%p)", pd->obj);
+ const Eina_Value_Type * const value_type = eina_value_type_get(value);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(value_type, false);
+
+ json_object *json = NULL;
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_BOOLEAN:
+ {
+ int v;
+ Eina_Bool ret = _eina_value_as(value, EINA_VALUE_TYPE_INT, &v);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false);
+
+ json = json_object_new_boolean(v ? TRUE : FALSE);
+ break;
+ }
+ case EJSON_MODEL_TYPE_DOUBLE:
+ {
+ double v;
+ Eina_Bool ret = _eina_value_as(value, EINA_VALUE_TYPE_DOUBLE, &v);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false);
+
+ json = json_object_new_double(v);
+ break;
+ }
+ case EJSON_MODEL_TYPE_INT:
+ {
+ int64_t v;
+ Eina_Bool ret = _eina_value_as(value, EINA_VALUE_TYPE_INT64, &v);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false);
+
+ json = json_object_new_int64(v);
+ break;
+ }
+ case EJSON_MODEL_TYPE_STRING:
+ {
+ const char *v;
+ Eina_Bool ret = _eina_value_as(value, EINA_VALUE_TYPE_STRING, &v);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false);
+
+ json = json_object_new_string(v);
+ break;
+ }
+ case EJSON_MODEL_TYPE_NULL:
+ return false;
+ case EJSON_MODEL_TYPE_OBJECT:
+ return false;
+ case EJSON_MODEL_TYPE_ARRAY:
+ return false;
+ }
+
+ Eina_Value *prop_value = eina_hash_find(pd->properties_hash, EJSON_MODEL_VALUE_PROPERTY);
+ EINA_SAFETY_ON_NULL_GOTO(prop_value, on_error);
+
+ eina_value_flush(prop_value);
+ Eina_Bool ret = eina_value_copy(value, prop_value);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+
+ json_object_put(pd->json);
+ pd->json = json;
+
+ return true;
+
+on_error:
+ json_object_put(json);
+ return false;
+}
+
+static bool
+_json_property_set(Ejson_Model_Data *pd, const Eina_Value *value)
+{
+ DBG("(%p)", pd->obj);
+ char *stream = eina_value_to_string(value);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(stream, false);
+
+ Efl_Model_Load_Status old_status = pd->load.status;
+ switch (old_status & EFL_MODEL_LOAD_STATUS_LOADED)
+ {
+ case EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES:
+ efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES);
+ break;
+ case EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN:
+ efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN);
+ break;
+ case EFL_MODEL_LOAD_STATUS_LOADED:
+ efl_model_load_set(pd->obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING);
+ break;
+ default: break;
+ }
+
+ _clear(pd);
+
+ pd->stream = stream;
+
+ _stream_load(pd);
+
+ if (old_status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)
+ _properties_load(pd);
+
+ if (old_status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN)
+ _children_load(pd);
+
+ efl_model_load_set(pd->obj, &pd->load, old_status);
+ return true;
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_property_get(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd,
+ const char *property,
+ const Eina_Value **value)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(value, EFL_MODEL_LOAD_STATUS_ERROR);
+ DBG("(%p): property=%s", obj, property);
+
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES))
+ return EFL_MODEL_LOAD_STATUS_ERROR;
+
+ *value = eina_hash_find(pd->properties_hash, property);
+ if (!(*value))
+ return EFL_MODEL_LOAD_STATUS_ERROR;
+
+ if ((strcmp(EJSON_MODEL_JSON_PROPERTY, property) == 0) && !pd->json_property_valid)
+ {
+ const char *json = json_object_to_json_string(pd->json);
+ Eina_Bool ret = eina_value_set((Eina_Value*)*value, json);
+ pd->json_property_valid = EINA_TRUE == ret;
+
+ if (!pd->json_property_valid)
+ return EFL_MODEL_LOAD_STATUS_ERROR;
+ }
+
+ return pd->load.status;
+}
+
+static void
+_ejson_model_efl_model_base_load(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ if ((pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED) == EFL_MODEL_LOAD_STATUS_LOADED)
+ return;
+
+ if (!_init(pd))
+ return;
+
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES))
+ {
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES);
+ _properties_load(pd);
+ }
+
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN))
+ {
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN);
+ _children_load(pd);
+ }
+
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED);
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_load_status_get(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+ return pd->load.status;
+}
+
+static void
+_ejson_model_efl_model_base_unload(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ _clear(pd);
+
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_UNLOADED);
+}
+
+static Eina_Bool
+_ejson_model_child_properties_changed_cb(void *data,
+ Eo *child,
+ const Eo_Event_Description *desc EINA_UNUSED,
+ void *event_info)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, EO_CALLBACK_STOP);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(event_info, EO_CALLBACK_STOP);
+ Ejson_Model_Data *pd = (Ejson_Model_Data*)data;
+ DBG("(%p)", pd->obj);
+
+ // Only user changes count
+ Efl_Model_Load_Status child_status;
+ eo_do(child, child_status = efl_model_load_status_get());
+ if (!(child_status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES))
+ return EO_CALLBACK_CONTINUE;
+
+ Ejson_Model_Data *child_pd = eo_data_scope_get(child, EJSON_MODEL_CLASS);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_pd, EO_CALLBACK_STOP);
+
+ const char *current_name = NULL;
+ if (EJSON_MODEL_TYPE_OBJECT == pd->type)
+ {
+ Eina_Value *value = eina_hash_find(child_pd->properties_hash,
+ EJSON_MODEL_NAME_PROPERTY);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(value, EO_CALLBACK_STOP);
+
+ Eina_Bool ret = eina_value_get(value, &current_name);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EO_CALLBACK_STOP);
+
+ // Continue only if the name is already assigned
+ if (!current_name || !strlen(current_name))
+ return EO_CALLBACK_CONTINUE;
+ }
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->children_hash, EO_CALLBACK_STOP);
+ Ejson_Model_Child_Data *child_data = eina_hash_find(pd->children_hash, &child);
+ if (child_data)
+ {
+ DBG("(%p) Setting child %p value", pd->obj, child_pd->obj);
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_OBJECT:
+ {
+ const bool is_name_changed = strcmp(current_name, child_data->name) == 0;
+ if (!is_name_changed)
+ {
+ json_object_object_del(pd->json, child_data->name);
+ eina_stringshare_del(child_data->name);
+ child_data->name = eina_stringshare_add(current_name);
+ }
+
+ json_object_object_add(pd->json,
+ child_data->name,
+ json_object_get(child_pd->json));
+ break;
+ }
+ case EJSON_MODEL_TYPE_ARRAY:
+ json_object_array_put_idx(pd->json,
+ child_data->index,
+ json_object_get(child_pd->json));
+ break;
+ default:
+ ERR("Type cannot have children: %d", pd->type);
+ return EO_CALLBACK_STOP;
+ }
+ }
+ else
+ {
+
+ bool ok = false;
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_OBJECT:
+ json_object_object_add(pd->json,
+ current_name,
+ json_object_get(child_pd->json));
+ ok = _object_child_add(pd, child_pd, current_name);
+ break;
+ case EJSON_MODEL_TYPE_ARRAY:
+ {
+ int index = json_object_array_add(pd->json,
+ json_object_get(child_pd->json));
+ ok = _array_child_add(pd, child_pd, index);
+ break;
+ }
+ default:
+ ERR("Current type cannot have children: %d", pd->type);
+ }
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ok, EO_CALLBACK_STOP);
+
+ unsigned int count = eina_list_count(pd->children_list);
+ Efl_Model_Children_Event evt = {.child = child, .index = count};
+ eo_do(pd->obj,
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILD_ADDED, &evt),
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count));
+ }
+
+ _invalidate(pd);
+
+ Efl_Model_Property_Event evt = {
+ .invalidated_properties = pd->properties_calc
+ };
+ eo_do(pd->obj,
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt));
+
+ return EO_CALLBACK_CONTINUE;
+}
+
+Eo *
+_ejson_model_efl_model_base_child_add(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN))
+ return NULL;
+
+ json_type type = json_object_get_type(pd->json);
+ switch (type)
+ {
+ case json_type_array:
+ case json_type_object:
+ break;
+ default:
+ ERR("Value types cannot have children: %d", type);
+ return NULL;
+ }
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->json, NULL);
+
+ Eo *child = eo_add(EJSON_MODEL_CLASS,
+ obj,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ eo_do(child, eo_event_callback_add(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED,
+ _ejson_model_child_properties_changed_cb,
+ pd));
+ return child;
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_child_del(Eo *obj,
+ Ejson_Model_Data *pd,
+ Eo *child)
+{
+ DBG("(%p)", obj);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child, EFL_MODEL_LOAD_STATUS_ERROR);
+
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN))
+ return EFL_MODEL_LOAD_STATUS_ERROR;
+
+ Ejson_Model_Child_Data *child_data = eina_hash_find(pd->children_hash, &child);
+ if (!child_data)
+ {
+ ERR("Child not found: %p", child);
+ return EFL_MODEL_LOAD_STATUS_ERROR;
+ }
+
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_OBJECT:
+ {
+ json_object_object_del(pd->json, child_data->name);
+ break;
+ }
+ case EJSON_MODEL_TYPE_ARRAY:
+ {
+ int index = child_data->index;
+ json_object *json = json_object_new_array();
+
+ int length = json_object_array_length(pd->json);
+
+ for (int i = 0; i < index; ++i)
+ {
+ json_object *item = json_object_array_get_idx(pd->json, i);
+ json_object_array_add(json, json_object_get(item));
+ }
+
+ for (int i = index + 1; i < length; ++i)
+ {
+ json_object *item = json_object_array_get_idx(pd->json, i);
+ json_object_array_add(json, json_object_get(item));
+ }
+
+ Ejson_Model_Child_Data *data;
+ Eina_Iterator *it = eina_hash_iterator_data_new(pd->children_hash);
+ EINA_ITERATOR_FOREACH(it, data)
+ {
+ if (data->index > index)
+ --data->index;
+ }
+ eina_iterator_free(it);
+
+ json_object_put(pd->json);
+ pd->json = json;
+ break;
+ }
+ default: return EFL_MODEL_LOAD_STATUS_ERROR;
+ }
+
+ pd->children_list = eina_list_remove(pd->children_list, child);
+ Eina_Bool ret = eina_hash_del(pd->children_hash, &child, NULL);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, EFL_MODEL_LOAD_STATUS_ERROR);
+ eo_del(child);
+
+ pd->json_property_valid = false;
+ unsigned int count = eina_list_count(pd->children_list);
+ Efl_Model_Property_Event prop_event = {
+ .changed_properties = pd->properties_array,
+ .invalidated_properties = pd->properties_calc
+ };
+ Efl_Model_Children_Event added_event = {.child = child, .index = count};
+ eo_do(obj,
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &prop_event),
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILD_ADDED, &added_event),
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count));
+
+ return pd->load.status;
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_children_slice_get(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd,
+ unsigned start,
+ unsigned count,
+ Eina_Accessor **children_accessor)
+{
+ DBG("(%p)", obj);
+
+ if (!(pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN))
+ {
+ WRN("(%p): Children not loaded", obj);
+ *children_accessor = NULL;
+ return pd->load.status;
+ }
+
+ *children_accessor = efl_model_list_slice(pd->children_list, start, count);
+ return pd->load.status;
+}
+
+static Efl_Model_Load_Status
+_ejson_model_efl_model_base_children_count_get(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd,
+ unsigned *children_count)
+{
+ DBG("(%p)", obj);
+ *children_count = eina_list_count(pd->children_list);
+ return pd->load.status;
+}
+
+static void
+_ejson_model_efl_model_base_children_load(Eo *obj, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+
+ if (pd->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN)
+ return;
+
+ if (!_init(pd))
+ return;
+
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN);
+
+ _children_load(pd);
+
+ efl_model_load_set(obj, &pd->load, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN);
+}
+
+static void
+_children_load(Ejson_Model_Data *pd)
+{
+ DBG("(%p)", pd->obj);
+ if (!pd->json)
+ return;
+
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_OBJECT:
+ {
+ Ejson_Model_Child_Data_New cb = (Ejson_Model_Child_Data_New)_object_child_data_new;
+ json_object_object_foreach(pd->json, name, json)
+ _child_new(pd, json, name, cb);
+ break;
+ }
+ case EJSON_MODEL_TYPE_ARRAY:
+ {
+ Ejson_Model_Child_Data_New cb = (Ejson_Model_Child_Data_New)_array_child_data_new;
+ int length = json_object_array_length(pd->json);
+ for (int i = 0; i < length; ++i)
+ {
+ json_object *json = json_object_array_get_idx(pd->json, i);
+ _child_new(pd, json, &i, cb);
+ }
+ break;
+ }
+ default:
+ ERR("Current type cannot have children: %d", pd->type);
+ }
+
+ unsigned int count = eina_list_count(pd->children_list);
+ if (count)
+ eo_do(pd->obj,
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count));
+}
+
+static void
+_child_new(Ejson_Model_Data *pd,
+ json_object *json,
+ void *data,
+ Ejson_Model_Child_Data_New child_data_new)
+{
+ Ejson_Model_Type type = _type_from_json_object(json);
+
+ Eo *child = eo_add(EJSON_MODEL_CLASS,
+ pd->obj,
+ ejson_model_constructor(type));
+
+ Ejson_Model_Data *child_pd = eo_data_scope_get(child, EJSON_MODEL_CLASS);
+ EINA_SAFETY_ON_NULL_RETURN(child_pd);
+
+ child_pd->json = json_object_get(json);
+
+ Ejson_Model_Child_Data *child_data = child_data_new(data);
+ _child_add(pd, child_pd, child_data);
+
+ eo_do(child, eo_event_callback_add(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED,
+ _ejson_model_child_properties_changed_cb,
+ pd));
+}
+
+static Ejson_Model_Type
+_ejson_model_type_get(Eo *obj EINA_UNUSED, Ejson_Model_Data *pd)
+{
+ DBG("(%p)", obj);
+ return pd->type;
+}
+
+static void
+_ejson_model_type_set(Eo *obj EINA_UNUSED,
+ Ejson_Model_Data *pd,
+ const Ejson_Model_Type value)
+{
+ DBG("(%p)", obj);
+ EINA_SAFETY_ON_FALSE_RETURN(value >= 0 && value <= EJSON_MODEL_TYPE_ARRAY);
+ pd->type = value;
+}
+
+static bool
+_init(Ejson_Model_Data *pd)
+{
+ if (pd->json)
+ return true;
+
+ if (pd->stream)
+ {
+ if (!_stream_load(pd))
+ return false;
+ }
+ else
+ {
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_OBJECT:
+ pd->json = json_object_new_object();
+ break;
+ case EJSON_MODEL_TYPE_ARRAY:
+ pd->json = json_object_new_array();
+ break;
+ default: break;
+ }
+ }
+
+ return true;
+}
+
+static void
+_clear(Ejson_Model_Data *pd)
+{
+ EINA_SAFETY_ON_NULL_RETURN(pd);
+
+ eina_hash_free_buckets(pd->children_hash);
+
+ Eo *child;
+ EINA_LIST_FREE(pd->children_list, child)
+ eo_del(child);
+
+ eina_hash_free_buckets(pd->properties_hash);
+
+ if (pd->properties_array)
+ {
+ eina_array_free(pd->properties_array);
+ pd->properties_array = NULL;
+ }
+
+ json_object_put(pd->json);
+ pd->json = NULL;
+ pd->json_property_valid = false;
+
+ free(pd->stream);
+ pd->stream = NULL;
+}
+
+typedef struct
+{
+ Ejson_Model_Data *pd;
+ const char *member_name[3];
+ const Eina_Value_Type *member_type[3];
+} Ejson_Model_Properties_Setup;
+
+static void
+_properties_setup(Ejson_Model_Data *pd)
+{
+ DBG("(%p) Setup type: %d", pd->obj, pd->type);
+
+ Ejson_Model_Properties_Setup setup = {.pd = pd};
+
+ setup.member_name[0] = EJSON_MODEL_JSON_PROPERTY;
+ setup.member_type[0] = EINA_VALUE_TYPE_STRING;
+ unsigned int member_count = 1;
+
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_BOOLEAN:
+ setup.member_type[member_count] = EINA_VALUE_TYPE_INT;
+ break;
+ case EJSON_MODEL_TYPE_INT:
+ setup.member_type[member_count] = EINA_VALUE_TYPE_INT64;
+ break;
+ case EJSON_MODEL_TYPE_DOUBLE:
+ setup.member_type[member_count] = EINA_VALUE_TYPE_DOUBLE;
+ break;
+ case EJSON_MODEL_TYPE_STRING:
+ setup.member_type[member_count] = EINA_VALUE_TYPE_STRING;
+ break;
+ case EJSON_MODEL_TYPE_NULL:
+ case EJSON_MODEL_TYPE_OBJECT:
+ case EJSON_MODEL_TYPE_ARRAY:
+ break;
+ }
+
+ switch (pd->type)
+ {
+ case EJSON_MODEL_TYPE_BOOLEAN:
+ case EJSON_MODEL_TYPE_INT:
+ case EJSON_MODEL_TYPE_DOUBLE:
+ case EJSON_MODEL_TYPE_STRING:
+ setup.member_name[member_count] = EJSON_MODEL_VALUE_PROPERTY;
+ ++member_count;
+ break;
+ case EJSON_MODEL_TYPE_NULL:
+ case EJSON_MODEL_TYPE_OBJECT:
+ case EJSON_MODEL_TYPE_ARRAY:
+ break;
+ }
+
+ Ejson_Model_Type parent_type = _parent_type_get(pd);
+
+ if (EJSON_MODEL_TYPE_OBJECT == parent_type)
+ {
+ setup.member_name[member_count] = EJSON_MODEL_NAME_PROPERTY;
+ setup.member_type[member_count] = EINA_VALUE_TYPE_STRING;
+ ++member_count;
+ }
+
+ pd->properties_array = eina_array_new(member_count);
+ for (size_t i = 0; i < member_count; ++i)
+ {
+ Eina_Bool ret = eina_array_push(pd->properties_array,
+ setup.member_name[i]);
+ EINA_SAFETY_ON_FALSE_RETURN(ret);
+
+ const Eina_Value_Type *type = setup.member_type[i];
+ Eina_Value *value = eina_value_new(type);
+ EINA_SAFETY_ON_NULL_RETURN(value);
+
+ ret = eina_hash_add(pd->properties_hash, setup.member_name[i], value);
+ EINA_SAFETY_ON_FALSE_RETURN(ret);
+ }
+
+ DBG("(%p) Properties setup done", pd->obj);
+}
+
+static Ejson_Model_Type
+_parent_type_get(Ejson_Model_Data *pd)
+{
+ Eo *parent = eo_do_ret(pd->obj, parent, eo_parent_get());
+ if (!parent)
+ return EJSON_MODEL_TYPE_NULL;
+
+ Ejson_Model_Data *parent_pd = eo_data_scope_get(parent, EJSON_MODEL_CLASS);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(parent_pd, EJSON_MODEL_TYPE_NULL);
+
+ return parent_pd->type;
+}
+
+static void
+_children_hash_free(Ejson_Model_Child_Data *child)
+{
+ eina_stringshare_del(child->name);
+ free(child);
+}
+
+static void
+_properties_hash_free(Eina_Value *value)
+{
+ eina_value_free(value);
+}
+
+static void
+_invalidate(Ejson_Model_Data *pd)
+{
+ Eo *parent = pd->obj;
+ while ((parent = eo_do_ret(parent, parent, eo_parent_get())))
+ {
+ Ejson_Model_Data *parent_pd = eo_data_scope_get(parent, EJSON_MODEL_CLASS);
+ parent_pd->json_property_valid = false;
+
+ Efl_Model_Property_Event evt = {
+ .invalidated_properties = parent_pd->properties_calc
+ };
+ eo_do(parent,
+ eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt));
+ }
+
+ pd->json_property_valid = false;
+}
+
+static bool
+_stream_load(Ejson_Model_Data *pd)
+{
+ enum json_tokener_error error;
+ pd->json = json_tokener_parse_verbose(pd->stream, &error);
+ if (json_tokener_success != error)
+ {
+ ERR("Invalid json stream. Error: %d, stream = %s", error, pd->stream);
+ return false;
+ }
+
+ pd->type = _type_from_json_object(pd->json);
+ return true;
+}
+
+static Ejson_Model_Type
+_type_from_json_object(json_object *json)
+{
+ json_type type = json_object_get_type(json);
+ switch (type)
+ {
+ case json_type_null: return EJSON_MODEL_TYPE_NULL;
+ case json_type_boolean: return EJSON_MODEL_TYPE_BOOLEAN;
+ case json_type_double: return EJSON_MODEL_TYPE_DOUBLE;
+ case json_type_int: return EJSON_MODEL_TYPE_INT;
+ case json_type_object: return EJSON_MODEL_TYPE_OBJECT;
+ case json_type_array: return EJSON_MODEL_TYPE_ARRAY;
+ case json_type_string: return EJSON_MODEL_TYPE_STRING;
+ default:
+ ERR("Unrecognized type: %d", type);
+ return EJSON_MODEL_TYPE_NULL;
+ }
+}
+
+static Eina_Bool
+_eina_value_as(const Eina_Value *value, const Eina_Value_Type *type, void *mem)
+{
+ const Eina_Value_Type * const value_type = eina_value_type_get(value);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(value_type, false);
+
+ if (value_type == type)
+ return eina_value_get(value, mem);
+
+ Eina_Value convert;
+ Eina_Bool ret = eina_value_setup(&convert, type)
+ && eina_value_convert(value, &convert)
+ && eina_value_get(&convert, mem);
+ eina_value_flush(&convert);
+
+ return ret;
+}
+
+static Ejson_Model_Child_Data *
+_object_child_data_new(const char *name)
+{
+ Ejson_Model_Child_Data *child_data = calloc(1, sizeof(Ejson_Model_Child_Data));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_data, NULL);
+ child_data->name = eina_stringshare_add(name);
+
+ return child_data;
+}
+
+static Ejson_Model_Child_Data *
+_array_child_data_new(int *index)
+{
+ Ejson_Model_Child_Data *child_data = calloc(1, sizeof(Ejson_Model_Child_Data));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_data, NULL);
+ child_data->index = *index;
+
+ return child_data;
+}
+
+static bool
+_object_child_add(Ejson_Model_Data *pd,
+ Ejson_Model_Data *child_pd,
+ const char *name)
+{
+ Ejson_Model_Child_Data *child_data = _object_child_data_new(name);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_data, false);
+
+ return _child_add(pd, child_pd, child_data);
+}
+
+static bool
+_array_child_add(Ejson_Model_Data *pd, Ejson_Model_Data *child_pd, int index)
+{
+ Ejson_Model_Child_Data *child_data = _array_child_data_new(&index);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_data, false);
+
+ return _child_add(pd, child_pd, child_data);
+}
+
+static bool
+_child_add(Ejson_Model_Data *pd,
+ Ejson_Model_Data *child_pd,
+ Ejson_Model_Child_Data *child_data)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_pd, false);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(child_data, false);
+
+ DBG("(%p) Adding new child %p of type: %d", pd->obj, child_pd->obj, child_pd->type);
+ pd->children_list = eina_list_append(pd->children_list, child_pd->obj);
+
+ Eina_Bool ret = eina_hash_add(pd->children_hash, &child_pd->obj, child_data);
+ EINA_SAFETY_ON_FALSE_GOTO(ret, on_error);
+ return true;
+
+on_error:
+ free(child_data);
+ return false;
+}
+
+#include "ejson_model.eo.c"
diff --git a/src/lib/ejson/ejson_model.eo b/src/lib/ejson/ejson_model.eo
new file mode 100644
index 0000000000..ab4703cdb7
--- /dev/null
+++ b/src/lib/ejson/ejson_model.eo
@@ -0,0 +1,50 @@
+enum Ejson.Model.Type {
+ NULL,
+ BOOLEAN,
+ DOUBLE,
+ INT,
+ STRING,
+ OBJECT,
+ ARRAY
+}
+
+class Ejson.Model (Eo.Base, Efl.Model.Base) {
+ legacy_prefix: null;
+ methods {
+ constructor {
+ [[
+ Custom Ejson_Model constructor.
+ @.constructor
+
+ @since 1.13
+ ]]
+ params {
+ type: Ejson.Model.Type; [[Json object type]]
+ }
+ }
+ @property type {
+ values {
+ value: Ejson.Model.Type;
+ }
+ }
+ }
+ implements {
+ Eo.Base.constructor;
+ Eo.Base.destructor;
+ Efl.Model.Base.properties.get;
+ Efl.Model.Base.properties_load;
+ Efl.Model.Base.property.set;
+ Efl.Model.Base.property.get;
+ Efl.Model.Base.load;
+ Efl.Model.Base.load_status.get;
+ Efl.Model.Base.unload;
+ Efl.Model.Base.child_add;
+ Efl.Model.Base.child_del;
+ Efl.Model.Base.children_slice.get;
+ Efl.Model.Base.children_count.get;
+ Efl.Model.Base.children_load;
+ }
+ constructors {
+ .constructor;
+ }
+}
diff --git a/src/lib/ejson/ejson_model_private.h b/src/lib/ejson/ejson_model_private.h
new file mode 100644
index 0000000000..beaacb21f2
--- /dev/null
+++ b/src/lib/ejson/ejson_model_private.h
@@ -0,0 +1,31 @@
+#ifndef _EJSON_MODEL_PRIVATE_H
+#define _EJSON_MODEL_PRIVATE_H
+
+#include "ejson_private.h"
+
+#include <json.h>
+
+#include <stdbool.h>
+
+typedef struct _Ejson_Model_Data Ejson_Model_Data;
+
+/**
+ * ejson_model
+ */
+struct _Ejson_Model_Data
+{
+ Eo *obj;
+ Efl_Model_Load load;
+ json_object *json;
+ Eina_Array *properties_array;
+ Eina_Hash *properties_hash;
+ Eina_List *children_list;
+ Eina_Hash *children_hash;
+ Ejson_Model_Type type;
+ Eina_Array *properties_calc;
+ bool json_property_valid;
+ char *stream;
+};
+
+#endif
+
diff --git a/src/lib/ejson/ejson_private.h b/src/lib/ejson/ejson_private.h
new file mode 100644
index 0000000000..502c850d22
--- /dev/null
+++ b/src/lib/ejson/ejson_private.h
@@ -0,0 +1,15 @@
+#ifndef _EJSON_PRIVATE_H
+#define _EJSON_PRIVATE_H
+
+#include <Ejson.h>
+
+/* logging support */
+extern int _ejson_log_dom;
+
+#define CRI(...) EINA_LOG_DOM_CRIT(_ejson_log_dom, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_ejson_log_dom, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_ejson_log_dom, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_ejson_log_dom, __VA_ARGS__)
+#define DBG(...) EINA_LOG_DOM_DBG(_ejson_log_dom, __VA_ARGS__)
+
+#endif
diff --git a/src/tests/ejson/Makefile.am b/src/tests/ejson/Makefile.am
new file mode 100644
index 0000000000..01b722e8e2
--- /dev/null
+++ b/src/tests/ejson/Makefile.am
@@ -0,0 +1,11 @@
+check_PROGRAMS = \
+ src/tests/ejson_suite
+TESTS = src/tests/ejson_suite
+
+src_tests_ejson_suite_SOURCES = \
+ src/tests/ejson_suite.c \
+ src/tests/ejson_test_ejson_model.c
+src_tests_ejson_suite_CFLAGS = $(MOD_CFLAGS) \
+ -DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests\"
+src_tests_ejson_suite_LDADD = $(MOD_LIBS) $(CHECK_LIBS)
+
diff --git a/src/tests/ejson/ejson_suite.c b/src/tests/ejson/ejson_suite.c
new file mode 100644
index 0000000000..cd50da5bc8
--- /dev/null
+++ b/src/tests/ejson/ejson_suite.c
@@ -0,0 +1,121 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ejson_suite.h"
+
+#include <Eina.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int _test_ejson_model_log_dom = -1;
+
+typedef struct _Ejson_Test_Case Ejson_Test_Case;
+
+struct _Ejson_Test_Case
+{
+ const char *test_case;
+ void (*build)(TCase *tc);
+};
+
+static const Ejson_Test_Case etc[] = {
+ { "Ejson_Model", ejson_test_ejson_model },
+ { NULL, NULL }
+};
+
+static void
+_list_tests(void)
+{
+ const Ejson_Test_Case *it = etc;
+ fputs("Available Test Cases:\n", stderr);
+ for (; it->test_case; it++)
+ fprintf(stderr, "\t%s\n", it->test_case);
+}
+
+static bool
+_use_test(int argc, const char **argv, const char *test_case)
+{
+ if (argc < 1)
+ return true;
+
+ for (; argc > 0; argc--, argv++)
+ if (strcmp(test_case, *argv) == 0)
+ return true;
+ return false;
+}
+
+static Suite *
+_ejson_suite_build(int argc, const char **argv)
+{
+ Suite *s = suite_create("Ejson");
+
+ for (int i = 0; etc[i].test_case; ++i)
+ {
+ if (!_use_test(argc, argv, etc[i].test_case)) continue;
+ TCase *tc = tcase_create(etc[i].test_case);
+
+ etc[i].build(tc);
+
+ suite_add_tcase(s, tc);
+ //tcase_set_timeout(tc, 0);
+ }
+
+ return s;
+}
+
+static void
+_init_logging(void)
+{
+ _test_ejson_model_log_dom = eina_log_domain_register("test_ejson_model", EINA_COLOR_LIGHTBLUE);
+ if (_test_ejson_model_log_dom < 0)
+ ck_abort_msg("Could not register log domain: test_ejson_model");
+
+ //eina_log_domain_level_set("esskyuehl", EINA_LOG_LEVEL_DBG);
+ //eina_log_domain_level_set("ejson_model", EINA_LOG_LEVEL_DBG);
+ eina_log_domain_level_set("test_ejson_model", EINA_LOG_LEVEL_DBG);
+}
+
+static void
+_shutdown_logging(void)
+{
+ eina_log_domain_unregister(_test_ejson_model_log_dom);
+ _test_ejson_model_log_dom = -1;
+}
+
+int
+main(int argc, char **argv)
+{
+ for (int i = 1; i < argc; ++i)
+ {
+ if ((strcmp(argv[i], "-h") == 0) ||
+ (strcmp(argv[i], "--help") == 0))
+ {
+ fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n", argv[0]);
+ _list_tests();
+ return 0;
+ }
+ else if ((strcmp(argv[i], "-l") == 0) ||
+ (strcmp(argv[i], "--list") == 0))
+ {
+ _list_tests();
+ return 0;
+ }
+ }
+
+ _init_logging();
+
+ Suite *s = _ejson_suite_build(argc - 1, (const char **)argv + 1);
+ SRunner *sr = srunner_create(s);
+
+ srunner_set_xml(sr, TESTS_BUILD_DIR "/check-results.xml");
+
+ srunner_run_all(sr, CK_ENV);
+ int failed_count = srunner_ntests_failed(sr);
+ srunner_free(sr);
+
+ _shutdown_logging();
+
+ return (failed_count == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/tests/ejson/ejson_suite.h b/src/tests/ejson/ejson_suite.h
new file mode 100644
index 0000000000..ac1dbcdf47
--- /dev/null
+++ b/src/tests/ejson/ejson_suite.h
@@ -0,0 +1,16 @@
+#ifndef _EJSON_SUITE_H
+#define _EJSON_SUITE_H
+
+#include <check.h>
+
+extern int _test_ejson_model_log_dom;
+
+#define CRI(...) EINA_LOG_DOM_CRIT(_test_ejson_model_log_dom, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_test_ejson_model_log_dom, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_test_ejson_model_log_dom, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_test_ejson_model_log_dom, __VA_ARGS__)
+#define DBG(...) EINA_LOG_DOM_DBG(_test_ejson_model_log_dom, __VA_ARGS__)
+
+void ejson_test_ejson_model(TCase *tc);
+
+#endif
diff --git a/src/tests/ejson/ejson_test_ejson_model.c b/src/tests/ejson/ejson_test_ejson_model.c
new file mode 100644
index 0000000000..51d3780efc
--- /dev/null
+++ b/src/tests/ejson/ejson_test_ejson_model.c
@@ -0,0 +1,782 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ejson_test_ejson_model.h"
+#include "ejson_suite.h"
+
+#include <Ecore.h>
+#include <Eina.h>
+#include <Ejson.h>
+
+#include <stdbool.h>
+
+static void
+_setup(void)
+{
+ int ret = ejson_init();
+ ck_assert_int_ge(ret, 1);
+}
+
+static void
+_teardown(void)
+{
+ int ret = ejson_shutdown();
+ ck_assert_int_eq(ret, 0);
+}
+
+static Eina_Bool
+_eo_event_load_status_cb(void *data,
+ Eo *obj EINA_UNUSED,
+ const Eo_Event_Description *desc EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ Efl_Model_Load_Status expected_status = *((Efl_Model_Load_Status*)data);
+ Efl_Model_Load load = *((Efl_Model_Load*)event_info);
+ if ((load.status & expected_status) != expected_status)
+ return EINA_TRUE;
+
+ ecore_main_loop_quit();
+ return EINA_FALSE;
+}
+
+static void
+_wait_until_load_status(Efl_Model_Base *emodel,
+ Efl_Model_Load_Status expected_status)
+{
+ Efl_Model_Load_Status actual_status;
+ eo_do(emodel, actual_status = efl_model_load_status_get());
+ if (expected_status == actual_status) return;
+
+ eo_do(emodel, eo_event_callback_add(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _eo_event_load_status_cb, &expected_status));
+ ecore_main_loop_begin();
+ eo_do(emodel, eo_event_callback_del(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _eo_event_load_status_cb, &expected_status));
+}
+
+static void
+_check_json_string(Efl_Model_Base *model, const char *expected_json_string)
+{
+ const Eina_Value *json_value;
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_property_get(EJSON_MODEL_JSON_PROPERTY, &json_value));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES, status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES);
+ ck_assert_ptr_ne(NULL, json_value);
+
+ const char *actual_json_string;
+ fail_unless(eina_value_get(json_value, &actual_json_string));
+
+ ck_assert_str_eq(expected_json_string, actual_json_string);
+}
+
+static void
+_check_value_type_cannot_have_children(Efl_Model_Base *model)
+{
+ Efl_Model_Base *child = eo_do_ret(model, child, efl_model_child_add());
+ ck_assert_ptr_eq(NULL, child);
+}
+
+static Efl_Model_Load_Status
+_json_property_set(Efl_Model_Base *model, const char *v)
+{
+ Eina_Value json;
+ eina_value_setup(&json, EINA_VALUE_TYPE_STRING);
+ eina_value_set(&json, v);
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_property_set(EJSON_MODEL_JSON_PROPERTY, &json));
+ eina_value_flush(&json);
+ return status;
+}
+
+static void
+_check_name_property_set(Efl_Model_Base *model, const char *v)
+{
+ Eina_Value name;
+ eina_value_setup(&name, EINA_VALUE_TYPE_STRING);
+ eina_value_set(&name, v);
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_property_set(EJSON_MODEL_NAME_PROPERTY, &name));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+ eina_value_flush(&name);
+}
+
+static void
+_check_int_value_property_set(Efl_Model_Base *model, int64_t v)
+{
+ Eina_Value value;
+ eina_value_setup(&value, EINA_VALUE_TYPE_INT64);
+ eina_value_set(&value, v);
+
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_property_set(EJSON_MODEL_VALUE_PROPERTY, &value));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%lld", (long long)v);
+
+ _check_json_string(model, buf);
+
+ eina_value_flush(&value);
+}
+
+static void
+_check_bool_value_property_set(Efl_Model_Base *model, bool v)
+{
+ Eina_Value value;
+ eina_value_setup(&value, EINA_VALUE_TYPE_INT);
+ eina_value_set(&value, (int)v);
+
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_property_set(EJSON_MODEL_VALUE_PROPERTY, &value));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+
+ _check_json_string(model, v ? "true" : "false");
+
+ eina_value_flush(&value);
+}
+
+static void
+_check_string_value_property_set(Efl_Model_Base *model, const char *v)
+{
+ Eina_Value value;
+ eina_value_setup(&value, EINA_VALUE_TYPE_STRING);
+ eina_value_set(&value, v);
+
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_property_set(EJSON_MODEL_VALUE_PROPERTY, &value));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+
+ size_t len = 1 + strlen(v) + 1 + 1;
+ char buf[len];
+ snprintf(buf, len, "\"%s\"", v);
+
+ _check_json_string(model, buf);
+
+ eina_value_flush(&value);
+}
+
+static void
+_check_emodel_children_count_eq(Efl_Model_Base *model,
+ unsigned int expected_children_count)
+{
+ unsigned int actual_children_count = 0;
+ eo_do(model, efl_model_children_count_get(&actual_children_count));
+ ck_assert_int_eq(expected_children_count, actual_children_count);
+}
+
+static void
+_check_emodel_property_int_eq(Efl_Model_Base *emodel,
+ const char *property,
+ int64_t expected_value)
+{
+ const Eina_Value *value;
+ Efl_Model_Load_Status status;
+ eo_do(emodel, status = efl_model_property_get(property, &value));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+ ck_assert_ptr_ne(NULL, value);
+
+ const Eina_Value_Type *property_type = eina_value_type_get(value);
+ ck_assert_ptr_eq(EINA_VALUE_TYPE_INT64, property_type);
+
+ int64_t actual_value = 0;
+ eina_value_get(value, &actual_value);
+ ck_assert_int_eq(expected_value, actual_value);
+}
+
+static void
+_check_emodel_property_str_eq(Efl_Model_Base *emodel,
+ const char *property,
+ const char *expected_value)
+{
+ const Eina_Value *value;
+ Efl_Model_Load_Status status;
+ eo_do(emodel, status = efl_model_property_get(property, &value));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+ ck_assert_ptr_ne(NULL, value);
+
+ const Eina_Value_Type *property_type = eina_value_type_get(value);
+ fail_if(EINA_VALUE_TYPE_STRING != property_type && EINA_VALUE_TYPE_STRINGSHARE != property_type);
+
+ const char *actual_value = NULL;
+ eina_value_get(value, &actual_value);
+ ck_assert_str_eq(expected_value, actual_value);
+}
+
+static Efl_Model_Base *
+_emodel_nth_child_get(Efl_Model_Base *emodel, unsigned int n)
+{
+ Eina_Accessor *accessor;
+ Efl_Model_Load_Status status;
+ eo_do(emodel, status = efl_model_children_slice_get(n, 1, &accessor));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+ ck_assert_ptr_ne(NULL, accessor);
+ Efl_Model_Base *child = NULL;
+ Eina_Bool ret = eina_accessor_data_get(accessor, 0, (void**)&child);
+ eina_accessor_free(accessor);
+ ck_assert(ret);
+ ck_assert_ptr_ne(NULL, child);
+ return child;
+}
+
+static void
+_check_ejson_type(Efl_Model_Base *model, Ejson_Model_Type expected_type)
+{
+ Ejson_Model_Type actual_type;
+ eo_do(model, actual_type = ejson_model_type_get());
+ ck_assert_int_eq(expected_type, actual_type);
+}
+
+static void
+_check_emodel_load(Efl_Model_Base *model)
+{
+ eo_do(model, efl_model_load());
+ _wait_until_load_status(model, EFL_MODEL_LOAD_STATUS_LOADED);
+}
+
+static void
+_check_emodel_properties(Efl_Model_Base *model,
+ const char *expected_properties[])
+{
+ Eina_Array *properties = NULL;
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_properties_get(&properties));
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+ ck_assert_ptr_ne(NULL, properties);
+
+ unsigned int actual_properties_count = eina_array_count(properties);
+
+ unsigned int expected_properties_count = 0;
+ const char *expected_property = NULL;
+ while ((expected_property = *expected_properties++))
+ {
+ const char *actual_property = eina_array_data_get(properties,
+ expected_properties_count);
+ ck_assert_str_eq(expected_property, actual_property);
+ ++expected_properties_count;
+ ck_assert_int_le(expected_properties_count, actual_properties_count);
+ }
+
+ ck_assert_int_eq(expected_properties_count, actual_properties_count);
+}
+
+static void
+_check_properties_count_eq(Efl_Model_Base *model,
+ unsigned int expected_properties_count)
+{
+ Eina_Array *properties = NULL;
+ Efl_Model_Load_Status status;
+ eo_do(model, status = efl_model_properties_get(&properties));
+ ck_assert_int_ne(EFL_MODEL_LOAD_STATUS_ERROR, status);
+ if (!expected_properties_count && !properties)
+ return;
+
+ ck_assert_ptr_ne(NULL, properties);
+
+ unsigned int actual_properties_count = eina_array_count(properties);
+ ck_assert_int_eq(expected_properties_count, actual_properties_count);
+}
+
+START_TEST(smoke)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(load_object)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_OBJECT));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(object_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_OBJECT));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "{ }");
+
+ Efl_Model_Base *child = eo_do_ret(json, child, efl_model_child_add());
+ ck_assert_ptr_ne(NULL, child);
+
+ _check_json_string(json, "{ }");
+
+ Ejson_Model_Type type = eo_do_ret(child, type, ejson_model_type_get());
+ ck_assert_int_eq(EJSON_MODEL_TYPE_NULL, type);
+ eo_do(child, ejson_model_type_set(EJSON_MODEL_TYPE_INT));
+ eo_do(child, type = ejson_model_type_get());
+ ck_assert_int_eq(EJSON_MODEL_TYPE_INT, type);
+
+ _check_emodel_load(child);
+
+ _check_json_string(child, "null");
+ _check_json_string(json, "{ }");
+
+ // set member name
+ _check_name_property_set(child, "a");
+ _check_json_string(child, "null");
+ _check_json_string(json, "{ \"a\": null }");
+
+ // set member value
+ _check_int_value_property_set(child, 1234);
+ _check_json_string(json, "{ \"a\": 1234 }");
+
+ // change member name
+ _check_name_property_set(child, "b");
+ _check_json_string(json, "{ \"b\": 1234 }");
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(array_object_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_ARRAY));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "[ ]");
+
+ Efl_Model_Base *child = eo_do_ret(json, child, efl_model_child_add());
+ ck_assert_ptr_ne(NULL, child);
+
+ _check_json_string(json, "[ ]");
+
+ Ejson_Model_Type type = eo_do_ret(child, type, ejson_model_type_get());
+ ck_assert_int_eq(EJSON_MODEL_TYPE_NULL, type);
+ eo_do(child, ejson_model_type_set(EJSON_MODEL_TYPE_INT));
+ eo_do(child, type = ejson_model_type_get());
+ ck_assert_int_eq(EJSON_MODEL_TYPE_INT, type);
+
+ _check_emodel_load(child);
+
+ _check_json_string(child, "null");
+ _check_json_string(json, "[ ]");
+
+ _check_int_value_property_set(child, 1234);
+
+ _check_json_string(json, "[ 1234 ]");
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(int_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_INT));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "null");
+
+ _check_int_value_property_set(json, 1234);
+
+ _check_value_type_cannot_have_children(json);
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(boolean_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_BOOLEAN));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "null");
+
+ _check_bool_value_property_set(json, true);
+ _check_bool_value_property_set(json, false);
+
+ _check_value_type_cannot_have_children(json);
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(string_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_STRING));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "null");
+
+ _check_string_value_property_set(json, "Hello world!");
+
+ _check_value_type_cannot_have_children(json);
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(json_object_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ _json_property_set(json, "{ \"a\": 1, \"b\": \"Hello\" }");
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "{ \"a\": 1, \"b\": \"Hello\" }");
+
+ _check_emodel_children_count_eq(json, 2);
+
+ Efl_Model_Base *child1 = _emodel_nth_child_get(json, 1);
+ Efl_Model_Base *child2 = _emodel_nth_child_get(json, 2);
+
+ _check_ejson_type(child1, EJSON_MODEL_TYPE_INT);
+ _check_ejson_type(child2, EJSON_MODEL_TYPE_STRING);
+
+ _check_emodel_load(child1);
+
+ _check_emodel_property_int_eq(child1, EJSON_MODEL_VALUE_PROPERTY, 1);
+ _check_emodel_property_str_eq(child1, EJSON_MODEL_NAME_PROPERTY, "a");
+
+ _check_emodel_load(child2);
+
+ _check_emodel_property_str_eq(child2, EJSON_MODEL_VALUE_PROPERTY, "Hello");
+ _check_emodel_property_str_eq(child2, EJSON_MODEL_NAME_PROPERTY, "b");
+
+ _json_property_set(child2, "{ \"c\": 2, \"d\": \"World\" }");
+
+ _check_json_string(json, "{ \"a\": 1, \"b\": { \"c\": 2, \"d\": \"World\" } }");
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(json_array_property_set)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ _json_property_set(json, "[ 1, \"Hello\" ]");
+
+ _check_emodel_load(json);
+
+ _check_json_string(json, "[ 1, \"Hello\" ]");
+
+ _check_emodel_children_count_eq(json, 2);
+
+ Efl_Model_Base *child1 = _emodel_nth_child_get(json, 1);
+ Efl_Model_Base *child2 = _emodel_nth_child_get(json, 2);
+
+ _check_ejson_type(child1, EJSON_MODEL_TYPE_INT);
+ _check_ejson_type(child2, EJSON_MODEL_TYPE_STRING);
+
+ _check_emodel_load(child1);
+
+ _check_emodel_property_int_eq(child1, EJSON_MODEL_VALUE_PROPERTY, 1);
+
+ _check_emodel_load(child2);
+
+ _check_emodel_property_str_eq(child2, EJSON_MODEL_VALUE_PROPERTY, "Hello");
+
+ _json_property_set(child2, "[ 2, \"World\" ]");
+
+ _check_json_string(json, "[ 1, [ 2, \"World\" ] ]");
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(load_status_get)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ Efl_Model_Load_Status status;
+ eo_do(json, status = efl_model_load_status_get());
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_UNLOADED, status);
+
+ _check_emodel_load(json);
+
+ eo_do(json, status = efl_model_load_status_get());
+ ck_assert_int_eq(EFL_MODEL_LOAD_STATUS_LOADED, status);
+
+ eo_unref(json);
+}
+END_TEST
+
+static const char *_all_json_types_object_string =
+ "{ \"i\": 1, "
+ "\"s\": \"Hello World!\", "
+ "\"b\": true, "
+ "\"d\": 1.234, "
+ "\"n\": null, "
+ "\"o\": { }, "
+ "\"a\": [ ] "
+ "}";
+
+static Efl_Model_Base *
+_create_model_with_all_json_types()
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _json_property_set(json, _all_json_types_object_string);
+ _check_json_string(json, _all_json_types_object_string);
+
+ _check_emodel_children_count_eq(json, 7);
+ return json;
+}
+
+START_TEST(properties_get)
+{
+ Efl_Model_Base *json = _create_model_with_all_json_types();
+
+ _check_emodel_properties(json, (const char*[]){"json", NULL});
+
+ Efl_Model_Base *child_i = _emodel_nth_child_get(json, 1);
+ Efl_Model_Base *child_s = _emodel_nth_child_get(json, 2);
+ Efl_Model_Base *child_b = _emodel_nth_child_get(json, 3);
+ Efl_Model_Base *child_d = _emodel_nth_child_get(json, 4);
+ Efl_Model_Base *child_n = _emodel_nth_child_get(json, 5);
+ Efl_Model_Base *child_o = _emodel_nth_child_get(json, 6);
+ Efl_Model_Base *child_a = _emodel_nth_child_get(json, 7);
+
+ _check_emodel_load(child_i);
+ _check_emodel_load(child_s);
+ _check_emodel_load(child_b);
+ _check_emodel_load(child_d);
+ _check_emodel_load(child_n);
+ _check_emodel_load(child_o);
+ _check_emodel_load(child_a);
+
+ _check_ejson_type(child_i, EJSON_MODEL_TYPE_INT);
+ _check_ejson_type(child_s, EJSON_MODEL_TYPE_STRING);
+ _check_ejson_type(child_b, EJSON_MODEL_TYPE_BOOLEAN);
+ _check_ejson_type(child_d, EJSON_MODEL_TYPE_DOUBLE);
+ _check_ejson_type(child_n, EJSON_MODEL_TYPE_NULL);
+ _check_ejson_type(child_o, EJSON_MODEL_TYPE_OBJECT);
+ _check_ejson_type(child_a, EJSON_MODEL_TYPE_ARRAY);
+
+ _check_emodel_properties(child_i, (const char*[]){"json", "value", "name", NULL});
+ _check_emodel_properties(child_s, (const char*[]){"json", "value", "name", NULL});
+ _check_emodel_properties(child_b, (const char*[]){"json", "value", "name", NULL});
+ _check_emodel_properties(child_d, (const char*[]){"json", "value", "name", NULL});
+ _check_emodel_properties(child_n, (const char*[]){"json", "name", NULL});
+ _check_emodel_properties(child_o, (const char*[]){"json", "name", NULL});
+ _check_emodel_properties(child_a, (const char*[]){"json", "name", NULL});
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(unload)
+{
+ Efl_Model_Base *json = _create_model_with_all_json_types();
+
+ eo_do(json, efl_model_unload());
+ _wait_until_load_status(json, EFL_MODEL_LOAD_STATUS_UNLOADED);
+
+ // reload
+ _check_emodel_load(json);
+ _check_json_string(json, "{ }");
+
+ _json_property_set(json, _all_json_types_object_string);
+ _check_json_string(json, _all_json_types_object_string);
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(properties_load)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ _json_property_set(json, _all_json_types_object_string);
+
+ eo_do(json, efl_model_properties_load());
+ _wait_until_load_status(json, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES);
+
+ _check_emodel_children_count_eq(json, 0);
+
+ _check_json_string(json, _all_json_types_object_string);
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(children_load)
+{
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS,
+ NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ _json_property_set(json, _all_json_types_object_string);
+
+ eo_do(json, efl_model_children_load());
+ _wait_until_load_status(json, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN);
+
+ _check_emodel_children_count_eq(json, 7);
+
+ _check_properties_count_eq(json, 0);
+
+ eo_unref(json);
+}
+END_TEST
+
+
+START_TEST(object_child_del)
+{
+ Efl_Model_Base *json = _create_model_with_all_json_types();
+
+ Efl_Model_Base *child_i = _emodel_nth_child_get(json, 1);
+ Efl_Model_Base *child_s = _emodel_nth_child_get(json, 2);
+ Efl_Model_Base *child_b = _emodel_nth_child_get(json, 3);
+ Efl_Model_Base *child_d = _emodel_nth_child_get(json, 4);
+ Efl_Model_Base *child_n = _emodel_nth_child_get(json, 5);
+ Efl_Model_Base *child_o = _emodel_nth_child_get(json, 6);
+ Efl_Model_Base *child_a = _emodel_nth_child_get(json, 7);
+
+ eo_do(json, efl_model_child_del(child_s));
+ _check_emodel_children_count_eq(json, 6);
+ _check_json_string(json, "{ \"i\": 1, \"b\": true, \"d\": 1.234, \"n\": null, \"o\": { }, \"a\": [ ] }");
+
+ eo_do(json, efl_model_child_del(child_i));
+ _check_emodel_children_count_eq(json, 5);
+ _check_json_string(json, "{ \"b\": true, \"d\": 1.234, \"n\": null, \"o\": { }, \"a\": [ ] }");
+
+ eo_do(json, efl_model_child_del(child_a));
+ _check_emodel_children_count_eq(json, 4);
+ _check_json_string(json, "{ \"b\": true, \"d\": 1.234, \"n\": null, \"o\": { } }");
+
+ eo_do(json, efl_model_child_del(child_d));
+ _check_emodel_children_count_eq(json, 3);
+ _check_json_string(json, "{ \"b\": true, \"n\": null, \"o\": { } }");
+
+ eo_do(json, efl_model_child_del(child_b));
+ _check_emodel_children_count_eq(json, 2);
+ _check_json_string(json, "{ \"n\": null, \"o\": { } }");
+
+ eo_do(json, efl_model_child_del(child_o));
+ _check_emodel_children_count_eq(json, 1);
+ _check_json_string(json, "{ \"n\": null }");
+
+ eo_do(json, efl_model_child_del(child_n));
+ _check_emodel_children_count_eq(json, 0);
+ _check_json_string(json, "{ }");
+
+ eo_unref(json);
+}
+END_TEST
+
+START_TEST(array_child_del)
+{
+ const char *_all_json_types_array_string =
+ "[ 1, \"Hello World!\", true, 1.234, null, { }, [ ] ]";
+
+ Efl_Model_Base *json = eo_add(EJSON_MODEL_CLASS, NULL,
+ ejson_model_constructor(EJSON_MODEL_TYPE_NULL));
+ ck_assert_ptr_ne(NULL, json);
+
+ _check_emodel_load(json);
+
+ _json_property_set(json, _all_json_types_array_string);
+ _check_json_string(json, _all_json_types_array_string);
+
+ _check_emodel_children_count_eq(json, 7);
+
+ Efl_Model_Base *child_i = _emodel_nth_child_get(json, 1);
+ Efl_Model_Base *child_s = _emodel_nth_child_get(json, 2);
+ Efl_Model_Base *child_b = _emodel_nth_child_get(json, 3);
+ Efl_Model_Base *child_d = _emodel_nth_child_get(json, 4);
+ Efl_Model_Base *child_n = _emodel_nth_child_get(json, 5);
+ Efl_Model_Base *child_o = _emodel_nth_child_get(json, 6);
+ Efl_Model_Base *child_a = _emodel_nth_child_get(json, 7);
+
+ eo_do(json, efl_model_child_del(child_s));
+ _check_emodel_children_count_eq(json, 6);
+ _check_json_string(json, "[ 1, true, 1.234, null, { }, [ ] ]");
+
+ eo_do(json, efl_model_child_del(child_i));
+ _check_emodel_children_count_eq(json, 5);
+ _check_json_string(json, "[ true, 1.234, null, { }, [ ] ]");
+
+ eo_do(json, efl_model_child_del(child_a));
+ _check_emodel_children_count_eq(json, 4);
+ _check_json_string(json, "[ true, 1.234, null, { } ]");
+
+ eo_do(json, efl_model_child_del(child_d));
+ _check_emodel_children_count_eq(json, 3);
+ _check_json_string(json, "[ true, null, { } ]");
+
+ eo_do(json, efl_model_child_del(child_b));
+ _check_emodel_children_count_eq(json, 2);
+ _check_json_string(json, "[ null, { } ]");
+
+ eo_do(json, efl_model_child_del(child_o));
+ _check_emodel_children_count_eq(json, 1);
+ _check_json_string(json, "[ null ]");
+
+ eo_do(json, efl_model_child_del(child_n));
+ _check_emodel_children_count_eq(json, 0);
+ _check_json_string(json, "[ ]");
+
+ eo_unref(json);
+}
+END_TEST
+
+void
+ejson_test_ejson_model(TCase *tc)
+{
+ tcase_add_checked_fixture(tc, _setup, _teardown);
+ tcase_add_test(tc, smoke);
+ tcase_add_test(tc, load_object);
+ tcase_add_test(tc, object_property_set);
+ tcase_add_test(tc, array_object_property_set);
+ tcase_add_test(tc, int_property_set);
+ tcase_add_test(tc, boolean_property_set);
+ tcase_add_test(tc, string_property_set);
+ tcase_add_test(tc, json_object_property_set);
+ tcase_add_test(tc, json_array_property_set);
+ tcase_add_test(tc, load_status_get);
+ tcase_add_test(tc, properties_get);
+ tcase_add_test(tc, unload);
+ tcase_add_test(tc, properties_load);
+ tcase_add_test(tc, children_load);
+ tcase_add_test(tc, object_child_del);
+ tcase_add_test(tc, array_child_del);
+}
diff --git a/src/tests/ejson/ejson_test_ejson_model.h b/src/tests/ejson/ejson_test_ejson_model.h
new file mode 100644
index 0000000000..ea5c939761
--- /dev/null
+++ b/src/tests/ejson/ejson_test_ejson_model.h
@@ -0,0 +1,6 @@
+#ifndef _EJSON_TEST_EJSON_MODEL_H
+#define _EJSON_TEST_EJSON_MODEL_H
+
+#include <Ejson.h>
+
+#endif