diff options
author | Guilherme Lepsch <lepsch@expertisesolutions.com.br> | 2015-07-30 15:51:02 -0300 |
---|---|---|
committer | Felipe Magno de Almeida <felipe@expertisesolutions.com.br> | 2015-08-31 16:45:45 -0300 |
commit | 6a1025c09e7c2f0489c64a3d09e48fd4197213fc (patch) | |
tree | 590343aa9b193327637d8ffa5a25c5947c986316 | |
parent | cb2cea98cdad2a76098139b1cf40b2b61dcbe4f0 (diff) | |
download | efl-6a1025c09e7c2f0489c64a3d09e48fd4197213fc.tar.gz |
ejson: Json Efl.Model implementation
-rw-r--r-- | cmakeconfig/EjsonConfig.cmake.in | 32 | ||||
-rw-r--r-- | cmakeconfig/EjsonCxxConfig.cmake.in | 30 | ||||
-rw-r--r-- | configure.ac | 54 | ||||
-rw-r--r-- | pc/ejson-cxx.pc.in | 12 | ||||
-rw-r--r-- | pc/ejson.pc.in | 12 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/Makefile_Ejson.am | 86 | ||||
-rw-r--r-- | src/Makefile_Ejson_Cxx.am | 22 | ||||
-rw-r--r-- | src/lib/ejson/Ejson.h | 67 | ||||
-rw-r--r-- | src/lib/ejson/ejson_main.c | 65 | ||||
-rw-r--r-- | src/lib/ejson/ejson_model.c | 1117 | ||||
-rw-r--r-- | src/lib/ejson/ejson_model.eo | 50 | ||||
-rw-r--r-- | src/lib/ejson/ejson_model_private.h | 31 | ||||
-rw-r--r-- | src/lib/ejson/ejson_private.h | 15 | ||||
-rw-r--r-- | src/tests/ejson/Makefile.am | 11 | ||||
-rw-r--r-- | src/tests/ejson/ejson_suite.c | 121 | ||||
-rw-r--r-- | src/tests/ejson/ejson_suite.h | 16 | ||||
-rw-r--r-- | src/tests/ejson/ejson_test_ejson_model.c | 782 | ||||
-rw-r--r-- | src/tests/ejson/ejson_test_ejson_model.h | 6 |
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, ¤t_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 |