diff --git a/legacy/eina/ChangeLog b/legacy/eina/ChangeLog index dfecb615d0..19bfb3f978 100644 --- a/legacy/eina/ChangeLog +++ b/legacy/eina/ChangeLog @@ -198,3 +198,7 @@ 2012-01-19 Shinwoo Kim * Fix compilation of eina_semaphore_lock() (Windows port) + +2012-01-20 Gustavo Barbieri + + * Add eina_model data type (generic hierarchy data access). diff --git a/legacy/eina/NEWS b/legacy/eina/NEWS index 8669aa7d17..328f9742e3 100644 --- a/legacy/eina/NEWS +++ b/legacy/eina/NEWS @@ -14,6 +14,7 @@ Additions: * Deprecated eina_array_count_get(), use eina_array_count() instead. * Added eina_inarray data type * Added eina_value data type (generic value storage) + * Added eina_model data type (generic hierarchy data access) Eina 1.1.0 diff --git a/legacy/eina/src/include/Eina.h b/legacy/eina/src/include/Eina.h index 757fb3bb47..f53582242f 100644 --- a/legacy/eina/src/include/Eina.h +++ b/legacy/eina/src/include/Eina.h @@ -196,6 +196,7 @@ extern "C" { #include "eina_mmap.h" #include "eina_xattr.h" #include "eina_value.h" +#include "eina_model.h" #ifdef __cplusplus } diff --git a/legacy/eina/src/include/eina_model.h b/legacy/eina/src/include/eina_model.h new file mode 100644 index 0000000000..958b31cbcd --- /dev/null +++ b/legacy/eina/src/include/eina_model.h @@ -0,0 +1,887 @@ +/* EINA - EFL data type library + * Copyright (C) 2012 ProFUSION embedded systems + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifndef EINA_MODEL_H_ +#define EINA_MODEL_H_ + +#include "eina_types.h" +#include "eina_value.h" +#include + +/** + * @addtogroup Eina_Data_Types_Group Data Types + * + * @since 1.2 + * + * @{ + */ + +/** + * @addtogroup Eina_Containers_Group Containers + * + * @{ + */ + +/** + * @defgroup Eina_Model_Group Data Model API. + * + * Abstracts data access to hierarchical data in an efficient way, + * extensible to different backign store such as database or remote + * access. + * + * It is heavily based on #Eina_Value, as properties are exchanged + * using this data type as interface, although internally models may + * store them as they want. See @ref Eina_Value_Group. + * + * Although extensible and easy to optimize, a simple generic type is + * provided as #EINA_MODEL_TYPE_GENERIC. It is recommended that people + * use it during development, get the logic right and just then + * optimize what is needed (properties or children management). + * + * @code + * + * static void _cb_on_deleted(void *data, Eina_Model *model, const Eina_Model_Event_Description *desc, void *event_info) + * { + * printf("deleted %p\n". model); + * } + * + * int main(void) + * { + * Eina_Model *m; + * char *s; + * int i; + * + * eina_init(); + * + * m = eina_model_new(EINA_MODEL_TYPE_GENERIC); + * + * eina_model_event_callback_add(m, "deleted", _cb_on_deleted, NULL); + * + * for (i = 0; i < 5; i++) + * { + * Eina_Value val; + * char name[2] = {'a'+ i, 0}; + * eina_value_setup(&val, EINA_VALUE_TYPE_INT); + * eina_value_set(&val, i); + * eina_model_property_set(m, name, &val); + * eina_value_flush(&val); + * } + * + * for (i = 0; i < 5; i++) + * { + * Eina_Value val; + * Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC); + * eina_value_setup(&val, EINA_VALUE_TYPE_INT); + * eina_value_set(&val, i); + * eina_model_property_set(c, "x", &val); + * + * eina_model_event_callback_add(c, "deleted", _cb_on_deleted, NULL); + * + * eina_model_child_append(m, c); + * eina_model_unref(c); + * eina_value_flush(&val); + * } + * + * s = eina_model_to_string(m); + * printf("model as string:\n%s\n", s); + * + * free(s); + * eina_model_unref(m); + * + * return 0; + * } + * @endcode + * + * @{ + */ + +/** + * @var EINA_ERROR_MODEL_FAILED + * Defined when model-specific errors happens. + */ +EAPI extern Eina_Error EINA_ERROR_MODEL_FAILED; + +/** + * @var EINA_ERROR_MODEL_METHOD_MISSING + * Defined when model-specific errors happens. + */ +EAPI extern Eina_Error EINA_ERROR_MODEL_METHOD_MISSING; + +/** + * @typedef Eina_Model + * Data Model Object. + * + * @since 1.2 + */ +typedef struct _Eina_Model Eina_Model; + + +/** + * @typedef Eina_Model_Type + * Data Model Type. + * + * @since 1.2 + */ +typedef struct _Eina_Model_Type Eina_Model_Type; + +/** + * @typedef Eina_Model_Interface + * Data Model Interface. + * + * @since 1.2 + */ +typedef struct _Eina_Model_Interface Eina_Model_Interface; + +/** + * @typedef Eina_Model_Event_Description + * Data Model Event Description. + * + * @since 1.2 + */ +typedef struct _Eina_Model_Event_Description Eina_Model_Event_Description; + +/** + * @typedef Eina_Model_Event_Cb + * Notifies of events in this model. + * + * @since 1.2 + */ +typedef void (*Eina_Model_Event_Cb)(void *data, Eina_Model *model, const Eina_Model_Event_Description *desc, void *event_info); + +EAPI Eina_Model *eina_model_new(const Eina_Model_Type *type); +EAPI void eina_model_del(Eina_Model *model) EINA_ARG_NONNULL(1); + +EAPI const Eina_Model_Type *eina_model_type_get(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI const Eina_Model_Interface *eina_model_interface_get(const Eina_Model *model, + const char *name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI Eina_Bool eina_model_instance_check(const Eina_Model *model, + const Eina_Model_Type *type) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI Eina_Bool eina_model_interface_implemented(const Eina_Model *model, const Eina_Model_Interface *iface) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI Eina_Model *eina_model_ref(Eina_Model *model) EINA_ARG_NONNULL(1); +EAPI void eina_model_unref(Eina_Model *model) EINA_ARG_NONNULL(1); +EAPI int eina_model_refcount(const Eina_Model *model) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool eina_model_event_callback_add(Eina_Model *model, + const char *event_name, + Eina_Model_Event_Cb cb, + const void *data) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_model_event_callback_del(Eina_Model *model, + const char *event_name, + Eina_Model_Event_Cb cb, + const void *data) EINA_ARG_NONNULL(1, 2, 3); + +EAPI const Eina_Model_Event_Description *eina_model_event_description_get(const Eina_Model *model, + const char *event_name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI Eina_List *eina_model_event_names_list_get(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI void eina_model_event_names_list_free(Eina_List *list); + +EAPI Eina_Bool eina_model_event_callback_call(Eina_Model *model, + const char *name, + const void *event_info) EINA_ARG_NONNULL(1, 2); + +EAPI int eina_model_event_callback_freeze(Eina_Model *model, + const char *name) EINA_ARG_NONNULL(1, 2); +EAPI int eina_model_event_callback_thaw(Eina_Model *model, + const char *name) EINA_ARG_NONNULL(1, 2); + + +EAPI Eina_Model *eina_model_copy(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_MALLOC; +EAPI Eina_Model *eina_model_deep_copy(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_MALLOC; + +EAPI int eina_model_compare(const Eina_Model *a, const Eina_Model *b) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2); + +EAPI Eina_Bool eina_model_load(Eina_Model *model) EINA_ARG_NONNULL(1); +EAPI Eina_Bool eina_model_unload(Eina_Model *model) EINA_ARG_NONNULL(1); + + +EAPI Eina_Bool eina_model_property_get(const Eina_Model *model, + const char *name, + Eina_Value *value) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_model_property_set(Eina_Model *model, + const char *name, + const Eina_Value *value) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_model_property_del(Eina_Model *model, + const char *name) EINA_ARG_NONNULL(1, 2); + +EAPI Eina_List *eina_model_properties_names_list_get(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI void eina_model_properties_names_list_free(Eina_List *list); + +EAPI int eina_model_child_count(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief Get the child at a given position from a model. + * @param model the model instance. + * @param position index of child to get. + * @return child instance with reference @b increased, or @c NULL on error. + * + * The given @a position must be valid, otherwise it may fail and + * return @c NULL, one can check for a valid position with + * eina_model_child_count(). + * + * The returned model has its reference increased, you must release it + * with eina_model_unref(). This convention is imposed to avoid the + * object being removed before the caller function has time to use it. + */ +EAPI Eina_Model *eina_model_child_get(const Eina_Model *model, + unsigned int position) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief Set the child at a given position from a model. + * @param model the model instance. + * @param position index of child to set. + * @param child the child to use at given position. + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * The given @a position must be valid, otherwise it may fail and + * return #EINA_FALSE, one can check for a valid position with + * eina_model_child_count(). + * + * The existing child is replaced. Its reference will be decreased + * automatically. To insert a new item instead of replacing, use + * eina_model_child_insert_at() or eina_model_child_append(). + * + * The given model will be adopted by @a model, that is, the @a child + * will have its reference increased if this call succeeds. + */ +EAPI Eina_Bool eina_model_child_set(Eina_Model *model, + unsigned int position, + Eina_Model *child) EINA_ARG_NONNULL(1, 3); + +EAPI Eina_Bool eina_model_child_del(Eina_Model *model, + unsigned int position) EINA_ARG_NONNULL(1); + +EAPI Eina_Bool eina_model_child_insert_at(Eina_Model *model, + unsigned int position, + Eina_Model *child) EINA_ARG_NONNULL(1, 3); + +EAPI int eina_model_child_append(Eina_Model *model, + Eina_Model *child) EINA_ARG_NONNULL(1, 2); + +EAPI int eina_model_child_find(const Eina_Model *model, + unsigned int start_position, + const Eina_Model *other) EINA_ARG_NONNULL(1, 3) EINA_WARN_UNUSED_RESULT; + +EAPI int eina_model_child_search(const Eina_Model *model, + unsigned int start_position, + Eina_Each_Cb match, + const void *data) EINA_ARG_NONNULL(1, 3) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool eina_model_child_sort(Eina_Model *model, + Eina_Compare_Cb compare) EINA_ARG_NONNULL(1, 2); + + +/** + * @brief create an iterator that outputs a child model on each iteration. + * @param model the model instance. + * @return newly created iterator instance on success or @c NULL on failure. + * + * Each iteration output a child model with reference @b increased! + * You must call eina_model_unref() after you're done with it. + * + * @code + * Eina_Model *child; + * Eina_Iterator *it = eina_model_child_iterator_get(model); + * EINA_ITERATOR_FOREACH(it, child) + * { + * use_child(child); + * eina_model_unref(child); + * } + * eina_iterator_free(it); + * @endcode + * + * @see eina_model_child_slice_iterator_get() + * @see eina_model_child_reversed_iterator_get() + * @see eina_model_child_sorted_iterator_get() + * @see eina_model_child_filtered_iterator_get() + * @since 1.2 + */ +EAPI Eina_Iterator *eina_model_child_iterator_get(Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Iterator *eina_model_child_slice_iterator_get(Eina_Model *model, + unsigned int start, + unsigned int count) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief create an iterator that outputs a child model in reversed order. + * @param model the model instance. + * @return newly created iterator instance on success or @c NULL on failure. + * + * Each iteration output a child model with reference @b increased! + * You must call eina_model_unref() after you're done with it. + * + * The order is reversed, that is, the last element is outputted first. + * + * @code + * Eina_Model *child; + * Eina_Iterator *it = eina_model_child_reversed_iterator_get(model); + * EINA_ITERATOR_FOREACH(it, child) + * { + * use_child(child); + * eina_model_unref(child); + * } + * eina_iterator_free(it); + * @endcode + * + * @see eina_model_child_slice_iterator_get() + * @see eina_model_child_reversed_iterator_get() + * @see eina_model_child_sorted_iterator_get() + * @see eina_model_child_filtered_iterator_get() + * @since 1.2 + */ +EAPI Eina_Iterator *eina_model_child_reversed_iterator_get(Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Iterator *eina_model_child_slice_reversed_iterator_get(Eina_Model *model, + unsigned int start, + unsigned int count) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT; + +/** + * @brief create an iterator that outputs a child model using sort criteria. + * @param model the model instance. + * @param compare compare function to use as sort criteria. + * @return newly created iterator instance on success or @c NULL on failure. + * + * Each iteration output a child model with reference @b increased! + * You must call eina_model_unref() after you're done with it. + * + * The sort will not affect the main object @a model, it's just a view + * of it. + * + * @code + * Eina_Model *child; + * Eina_Iterator *it = eina_model_child_sorted_iterator_get(model, EINA_COMPARE_CB(eina_model_compare)); + * EINA_ITERATOR_FOREACH(it, child) + * { + * use_child(child); + * eina_model_unref(child); + * } + * eina_iterator_free(it); + * @endcode + * + * @see eina_model_child_slice_iterator_get() + * @see eina_model_child_reversed_iterator_get() + * @see eina_model_child_sorted_iterator_get() + * @see eina_model_child_filtered_iterator_get() + * @since 1.2 + */ +EAPI Eina_Iterator *eina_model_child_sorted_iterator_get(Eina_Model *model, + Eina_Compare_Cb compare) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Iterator *eina_model_child_slice_sorted_iterator_get(Eina_Model *model, + unsigned int start, + unsigned int count, + Eina_Compare_Cb compare) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +/** + * @brief create an iterator that indexes of children that matches. + * @param model the model instance. + * @param match function to select children. + * @param data extra context given to @a match function. + * @return newly created iterator instance on success or @c NULL on failure. + * + * Unlike other iterators, each iteration output an integer index! + * This is useful if you want to highlight the matching model + * somewhere else. + * + * @code + * unsigned int idx; + * Eina_Iterator *it = eina_model_child_filtered_iterator_get(model, filter, ctx); + * EINA_ITERATOR_FOREACH(it, idx) + * { + * Eina_Model *child = eina_model_child_get(model, idx); + * printf("matches at %u %p\n", idx, child); + * eina_model_unref(child); + * } + * eina_iterator_free(it); + * @endcode + * + * @see eina_model_child_slice_iterator_get() + * @see eina_model_child_reversed_iterator_get() + * @see eina_model_child_sorted_iterator_get() + * @see eina_model_child_filtered_iterator_get() + * @since 1.2 + */ +EAPI Eina_Iterator *eina_model_child_filtered_iterator_get(Eina_Model *model, + Eina_Each_Cb match, + const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Iterator *eina_model_child_slice_filtered_iterator_get(Eina_Model *model, + unsigned int start, + unsigned int count, + Eina_Each_Cb match, + const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; + + +/** + * @brief Convert model to string. + * @param model the model instance. + * @return newly allocated memory or @c NULL on failure. + * @since 1.2 + */ +EAPI char *eina_model_to_string(const Eina_Model *model) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_MALLOC; + +/** + * @defgroup Eina_Model_Type_Group Data Model Type management + * + * @{ + */ + +/** + * @struct _Eina_Model_Type + * API to access models. + * + * The methods @c setup, @c flush, @c constructor, @c destructor and + * @c property_get are mandatory and must exist, otherwise type cannot + * be used. + * + * Each type of the hierarchy and each interface will get its own + * private data of size @c private_size (defined at each subtype or + * interface), this can be retrieved with + * eina_model_type_private_data_get() and + * eina_model_interface_private_data_get(). + * + * Private are created @b automatically and should be setup with @c + * setup and flushed with @c flush. All types (or interfaces) + * functions that exist are called! Don't call parent's @c setup or @c + * flush! The setup is done from parent to child. Flush is done from + * child to parent. + * + * After memory setup was done, @c constructor of the toplevel type + * defining it is called. If desired it may call parent's constructor + * in whatever order is desired. This may be used to create + * properties, children and may use parent's data if needed. Just the + * topmost type constructor is called, if interface constructors + * should be called, do them in the desired order from the type + * constructor. + * + * When the model is deleted, explicitly with eina_model_del() or + * implicitly with eina_model_unref() on the last reference, the @c + * destructor is called. It must release references to other + * models. When the last reference is dropped, every @c flush is + * called from child to parent, then memory is freed. Just the topmost + * type destructor is called, if interface destructors should be + * called, do them in the desired order from the type destructor. + * + * @note a runtime check will enforce just types with ABI version + * #EINA_MODEL_TYPE_VERSION are used by comparing with @c version + * member. + * + * @since 1.2 + */ +struct _Eina_Model_Type +{ + /** + * @def EINA_MODEL_TYPE_VERSION + * Current API version, used to validate type. + */ +#define EINA_MODEL_TYPE_VERSION (1) + unsigned int version; /**< must be #EINA_MODEL_TYPE_VERSION */ + unsigned int private_size; /**< used to allocate type private data */ + const char *name; /**< name for debug and introspection */ + const Eina_Model_Type *parent; /**< parent type */ + const Eina_Model_Interface **interfaces; /**< null terminated array of interfaces */ + const Eina_Model_Event_Description *events; /**< null terminated array of events */ + Eina_Bool (*setup)(Eina_Model *model); /**< setup type private data, do @b not call parent type setup! */ + Eina_Bool (*flush)(Eina_Model *model); /**< flush type private data, do @b not call parent type flush! */ + Eina_Bool (*constructor)(Eina_Model *model); /**< construct type instance, setup was already called. Should call parent's or interfaces' constructor if needed */ + Eina_Bool (*destructor)(Eina_Model *model); /**< destruct type instance, flush will be called after it. Should call parent's or interfaces' destructor if needed. Release reference to other models here. */ + Eina_Bool (*copy)(const Eina_Model *src, Eina_Model *dst); /**< copy type private data, do @b not call parent type copy! */ + Eina_Bool (*deep_copy)(const Eina_Model *src, Eina_Model *dst); /**< deep copy type private data, do @b not call parent type deep copy! */ + Eina_Bool (*compare)(const Eina_Model *a, const Eina_Model *b, int *cmp); + Eina_Bool (*load)(Eina_Model *model); + Eina_Bool (*unload)(Eina_Model *model); + Eina_Bool (*property_get)(const Eina_Model *model, const char *name, Eina_Value *value); + Eina_Bool (*property_set)(Eina_Model *model, const char *name, const Eina_Value *value); + Eina_Bool (*property_del)(Eina_Model *model, const char *name); + Eina_List *(*properties_names_list_get)(const Eina_Model *model); /**< list of stringshare */ + int (*child_count)(const Eina_Model *model); + Eina_Model *(*child_get)(const Eina_Model *model, unsigned int position); + Eina_Bool (*child_set)(Eina_Model *model, unsigned int position, Eina_Model *child); + Eina_Bool (*child_del)(Eina_Model *model, unsigned int position); + Eina_Bool (*child_insert_at)(Eina_Model *model, unsigned int position, Eina_Model *child); + int (*child_find)(const Eina_Model *model, unsigned int start_position, const Eina_Model *other); + int (*child_search)(const Eina_Model *model, unsigned int start_position, Eina_Each_Cb match, const void *data); + void (*child_sort)(Eina_Model *model, Eina_Compare_Cb compare); + Eina_Iterator *(*child_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count); + Eina_Iterator *(*child_reversed_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count); + Eina_Iterator *(*child_sorted_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count, Eina_Compare_Cb compare); + Eina_Iterator *(*child_filtered_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count, Eina_Each_Cb match, const void *data); + char *(*to_string)(const Eina_Model *model); /**< used to represent model as string, usually for debug purposes or user convenience */ + const void *value; /**< may hold extension methods */ +}; + +EAPI Eina_Bool eina_model_type_constructor(const Eina_Model_Type *type, + Eina_Model *model) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_model_type_destructor(const Eina_Model_Type *type, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_type_copy(const Eina_Model_Type *type, + const Eina_Model *src, + Eina_Model *dst) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_model_type_deep_copy(const Eina_Model_Type *type, + const Eina_Model *src, + Eina_Model *dst) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_model_type_compare(const Eina_Model_Type *type, + const Eina_Model *a, + const Eina_Model *b, + int *cmp) EINA_ARG_NONNULL(1, 2, 3, 4); +EAPI Eina_Bool eina_model_type_load(const Eina_Model_Type *type, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_type_unload(const Eina_Model_Type *type, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_type_property_get(const Eina_Model_Type *type, + const Eina_Model *model, + const char *name, + Eina_Value *value) EINA_ARG_NONNULL(1, 2, 3, 4); +EAPI Eina_Bool eina_model_type_property_set(const Eina_Model_Type *type, + Eina_Model *model, + const char *name, + const Eina_Value *value) EINA_ARG_NONNULL(1, 2, 3, 4); +EAPI Eina_Bool eina_model_type_property_del(const Eina_Model_Type *type, + Eina_Model *model, + const char *name) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_List *eina_model_type_properties_names_list_get(const Eina_Model_Type *type, + const Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI int eina_model_type_child_count(const Eina_Model_Type *type, + const Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Model *eina_model_type_child_get(const Eina_Model_Type *type, + const Eina_Model *model, + unsigned int position) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_type_child_set(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int position, + Eina_Model *child) EINA_ARG_NONNULL(1, 2, 4); +EAPI Eina_Bool eina_model_type_child_del(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int position) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_type_child_insert_at(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int position, + Eina_Model *child) EINA_ARG_NONNULL(1, 2, 4); +EAPI int eina_model_type_child_find(const Eina_Model_Type *type, + const Eina_Model *model, + unsigned int start_position, + const Eina_Model *other) EINA_ARG_NONNULL(1, 2, 4); +EAPI int eina_model_type_child_search(const Eina_Model_Type *type, + const Eina_Model *model, + unsigned int start_position, + Eina_Each_Cb match, + const void *data) EINA_ARG_NONNULL(1, 2, 4); +EAPI void eina_model_type_child_sort(const Eina_Model_Type *type, + Eina_Model *model, + Eina_Compare_Cb compare) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Iterator *eina_model_type_child_iterator_get(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int start, + unsigned int count) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Iterator *eina_model_type_child_reversed_iterator_get(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int start, + unsigned int count) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Iterator *eina_model_type_child_sorted_iterator_get(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int start, + unsigned int count, + Eina_Compare_Cb compare) EINA_ARG_NONNULL(1, 2, 5); +EAPI Eina_Iterator *eina_model_type_child_filtered_iterator_get(const Eina_Model_Type *type, + Eina_Model *model, + unsigned int start, + unsigned int count, + Eina_Each_Cb match, + const void *data) EINA_ARG_NONNULL(1, 2, 5); +EAPI char *eina_model_type_to_string(const Eina_Model_Type *type, + const Eina_Model *model) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_MALLOC; + +/** + * @struct _Eina_Model_Interface + * + * Interfaces are managed by name, then multiple Eina_Model_Interface + * may have the same name meaning it implements that name. + * + * @note use the same name pointer on queries to speed up the lookups! + * + * @since 1.2 + */ +struct _Eina_Model_Interface +{ + /** + * @def EINA_MODEL_INTERFACE_VERSION + * Current API version, used to validate interface. + */ +#define EINA_MODEL_INTERFACE_VERSION (1) + unsigned int version; /**< must be #EINA_MODEL_INTERFACE_VERSION */ + unsigned int private_size; /**< used to allocate interface private data */ + const char *name; /**< name for debug and introspection */ + const Eina_Model_Interface **interfaces; /**< null terminated array of parent interfaces */ + const Eina_Model_Event_Description *events; /**< null terminated array of events */ + Eina_Bool (*setup)(Eina_Model *model); /**< setup interface private data, do @b not call parent interface setup! */ + Eina_Bool (*flush)(Eina_Model *model); /**< flush interface private data, do @b not call parent interface flush! */ + Eina_Bool (*constructor)(Eina_Model *model); /**< construct interface instance, setup was already called. Should call parent's constructor if needed */ + Eina_Bool (*destructor)(Eina_Model *model); /**< destruct interface instance, flush will be called after it. Should call parent's destructor if needed. Release reference to other models here. */ + Eina_Bool (*copy)(const Eina_Model *src, Eina_Model *dst); /**< copy interface private data, do @b not call parent interface copy! */ + Eina_Bool (*deep_copy)(const Eina_Model *src, Eina_Model *dst); /**< deep copy interface private data, do @b not call parent interface deep copy! */ + const void *value; /**< holds the actual interface methods */ +}; + +EAPI Eina_Bool eina_model_interface_constructor(const Eina_Model_Interface *iface, + Eina_Model *model) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_model_interface_destructor(const Eina_Model_Interface *iface, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_interface_copy(const Eina_Model_Interface *iface, + const Eina_Model *src, + Eina_Model *dst) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_Bool eina_model_interface_deep_copy(const Eina_Model_Interface *iface, + const Eina_Model *src, + Eina_Model *dst) EINA_ARG_NONNULL(1, 2, 3); + + +struct _Eina_Model_Event_Description +{ + const char *name; /**< name used for lookups */ + const char *type; /**< used for introspection purposes, documents what goes as callback event information (@c event_info) */ + const char *doc; /**< documentation for introspection purposes */ +}; +#define EINA_MODEL_EVENT_DESCRIPTION(name, type, doc) {name, type, doc} +#define EINA_MODEL_EVENT_DESCRIPTION_SENTINEL {NULL, NULL, NULL} + +EAPI Eina_Bool eina_model_type_check(const Eina_Model_Type *type) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; +EAPI const char *eina_model_type_name_get(const Eina_Model_Type *type) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; +EAPI const Eina_Model_Type *eina_model_type_parent_get(const Eina_Model_Type *type) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI Eina_Bool eina_model_type_subclass_check(const Eina_Model_Type *type, + const Eina_Model_Type *self_or_parent) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE; + + +EAPI const Eina_Model_Interface *eina_model_type_interface_get(const Eina_Model_Type *type, + const char *name) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI void *eina_model_type_private_data_get(const Eina_Model *model, + const Eina_Model_Type *type) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2) EINA_PURE; + +EAPI Eina_Bool eina_model_interface_check(const Eina_Model_Interface *iface) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT EINA_PURE; + +EAPI void *eina_model_interface_private_data_get(const Eina_Model *model, + const Eina_Model_Interface *iface) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1, 2) EINA_PURE; + +/** + * @var EINA_MODEL_TYPE_BASE + * Base type for all types. + * + * @since 1.2 + */ +EAPI extern const Eina_Model_Type *EINA_MODEL_TYPE_BASE; + +/** + * @var EINA_MODEL_TYPE_MIXIN + * + * Type that uses #EINA_MODEL_INTERFACE_NAME_PROPERTIES and + * #EINA_MODEL_INTERFACE_NAME_CHILDREN to manage the model. + * + * This is an abstract type, it does not work out of the box as one + * need to subclass it and define the interface implementations for + * properties and children, as done by #EINA_MODEL_TYPE_GENERIC + * + * @see EINA_MODEL_TYPE_GENERIC + * + * @since 1.2 + */ +EAPI extern const Eina_Model_Type *EINA_MODEL_TYPE_MIXIN; + +/** + * @var EINA_MODEL_TYPE_GENERIC + * + * Subclass of #EINA_MODEL_TYPE_MIXIN that uses + * #EINA_MODEL_INTERFACE_PROPERTIES_HASH and + * #EINA_MODEL_INTERFACE_CHILDREN_INARRAY. + * + * Should be generic enough to hold lots of items with runtime + * configurable properties of any type. + * + * @since 1.2 + */ +EAPI extern const Eina_Model_Type *EINA_MODEL_TYPE_GENERIC; + +/** + * @var EINA_MODEL_INTERFACE_NAME_PROPERTIES + * + * Interface that uses #Eina_Model_Interface_Properties as + * Eina_Model_Interface::value and can manage the model properties. + * + * @since 1.2 + */ +EAPI extern const char *EINA_MODEL_INTERFACE_NAME_PROPERTIES; + +typedef struct _Eina_Model_Interface_Properties Eina_Model_Interface_Properties; +struct _Eina_Model_Interface_Properties +{ +#define EINA_MODEL_INTERFACE_PROPERTIES_VERSION (1) + unsigned int version; + Eina_Bool (*compare)(const Eina_Model *a, const Eina_Model *b, int *cmp); + Eina_Bool (*load)(Eina_Model *model); + Eina_Bool (*unload)(Eina_Model *model); + Eina_Bool (*get)(const Eina_Model *model, const char *name, Eina_Value *value); + Eina_Bool (*set)(Eina_Model *model, const char *name, const Eina_Value *value); + Eina_Bool (*del)(Eina_Model *model, const char *name); + Eina_List *(*names_list_get)(const Eina_Model *model); /**< list of stringshare */ +}; + +EAPI Eina_Bool eina_model_interface_properties_compare(const Eina_Model_Interface *iface, + const Eina_Model *a, + const Eina_Model *b, + int *cmp) EINA_ARG_NONNULL(1, 2, 3, 4) EINA_WARN_UNUSED_RESULT; + +EAPI Eina_Bool eina_model_interface_properties_load(const Eina_Model_Interface *iface, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_interface_properties_unload(const Eina_Model_Interface *iface, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_interface_properties_get(const Eina_Model_Interface *iface, + const Eina_Model *model, + const char *name, + Eina_Value *value) EINA_ARG_NONNULL(1, 2, 3, 4); +EAPI Eina_Bool eina_model_interface_properties_set(const Eina_Model_Interface *iface, + Eina_Model *model, + const char *name, + const Eina_Value *value) EINA_ARG_NONNULL(1, 2, 3, 4); +EAPI Eina_Bool eina_model_interface_properties_del(const Eina_Model_Interface *iface, + Eina_Model *model, + const char *name) EINA_ARG_NONNULL(1, 2, 3); +EAPI Eina_List *eina_model_interface_properties_names_list_get(const Eina_Model_Interface *iface, + const Eina_Model *model) EINA_ARG_NONNULL(1, 2); /**< list of stringshare */ + +/** + * @var EINA_MODEL_INTERFACE_PROPERTIES_HASH + * + * Implements #Eina_Model_Interface_Properties + * (#EINA_MODEL_INTERFACE_NAME_PROPERTIES) using #Eina_Hash. + * + * @note This function is generic but uses too much space given the + * hash data type. For huge number of elements it's better to + * use custom implementation instead. + * + * @since 1.2 + */ +EAPI extern const Eina_Model_Interface *EINA_MODEL_INTERFACE_PROPERTIES_HASH; + + +/** + * @var EINA_MODEL_INTERFACE_NAME_CHILDREN + * + * Interface that uses #Eina_Model_Interface_Children as + * Eina_Model_Interface::value and can manage the model children. + * + * @since 1.2 + */ +EAPI extern const char *EINA_MODEL_INTERFACE_NAME_CHILDREN; + +/** + * @typedef Eina_Model_Interface_Children + * + * The #Eina_Model_Interface::value when name is + * #EINA_MODEL_INTERFACE_NAME_CHILDREN interface is implemented. + * + * @since 1.2 + */ +typedef struct _Eina_Model_Interface_Children Eina_Model_Interface_Children; + +/** + * @struct _Eina_Model_Interface_Children + * + * The #Eina_Model_Interface::value when name is + * #EINA_MODEL_INTERFACE_NAME_CHILDREN interface is implemented. + * + * The methods are called in the same way children methods from + * #Eina_Model_Type. + * + * @since 1.2 + */ +struct _Eina_Model_Interface_Children +{ +#define EINA_MODEL_INTERFACE_CHILDREN_VERSION (1) + unsigned int version; + Eina_Bool (*compare)(const Eina_Model *a, const Eina_Model *b, int *cmp); + Eina_Bool (*load)(Eina_Model *model); + Eina_Bool (*unload)(Eina_Model *model); + int (*count)(const Eina_Model *model); + Eina_Model *(*get)(const Eina_Model *model, unsigned int position); + Eina_Bool (*set)(Eina_Model *model, unsigned int position, Eina_Model *child); + Eina_Bool (*del)(Eina_Model *model, unsigned int position); + Eina_Bool (*insert_at)(Eina_Model *model, unsigned int position, Eina_Model *child); + void (*sort)(Eina_Model *model, Eina_Compare_Cb compare); +}; + +EAPI Eina_Bool eina_model_interface_children_compare(const Eina_Model_Interface *iface, + const Eina_Model *a, + const Eina_Model *b, + int *cmp) EINA_ARG_NONNULL(1, 2, 3, 4) EINA_WARN_UNUSED_RESULT; +EAPI Eina_Bool eina_model_interface_children_load(const Eina_Model_Interface *iface, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_interface_children_unload(const Eina_Model_Interface *iface, + Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI int eina_model_interface_children_count(const Eina_Model_Interface *iface, + const Eina_Model *model) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Model *eina_model_interface_children_get(const Eina_Model_Interface *iface, + const Eina_Model *model, + unsigned int position) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_interface_children_set(const Eina_Model_Interface *iface, + Eina_Model *model, + unsigned int position, + Eina_Model *child) EINA_ARG_NONNULL(1, 2, 4); +EAPI Eina_Bool eina_model_interface_children_del(const Eina_Model_Interface *iface, + Eina_Model *model, + unsigned int position) EINA_ARG_NONNULL(1, 2); +EAPI Eina_Bool eina_model_interface_children_insert_at(const Eina_Model_Interface *iface, + Eina_Model *model, + unsigned int position, + Eina_Model *child) EINA_ARG_NONNULL(1, 2, 4); +EAPI void eina_model_interface_children_sort(const Eina_Model_Interface *iface, + Eina_Model *model, + Eina_Compare_Cb compare) EINA_ARG_NONNULL(1, 2, 3); + +/** + * @var EINA_MODEL_INTERFACE_CHILDREN_INARRAY + * + * Implements #Eina_Model_Interface_Children + * (#EINA_MODEL_INTERFACE_NAME_CHILDREN) using #Eina_Inarray. It + * should be efficient in space and time for most operations. + * + * @note it may become slow if eina_model_child_insert_at() is used at + * the beginning of the array as the members from that position + * to the end must be memmove()d. + * + * @since 1.2 + */ +EAPI extern const Eina_Model_Interface *EINA_MODEL_INTERFACE_CHILDREN_INARRAY; + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ +#endif diff --git a/legacy/eina/src/lib/Makefile.am b/legacy/eina/src/lib/Makefile.am index d8c9d2051f..e2ac704393 100644 --- a/legacy/eina/src/lib/Makefile.am +++ b/legacy/eina/src/lib/Makefile.am @@ -51,7 +51,8 @@ eina_unicode.c \ eina_ustrbuf.c \ eina_ustringshare.c \ eina_value.c \ -eina_xattr.c +eina_xattr.c \ +eina_model.c # Will be back for developper after 1.1 # eina_object.c diff --git a/legacy/eina/src/lib/eina_main.c b/legacy/eina/src/lib/eina_main.c index 79f8a36632..f233929571 100644 --- a/legacy/eina/src/lib/eina_main.c +++ b/legacy/eina/src/lib/eina_main.c @@ -69,6 +69,7 @@ #include "eina_inlist.h" #include "eina_inarray.h" #include "eina_value.h" +#include "eina_model.h" /*============================================================================* * Local * @@ -153,6 +154,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL; S(file); S(prefix); S(value); + S(model); #undef S struct eina_desc_setup @@ -189,7 +191,8 @@ static const struct eina_desc_setup _eina_desc_setup[] = { S(simple_xml), S(file), S(prefix), - S(value) + S(value), + S(model) #undef S }; static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) / diff --git a/legacy/eina/src/lib/eina_model.c b/legacy/eina/src/lib/eina_model.c new file mode 100644 index 0000000000..02ffd4c6c5 --- /dev/null +++ b/legacy/eina/src/lib/eina_model.c @@ -0,0 +1,4847 @@ +/* EINA - EFL data type library + * Copyright (C) 2012 ProFUSION embedded systems + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_ALLOCA_H +# include +#elif defined __GNUC__ +# define alloca __builtin_alloca +#elif defined _AIX +# define alloca __alloca +#elif defined _MSC_VER +# include +# define alloca _alloca +#else +# include +# ifdef __cplusplus +extern "C" +# endif +void *alloca (size_t); +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_error.h" +#include "eina_log.h" +#include "eina_mempool.h" +#include "eina_lock.h" +#include "eina_inlist.h" +#include "eina_strbuf.h" + +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_value.h" /* eina-safety used in inline.x */ +#include "eina_model.h" + +/*============================================================================* + * Local * + *============================================================================*/ + +/** + * @cond LOCAL + */ + +static Eina_Mempool *_eina_model_mp = NULL; +static Eina_Hash *_eina_model_inner_mps = NULL; +static Eina_Lock _eina_model_inner_mps_lock; +static char *_eina_model_mp_choice = NULL; +static Eina_Hash *_eina_model_descriptions = NULL; +static Eina_Lock _eina_model_descriptions_lock; +static int _eina_model_log_dom = -1; + +static const char _eina_model_str_deleted[] = "deleted"; +static const char _eina_model_str_freed[] = "freed"; +static const char _eina_model_str_property_set[] = "property,set"; +static const char _eina_model_str_property_del[] = "property,deleted"; +static const char _eina_model_str_children_changed[] = "children,changed"; +static const char _eina_model_str_child_inserted[] = "child,inserted"; +static const char _eina_model_str_child_set[] = "child,set"; +static const char _eina_model_str_child_del[] = "child,deleted"; + +#ifdef CRITICAL +#undef CRITICAL +#endif +#define CRITICAL(...) EINA_LOG_DOM_CRIT(_eina_model_log_dom, __VA_ARGS__) + +#ifdef ERR +#undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_eina_model_log_dom, __VA_ARGS__) + +#ifdef WRN +#undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_eina_model_log_dom, __VA_ARGS__) + +#ifdef INF +#undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_eina_model_log_dom, __VA_ARGS__) + +#ifdef DBG +#undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_eina_model_log_dom, __VA_ARGS__) + + +/* convenience sort array of Eina_Model* giving compare Eina_Model* instead of + * Eina_Model** + */ +static unsigned int +_eina_model_array_partition(Eina_Model **array, unsigned int start, unsigned int last, unsigned int pivot, Eina_Compare_Cb compare) +{ + Eina_Model **itr, **itr_end, *tmp, *pivot_value; + + pivot_value = tmp = array[pivot]; + array[pivot] = array[last]; + array[last] = tmp; + + pivot = start; + itr = array + start; + itr_end = array + last; + for (; itr < itr_end; itr++) + { + if (compare(*itr, pivot_value) < 0) + { + tmp = *itr; + *itr = array[pivot]; + array[pivot] = tmp; + pivot++; + } + } + + tmp = array[last]; + array[last] = array[pivot]; + array[pivot] = tmp; + + return pivot; +} + +static void +_eina_model_array_sort(Eina_Model **array, unsigned int start, unsigned int last, Eina_Compare_Cb compare) +{ + unsigned int pivot, new_pivot; + + if (last <= start) + return; + + pivot = start + (last - start) / 2; /* avoid overflow */ + new_pivot = _eina_model_array_partition(array, start, last, pivot, compare); + + if (start + 1 < new_pivot) + _eina_model_array_sort(array, start, new_pivot - 1, compare); + + if (new_pivot + 1 < last) + _eina_model_array_sort(array, new_pivot + 1, last, compare); +} + +/* + * Most of inner allocations are made with internal mempools, types + * and thus instace private data will repeat and it's good to use them. + * + * To save on the number of mempools, they are kept per size, not per + * type. + * + * This is done by means of _eina_model_inner_alloc() and + * _eina_model_inner_free(), both at thread safe. + * + */ +typedef struct _Eina_Model_Inner_Mp Eina_Model_Inner_Mp; +struct _Eina_Model_Inner_Mp +{ + Eina_Mempool *mempool; + int refcount; +}; + +static inline void +_eina_model_inner_mp_dispose(int size, Eina_Model_Inner_Mp *imp) +{ + EINA_SAFETY_ON_FALSE_RETURN(imp->refcount == 0); + + eina_hash_del_by_key(_eina_model_inner_mps, &size); + eina_mempool_del(imp->mempool); + free(imp); +} + +static inline Eina_Model_Inner_Mp * +_eina_model_inner_mp_get(int size) +{ + Eina_Model_Inner_Mp *imp = eina_hash_find(_eina_model_inner_mps, &size); + if (imp) return imp; + + imp = malloc(sizeof(Eina_Model_Inner_Mp)); + if (!imp) + return NULL; + + imp->refcount = 0; + + imp->mempool = eina_mempool_add(_eina_model_mp_choice, + "Eina_Model_Inner_Mp", NULL, size, 128); + if (!imp->mempool) + { + free(imp); + return NULL; + } + + if (!eina_hash_add(_eina_model_inner_mps, &size, imp)) + { + eina_mempool_del(imp->mempool); + free(imp); + return NULL; + } + + return imp; +} + +static inline void * +_eina_model_inner_alloc_internal(int size) +{ + Eina_Model_Inner_Mp *imp; + void *mem; + + imp = _eina_model_inner_mp_get(size); + if (!imp) return NULL; + + mem = eina_mempool_malloc(imp->mempool, size); + if (mem) imp->refcount++; + else if (imp->refcount == 0) _eina_model_inner_mp_dispose(size, imp); + + return mem; +} + +static inline void +_eina_model_inner_free_internal(int size, void *mem) +{ + Eina_Model_Inner_Mp *imp = eina_hash_find(_eina_model_inner_mps, &size); + EINA_SAFETY_ON_NULL_RETURN(imp); + + eina_mempool_free(imp->mempool, mem); + + imp->refcount--; + if (imp->refcount > 0) return; + _eina_model_inner_mp_dispose(size, imp); +} + +static void * +_eina_model_inner_alloc(size_t size) +{ + void *mem; + + if (size > 512) return malloc(size); + + eina_lock_take(&_eina_model_inner_mps_lock); + mem = _eina_model_inner_alloc_internal(size); + eina_lock_release(&_eina_model_inner_mps_lock); + + return mem; +} + +static void +_eina_model_inner_free(size_t size, void *mem) +{ + if (size > 512) + { + free(mem); + return; + } + + eina_lock_take(&_eina_model_inner_mps_lock); + _eina_model_inner_free_internal(size, mem); + eina_lock_release(&_eina_model_inner_mps_lock); +} + + +typedef union _Eina_Model_Provider Eina_Model_Provider; +union _Eina_Model_Provider +{ + const Eina_Model_Type *type; + const Eina_Model_Interface *iface; +}; + +/* store event name to aid searching */ +typedef struct _Eina_Model_Event_Description_Cache Eina_Model_Event_Description_Cache; +struct _Eina_Model_Event_Description_Cache +{ + const char *name; + const Eina_Model_Event_Description *desc; + Eina_Model_Provider provider; +}; + +/* description is an optimized structure for type. It's built at runtime + * to avoid user input errors and help declaration. + * + * lookups (ifaces, events) are sorted for binary search. + * + * recursion is avoided by expansion of every possible value in "cache" + * struct. + * + * the first usable operation is stopred for type at "ops" struct, + * avoiding usage of _eina_model_type_find_offset(). + * + * Get a model type description using _eina_model_description_get(), + * when it's not used anymore use + * _eina_model_description_dispose(). These operations are thread + * safe. + */ +typedef struct _Eina_Model_Description Eina_Model_Description; +struct _Eina_Model_Description +{ + struct { + const Eina_Model_Type **types; /* size = total.types */ + const Eina_Model_Interface **ifaces; /* sorted, size = total.ifaces */ + Eina_Model_Provider *privates; /* size = total.privates (types + ifaces) */ + Eina_Model_Event_Description_Cache *events; /* size = total.events */ + } cache; + struct { + /* ops are the topmost operation to use for type/interface */ + struct { + Eina_Bool (*setup)(Eina_Model *model); + Eina_Bool (*flush)(Eina_Model *model); + Eina_Bool (*constructor)(Eina_Model *model); + Eina_Bool (*destructor)(Eina_Model *model); + Eina_Bool (*copy)(const Eina_Model *src, Eina_Model *dst); + Eina_Bool (*deep_copy)(const Eina_Model *src, Eina_Model *dst); + Eina_Bool (*compare)(const Eina_Model *a, const Eina_Model *b, int *cmp); + Eina_Bool (*load)(Eina_Model *model); + Eina_Bool (*unload)(Eina_Model *model); + Eina_Bool (*property_get)(const Eina_Model *model, const char *name, Eina_Value *value); + Eina_Bool (*property_set)(Eina_Model *model, const char *name, const Eina_Value *value); + Eina_Bool (*property_del)(Eina_Model *model, const char *name); + Eina_List *(*properties_names_list_get)(const Eina_Model *model); + int (*child_count)(const Eina_Model *model); + Eina_Model *(*child_get)(const Eina_Model *model, unsigned int position); + Eina_Bool (*child_set)(Eina_Model *model, unsigned int position, Eina_Model *child); + Eina_Bool (*child_del)(Eina_Model *model, unsigned int position); + Eina_Bool (*child_insert_at)(Eina_Model *model, unsigned int position, Eina_Model *child); + int (*child_find)(const Eina_Model *model, unsigned int start_position, const Eina_Model *other); + int (*child_search)(const Eina_Model *model, unsigned int start_position, Eina_Each_Cb match, const void *data); + void (*child_sort)(Eina_Model *model, Eina_Compare_Cb compare); + Eina_Iterator *(*child_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count); + Eina_Iterator *(*child_reversed_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count); + Eina_Iterator *(*child_sorted_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count, Eina_Compare_Cb compare); + Eina_Iterator *(*child_filtered_iterator_get)(Eina_Model *model, unsigned int start, unsigned int count, Eina_Each_Cb match, const void *data); + char *(*to_string)(const Eina_Model *model); /**< used to represent model as string, usually for debug purposes or user convenience */ + } type; + } ops; + struct { + unsigned int types; + unsigned int ifaces; + unsigned int privates; + unsigned int size; /* sum of all private sizes */ + unsigned int events; + } total; + int refcount; +}; + +static Eina_Bool +_eina_model_description_type_fill(Eina_Model_Description *desc, const Eina_Model_Type *type) +{ + const Eina_Model_Type *itr; + unsigned int count; + + for (count = 0, itr = type; itr != NULL; itr = itr->parent, count++) + { + if (itr->version != EINA_MODEL_TYPE_VERSION) + { + CRITICAL("Type %p version is %u, expected %u instead.", + itr, itr->version, EINA_MODEL_TYPE_VERSION); + return EINA_FALSE; + } + if (!itr->name) + { + CRITICAL("Type %p provides no name!", itr); + return EINA_FALSE; + } + +#define DEF_METH(meth) \ + if (!desc->ops.type.meth) desc->ops.type.meth = itr->meth + DEF_METH(setup); + DEF_METH(flush); + DEF_METH(constructor); + DEF_METH(destructor); + DEF_METH(copy); + DEF_METH(deep_copy); + DEF_METH(compare); + DEF_METH(load); + DEF_METH(unload); + DEF_METH(property_get); + DEF_METH(property_set); + DEF_METH(property_del); + DEF_METH(properties_names_list_get); + DEF_METH(child_count); + DEF_METH(child_get); + DEF_METH(child_set); + DEF_METH(child_del); + DEF_METH(child_insert_at); + DEF_METH(child_find); + DEF_METH(child_search); + DEF_METH(child_sort); + DEF_METH(child_iterator_get); + DEF_METH(child_reversed_iterator_get); + DEF_METH(child_sorted_iterator_get); + DEF_METH(child_filtered_iterator_get); + DEF_METH(to_string); +#undef DEF_METH + + if ((!itr->parent) && (itr != EINA_MODEL_TYPE_BASE)) + { + CRITICAL("Type %p (%s) does not inherit from EINA_MODEL_TYPE_BASE!", + type, type->name); + return EINA_FALSE; + } + } + +#define CK_METH(meth) \ + if (!desc->ops.type.meth) \ + { \ + CRITICAL("Mandatory method "#meth \ + "() was not provided by type %p (%s).", \ + type, type->name); \ + return EINA_FALSE; \ + } + CK_METH(setup); + CK_METH(flush); + CK_METH(constructor); + CK_METH(destructor); + CK_METH(property_get); +#undef CK_METH + + desc->cache.types = malloc(count * sizeof(Eina_Model_Type *)); + EINA_SAFETY_ON_NULL_RETURN_VAL(desc->cache.types, EINA_FALSE); + desc->total.types = count; + + for (count = 0, itr = type; itr != NULL; itr = itr->parent, count++) + desc->cache.types[count] = itr; + + return EINA_TRUE; +} + +static inline Eina_Bool +_eina_model_interface_implements(const Eina_Model_Interface *iface, const Eina_Model_Interface *query) +{ + const Eina_Model_Interface **itr; + + if (iface == query) + return EINA_TRUE; + + if (!iface->interfaces) + return EINA_FALSE; + + for (itr = iface->interfaces; *itr != NULL; itr++) + if (_eina_model_interface_implements(*itr, query)) + return EINA_TRUE; + + return EINA_FALSE; +} + +/* apply topological sort and remove duplicates */ +static Eina_Bool +_eina_model_description_ifaces_fix(Eina_Model_Description *desc) +{ + struct node { + const Eina_Model_Interface *iface; + unsigned int users; + Eina_List *deps; + } *nodes, **pending, **roots; + unsigned int n_nodes = desc->total.ifaces, n_pending = 0, n_roots = 0, i; + Eina_Bool ret = EINA_TRUE; + + nodes = alloca(n_nodes * sizeof(struct node)); + pending = alloca(n_nodes * sizeof(struct node *)); + roots = alloca(n_nodes * sizeof(struct node *)); + + /* populate */ + for (i = 0; i < n_nodes; i++) + { + nodes[i].iface = desc->cache.ifaces[i]; + nodes[i].users = 0; + nodes[i].deps = NULL; + } + for (i = 0; i < n_nodes; i++) + { + unsigned int j; + for (j = 0; j < n_nodes; j++) + { + if (i == j) continue; + if (!_eina_model_interface_implements(nodes[j].iface, + nodes[i].iface)) + continue; + + nodes[i].users++; + nodes[j].deps = eina_list_append(nodes[j].deps, nodes + i); + } + } + for (i = 0; i < n_nodes; i++) + { + if (nodes[i].users == 0) + { + roots[n_roots] = nodes + i; + n_roots++; + } + else + { + pending[n_pending] = nodes + i; + n_pending++; + } + } + + /* topological sort */ + desc->total.ifaces = 0; + while (n_roots > 0) + { + struct node *r, *d; + + n_roots--; + r = roots[n_roots]; + + desc->cache.ifaces[desc->total.ifaces] = r->iface; + desc->total.ifaces++; + + EINA_LIST_FREE(r->deps, d) + { + unsigned int j; + + d->users--; + if (d->users > 0) continue; + + roots[n_roots] = d; + n_roots++; + + /* remove node, it became a root */ + for (j = 0; j < n_pending; j++) + { + if (pending[j] == d) + { + n_pending--; + if (j < n_pending) + pending[j] = pending[n_pending]; + break; + } + } + } + } + + if (n_pending > 0) + { + ERR("Dependency loop found for interfaces!"); + for (i = 0; i < n_pending; i++) + ERR("%p (%s) is part of dependency loop!", + pending[i]->iface, pending[i]->iface->name); + CRITICAL("Cannot use type %p (%s) with broken interfaces!", + desc->cache.types[0], desc->cache.types[0]->name); + free(desc->cache.ifaces); + ret = EINA_FALSE; + } + + /* likely from still pending (dependency loops) */ + for (i = 0; i < n_nodes; i++) + eina_list_free(nodes[i].deps); + + return ret; +} + +static Eina_Bool +_eina_model_description_ifaces_validate_and_count(const Eina_Model_Interface *iface, unsigned int *count) +{ + if (iface->version != EINA_MODEL_INTERFACE_VERSION) + { + CRITICAL("Interface %p version is %u, expected %u instead.", + iface, iface->version, EINA_MODEL_INTERFACE_VERSION); + return EINA_FALSE; + } + + if (!iface->name) + { + CRITICAL("Interface %p provides no name!", iface); + return EINA_FALSE; + } + + if (iface->interfaces) + { + const Eina_Model_Interface **itr = iface->interfaces; + for (; *itr != NULL; itr++) + if (!_eina_model_description_ifaces_validate_and_count(*itr, count)) + return EINA_FALSE; + } + + (*count)++; + return EINA_TRUE; +} + +static void +_eina_model_description_ifaces_populate(Eina_Model_Description *desc, const Eina_Model_Interface *iface) +{ + desc->cache.ifaces[desc->total.ifaces] = iface; + desc->total.ifaces++; + + if (iface->interfaces) + { + const Eina_Model_Interface **itr = iface->interfaces; + for (; *itr != NULL; itr++) + _eina_model_description_ifaces_populate(desc, *itr); + } +} + +static Eina_Bool +_eina_model_description_ifaces_fill(Eina_Model_Description *desc) +{ + const Eina_Model_Type **titr, **titr_end; + unsigned int count; + + titr = desc->cache.types; + titr_end = titr + desc->total.types; + + /* naively count all interfaces, remove duplicates later */ + for (count = 0; titr < titr_end; titr++) + { + const Eina_Model_Type *type = *titr; + const Eina_Model_Interface **iitr = type->interfaces; + if (!type->interfaces) continue; + + for (; *iitr != NULL; iitr++) + if (!_eina_model_description_ifaces_validate_and_count(*iitr, &count)) + return EINA_FALSE; + } + if (count == 0) + { + desc->cache.ifaces = NULL; + desc->total.ifaces = 0; + return EINA_TRUE; + } + + desc->cache.ifaces = malloc(count * sizeof(Eina_Model_Interface *)); + EINA_SAFETY_ON_NULL_RETURN_VAL(desc->cache.ifaces, EINA_FALSE); + + titr = desc->cache.types; + desc->total.ifaces = 0; + for (; titr < titr_end; titr++) + { + const Eina_Model_Type *type = *titr; + const Eina_Model_Interface **iitr = type->interfaces; + + if (!type->interfaces) continue; + + for (; *iitr != NULL; iitr++) + _eina_model_description_ifaces_populate(desc, *iitr); + } + + return _eina_model_description_ifaces_fix(desc); +} + +static Eina_Bool +_eina_model_description_privates_fill(Eina_Model_Description *desc) +{ + unsigned int i; + + desc->total.privates = desc->total.types + desc->total.ifaces; + desc->cache.privates = malloc(desc->total.privates * + sizeof(Eina_Model_Provider)); + EINA_SAFETY_ON_NULL_RETURN_VAL(desc->cache.privates, EINA_FALSE); + + desc->total.size = 0; + + for (i = 0; i < desc->total.types; i++) + { + const Eina_Model_Type *type = desc->cache.types[i]; + desc->cache.privates[i].type = type; + if (type->private_size > 0) + { + unsigned int size = type->private_size; + if (size % sizeof(void *) != 0) + size += sizeof(void *) - (size % sizeof(void *)); + desc->total.size += size; + } + } + + for (i = 0; i < desc->total.ifaces; i++) + { + const Eina_Model_Interface *iface = desc->cache.ifaces[i]; + desc->cache.privates[desc->total.types + i].iface = iface; + if (iface->private_size > 0) + { + unsigned int size = iface->private_size; + if (size % sizeof(void *) != 0) + size += sizeof(void *) - (size % sizeof(void *)); + desc->total.size += size; + } + } + + return EINA_TRUE; +} + +static int +_eina_model_description_events_cmp(const void *pa, const void *pb) +{ + const Eina_Model_Event_Description_Cache *a = pa, *b = pb; + return strcmp(a->name, b->name); +} + +static int +_eina_model_description_events_find(const Eina_Model_Description *desc, const Eina_Model_Event_Description *query) +{ + unsigned int i; + for (i = 0; i < desc->total.events; i++) + { + const Eina_Model_Event_Description_Cache *itr = desc->cache.events + i; + if ((itr->name == query->name) || (strcmp(itr->name, query->name) == 0)) + return i; + } + + return -1; +} + +/* warn and remove duplicates, sort items to speed up lookups */ +static Eina_Bool +_eina_model_description_events_fill(Eina_Model_Description *desc) +{ + unsigned int i, count = 0, type_events; + + for (i = 0; i < desc->total.types; i++) + { + const Eina_Model_Event_Description *itr = desc->cache.types[i]->events; + if (!itr) continue; + for (; itr->name != NULL; itr++) + { + count++; + } + } + type_events = count; + + for (i = 0; i < desc->total.ifaces; i++) + { + const Eina_Model_Event_Description *itr = desc->cache.ifaces[i]->events; + if (!itr) continue; + for (; itr->name != NULL; itr++) + count++; + } + + if (count == 0) + { + desc->cache.events = NULL; + desc->total.events = 0; + return EINA_TRUE; + } + + desc->cache.events = malloc(count * + sizeof(Eina_Model_Event_Description_Cache)); + EINA_SAFETY_ON_NULL_RETURN_VAL(desc->cache.events, EINA_FALSE); + desc->total.events = 0; + + for (i = 0; i < desc->total.types; i++) + { + const Eina_Model_Type *mtype = desc->cache.types[i]; + const Eina_Model_Event_Description *itr = mtype->events; + if (!itr) continue; + for (; itr->name != NULL; itr++) + { + int j = _eina_model_description_events_find(desc, itr); + if (j >= 0) + { + const Eina_Model_Event_Description_Cache *o = desc->cache.events + j; + const Eina_Model_Type *omtype = o->provider.type; + WRN("Ignored duplicated event '%s' (type: '%s') from " + "model type %p (%s): already exists with type '%s' " + "from model type %p (%s)", + itr->name, + itr->type ? itr->type : "", + mtype, mtype->name, + o->desc->type ? o->desc->type : "", + omtype, omtype->name); + continue; + } + + desc->cache.events[desc->total.events].name = itr->name; + desc->cache.events[desc->total.events].desc = itr; + desc->cache.events[desc->total.events].provider.type = mtype; + desc->total.events++; + } + } + + for (i = 0; i < desc->total.ifaces; i++) + { + const Eina_Model_Interface *miface = desc->cache.ifaces[i]; + const Eina_Model_Event_Description *itr = desc->cache.ifaces[i]->events; + if (!itr) continue; + for (; itr->name != NULL; itr++) + { + int j = _eina_model_description_events_find(desc, itr); + if (j >= 0) + { + const Eina_Model_Event_Description_Cache *o = desc->cache.events + j; + if ((unsigned)j < type_events) + { + const Eina_Model_Type *omtype = o->provider.type; + WRN("Ignored duplicated event '%s' (type: '%s') from " + "model interface %p (%s): already exists with " + "type '%s' from model interface %p (%s)", + itr->name, + itr->type ? itr->type : "", + miface, miface->name, + o->desc->type ? o->desc->type : "", + omtype, omtype->name); + } + else + { + const Eina_Model_Interface *omiface = o->provider.iface; + WRN("Ignored duplicated event '%s' (iface: '%s') from " + "model interface %p (%s): already exists with " + "interface '%s' from model interface %p (%s)", + itr->name, + itr->type ? itr->type : "", + miface, miface->name, + o->desc->type ? o->desc->type : "", + omiface, omiface->name); + } + continue; + } + + desc->cache.events[desc->total.events].name = itr->name; + desc->cache.events[desc->total.events].desc = itr; + desc->cache.events[desc->total.events].provider.iface = miface; + desc->total.events++; + } + } + + qsort(desc->cache.events, desc->total.events, + sizeof(Eina_Model_Event_Description_Cache), + _eina_model_description_events_cmp); + + return EINA_TRUE; +} + +static const Eina_Model_Description * +_eina_model_description_get_internal(const Eina_Model_Type *type) +{ + Eina_Model_Description *desc; + + desc = eina_hash_find(_eina_model_descriptions, &type); + if (desc) + { + desc->refcount++; + return desc; + } + + desc = calloc(1, sizeof(Eina_Model_Description)); + EINA_SAFETY_ON_NULL_RETURN_VAL(desc, NULL); + + if (!_eina_model_description_type_fill(desc, type)) goto failed_type; + if (!_eina_model_description_ifaces_fill(desc)) goto failed_ifaces; + if (!_eina_model_description_privates_fill(desc)) goto failed_privates; + if (!_eina_model_description_events_fill(desc)) goto failed_events; + if (!eina_hash_add(_eina_model_descriptions, &type, desc)) goto failed_hash; + + desc->refcount = 1; + return desc; + + failed_hash: + free(desc->cache.events); + failed_events: + free(desc->cache.privates); + failed_privates: + free(desc->cache.ifaces); + failed_ifaces: + free(desc->cache.types); + failed_type: + free(desc); + return NULL; +} + +static void +_eina_model_description_dispose_internal(Eina_Model_Description *desc) +{ + const Eina_Model_Type *type; + + EINA_SAFETY_ON_FALSE_RETURN(desc->refcount > 0); + desc->refcount--; + if (desc->refcount > 0) return; + + type = desc->cache.types[0]; + if (!eina_hash_del_by_key(_eina_model_descriptions, &type)) + ERR("Cannot find type %p (%s) in descriptions hash!", + type, type->name); + + INF("Disposed model description for type %p (%s)", type, type->name); + + free(desc->cache.types); + free(desc->cache.ifaces); + free(desc->cache.privates); + free(desc->cache.events); + free(desc); +} + +static const Eina_Model_Description * +_eina_model_description_get(const Eina_Model_Type *type) +{ + const Eina_Model_Description *desc; + + eina_lock_take(&_eina_model_descriptions_lock); + desc = _eina_model_description_get_internal(type); + eina_lock_release(&_eina_model_descriptions_lock); + + return desc; +} + +static void +_eina_model_description_dispose(const Eina_Model_Description *desc) +{ + eina_lock_take(&_eina_model_descriptions_lock); + _eina_model_description_dispose_internal((Eina_Model_Description *)desc); + eina_lock_release(&_eina_model_descriptions_lock); +} + +static inline int +_eina_model_description_event_id_find(const Eina_Model_Description *desc, const char *event_name) +{ + const Eina_Model_Event_Description_Cache *cache; + Eina_Model_Event_Description_Cache search; + + search.name = event_name; + cache = bsearch(&search, desc->cache.events, desc->total.events, + sizeof(Eina_Model_Event_Description_Cache), + _eina_model_description_events_cmp); + if (!cache) + { + ERR("No event named %s for type %p (%s)", event_name, + desc->cache.types[0], desc->cache.types[0]->name); + return -1; + } + + return cache - desc->cache.events; +} + +/* + * Model management and book keeping + */ +typedef struct _Eina_Model_Event_Listener Eina_Model_Event_Listener; +struct _Eina_Model_Event_Listener +{ + EINA_INLIST; + Eina_Model_Event_Cb cb; + const void *data; + Eina_Bool deleted:1; +}; + +struct _Eina_Model +{ + const Eina_Model_Description *desc; /**< optimized model description */ + struct { + Eina_Inlist **entries; /**< connected/listeners for each event, array of lists of Eina_Model_Event_Listener */ + Eina_List **deleted; /**< deleted listeners while was walking. array of lists of Eina_Model_Event_Listener with deleted flag */ + int *freeze; /**< freeze count for each event */ + int walking; /**< increased while walking entries lists */ + } listeners; + void **privates; /**< private data per type and interface, each level gets its own stuff */ + int refcount; /**< number of users of this model instance */ + Eina_Bool deleted:1; /**< if deleted but still have references */ + EINA_MAGIC +}; + +static inline Eina_Bool +_eina_model_type_check(const Eina_Model_Type *type) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(type->version == EINA_MODEL_TYPE_VERSION, + EINA_FALSE); + return EINA_TRUE; +} + +/* find in type hierarchy the first one that the given offset is not a null + * pointer. Use this to discover which method to call on a parent. + */ +static const void * +_eina_model_type_find_offset(const Eina_Model_Type *type, unsigned int offset) +{ + const unsigned char *ptr = (const unsigned char *)type; + const void **addr = (const void **)(ptr + offset); + + if (*addr) return *addr; + if (!type->parent) return NULL; + return _eina_model_type_find_offset(type->parent, offset); +} + +/* find in interface hierarchy the first one that the given offset is + * not a null pointer. Use this to discover which method to call on a + * parent. + * + * TODO: Keep Eina_Model_Interface_Description with topological sorted + * entries for each interface? + * I smell problems with the current code in more complex + * situations (k-s) + * + * iface1 + * ^ + * | + * .---------+---------. + * | | | + * iface2 iface3 iface4 + * ^ ^ ^ + * | | | + * `---------+---------' + * | + * iface5 + * + * It should look: iface5 -> iface2 -> iface3 -> iface4 -> iface1 + * Now it does: iface5 -> iface2 -> iface1 -> iface3 -> iface1 -> iface4 -> iface1 + * + * + * iface1 + * ^ + * | + * iface2 + * ^ + * | + * .---------+---------. + * | | + * iface3 iface4 + * ^ ^ + * | | + * `---------+---------' + * | + * iface5 + * + * It should look: iface5 -> iface3 -> iface4 -> iface2 -> iface1 + * Now it does: iface5 -> iface3 -> iface2 -> iface1 -> iface4 -> iface2 -> iface1 + * + * + * iface1 iface2 + * ^ ^ + * | | + * `---------+---------' + * | + * iface3 + * + * It should look: iface3 -> iface1 -> iface2 + * Now it does: iface3 -> iface1 -> iface2 + * + * For the common case it should work, let's see. + */ +static const void * +_eina_model_interface_find_offset(const Eina_Model_Interface *iface, unsigned int offset) +{ + const Eina_Model_Interface **itr; + const unsigned char *ptr = (const unsigned char *)iface; + const void **addr = (const void **)(ptr + offset); + + if (*addr) return *addr; + if (!iface->interfaces) return NULL; + + for (itr = iface->interfaces; *itr != NULL; itr++) + { + const void *r = _eina_model_interface_find_offset(*itr, offset); + if (r) + return r; + } + + return NULL; +} + +/* similar to _eina_model_interface_find_offset(), but looks for + * offset in Eina_Model_Interface::value instead of the interface + * itself. + */ +static const void * +_eina_model_interface_value_find_offset(const Eina_Model_Interface *iface, unsigned int offset) +{ + const Eina_Model_Interface **itr; + const unsigned char *ptr = iface->value; + const void **addr = (const void **)(ptr + offset); + + if ((ptr) && (*addr)) return *addr; + if (!iface->interfaces) return NULL; + + for (itr = iface->interfaces; *itr != NULL; itr++) + { + const void *r = _eina_model_interface_value_find_offset(*itr, offset); + if (r) + return r; + } + + return NULL; +} + +static void +_eina_model_event_callback_free_deleted(Eina_Model *model) +{ + unsigned int i; + + for (i = 0; i < model->desc->total.events; i++) + { + Eina_Model_Event_Listener *el; + EINA_LIST_FREE(model->listeners.deleted[i], el) + { + model->listeners.entries[i] = eina_inlist_remove + (model->listeners.entries[i], EINA_INLIST_GET(el)); + _eina_model_inner_free(sizeof(Eina_Model_Event_Listener), el); + } + } + + _eina_model_inner_free(model->desc->total.events * sizeof(Eina_List *), + model->listeners.deleted); + model->listeners.deleted = NULL; +} + +static inline Eina_Bool +_eina_model_event_callback_call(Eina_Model *model, const char *name, const void *event_info) +{ + Eina_Inlist *lst; + Eina_Model_Event_Listener *el; + const Eina_Model_Event_Description *ev_desc; + int event_id = _eina_model_description_event_id_find(model->desc, name); + + if (event_id < 0) return EINA_FALSE; + if (!model->listeners.entries) return EINA_TRUE; + + if ((model->listeners.freeze) && (model->listeners.freeze[event_id])) + { + DBG("Ignored event callback '%s' of model %p (%s): frozen", + name, model, model->desc->cache.types[0]->name); + return EINA_TRUE; + } + + lst = model->listeners.entries[event_id]; + if (!lst) return EINA_TRUE; + + ev_desc = model->desc->cache.events[event_id].desc; + + model->listeners.walking++; + EINA_INLIST_FOREACH(lst, el) + { + if (el->deleted) continue; + el->cb((void *)el->data, model, ev_desc, (void *)event_info); + } + model->listeners.walking--; + + if ((model->listeners.walking == 0) && (model->listeners.deleted)) + _eina_model_event_callback_free_deleted(model); + + return EINA_FALSE; +} + +static const char EINA_ERROR_MODEL_FAILED_STR[] = "Model check failed."; +static const char EINA_ERROR_MODEL_METHOD_MISSING_STR[] = "Model method is missing."; +static const char EINA_MAGIC_MODEL_STR[] = "Eina Model"; + +static void _eina_model_unref(Eina_Model *model); + +/** + * @endcond + */ + +/* EINA_MODEL_TYPE_BASE: base of all other types **********************/ + +static Eina_Bool +_eina_model_type_base_setup(Eina_Model *model) +{ + DBG("base setup of %p", model); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_flush(Eina_Model *model) +{ + DBG("base flush of %p", model); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_constructor(Eina_Model *model) +{ + DBG("base constructor of %p", model); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_destructor(Eina_Model *model) +{ + DBG("base destructor of %p", model); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_properties_copy(const Eina_Model *model, Eina_Model *copy) +{ + Eina_List *l, *props = eina_model_properties_names_list_get(model); + const char *name; + EINA_LIST_FOREACH(props, l, name) + { + Eina_Value tmp; + if (!eina_model_property_get(model, name, &tmp)) + { + ERR("Could not get property %s from model %p (%s)", + name, model, model->desc->cache.types[0]->name); + eina_model_properties_names_list_free(props); + return EINA_FALSE; + } + if (!eina_model_property_set(copy, name, &tmp)) + { + ERR("Could not set property %s on model %p (%s)", + name, copy, copy->desc->cache.types[0]->name); + eina_value_flush(&tmp); + eina_model_properties_names_list_free(props); + return EINA_FALSE; + } + eina_value_flush(&tmp); + } + eina_model_properties_names_list_free(props); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_children_copy(const Eina_Model *model, Eina_Model *copy) +{ + int i, count = eina_model_child_count(model); + + if (count < 0) + { + ERR("Could not get children count of model %p (%s)", + model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + for (i = 0; i < count; i++) + { + Eina_Model *child = eina_model_child_get(model, i); + Eina_Bool ret; + + if (!child) + { + ERR("Could not get child #%d from model %p (%s)", + i, model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + ret = eina_model_child_insert_at(copy, i, child); + _eina_model_unref(child); + + if (!ret) + { + ERR("Could not set child #%d on model %p (%s)", + i, copy, copy->desc->cache.types[0]->name); + return EINA_FALSE; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_copy(const Eina_Model *model, Eina_Model *copy) +{ + DBG("base copy of %p to %p", model, copy); + + return _eina_model_type_base_properties_copy(model, copy) && + _eina_model_type_base_children_copy(model, copy); +} + +static Eina_Bool +_eina_model_type_base_children_deep_copy(const Eina_Model *model, Eina_Model *copy) +{ + int i, count = eina_model_child_count(model); + + if (count < 0) + { + ERR("Could not get children count of model %p (%s)", + model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + for (i = 0; i < count; i++) + { + Eina_Model *child_copy, *child = eina_model_child_get(model, i); + Eina_Bool ret; + + if (!child) + { + ERR("Could not get child #%d from model %p (%s)", + i, model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + child_copy = eina_model_deep_copy(child); + if (!child_copy) + { + ERR("Could not deep copy child #%d %p (%s) from model %p (%s)", i, + child, child->desc->cache.types[0]->name, + model, model->desc->cache.types[0]->name); + _eina_model_unref(child); + return EINA_FALSE; + } + _eina_model_unref(child); + + ret = eina_model_child_insert_at(copy, i, child_copy); + _eina_model_unref(child_copy); + + if (!ret) + { + ERR("Could not set child #%d on model %p (%s)", + i, copy, copy->desc->cache.types[0]->name); + return EINA_FALSE; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_base_deep_copy(const Eina_Model *model, Eina_Model *copy) +{ + DBG("base deep copy of %p to %p", model, copy); + + return _eina_model_type_base_properties_copy(model, copy) && + _eina_model_type_base_children_deep_copy(model, copy); +} + +static Eina_Bool +_eina_model_type_base_properties_compare(const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + Eina_List *al, *aprops = eina_model_properties_names_list_get(a); + Eina_List *bl, *bprops = eina_model_properties_names_list_get(b); + Eina_List *l, *props = NULL; + const char *aname, *bname, *name; + Eina_Bool ret = EINA_TRUE; + + EINA_LIST_FOREACH(aprops, al, aname) + { + EINA_LIST_FOREACH(bprops, bl, bname) + if (strcmp(aname, bname) == 0) + { + props = eina_list_append(props, aname); + break; + } + } + + *cmp = 0; + EINA_LIST_FOREACH(props, l, name) + { + Eina_Value atmp, btmp; + + if (!eina_model_property_get(a, name, &atmp)) + { + ERR("Could not get property %s from model %p (%s)", + name, a, a->desc->cache.types[0]->name); + ret = EINA_FALSE; + *cmp = -1; + break; + } + + if (!eina_model_property_get(b, name, &btmp)) + { + ERR("Could not get property %s from model %p (%s)", + name, b, b->desc->cache.types[0]->name); + ret = EINA_FALSE; + *cmp = -1; + eina_value_flush(&atmp); + break; + } + + *cmp = eina_value_compare(&atmp, &btmp); + if (eina_error_get() != 0) + { + char *astr = eina_value_to_string(&atmp); + char *bstr = eina_value_to_string(&btmp); + ERR("Could not compare property %s: %s=%s, %s=%s", name, + eina_value_type_name_get(eina_value_type_get(&atmp)), astr, + eina_value_type_name_get(eina_value_type_get(&btmp)), bstr); + free(astr); + free(bstr); + ret = EINA_FALSE; + *cmp = -1; + } + + eina_value_flush(&atmp); + eina_value_flush(&btmp); + + if ((!ret) || (*cmp != 0)) + break; + } + + if ((ret) && (*cmp == 0)) + { + int acount = eina_list_count(aprops); + int bcount = eina_list_count(bprops); + + if (acount < bcount) + *cmp = -1; + else if (acount > bcount) + *cmp = 1; + } + + eina_model_properties_names_list_free(aprops); + eina_model_properties_names_list_free(bprops); + eina_list_free(props); + return ret; +} + +static Eina_Bool +_eina_model_type_base_children_compare(const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + int acount = eina_model_child_count(a); + int bcount = eina_model_child_count(b); + int i, count; + Eina_Bool ret = EINA_TRUE; + + if (acount < 0) + { + ERR("Could not get children count of model %p (%s)", + a, a->desc->cache.types[0]->name); + return EINA_FALSE; + } + if (bcount < 0) + { + ERR("Could not get children count of model %p (%s)", + b, b->desc->cache.types[0]->name); + return EINA_FALSE; + } + + if (acount < bcount) + count = acount; + else + count = bcount; + + for (i = 0; i < count; i++) + { + Eina_Model *achild, *bchild; + + achild = eina_model_child_get(a, i); + if (!achild) + { + ERR("Could not get child #%d from model %p (%s)", + i, a, a->desc->cache.types[0]->name); + *cmp = -1; + return EINA_FALSE; + } + + bchild = eina_model_child_get(b, i); + if (!bchild) + { + ERR("Could not get child #%d from model %p (%s)", + i, b, b->desc->cache.types[0]->name); + *cmp = -1; + _eina_model_unref(achild); + return EINA_FALSE; + } + + *cmp = eina_model_compare(achild, bchild); + if (eina_error_get()) + { + ERR("Could not compare children #%d %p (%s) and %p (%s) " + "from models %p (%s) and %p (%s)", i, + achild, + eina_model_type_name_get(eina_model_type_get(achild)), + bchild, + eina_model_type_name_get(eina_model_type_get(bchild)), + a, a->desc->cache.types[0]->name, + b, b->desc->cache.types[0]->name); + ret = EINA_FALSE; + } + _eina_model_unref(achild); + _eina_model_unref(bchild); + + if ((!ret) || (*cmp != 0)) + break; + } + + if ((ret) && (*cmp == 0)) + { + if (acount < bcount) + *cmp = -1; + else if (acount > bcount) + *cmp = 1; + } + + return ret; +} + +static Eina_Bool +_eina_model_type_base_compare(const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + *cmp = 0; + DBG("base compare of %p and %p", a, b); + + if (!_eina_model_type_base_properties_compare(a, b, cmp)) + return EINA_FALSE; + + if (*cmp != 0) + return EINA_TRUE; + + return _eina_model_type_base_children_compare(a, b, cmp); +} + +static int +_eina_model_type_base_child_count(const Eina_Model *model) +{ + DBG("base child_count of %p", model); + return 0; +} + +static int +_eina_model_type_base_child_find(const Eina_Model *model, unsigned int start_position, const Eina_Model *other) +{ + int x = eina_model_child_count(model); + unsigned int i, count; + + DBG("base child_find of %p, %d children", model, x); + + if (x < 0) + return -1; + + count = x; + for (i = start_position; i < count; i++) + { + Eina_Model *current = eina_model_child_get(model, i); + if (current) + { + _eina_model_unref(current); /* we'll not use it's value anyway */ + if (current == other) + return i; + } + } + + return -1; +} + +static int +_eina_model_type_base_child_search(const Eina_Model *model, unsigned int start_position, Eina_Each_Cb match, const void *user_data) +{ + int x = eina_model_child_count(model); + unsigned int i, count; + + DBG("base child_search of %p, %d children", model, x); + + if (x < 0) + return -1; + + count = x; + for (i = start_position; i < count; i++) + { + Eina_Model *current = eina_model_child_get(model, i); + if (current) + { + Eina_Bool r = match(model, current, (void *)user_data); + _eina_model_unref(current); + if (r) + return i; + } + } + + return -1; +} + +typedef struct _Eina_Iterator_Model_Base Eina_Iterator_Model_Base; +struct _Eina_Iterator_Model_Base +{ + Eina_Iterator base; + Eina_Model *model; + unsigned int current; + unsigned int end; +}; + +static Eina_Bool +_eina_model_type_base_child_iterator_next(Eina_Iterator *base, void **data) +{ + Eina_Iterator_Model_Base *it; + + it = (Eina_Iterator_Model_Base *)base; + if (it->current >= it->end) + return EINA_FALSE; + + *data = eina_model_child_get(it->model, it->current); + if (!*data) + return EINA_FALSE; + + it->current++; + return EINA_TRUE; +} + +static void * +_eina_model_type_base_child_iterator_get_container(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base *it; + it = (Eina_Iterator_Model_Base *)base; + return it->model; +} + +static void +_eina_model_type_base_child_iterator_free(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base *it; + it = (Eina_Iterator_Model_Base *)base; + _eina_model_unref(it->model); + free(it); +} + +static Eina_Iterator * +_eina_model_type_base_child_iterator_get(Eina_Model *model, unsigned int start, unsigned int count) +{ + Eina_Iterator_Model_Base *it = calloc(1, sizeof(*it)); + EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); + + EINA_MAGIC_SET(&it->base, EINA_MAGIC_ITERATOR); + it->base.version = EINA_ITERATOR_VERSION; + it->base.next = _eina_model_type_base_child_iterator_next; + it->base.get_container = _eina_model_type_base_child_iterator_get_container; + it->base.free = _eina_model_type_base_child_iterator_free; + + it->model = eina_model_ref(model); + it->current = start; + it->end = start + count; + + return &it->base; +} + +typedef struct _Eina_Iterator_Model_Base_Reversed Eina_Iterator_Model_Base_Reversed; +struct _Eina_Iterator_Model_Base_Reversed +{ + Eina_Iterator base; + Eina_Model *model; + unsigned int current; + unsigned int end; +}; + +static Eina_Bool +_eina_model_type_base_child_reversed_iterator_next(Eina_Iterator *base, void **data) +{ + Eina_Iterator_Model_Base_Reversed *it; + + it = (Eina_Iterator_Model_Base_Reversed *)base; + if (it->current == it->end) + return EINA_FALSE; + + it->current--; + *data = eina_model_child_get(it->model, it->current); + if (!*data) + return EINA_FALSE; + + return EINA_TRUE; +} + +static void * +_eina_model_type_base_child_reversed_iterator_get_container(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base_Reversed *it; + it = (Eina_Iterator_Model_Base_Reversed *)base; + return it->model; +} + +static void +_eina_model_type_base_child_reversed_iterator_free(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base_Reversed *it; + it = (Eina_Iterator_Model_Base_Reversed *)base; + _eina_model_unref(it->model); + free(it); +} + +static Eina_Iterator * +_eina_model_type_base_child_reversed_iterator_get(Eina_Model *model, unsigned int start, unsigned int count) +{ + Eina_Iterator_Model_Base_Reversed *it; + int children_count; + + children_count = eina_model_child_count(model); + if (children_count < 0) + return NULL; + + if (start + count > (unsigned int)children_count) + { + if (start >= (unsigned int)children_count) + count = 0; + else + count = children_count - start; + } + + it = calloc(1, sizeof(*it)); + EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); + EINA_MAGIC_SET(&it->base, EINA_MAGIC_ITERATOR); + it->base.version = EINA_ITERATOR_VERSION; + it->base.next = _eina_model_type_base_child_reversed_iterator_next; + it->base.get_container = _eina_model_type_base_child_reversed_iterator_get_container; + it->base.free = _eina_model_type_base_child_reversed_iterator_free; + + it->model = eina_model_ref(model); + it->current = start + count; + it->end = start; + + return &it->base; +} + +typedef struct _Eina_Iterator_Model_Base_Sorted Eina_Iterator_Model_Base_Sorted; +struct _Eina_Iterator_Model_Base_Sorted +{ + Eina_Iterator base; + Eina_Model *model; + unsigned int current; + unsigned int count; + Eina_Model *elements[]; +}; + +static Eina_Bool +_eina_model_type_base_child_sorted_iterator_next(Eina_Iterator *base, void **data) +{ + Eina_Iterator_Model_Base_Sorted *it; + + it = (Eina_Iterator_Model_Base_Sorted *)base; + if (it->current == it->count) + return EINA_FALSE; + + *data = eina_model_ref(it->elements[it->current]); + it->current++; + return EINA_TRUE; +} + +static void * +_eina_model_type_base_child_sorted_iterator_get_container(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base_Sorted *it; + it = (Eina_Iterator_Model_Base_Sorted *)base; + return it->model; +} + +static void +_eina_model_type_base_child_sorted_iterator_free(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base_Sorted *it; + unsigned int i; + it = (Eina_Iterator_Model_Base_Sorted *)base; + _eina_model_unref(it->model); + + for (i = 0; i < it->count; i++) + _eina_model_unref(it->elements[i]); + + free(it); +} + +static Eina_Iterator * +_eina_model_type_base_child_sorted_iterator_get(Eina_Model *model, unsigned int start, unsigned int count, Eina_Compare_Cb compare) +{ + Eina_Iterator_Model_Base_Sorted *it; + int children_count; + unsigned int i; + + children_count = eina_model_child_count(model); + if (children_count < 0) + return NULL; + + if (start + count > (unsigned int)children_count) + { + if (start >= (unsigned int)children_count) + count = 0; + else + count = children_count - start; + } + + it = calloc(1, sizeof(*it) + count * sizeof(Eina_Model *)); + EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); + EINA_MAGIC_SET(&it->base, EINA_MAGIC_ITERATOR); + it->base.version = EINA_ITERATOR_VERSION; + it->base.next = _eina_model_type_base_child_sorted_iterator_next; + it->base.get_container = _eina_model_type_base_child_sorted_iterator_get_container; + it->base.free = _eina_model_type_base_child_sorted_iterator_free; + + it->model = eina_model_ref(model); + it->current = 0; + it->count = count; + + for (i = 0; i < count; i++) + { + it->elements[i] = eina_model_child_get(model, i + start); + if (!it->elements[i]) + { + ERR("Failed to get child %u of model %p (%s)", + i + start, model, model->desc->cache.types[0]->name); + free(it); + return NULL; + } + } + + if (count > 1) + _eina_model_array_sort(it->elements, 0, count - 1, compare); + + return &it->base; +} + +typedef struct _Eina_Iterator_Model_Base_Filtered Eina_Iterator_Model_Base_Filtered; +struct _Eina_Iterator_Model_Base_Filtered +{ + Eina_Iterator base; + Eina_Model *model; + Eina_Each_Cb match; + const void *data; + unsigned int current; + unsigned int count; +}; + +static Eina_Bool +_eina_model_type_base_child_filtered_iterator_next(Eina_Iterator *base, void **data) +{ + Eina_Iterator_Model_Base_Filtered *it; + unsigned int *ret; + int i; + + it = (Eina_Iterator_Model_Base_Filtered *)base; + if (it->count == 0) return EINA_FALSE; + + i = eina_model_child_search(it->model, it->current, it->match, it->data); + if (i < 0) return EINA_FALSE; + + it->current = i + 1; + it->count--; + ret = (unsigned int *)data; + *ret = i; + return EINA_TRUE; +} + +static void * +_eina_model_type_base_child_filtered_iterator_get_container(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base_Filtered *it; + it = (Eina_Iterator_Model_Base_Filtered *)base; + return it->model; +} + +static void +_eina_model_type_base_child_filtered_iterator_free(Eina_Iterator *base) +{ + Eina_Iterator_Model_Base_Filtered *it; + it = (Eina_Iterator_Model_Base_Filtered *)base; + _eina_model_unref(it->model); + free(it); +} + +static Eina_Iterator * +_eina_model_type_base_child_filtered_iterator_get(Eina_Model *model, unsigned int start, unsigned int count, Eina_Each_Cb match, const void *data) +{ + Eina_Iterator_Model_Base_Filtered *it = calloc(1, sizeof(*it)); + EINA_SAFETY_ON_NULL_RETURN_VAL(it, NULL); + + EINA_MAGIC_SET(&it->base, EINA_MAGIC_ITERATOR); + it->base.version = EINA_ITERATOR_VERSION; + it->base.next = _eina_model_type_base_child_filtered_iterator_next; + it->base.get_container = _eina_model_type_base_child_filtered_iterator_get_container; + it->base.free = _eina_model_type_base_child_filtered_iterator_free; + + it->model = eina_model_ref(model); + it->match = match; + it->data = data; + it->current = start; + it->count = count; + + return &it->base; +} + +static char * +_eina_model_type_base_to_string(const Eina_Model *model) +{ + Eina_List *l, *props; + const char *name; + Eina_Strbuf *str; + Eina_Bool first; + int i, count; + char *ret; + + str = eina_strbuf_new(); + EINA_SAFETY_ON_NULL_RETURN_VAL(str, NULL); + + eina_strbuf_append_printf(str, "%s({", model->desc->cache.types[0]->name); + + props = eina_model_properties_names_list_get(model); + props = eina_list_sort(props, 0, EINA_COMPARE_CB(strcmp)); + + first = EINA_TRUE; + EINA_LIST_FOREACH(props, l, name) + { + Eina_Value val; + + if (!first) + eina_strbuf_append_printf(str, ", %s: ", name); + else + { + eina_strbuf_append_printf(str, "%s: ", name); + first = EINA_FALSE; + } + + if (!eina_model_property_get(model, name, &val)) + eina_strbuf_append_char(str, '?'); + else + { + char *tmp = eina_value_to_string(&val); + eina_strbuf_append(str, tmp ? tmp : "?"); + free(tmp); + eina_value_flush(&val); + } + } + + eina_strbuf_append(str, "}, ["); + + count = eina_model_child_count(model); + first = EINA_TRUE; + for (i = 0; i < count; i++) + { + Eina_Model *c = eina_model_child_get(model, i); + if (!c) + { + if (!first) + eina_strbuf_append(str, ", ?"); + else + { + eina_strbuf_append_char(str, '?'); + first = EINA_FALSE; + } + } + else + { + char *tmp = eina_model_to_string(c); + if (!first) + eina_strbuf_append_printf(str, ", %s", tmp ? tmp : "?"); + else + { + eina_strbuf_append(str, tmp ? tmp : "?"); + first = EINA_FALSE; + } + free(tmp); + _eina_model_unref(c); + } + } + + eina_strbuf_append(str, "])"); + + ret = eina_strbuf_string_steal(str); + eina_strbuf_free(str); + + return ret; +} + +static const Eina_Model_Event_Description _eina_model_type_base_events[] = { + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_deleted, "", "model was deleted"), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_freed, "", "model memory was released"), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_property_set, "s", "model data was set, data name given as event information."), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_property_del, "s", "model data was deleted, data name given as event information."), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_children_changed, "", "model children changed (deleted, inserted)."), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_child_inserted, "u", "model child was inserted, child position is given."), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_child_set, "u", "model child was set, child position is given."), + EINA_MODEL_EVENT_DESCRIPTION(_eina_model_str_child_del, "u", "model child was deleted, child position is given."), + EINA_MODEL_EVENT_DESCRIPTION_SENTINEL +}; + +static const Eina_Model_Type _EINA_MODEL_TYPE_BASE = { + EINA_MODEL_TYPE_VERSION, + 0, /* there is no private data */ + "Eina_Model_Type_Base", + NULL, /* should be the only type with NULL here! */ + NULL, /* no interfaces implemented */ + _eina_model_type_base_events, + _eina_model_type_base_setup, + _eina_model_type_base_flush, + _eina_model_type_base_constructor, + _eina_model_type_base_destructor, + _eina_model_type_base_copy, + _eina_model_type_base_deep_copy, + _eina_model_type_base_compare, + NULL, /* no load */ + NULL, /* no unload */ + NULL, /* no property value get */ + NULL, /* no property value set */ + NULL, /* no property del */ + NULL, /* no properties names list */ + _eina_model_type_base_child_count, + NULL, /* no child get */ + NULL, /* no child set */ + NULL, /* no child del */ + NULL, /* no child insert */ + _eina_model_type_base_child_find, + _eina_model_type_base_child_search, + NULL, /* no child sort */ + _eina_model_type_base_child_iterator_get, + _eina_model_type_base_child_reversed_iterator_get, + _eina_model_type_base_child_sorted_iterator_get, + _eina_model_type_base_child_filtered_iterator_get, + _eina_model_type_base_to_string, + NULL /* no extensions */ +}; + +/* + * EINA_MODEL_TYPE_MIXIN: + * + * Mix-in is a type that uses 2 interfaces, one for properties, + * another for children. Users should inherit this model and implement + * at least onf of the interfaces to get an usable model without + * defining the methods. + */ + +static const char _EINA_MODEL_INTERFACE_NAME_PROPERTIES[] = "Eina_Model_Interface_Properties"; +static const char _EINA_MODEL_INTERFACE_NAME_CHILDREN[] = "Eina_Model_Interface_Children"; + +typedef struct _Eina_Model_Type_Mixin_Data Eina_Model_Type_Mixin_Data; +struct _Eina_Model_Type_Mixin_Data +{ + /* just keep interfaces to avoid lookups */ + const Eina_Model_Interface *if_properties; + const Eina_Model_Interface *if_children; +}; + +static Eina_Bool +_eina_model_type_mixin_setup(Eina_Model *model) +{ + DBG("mix-in setup of %p", model); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_mixin_flush(Eina_Model *model) +{ + DBG("mix-in flush of %p", model); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_mixin_constructor(Eina_Model *model) +{ + Eina_Model_Type_Mixin_Data *priv = eina_model_type_private_data_get + (model, EINA_MODEL_TYPE_MIXIN); + + DBG("mix-in constructor of %p (priv=%p)", model, priv); + + priv->if_properties = eina_model_interface_get + (model, EINA_MODEL_INTERFACE_NAME_PROPERTIES); + if (priv->if_properties) + { + if (!eina_model_interface_constructor(priv->if_properties, model)) + { + ERR("Could not construct properties interface %p of %p (%s)", + model, priv->if_properties, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + } + + priv->if_children = eina_model_interface_get + (model, EINA_MODEL_INTERFACE_NAME_CHILDREN); + if (priv->if_children) + { + if (!eina_model_interface_constructor(priv->if_children, model)) + { + ERR("Could not construct children interface %p of %p (%s)", + model, priv->if_children, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + } + + if ((!priv->if_properties) && (!priv->if_children)) + { + ERR("Mix-in model %p (%s) does not implement properties or children " + "interfaces!", + model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +#define EINA_MODEL_TYPE_MIXIN_GET(model) \ + Eina_Model_Type_Mixin_Data *priv = eina_model_type_private_data_get \ + (model, EINA_MODEL_TYPE_MIXIN) + +static Eina_Bool +_eina_model_type_mixin_destructor(Eina_Model *model) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + DBG("mixin destructor of %p", model); + + if (priv->if_properties) + eina_model_interface_destructor(priv->if_properties, model); + + if (priv->if_children) + eina_model_interface_destructor(priv->if_children, model); + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_type_mixin_compare(const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + Eina_Bool ret = EINA_TRUE, did_prop = EINA_FALSE, did_child = EINA_FALSE; + + *cmp = 0; + + EINA_MODEL_TYPE_MIXIN_GET(a); + + if (priv->if_properties) + { + Eina_Bool (*compare)(const Eina_Model*, const Eina_Model*, int *) = + _eina_model_interface_value_find_offset + (priv->if_properties, + offsetof(Eina_Model_Interface_Properties, compare)); + + if (compare) + { + ret &= compare(a, b, cmp); + did_prop = EINA_TRUE; + } + } + + if ((ret) && (cmp == 0)) + { + if (priv->if_children) + { + Eina_Bool (*compare)(const Eina_Model*, const Eina_Model*, int *) = + _eina_model_interface_value_find_offset + (priv->if_children, + offsetof(Eina_Model_Interface_Children, compare)); + + if (compare) + { + ret &= compare(a, b, cmp); + did_child = EINA_TRUE; + } + } + } + + if ((!did_prop) && (!did_child)) + return eina_model_type_compare(EINA_MODEL_TYPE_BASE, a, b, cmp); + + return ret; +} + +static Eina_Bool +_eina_model_type_mixin_load(Eina_Model *model) +{ + Eina_Bool ret = EINA_TRUE; + + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (priv->if_properties) + ret &= eina_model_interface_properties_load(priv->if_properties, model); + + if (priv->if_children) + ret &= eina_model_interface_children_load(priv->if_children, model); + + return ret; +} + +static Eina_Bool +_eina_model_type_mixin_unload(Eina_Model *model) +{ + Eina_Bool ret = EINA_TRUE; + + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (priv->if_properties) + ret &= eina_model_interface_properties_unload(priv->if_properties, model); + + if (priv->if_children) + ret &= eina_model_interface_children_unload(priv->if_children, model); + + return ret; +} + +static Eina_Bool +_eina_model_type_mixin_property_get(const Eina_Model *model, const char *name, Eina_Value *value) +{ + Eina_Bool ret = EINA_FALSE; + + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (priv->if_properties) + ret = eina_model_interface_properties_get + (priv->if_properties, model, name, value); + + return ret; +} + +static Eina_Bool +_eina_model_type_mixin_property_set(Eina_Model *model, const char *name, const Eina_Value *value) +{ + Eina_Bool ret = EINA_FALSE; + + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (priv->if_properties) + ret = eina_model_interface_properties_set + (priv->if_properties, model, name, value); + + return ret; +} + +static Eina_Bool +_eina_model_type_mixin_property_del(Eina_Model *model, const char *name) +{ + Eina_Bool ret = EINA_FALSE; + + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (priv->if_properties) + ret = eina_model_interface_properties_del + (priv->if_properties, model, name); + + return ret; +} + +static Eina_List * +_eina_model_type_mixin_properties_names_list_get(const Eina_Model *model) +{ + Eina_List *ret = NULL; + + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (priv->if_properties) + ret = eina_model_interface_properties_names_list_get + (priv->if_properties, model); + + return ret; +} + +static int +_eina_model_type_mixin_child_count(const Eina_Model *model) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (!priv->if_children) + return 0; + + return eina_model_interface_children_count(priv->if_children, model); +} + +static Eina_Model * +_eina_model_type_mixin_child_get(const Eina_Model *model, unsigned int position) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (!priv->if_children) + return 0; + + return eina_model_interface_children_get(priv->if_children, model, position); +} + +static Eina_Bool +_eina_model_type_mixin_child_set(Eina_Model *model, unsigned int position, Eina_Model *child) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (!priv->if_children) + return 0; + + return eina_model_interface_children_set + (priv->if_children, model, position, child); +} + +static Eina_Bool +_eina_model_type_mixin_child_del(Eina_Model *model, unsigned int position) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (!priv->if_children) + return 0; + + return eina_model_interface_children_del + (priv->if_children, model, position); +} + +static Eina_Bool +_eina_model_type_mixin_child_insert_at(Eina_Model *model, unsigned int position, Eina_Model *child) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (!priv->if_children) + return 0; + + return eina_model_interface_children_insert_at + (priv->if_children, model, position, child); +} + +static void +_eina_model_type_mixin_child_sort(Eina_Model *model, Eina_Compare_Cb compare) +{ + EINA_MODEL_TYPE_MIXIN_GET(model); + + if (!priv->if_children) + return; + eina_model_interface_children_sort(priv->if_children, model, compare); +} + +static const Eina_Model_Type _EINA_MODEL_TYPE_MIXIN = { + EINA_MODEL_TYPE_VERSION, + sizeof(Eina_Model_Type_Mixin_Data), + "Eina_Model_Type_Mixin", + &_EINA_MODEL_TYPE_BASE, + NULL, /* no interfaces implemented */ + NULL, /* no extra events */ + _eina_model_type_mixin_setup, + _eina_model_type_mixin_flush, + _eina_model_type_mixin_constructor, + _eina_model_type_mixin_destructor, + NULL, /* no copy, as interface is called automatically */ + NULL, /* no deep copy, as interface is called automatically */ + _eina_model_type_mixin_compare, + _eina_model_type_mixin_load, + _eina_model_type_mixin_unload, + _eina_model_type_mixin_property_get, + _eina_model_type_mixin_property_set, + _eina_model_type_mixin_property_del, + _eina_model_type_mixin_properties_names_list_get, + _eina_model_type_mixin_child_count, + _eina_model_type_mixin_child_get, + _eina_model_type_mixin_child_set, + _eina_model_type_mixin_child_del, + _eina_model_type_mixin_child_insert_at, + NULL, /* use default find */ + NULL, /* use default search */ + _eina_model_type_mixin_child_sort, + NULL, /* use default iterator get */ + NULL, /* use default reversed iterator get */ + NULL, /* use default sorted iterator get */ + NULL, /* use default filtered iterator get */ + NULL, /* use default to string */ + NULL /* no extensions */ +}; +#undef EINA_MODEL_TYPE_MIXIN_GET + + +/* EINA_MODEL_INTERFACE_PROPERTIES_HASH ******************************/ + +#define EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model) \ + Eina_Hash *priv = *(Eina_Hash **)eina_model_interface_private_data_get \ + (model, EINA_MODEL_INTERFACE_PROPERTIES_HASH) + +static Eina_Bool +_eina_model_interface_properties_hash_setup(Eina_Model *model) +{ + Eina_Hash **p_priv = eina_model_interface_private_data_get + (model, EINA_MODEL_INTERFACE_PROPERTIES_HASH); + + DBG("setup interface properties (hash) at %p model %p (%s)", + p_priv, model, model->desc->cache.types[0]->name); + + *p_priv = eina_hash_string_small_new(NULL); + return !!*p_priv; +} + +static Eina_Bool +_eina_model_interface_properties_hash_flush(Eina_Model *model) +{ + EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model); + + DBG("flush interface properties (hash) at %p model %p (%s)", + priv, model, model->desc->cache.types[0]->name); + + if (priv) + { + ERR("interface properties flushed with values! priv=%p, model %p (%s)", + priv, model, model->desc->cache.types[0]->name); + eina_hash_free(priv); + } + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_properties_hash_constructor(Eina_Model *model) +{ + EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model); + + DBG("construct interface properties (hash) at %p model %p (%s)", + priv, model, model->desc->cache.types[0]->name); + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_properties_hash_destructor_foreach(const Eina_Hash *hash __UNUSED__, const void *key __UNUSED__, void *data, void *fdata __UNUSED__) +{ + eina_value_free(data); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_properties_hash_destructor(Eina_Model *model) +{ + Eina_Hash **p_priv = eina_model_interface_private_data_get + (model, EINA_MODEL_INTERFACE_PROPERTIES_HASH); + int count = eina_hash_population(*p_priv); + + DBG("destroy interface properties (hash) at %p model %p (%s). %d values.", + *p_priv, model, model->desc->cache.types[0]->name, count); + + eina_hash_foreach + (*p_priv, _eina_model_interface_properties_hash_destructor_foreach, NULL); + eina_hash_free(*p_priv); + *p_priv = NULL; + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_properties_hash_get(const Eina_Model *model, const char *name, Eina_Value *value) +{ + EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model); + const Eina_Value *prop = eina_hash_find(priv, name); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + return eina_value_copy(prop, value); +} + +static Eina_Bool +_eina_model_interface_properties_hash_set(Eina_Model *model, const char *name, const Eina_Value *value) +{ + EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model); + Eina_Value *prop, *old = eina_hash_find(priv, name); + + prop = eina_value_new(eina_value_type_get(value)); + EINA_SAFETY_ON_NULL_RETURN_VAL(prop, EINA_FALSE); + + eina_value_flush(prop); + if (!eina_value_copy(value, prop)) + { + ERR("Could not copy value '%s' from %p to %p", name, value, prop); + eina_value_free(prop); + return EINA_FALSE; + } + + if (!old) + { + if (!eina_hash_add(priv, name, prop)) + { + ERR("Could not add value %p to hash as key '%s'", prop, name); + eina_value_free(prop); + return EINA_FALSE; + } + } + else + { + eina_value_free(old); + if (!eina_hash_modify(priv, name, prop)) + { + ERR("Could not modify hash key '%s' value from %p to %p", + name, old, prop); + eina_hash_del_by_key(priv, name); + eina_value_free(prop); + return EINA_FALSE; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_properties_hash_del(Eina_Model *model, const char *name) +{ + EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model); + Eina_Value *old = eina_hash_find(priv, name); + EINA_SAFETY_ON_NULL_RETURN_VAL(old, EINA_FALSE); + eina_value_free(old); + return eina_hash_del_by_key(priv, name); +} + +static Eina_Bool +_eina_model_interface_properties_hash_names_list_foreach(const Eina_Hash *hash __UNUSED__, const void *key, void *data __UNUSED__, void *fdata) +{ + Eina_List **p_list = fdata; + *p_list = eina_list_append(*p_list, eina_stringshare_add(key)); + return EINA_TRUE; +} + +static Eina_List * +_eina_model_interface_properties_hash_names_list(const Eina_Model *model) +{ + EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET(model); + Eina_List *list = NULL; + eina_hash_foreach + (priv, _eina_model_interface_properties_hash_names_list_foreach, &list); + return list; +} +#undef EINA_MODEL_INTERFACE_PROPERTIES_HASH_GET + +static const Eina_Model_Interface_Properties _EINA_MODEL_INTERFACE_PROPERTIES_HASH_VALUE = { + EINA_MODEL_INTERFACE_PROPERTIES_VERSION, + NULL, /* no compare */ + NULL, /* no load */ + NULL, /* no unload */ + _eina_model_interface_properties_hash_get, + _eina_model_interface_properties_hash_set, + _eina_model_interface_properties_hash_del, + _eina_model_interface_properties_hash_names_list +}; + +static const Eina_Model_Interface _EINA_MODEL_INTERFACE_PROPERTIES_HASH = { + EINA_MODEL_INTERFACE_VERSION, + sizeof(Eina_Hash *), + _EINA_MODEL_INTERFACE_NAME_PROPERTIES, + NULL, /* no parent interfaces */ + NULL, /* no extra events */ + _eina_model_interface_properties_hash_setup, + _eina_model_interface_properties_hash_flush, + _eina_model_interface_properties_hash_constructor, + _eina_model_interface_properties_hash_destructor, + NULL, + NULL, + &_EINA_MODEL_INTERFACE_PROPERTIES_HASH_VALUE +}; + +/* TODO: properties using Eina_Value_Type_Struct + * + * Would be bit more cumbersome to do, as the user still needs to + * derivate the interface to specify the Eina_Value_Struct_Desc, but + * given this struct everything should be easy to do (query it for + * valid properties and their type, covert type if needed). + */ + +/* EINA_MODEL_INTERFACE_CHILDREN_INARRAY ******************************/ + +#define EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model) \ + Eina_Inarray *priv = eina_model_interface_private_data_get \ + (model, EINA_MODEL_INTERFACE_CHILDREN_INARRAY) + +static Eina_Bool +_eina_model_interface_children_inarray_setup(Eina_Model *model) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + + DBG("setup interface children (inarray) at %p model %p (%s)", + priv, model, model->desc->cache.types[0]->name); + + eina_inarray_setup(priv, sizeof(Eina_Model *), 0); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_children_inarray_flush(Eina_Model *model) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + int count; + + DBG("flush interface children (inarray) at %p model %p (%s)", + priv, model, model->desc->cache.types[0]->name); + + count = eina_inarray_count(priv); + if (count > 0) + ERR("interface children flushed with %d members! priv=%p, model %p (%s)", + count, priv, model, model->desc->cache.types[0]->name); + + eina_inarray_flush(priv); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_children_inarray_constructor(Eina_Model *model) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + + DBG("construct interface children (inarray) at %p model %p (%s)", + priv, model, model->desc->cache.types[0]->name); + + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_children_inarray_destructor(Eina_Model *model) +{ + Eina_Model **itr, **itr_end; + int count; + + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + + count = eina_inarray_count(priv); + + DBG("destroy interface children (inarray) at %p model %p (%s). %d members.", + priv, model, model->desc->cache.types[0]->name, count); + + itr = priv->members; + itr_end = itr + count; + for (; itr < itr_end; itr++) + _eina_model_unref(*itr); + eina_inarray_flush(priv); + + return EINA_TRUE; +} + +static int +_eina_model_interface_children_inarray_count(const Eina_Model *model) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + return eina_inarray_count(priv); +} + +static Eina_Model * +_eina_model_interface_children_inarray_get(const Eina_Model *model, unsigned int position) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + Eina_Model **child = eina_inarray_nth(priv, position); + if (!child) + return NULL; + return eina_model_ref(*child); +} + +static Eina_Bool +_eina_model_interface_children_inarray_set(Eina_Model *model, unsigned int position, Eina_Model *child) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + Eina_Model **p_old = eina_inarray_nth(priv, position); + Eina_Model *old; + + if (!p_old) + return EINA_FALSE; + + old = *p_old; + if (!eina_inarray_replace_at(priv, position, &child)) + return EINA_FALSE; + + eina_model_ref(child); + _eina_model_unref(old); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_children_inarray_del(Eina_Model *model, unsigned int position) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + Eina_Model **p_old = eina_inarray_nth(priv, position); + Eina_Model *old; + + if (!p_old) + return EINA_FALSE; + + old = *p_old; + if (!eina_inarray_remove_at(priv, position)) + return EINA_FALSE; + + _eina_model_unref(old); + return EINA_TRUE; +} + +static Eina_Bool +_eina_model_interface_children_inarray_insert_at(Eina_Model *model, unsigned int position, Eina_Model *child) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + + if (!eina_inarray_insert_at(priv, position, &child)) + return EINA_FALSE; + + eina_model_ref(child); + return EINA_TRUE; +} + +static void +_eina_model_interface_children_inarray_sort(Eina_Model *model, Eina_Compare_Cb compare) +{ + EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET(model); + int count = eina_inarray_count(priv); + EINA_SAFETY_ON_FALSE_RETURN(count >= 0); + + if (count > 1) + _eina_model_array_sort(priv->members, 0, count - 1, compare); +} +#undef EINA_MODEL_INTERFACE_CHILDREN_INARRAY_GET + +static const Eina_Model_Interface_Children _EINA_MODEL_INTERFACE_CHILDREN_INARRAY_VALUE = { + EINA_MODEL_INTERFACE_CHILDREN_VERSION, + NULL, /* no compare */ + NULL, /* no load */ + NULL, /* no unload */ + _eina_model_interface_children_inarray_count, + _eina_model_interface_children_inarray_get, + _eina_model_interface_children_inarray_set, + _eina_model_interface_children_inarray_del, + _eina_model_interface_children_inarray_insert_at, + _eina_model_interface_children_inarray_sort +}; + +static const Eina_Model_Interface _EINA_MODEL_INTERFACE_CHILDREN_INARRAY = { + EINA_MODEL_INTERFACE_VERSION, + sizeof(Eina_Inarray), + _EINA_MODEL_INTERFACE_NAME_CHILDREN, + NULL, /* no parent interfaces */ + NULL, /* no extra events */ + _eina_model_interface_children_inarray_setup, + _eina_model_interface_children_inarray_flush, + _eina_model_interface_children_inarray_constructor, + _eina_model_interface_children_inarray_destructor, + NULL, + NULL, + &_EINA_MODEL_INTERFACE_CHILDREN_INARRAY_VALUE +}; + +/* EINA_MODEL_TYPE_GENERIC ********************************************/ + +static const Eina_Model_Interface *_EINA_MODEL_TYPE_GENERIC_IFACES[] = { + &_EINA_MODEL_INTERFACE_PROPERTIES_HASH, + &_EINA_MODEL_INTERFACE_CHILDREN_INARRAY, + NULL +}; + +static const Eina_Model_Type _EINA_MODEL_TYPE_GENERIC = { + EINA_MODEL_TYPE_VERSION, + 0, + "Eina_Model_Type_Generic", + &_EINA_MODEL_TYPE_MIXIN, + _EINA_MODEL_TYPE_GENERIC_IFACES, + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL, /* inherit from mix-in */ + NULL /* inherit from mix-in */ +}; + +/** + */ + +/** + * @internal + * @brief Initialize the model module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function sets up the model module of Eina. It is called + * by eina_init(). + * + * @see eina_init() + */ +Eina_Bool +eina_model_init(void) +{ + const char *choice, *tmp; + + _eina_model_log_dom = eina_log_domain_register("eina_model", + EINA_LOG_COLOR_DEFAULT); + if (_eina_model_log_dom < 0) + { + EINA_LOG_ERR("Could not register log domain: eina_model"); + return EINA_FALSE; + } + +#ifdef EINA_DEFAULT_MEMPOOL + choice = "pass_through"; +#else + choice = "chained_mempool"; +#endif + tmp = getenv("EINA_MEMPOOL"); + if (tmp && tmp[0]) + choice = tmp; + + if (choice) + _eina_model_mp_choice = strdup(choice); + + _eina_model_mp = eina_mempool_add + (_eina_model_mp_choice, "model", NULL, sizeof(Eina_Model), 320); + if (!_eina_model_mp) + { + ERR("Mempool for model cannot be allocated in model init."); + goto on_init_fail_mp; + } + + if (!eina_lock_new(&_eina_model_inner_mps_lock)) + { + ERR("Cannot create inner mempools lock in model init."); + goto on_init_fail_lock_mp; + } + _eina_model_inner_mps = eina_hash_int32_new(NULL); + if (!_eina_model_inner_mps) + { + ERR("Cannot create hash for inner mempools in model init."); + goto on_init_fail_hash_mp; + } + + if (!eina_lock_new(&_eina_model_descriptions_lock)) + { + ERR("Cannot create model descriptions lock in model init."); + goto on_init_fail_lock_desc; + } + _eina_model_descriptions = eina_hash_pointer_new(NULL); + if (!_eina_model_descriptions) + { + ERR("Cannot create model descriptions hash in model init."); + goto on_init_fail_hash_desc; + } + + EINA_ERROR_MODEL_FAILED = eina_error_msg_static_register( + EINA_ERROR_MODEL_FAILED_STR); + EINA_ERROR_MODEL_METHOD_MISSING = eina_error_msg_static_register( + EINA_ERROR_MODEL_METHOD_MISSING_STR); + + EINA_MODEL_TYPE_BASE = &_EINA_MODEL_TYPE_BASE; + EINA_MODEL_TYPE_MIXIN = &_EINA_MODEL_TYPE_MIXIN; + EINA_MODEL_TYPE_GENERIC = &_EINA_MODEL_TYPE_GENERIC; + + EINA_MODEL_INTERFACE_PROPERTIES_HASH = &_EINA_MODEL_INTERFACE_PROPERTIES_HASH; + + EINA_MODEL_INTERFACE_CHILDREN_INARRAY = &_EINA_MODEL_INTERFACE_CHILDREN_INARRAY; + + EINA_MODEL_INTERFACE_NAME_PROPERTIES = _EINA_MODEL_INTERFACE_NAME_PROPERTIES; + EINA_MODEL_INTERFACE_NAME_CHILDREN = _EINA_MODEL_INTERFACE_NAME_CHILDREN; + + eina_magic_string_static_set(EINA_MAGIC_MODEL, EINA_MAGIC_MODEL_STR); + + return EINA_TRUE; + + on_init_fail_hash_desc: + eina_lock_free(&_eina_model_descriptions_lock); + on_init_fail_lock_desc: + eina_hash_free(_eina_model_inner_mps); + _eina_model_inner_mps = NULL; + on_init_fail_hash_mp: + eina_lock_free(&_eina_model_inner_mps_lock); + on_init_fail_lock_mp: + eina_mempool_del(_eina_model_mp); + on_init_fail_mp: + free(_eina_model_mp_choice); + _eina_model_mp_choice = NULL; + eina_log_domain_unregister(_eina_model_log_dom); + _eina_model_log_dom = -1; + return EINA_FALSE; +} + +/** + * @internal + * @brief Shut down the model module. + * + * @return #EINA_TRUE on success, #EINA_FALSE on failure. + * + * This function shuts down the model module set up by + * eina_model_init(). It is called by eina_shutdown(). + * + * @see eina_shutdown() + */ +Eina_Bool +eina_model_shutdown(void) +{ + eina_lock_take(&_eina_model_inner_mps_lock); + if (eina_hash_population(_eina_model_inner_mps) != 0) + ERR("Cannot free eina_model internal memory pools -- still in use!"); + else + eina_hash_free(_eina_model_inner_mps); + eina_lock_release(&_eina_model_inner_mps_lock); + eina_lock_free(&_eina_model_inner_mps_lock); + + eina_lock_take(&_eina_model_descriptions_lock); + if (eina_hash_population(_eina_model_descriptions) != 0) + ERR("Cannot free eina_model internal descriptions -- still in use!"); + else + eina_hash_free(_eina_model_descriptions); + eina_lock_release(&_eina_model_descriptions_lock); + eina_lock_free(&_eina_model_descriptions_lock); + + free(_eina_model_mp_choice); + _eina_model_mp_choice = NULL; + eina_mempool_del(_eina_model_mp); + eina_log_domain_unregister(_eina_model_log_dom); + _eina_model_log_dom = -1; + return EINA_TRUE; +} + +/*============================================================================* + * Global * + *============================================================================*/ + +/*============================================================================* + * API * + *============================================================================*/ + + +EAPI Eina_Error EINA_ERROR_MODEL_FAILED = 0; +EAPI Eina_Error EINA_ERROR_MODEL_METHOD_MISSING = 0; + +EAPI const Eina_Model_Type *EINA_MODEL_TYPE_BASE = NULL; +EAPI const Eina_Model_Type *EINA_MODEL_TYPE_MIXIN = NULL; +EAPI const Eina_Model_Type *EINA_MODEL_TYPE_GENERIC = NULL; + +EAPI const Eina_Model_Interface *EINA_MODEL_INTERFACE_PROPERTIES_HASH = NULL; +EAPI const Eina_Model_Interface *EINA_MODEL_INTERFACE_CHILDREN_INARRAY = NULL; + +EAPI const char *EINA_MODEL_INTERFACE_NAME_PROPERTIES = "Eina_Model_Interface_Properties"; +EAPI const char *EINA_MODEL_INTERFACE_NAME_CHILDREN = "Eina_Model_Interface_Children"; + +EAPI Eina_Model * +eina_model_new(const Eina_Model_Type *type) +{ + const Eina_Model_Description *desc; + Eina_Model *model; + unsigned int i; + + EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_model_type_check(type), NULL); + + desc = _eina_model_description_get(type); + EINA_SAFETY_ON_NULL_RETURN_VAL(desc, NULL); + + model = eina_mempool_malloc(_eina_model_mp, sizeof(Eina_Model)); + EINA_SAFETY_ON_NULL_GOTO(model, failed_model); + + model->desc = desc; + model->listeners.entries = NULL; + model->listeners.deleted = NULL; + model->listeners.freeze = NULL; + model->listeners.walking = 0; + + if (desc->total.size == 0) + model->privates = NULL; + else + { + unsigned char *ptr; + + model->privates = _eina_model_inner_alloc + (desc->total.privates * sizeof(void *) + + desc->total.size); + EINA_SAFETY_ON_NULL_GOTO(model->privates, failed_privates); + + ptr = (unsigned char *)(model->privates + desc->total.privates); + for (i = 0; i < desc->total.privates; i++) + { + unsigned int size; + if (i < desc->total.types) + size = desc->cache.privates[i].type->private_size; + else + size = desc->cache.privates[i].iface->private_size; + + if (size == 0) + { + model->privates[i] = NULL; + continue; + } + + model->privates[i] = ptr; + memset(ptr, 0, size); + + if (size % sizeof(void *) != 0) + size += sizeof(void *) - (size % sizeof(void *)); + ptr += size; + } + } + + model->refcount = 1; + model->deleted = EINA_FALSE; + EINA_MAGIC_SET(model, EINA_MAGIC_MODEL); + + /* call setup of every type in the reverse order, + * they should not call parent's setup. + */ + for (i = desc->total.types; i > 0; i--) + { + if (desc->cache.types[i - 1]->setup) + { + if (!desc->cache.types[i - 1]->setup(model)) + { + ERR("Failed to setup model %p at type %p (%s)", + model, desc->cache.types[i - 1], + desc->cache.types[i - 1]->name); + goto failed_types; + } + } + } + + /* call setup of every interface in the reverse order, + * they should not call parent's setup. + */ + for (i = desc->total.ifaces; i > 0; i--) + { + if (desc->cache.ifaces[i - 1]->setup) + { + if (!desc->cache.ifaces[i - 1]->setup(model)) + { + ERR("Failed to setup model %p at interface %p (%s)", + model, desc->cache.ifaces[i - 1], + desc->cache.ifaces[i - 1]->name); + goto failed_ifaces; + } + } + } + + if (!desc->ops.type.constructor(model)) + { + ERR("Failed to construct model %p, type %p (%s)", + model, desc->cache.types[0], desc->cache.types[0]->name); + goto failed_constructor; + } + + return model; + + failed_constructor: + i = 0; + failed_ifaces: + /* flush every setup interface, natural order */ + for (; i < desc->total.ifaces; i++) + desc->cache.ifaces[i]->flush(model); + i = 0; + failed_types: + /* flush every setup type, natural order */ + for (; i < desc->total.types; i++) + desc->cache.types[i]->flush(model); + + if (model->privates) + _eina_model_inner_free(desc->total.privates * sizeof(void *) + + desc->total.size, + model->privates); + + failed_privates: + EINA_MAGIC_SET(model, EINA_MAGIC_NONE); + eina_mempool_free(_eina_model_mp, model); + failed_model: + _eina_model_description_dispose(desc); + return NULL; +} + +static void +_eina_model_free(Eina_Model *model) +{ + const Eina_Model_Description *desc = model->desc; + unsigned int i; + + DBG("model %p (%s) refcount=%d deleted=%hhu", + model, model->desc->cache.types[0]->name, + model->refcount, model->deleted); + + /* flush every interface, natural order */ + for (i = 0; i < desc->total.ifaces; i++) + if (desc->cache.ifaces[i]->flush) + desc->cache.ifaces[i]->flush(model); + + /* flush every type, natural order */ + for (i = 0; i < desc->total.types; i++) + if (desc->cache.types[i]->flush) + desc->cache.types[i]->flush(model); + + model->refcount--; + _eina_model_event_callback_call(model, _eina_model_str_freed, NULL); + + if (model->privates) + _eina_model_inner_free(desc->total.privates * sizeof(void *) + + desc->total.size, + model->privates); + + if (model->listeners.deleted) + _eina_model_event_callback_free_deleted(model); + + if (model->listeners.entries) + { + for (i = 0; i < desc->total.events; i++) + { + Eina_Inlist *lst = model->listeners.entries[i]; + while (lst) + { + void *tmp = lst; + lst = lst->next; + _eina_model_inner_free(sizeof(Eina_Model_Event_Listener), + tmp); + } + } + + _eina_model_inner_free(desc->total.events * sizeof(Eina_Inlist *), + model->listeners.entries); + } + + if (model->listeners.freeze) + _eina_model_inner_free(model->desc->total.events * sizeof(int), + model->listeners.freeze); + + EINA_MAGIC_SET(model, EINA_MAGIC_NONE); + eina_mempool_free(_eina_model_mp, model); + + _eina_model_description_dispose(desc); +} + +static void +_eina_model_del(Eina_Model *model) +{ + const Eina_Model_Description *desc = model->desc; + + DBG("model %p (%s) refcount=%d deleted=%hhu", + model, model->desc->cache.types[0]->name, + model->refcount, model->deleted); + + EINA_SAFETY_ON_TRUE_RETURN(model->deleted); + + model->deleted = EINA_TRUE; + _eina_model_event_callback_call(model, _eina_model_str_deleted, NULL); + + if (!desc->ops.type.destructor(model)) + ERR("Failed to destroy model %p, type %p (%s)", + model, desc->cache.types[0], desc->cache.types[0]->name); +} + +static void +_eina_model_unref(Eina_Model *model) +{ + DBG("model %p (%s) refcount=%d deleted=%hhu", + model, model->desc->cache.types[0]->name, + model->refcount, model->deleted); + + if (model->refcount > 1) + { + model->refcount--; + return; + } + + if (!model->deleted) _eina_model_del(model); + _eina_model_free(model); +} + +#define EINA_MODEL_INSTANCE_CHECK_VAL(inst, retval) \ + do \ + { \ + if (!EINA_MAGIC_CHECK(inst, EINA_MAGIC_MODEL)) \ + { \ + EINA_MAGIC_FAIL(inst, EINA_MAGIC_MODEL); \ + return retval; \ + } \ + EINA_SAFETY_ON_NULL_RETURN_VAL(inst->desc, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(inst->refcount > 0, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(inst->desc->refcount > 0, retval); \ + } \ + while (0) + +#define EINA_MODEL_INSTANCE_CHECK(inst) \ + do \ + { \ + if (!EINA_MAGIC_CHECK(inst, EINA_MAGIC_MODEL)) \ + { \ + EINA_MAGIC_FAIL(inst, EINA_MAGIC_MODEL); \ + return; \ + } \ + EINA_SAFETY_ON_NULL_RETURN(inst->desc); \ + EINA_SAFETY_ON_FALSE_RETURN(inst->refcount > 0); \ + EINA_SAFETY_ON_FALSE_RETURN(inst->desc->refcount > 0); \ + } \ + while (0) + +#define EINA_MODEL_TYPE_CALL_OPTIONAL_RETURN(model, method, def_retval, ...) \ + do \ + { \ + eina_error_set(0); \ + if (model->desc->ops.type.method) \ + return model->desc->ops.type.method(model, ## __VA_ARGS__); \ + DBG("Optional method" # method "() not implemented for model %p (%s)", \ + model, model->desc->cache.types[0]->name); \ + return def_retval; \ + } \ + while (0) + +#define EINA_MODEL_TYPE_CALL_OPTIONAL(model, method, ...) \ + do \ + { \ + eina_error_set(0); \ + if (model->desc->ops.type.method) \ + model->desc->ops.type.method(model, ## __VA_ARGS__); \ + else \ + DBG("Optional method" # method "() not implemented for model %p (%s)", \ + model, model->desc->cache.types[0]->name); \ + } \ + while (0) + +#define EINA_MODEL_TYPE_CALL_MANDATORY_RETURN(model, method, def_retval, ...) \ + do \ + { \ + eina_error_set(0); \ + if (model->desc->ops.type.method) \ + return model->desc->ops.type.method(model, ## __VA_ARGS__); \ + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); \ + CRITICAL("Mandatory method" # method "() not implemented for model %p (%s)", \ + model, model->desc->cache.types[0]->name); \ + return def_retval; \ + } \ + while (0) + +#define EINA_MODEL_TYPE_CALL_MANDATORY(model, method, ...) \ + do \ + { \ + eina_error_set(0); \ + if (model->desc->ops.type.method) \ + model->desc->ops.type.method(model, ## __VA_ARGS__); \ + else \ + { \ + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); \ + CRITICAL("Mandatory method" # method "() not implemented for model %p (%s)", \ + model, model->desc->cache.types[0]->name); \ + } \ + } \ + while (0) + + +#define EINA_MODEL_TYPE_CALL_RETURN(model, method, def_retval, ...) \ + do \ + { \ + eina_error_set(0); \ + if (model->desc->ops.type.method) \ + return model->desc->ops.type.method(model, ## __VA_ARGS__); \ + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); \ + ERR("Method" # method "() not implemented for model %p (%s)", \ + model, model->desc->cache.types[0]->name); \ + return def_retval; \ + } \ + while (0) + +#define EINA_MODEL_TYPE_CALL(model, method, ...) \ + do \ + { \ + eina_error_set(0); \ + if (model->desc->ops.type.method) \ + model->desc->ops.type.method(model, ## __VA_ARGS__); \ + else \ + { \ + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); \ + ERR("Method" # method "() not implemented for model %p (%s)", \ + model, model->desc->cache.types[0]->name); \ + } \ + } \ + while (0) + +EAPI void +eina_model_del(Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK(model); + _eina_model_del(model); + _eina_model_unref(model); +} + +EAPI const Eina_Model_Type * +eina_model_type_get(const Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + return model->desc->cache.types[0]; +} + +EAPI const Eina_Model_Interface * +eina_model_interface_get(const Eina_Model *model, const char *name) +{ + const Eina_Model_Description *desc; + const Eina_Model_Interface **itr, **itr_end; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL); + + desc = model->desc; + itr = desc->cache.ifaces; + itr_end = itr + desc->total.ifaces; + + /* try pointer comparison for the speed aware users */ + for (; itr < itr_end; itr++) + if ((*itr)->name == name) + return *itr; + + /* fallback to strcmp if user is lazy about speed */ + itr = desc->cache.ifaces; + for (; itr < itr_end; itr++) + if (strcmp((*itr)->name, name) == 0) + return *itr; + + return NULL; +} + +static Eina_Bool +_eina_model_instance_check(const Eina_Model *model, const Eina_Model_Type *type) +{ + const Eina_Model_Type **itr, **itr_end; + + itr = model->desc->cache.types; + itr_end = itr + model->desc->total.types; + + for (; itr < itr_end; itr++) + if (*itr == type) + return EINA_TRUE; + + return EINA_FALSE; +} + +EAPI Eina_Bool +eina_model_instance_check(const Eina_Model *model, const Eina_Model_Type *type) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), EINA_FALSE); + return _eina_model_instance_check(model, type); +} + +EAPI Eina_Model * +eina_model_ref(Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + DBG("model %p (%s) refcount=%d deleted=%hhu", + model, model->desc->cache.types[0]->name, + model->refcount, model->deleted); + model->refcount++; + return model; +} + +EAPI void +eina_model_unref(Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK(model); + _eina_model_unref(model); +} + +EAPI int +eina_model_refcount(const Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + return model->refcount; +} + +EAPI Eina_Bool +eina_model_event_callback_add(Eina_Model *model, const char *event_name, Eina_Model_Event_Cb cb, const void *data) +{ + const Eina_Model_Description *desc; + Eina_Model_Event_Listener *el; + int event_id; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(event_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE); + + desc = model->desc; + event_id = _eina_model_description_event_id_find(desc, event_name); + if (event_id < 0) + { + ERR("No event named %s for model %p (%s)", + event_name, model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + if (!model->listeners.entries) + { + model->listeners.entries = _eina_model_inner_alloc + (desc->total.events * sizeof(Eina_Inlist *)); + EINA_SAFETY_ON_NULL_RETURN_VAL(model->listeners.entries, EINA_FALSE); + memset(model->listeners.entries, 0, + desc->total.events * sizeof(Eina_Inlist *)); + } + + el = _eina_model_inner_alloc(sizeof(Eina_Model_Event_Listener)); + EINA_SAFETY_ON_NULL_RETURN_VAL(el, EINA_FALSE); + + el->cb = cb; + el->data = data; + model->listeners.entries[event_id] = eina_inlist_append + (model->listeners.entries[event_id], EINA_INLIST_GET(el)); + + return EINA_TRUE; +} + +EAPI Eina_Bool +eina_model_event_callback_del(Eina_Model *model, const char *event_name, Eina_Model_Event_Cb cb, const void *data) +{ + int event_id; + Eina_Inlist *lst; + Eina_Model_Event_Listener *el; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(event_name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(cb, EINA_FALSE); + + if (!model->listeners.entries) + { + ERR("No event callbacks for model %p (%s)", + model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + event_id = _eina_model_description_event_id_find(model->desc, event_name); + if (event_id < 0) + { + ERR("No event named %s for model %p (%s)", + event_name, model, model->desc->cache.types[0]->name); + return EINA_FALSE; + } + + lst = model->listeners.entries[event_id]; + EINA_INLIST_FOREACH(lst, el) + { + if (el->cb != cb) continue; + if ((data) && (el->data != data)) continue; + + if (model->listeners.walking == 0) + { + model->listeners.entries[event_id] = eina_inlist_remove + (model->listeners.entries[event_id], EINA_INLIST_GET(el)); + _eina_model_inner_free(sizeof(Eina_Model_Event_Listener), el); + } + else + { + el->deleted = EINA_TRUE; + if (!model->listeners.deleted) + { + model->listeners.deleted = _eina_model_inner_alloc + (model->desc->total.events * sizeof(Eina_List *)); + EINA_SAFETY_ON_NULL_RETURN_VAL(model->listeners.deleted, + EINA_FALSE); + + memset(model->listeners.deleted, 0, + model->desc->total.events * sizeof(Eina_List *)); + + } + model->listeners.deleted[event_id] = eina_list_append + (model->listeners.deleted[event_id], el); + } + return EINA_TRUE; + } + + ERR("No callback %p data %p found for event named %s for model %p (%s)", + cb, data, event_name, model, model->desc->cache.types[0]->name); + return EINA_FALSE; +} + +EAPI const Eina_Model_Event_Description * +eina_model_event_description_get(const Eina_Model *model, const char *event_name) +{ + const Eina_Model_Description *desc; + int event_id; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(event_name, NULL); + + desc = model->desc; + event_id = _eina_model_description_event_id_find(desc, event_name); + if (event_id < 0) + return NULL; + + return desc->cache.events[event_id].desc; +} + +EAPI Eina_List * +eina_model_event_names_list_get(const Eina_Model *model) +{ + const Eina_Model_Event_Description_Cache *itr, *itr_end; + Eina_List *lst = NULL; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + + itr = model->desc->cache.events; + itr_end = itr + model->desc->total.events; + + for (; itr < itr_end; itr++) + lst = eina_list_append(lst, eina_stringshare_add(itr->name)); + + return lst; +} + +EAPI void +eina_model_event_names_list_free(Eina_List *list) +{ + const char *str; + EINA_LIST_FREE(list, str) + eina_stringshare_del(str); +} + +EAPI Eina_Bool +eina_model_event_callback_call(Eina_Model *model, const char *name, const void *event_info) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + return _eina_model_event_callback_call(model, name, event_info); +} + +EAPI int +eina_model_event_callback_freeze(Eina_Model *model, const char *name) +{ + int event_id; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, -1); + + event_id = _eina_model_description_event_id_find(model->desc, name); + if (event_id < 0) return -1; + + if (!model->listeners.freeze) + { + model->listeners.freeze = _eina_model_inner_alloc + (model->desc->total.events * sizeof(int)); + EINA_SAFETY_ON_NULL_RETURN_VAL(model->listeners.freeze, -1); + + memset(model->listeners.freeze, 0, + model->desc->total.events * sizeof(int)); + } + + if (model->listeners.freeze[event_id] == 0) + DBG("model %p (%s) event %s frozen", + model, model->desc->cache.types[0]->name, name); + + model->listeners.freeze[event_id]++; + return model->listeners.freeze[event_id]; +} + +EAPI int +eina_model_event_callback_thaw(Eina_Model *model, const char *name) +{ + int event_id; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(model->listeners.freeze, -1); + + event_id = _eina_model_description_event_id_find(model->desc, name); + if (event_id < 0) return -1; + + model->listeners.freeze[event_id]--; + if (model->listeners.freeze[event_id] == 0) + DBG("model %p (%s) event %s unfrozen", + model, model->desc->cache.types[0]->name, name); + return model->listeners.freeze[event_id]; +} + +EAPI Eina_Model * +eina_model_copy(const Eina_Model *model) +{ + const Eina_Model_Description *desc; + Eina_Model *copy; + unsigned int i; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + desc = model->desc; + copy = eina_model_new(desc->cache.types[0]); + EINA_SAFETY_ON_NULL_RETURN_VAL(copy, NULL); + + /* call copy of every type in the reverse order, + * they should not call parent's copy. + */ + for (i = desc->total.types; i > 0; i--) + { + if (desc->cache.types[i - 1]->copy) + { + if (!desc->cache.types[i - 1]->copy(model, copy)) + goto failed; + } + } + + /* call copy of every interface in the reverse order, + * they should not call parent's copy. + */ + for (i = desc->total.ifaces; i > 0; i--) + { + if (desc->cache.ifaces[i - 1]->copy) + { + if (!desc->cache.ifaces[i - 1]->copy(model, copy)) + goto failed; + } + } + + return copy; + + failed: + ERR("Failed to copy model %p %s", model, desc->cache.types[0]->name); + eina_model_del(copy); + return NULL; +} + +EAPI Eina_Model * +eina_model_deep_copy(const Eina_Model *model) +{ + const Eina_Model_Description *desc; + Eina_Model *deep_copy; + unsigned int i; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + desc = model->desc; + deep_copy = eina_model_new(desc->cache.types[0]); + EINA_SAFETY_ON_NULL_RETURN_VAL(deep_copy, NULL); + + /* call deep_copy of every type in the reverse order, + * they should not call parent's deep_copy. + */ + for (i = desc->total.types; i > 0; i--) + { + if (desc->cache.types[i - 1]->deep_copy) + { + if (!desc->cache.types[i - 1]->deep_copy(model, deep_copy)) + goto failed; + } + } + + /* call deep_copy of every interface in the reverse order, + * they should not call parent's deep_copy. + */ + for (i = desc->total.ifaces; i > 0; i--) + { + if (desc->cache.ifaces[i - 1]->deep_copy) + { + if (!desc->cache.ifaces[i - 1]->deep_copy(model, deep_copy)) + goto failed; + } + } + + return deep_copy; + + failed: + ERR("Failed to deep copy model %p %s", model, desc->cache.types[0]->name); + eina_model_del(deep_copy); + return NULL; +} + +EAPI int +eina_model_compare(const Eina_Model *a, const Eina_Model *b) +{ + const Eina_Model_Description *desc_a, *desc_b; + Eina_Bool ok; + int cmp = -1; + + EINA_MODEL_INSTANCE_CHECK_VAL(a, -1); + EINA_MODEL_INSTANCE_CHECK_VAL(b, -1); + desc_a = a->desc; + desc_b = b->desc; + + if ((!desc_a->ops.type.compare) && (!desc_b->ops.type.compare)) + { + ERR("Models %p (%s) and %p (%s) can't compare", + a, desc_a->cache.types[0]->name, + b, desc_b->cache.types[0]->name); + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + return -1; + } + else if ((desc_a->ops.type.compare) && (desc_b->ops.type.compare)) + { + ok = desc_a->ops.type.compare(a, b, &cmp); + if (!ok) + { + ok = desc_b->ops.type.compare(b, a, &cmp); + if (ok) + cmp = -cmp; /* swapped sides! */ + } + } + else if (desc_a->ops.type.compare) + ok = desc_a->ops.type.compare(a, b, &cmp); + else + { + ok = desc_b->ops.type.compare(b, a, &cmp); + if (ok) + cmp = -cmp; /* swapped sides! */ + } + + if (!ok) + { + ERR("Could not compare models %p (%s) and %p (%s)", + a, desc_a->cache.types[0]->name, + b, desc_b->cache.types[0]->name); + eina_error_set(EINA_ERROR_MODEL_FAILED); + return -1; + } + + return cmp; +} + +EAPI Eina_Bool +eina_model_load(Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_MODEL_TYPE_CALL_OPTIONAL_RETURN(model, load, EINA_TRUE); +} + +EAPI Eina_Bool +eina_model_unload(Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_MODEL_TYPE_CALL_OPTIONAL_RETURN(model, unload, EINA_TRUE); +} + +EAPI Eina_Bool +eina_model_property_get(const Eina_Model *model, const char *name, Eina_Value *value) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + EINA_MODEL_TYPE_CALL_MANDATORY_RETURN(model, property_get, EINA_FALSE, + name, value); +} + +EAPI Eina_Bool +eina_model_property_set(Eina_Model *model, const const char *name, const Eina_Value *value) +{ + Eina_Bool ret; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(value->type), EINA_FALSE); + + eina_error_set(0); + if (model->desc->ops.type.property_set) + { + ret = model->desc->ops.type.property_set(model, name, value); + if (ret) + _eina_model_event_callback_call + (model, _eina_model_str_property_set, name); + } + else + { + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + ret = EINA_FALSE; + ERR("Method property_set() not implemented for model %p (%s)", + model, model->desc->cache.types[0]->name); + } + + return ret; +} + +EAPI Eina_Bool +eina_model_property_del(Eina_Model *model, const char *name) +{ + Eina_Bool ret; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + + eina_error_set(0); + if (model->desc->ops.type.property_del) + { + ret = model->desc->ops.type.property_del(model, name); + if (ret) + _eina_model_event_callback_call + (model, _eina_model_str_property_del, name); + } + else + { + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + ret = EINA_FALSE; + ERR("Method property_del() not implemented for model %p (%s)", + model, model->desc->cache.types[0]->name); + } + + return ret; +} + +EAPI Eina_List * +eina_model_properties_names_list_get(const Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_MODEL_TYPE_CALL_OPTIONAL_RETURN(model, properties_names_list_get, NULL); +} + +EAPI void +eina_model_properties_names_list_free(Eina_List *list) +{ + const char *str; + EINA_LIST_FREE(list, str) + eina_stringshare_del(str); +} + +EAPI int +eina_model_child_count(const Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + EINA_MODEL_TYPE_CALL_OPTIONAL_RETURN(model, child_count, 0); +} + +EAPI Eina_Model * +eina_model_child_get(const Eina_Model *model, unsigned int position) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_MODEL_TYPE_CALL_RETURN(model, child_get, NULL, position); +} + +EAPI Eina_Bool +eina_model_child_set(Eina_Model *model, unsigned int position, Eina_Model *child) +{ + Eina_Bool ret; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_MODEL_INSTANCE_CHECK_VAL(child, EINA_FALSE); + + eina_error_set(0); + if (model->desc->ops.type.child_set) + { + ret = model->desc->ops.type.child_set(model, position, child); + if (ret) + _eina_model_event_callback_call + (model, _eina_model_str_child_set, &position); + } + else + { + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + ret = EINA_FALSE; + ERR("Method child_set() not implemented for model %p (%s)", + model, model->desc->cache.types[0]->name); + } + + return ret; +} + +EAPI Eina_Bool +eina_model_child_del(Eina_Model *model, unsigned int position) +{ + Eina_Bool ret; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + + eina_error_set(0); + if (model->desc->ops.type.child_del) + { + ret = model->desc->ops.type.child_del(model, position); + if (ret) + { + _eina_model_event_callback_call + (model, _eina_model_str_child_del, &position); + _eina_model_event_callback_call + (model, _eina_model_str_children_changed, NULL); + } + } + else + { + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + ret = EINA_FALSE; + ERR("Method child_del() not implemented for model %p (%s)", + model, model->desc->cache.types[0]->name); + } + + return ret; +} + +EAPI Eina_Bool +eina_model_child_insert_at(Eina_Model *model, unsigned int position, Eina_Model *child) +{ + Eina_Bool ret; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(child, EINA_FALSE); + + eina_error_set(0); + if (model->desc->ops.type.child_insert_at) + { + ret = model->desc->ops.type.child_insert_at(model, position, child); + if (ret) + { + _eina_model_event_callback_call + (model, _eina_model_str_child_inserted, &position); + _eina_model_event_callback_call + (model, _eina_model_str_children_changed, NULL); + } + } + else + { + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + ret = EINA_FALSE; + ERR("Method child_insert_at() not implemented for model %p (%s)", + model, model->desc->cache.types[0]->name); + } + + return ret; +} + +EAPI int +eina_model_child_append(Eina_Model *model, Eina_Model *child) +{ + Eina_Bool ret; + int position; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(child, -1); + + position = eina_model_child_count(model); + if (position < 0) + return -1; + + eina_error_set(0); + if (model->desc->ops.type.child_insert_at) + { + ret = model->desc->ops.type.child_insert_at(model, position, child); + if (ret) + { + _eina_model_event_callback_call + (model, _eina_model_str_child_inserted, &position); + _eina_model_event_callback_call + (model, _eina_model_str_children_changed, NULL); + } + } + else + { + eina_error_set(EINA_ERROR_MODEL_METHOD_MISSING); + ret = EINA_FALSE; + ERR("Method child_insert_at() not implemented for model %p (%s)", + model, model->desc->cache.types[0]->name); + } + + return ret ? position : -1; +} + +EAPI int +eina_model_child_find(const Eina_Model *model, unsigned int start_position, const Eina_Model *other) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(other, -1); + EINA_MODEL_TYPE_CALL_RETURN(model, child_find, -1, start_position, other); +} + +EAPI int +eina_model_child_search(const Eina_Model *model, unsigned int start_position, Eina_Each_Cb match, const void *data) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, -1); + EINA_SAFETY_ON_NULL_RETURN_VAL(match, -1); + EINA_MODEL_TYPE_CALL_RETURN(model, child_search, -1, + start_position, match, data); +} + +EAPI Eina_Bool +eina_model_child_sort(Eina_Model *model, Eina_Compare_Cb compare) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, EINA_FALSE); + EINA_MODEL_TYPE_CALL(model, child_sort, compare); + _eina_model_event_callback_call + (model, _eina_model_str_children_changed, NULL); + return EINA_TRUE; +} + +EAPI Eina_Iterator * +eina_model_child_iterator_get(Eina_Model *model) +{ + int count; + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + count = eina_model_child_count(model); + if (count < 0) + return NULL; + EINA_MODEL_TYPE_CALL_RETURN(model, child_iterator_get, NULL, 0, count); +} + +EAPI Eina_Iterator * +eina_model_child_slice_iterator_get(Eina_Model *model, unsigned int start, unsigned int count) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_MODEL_TYPE_CALL_RETURN(model, child_iterator_get, NULL, start, count); +} + +EAPI Eina_Iterator * +eina_model_child_reversed_iterator_get(Eina_Model *model) +{ + int count; + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + count = eina_model_child_count(model); + if (count < 0) + return NULL; + EINA_MODEL_TYPE_CALL_RETURN(model, child_reversed_iterator_get, NULL, + 0, count); +} + +EAPI Eina_Iterator * +eina_model_child_slice_reversed_iterator_get(Eina_Model *model, unsigned int start, unsigned int count) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_MODEL_TYPE_CALL_RETURN(model, child_reversed_iterator_get, NULL, + start, count); +} + +EAPI Eina_Iterator * +eina_model_child_sorted_iterator_get(Eina_Model *model, Eina_Compare_Cb compare) +{ + int count; + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, NULL); + count = eina_model_child_count(model); + if (count < 0) + return NULL; + EINA_MODEL_TYPE_CALL_RETURN(model, child_sorted_iterator_get, NULL, + 0, count, compare); +} + +EAPI Eina_Iterator * +eina_model_child_slice_sorted_iterator_get(Eina_Model *model, unsigned int start, unsigned int count, Eina_Compare_Cb compare) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, NULL); + EINA_MODEL_TYPE_CALL_RETURN(model, child_sorted_iterator_get, NULL, + start, count, compare); +} + +EAPI Eina_Iterator * +eina_model_child_filtered_iterator_get(Eina_Model *model, Eina_Each_Cb match, const void *data) +{ + int count; + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(match, NULL); + count = eina_model_child_count(model); + if (count < 0) + return NULL; + EINA_MODEL_TYPE_CALL_RETURN(model, child_filtered_iterator_get, NULL, + 0, count, match, data); +} + +EAPI Eina_Iterator * +eina_model_child_slice_filtered_iterator_get(Eina_Model *model, unsigned int start, unsigned int count, Eina_Each_Cb match, const void *data) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(match, NULL); + EINA_MODEL_TYPE_CALL_RETURN(model, child_filtered_iterator_get, NULL, + start, count, match, data); +} + +EAPI char * +eina_model_to_string(const Eina_Model *model) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_MODEL_TYPE_CALL_RETURN(model, to_string, NULL); +} + +/* type functions *****************************************************/ + +EAPI Eina_Bool +eina_model_type_check(const Eina_Model_Type *type) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + return _eina_model_type_check(type); +} + +EAPI const char * +eina_model_type_name_get(const Eina_Model_Type *type) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), NULL); + return type->name; +} + +EAPI const Eina_Model_Type * +eina_model_type_parent_get(const Eina_Model_Type *type) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), NULL); + return type->parent; +} + + +#define EINA_MODEL_TYPE_INSTANCE_CHECK(type, model) \ + EINA_SAFETY_ON_NULL_RETURN(type); \ + EINA_SAFETY_ON_FALSE_RETURN(_eina_model_type_check(type)); \ + EINA_MODEL_INSTANCE_CHECK(model); \ + EINA_SAFETY_ON_FALSE_RETURN(_eina_model_instance_check(model, type)); + +#define EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, retval) \ + EINA_SAFETY_ON_NULL_RETURN_VAL(type, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), retval); \ + EINA_MODEL_INSTANCE_CHECK_VAL(model, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_instance_check(model, type), retval); + +EAPI Eina_Bool +eina_model_type_constructor(const Eina_Model_Type *type, Eina_Model *model) +{ + Eina_Bool (*constructor)(Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + constructor = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, constructor)); + EINA_SAFETY_ON_NULL_RETURN_VAL(constructor, EINA_FALSE); + + return constructor(model); +} + +EAPI Eina_Bool +eina_model_type_destructor(const Eina_Model_Type *type, Eina_Model *model) +{ + Eina_Bool (*destructor)(Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + destructor = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, destructor)); + EINA_SAFETY_ON_NULL_RETURN_VAL(destructor, EINA_FALSE); + + return destructor(model); +} + +EAPI Eina_Bool +eina_model_type_copy(const Eina_Model_Type *type, const Eina_Model *src, Eina_Model *dst) +{ + Eina_Bool (*copy)(const Eina_Model *, Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, src, EINA_FALSE); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, dst, EINA_FALSE); + + copy = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, copy)); + EINA_SAFETY_ON_NULL_RETURN_VAL(copy, EINA_FALSE); + + return copy(src, dst); +} + +EAPI Eina_Bool +eina_model_type_deep_copy(const Eina_Model_Type *type, const Eina_Model *src, Eina_Model *dst) +{ + Eina_Bool (*deep_copy)(const Eina_Model *, Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, src, EINA_FALSE); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, dst, EINA_FALSE); + + deep_copy = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, deep_copy)); + EINA_SAFETY_ON_NULL_RETURN_VAL(deep_copy, EINA_FALSE); + + return deep_copy(src, dst); +} + +EAPI Eina_Bool +eina_model_type_compare(const Eina_Model_Type *type, const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + Eina_Bool (*compare)(const Eina_Model *, const Eina_Model *, int *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(cmp, EINA_FALSE); + *cmp = 0; + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, a, EINA_FALSE); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, b, EINA_FALSE); + + compare = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, compare)); + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, EINA_FALSE); + + return compare(a, b, cmp); +} + +EAPI Eina_Bool +eina_model_type_load(const Eina_Model_Type *type, Eina_Model *model) +{ + Eina_Bool (*load)(Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + load = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, load)); + EINA_SAFETY_ON_NULL_RETURN_VAL(load, EINA_FALSE); + + return load(model); +} + +EAPI Eina_Bool +eina_model_type_unload(const Eina_Model_Type *type, Eina_Model *model) +{ + Eina_Bool (*unload)(Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + unload = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, unload)); + EINA_SAFETY_ON_NULL_RETURN_VAL(unload, EINA_FALSE); + + return unload(model); +} + +EAPI Eina_Bool +eina_model_type_property_get(const Eina_Model_Type *type, const Eina_Model *model, const char *name, Eina_Value *value) +{ + Eina_Bool (*property_get)(const Eina_Model *, const char *, Eina_Value *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + property_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, property_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(property_get, EINA_FALSE); + + return property_get(model, name, value); +} + +EAPI Eina_Bool +eina_model_type_property_set(const Eina_Model_Type *type, Eina_Model *model, const char *name, const Eina_Value *value) +{ + Eina_Bool (*property_set)(Eina_Model *, const char *, const Eina_Value *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(value->type), EINA_FALSE); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + property_set = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, property_set)); + EINA_SAFETY_ON_NULL_RETURN_VAL(property_set, EINA_FALSE); + + return property_set(model, name, value); +} + +EAPI Eina_Bool +eina_model_type_property_del(const Eina_Model_Type *type, Eina_Model *model, const char *name) +{ + Eina_Bool (*property_del)(const Eina_Model *, const char *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + property_del = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, property_del)); + EINA_SAFETY_ON_NULL_RETURN_VAL(property_del, EINA_FALSE); + + return property_del(model, name); +} + +EAPI Eina_List * +eina_model_type_properties_names_list_get(const Eina_Model_Type *type, const Eina_Model *model) +{ + Eina_List *(*properties_names_list_get)(const Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + properties_names_list_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, properties_names_list_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(properties_names_list_get, NULL); + + return properties_names_list_get(model); +} + +EAPI int +eina_model_type_child_count(const Eina_Model_Type *type, const Eina_Model *model) +{ + int (*child_count)(const Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, -1); + + child_count = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_count)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_count, -1); + + return child_count(model); +} + +EAPI Eina_Model * +eina_model_type_child_get(const Eina_Model_Type *type, const Eina_Model *model, unsigned int position) +{ + Eina_Model *(*child_get)(const Eina_Model *, unsigned int); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + child_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_get, NULL); + + return child_get(model, position); +} + +EAPI Eina_Bool +eina_model_type_child_set(const Eina_Model_Type *type, Eina_Model *model, unsigned int position, Eina_Model *child) +{ + Eina_Bool (*child_set)(Eina_Model *, unsigned int, Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + EINA_MODEL_INSTANCE_CHECK_VAL(child, EINA_FALSE); + + child_set = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_set)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_set, EINA_FALSE); + + return child_set(model, position, child); +} + +EAPI Eina_Bool +eina_model_type_child_del(const Eina_Model_Type *type, Eina_Model *model, unsigned int position) +{ + Eina_Bool (*child_del)(Eina_Model *, unsigned int); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + + child_del = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_del)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_del, EINA_FALSE); + + return child_del(model, position); +} + +EAPI Eina_Bool +eina_model_type_child_insert_at(const Eina_Model_Type *type, Eina_Model *model, unsigned int position, Eina_Model *child) +{ + Eina_Bool (*child_insert_at)(Eina_Model *, unsigned int, Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, EINA_FALSE); + EINA_MODEL_INSTANCE_CHECK_VAL(child, EINA_FALSE); + + child_insert_at = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_insert_at)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_insert_at, EINA_FALSE); + + return child_insert_at(model, position, child); +} + +EAPI int +eina_model_type_child_find(const Eina_Model_Type *type, const Eina_Model *model, unsigned int start_position, const Eina_Model *other) +{ + int (*child_find)(const Eina_Model *, unsigned int, const Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, -1); + EINA_MODEL_INSTANCE_CHECK_VAL(other, -1); + + child_find = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_find)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_find, -1); + + return child_find(model, start_position, other); +} + +EAPI int +eina_model_type_child_search(const Eina_Model_Type *type, const Eina_Model *model, unsigned int start_position, Eina_Each_Cb match, const void *data) +{ + int (*child_search)(const Eina_Model *, unsigned int, Eina_Each_Cb, const void *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(match, -1); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, -1); + + child_search = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_search)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_search, -1); + + return child_search(model, start_position, match, data); +} + +EAPI void +eina_model_type_child_sort(const Eina_Model_Type *type, Eina_Model *model, Eina_Compare_Cb compare) +{ + void (*child_sort)(Eina_Model *, Eina_Compare_Cb); + + EINA_SAFETY_ON_NULL_RETURN(compare); + EINA_MODEL_TYPE_INSTANCE_CHECK(type, model); + + child_sort = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_sort)); + EINA_SAFETY_ON_NULL_RETURN(child_sort); + + return child_sort(model, compare); +} + +EAPI Eina_Iterator * +eina_model_type_child_iterator_get(const Eina_Model_Type *type, Eina_Model *model, unsigned int start, unsigned int count) +{ + Eina_Iterator *(*child_iterator_get)(const Eina_Model *, unsigned int, unsigned int); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + child_iterator_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_iterator_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_iterator_get, NULL); + + return child_iterator_get(model, start, count); +} + +EAPI Eina_Iterator * +eina_model_type_child_reversed_iterator_get(const Eina_Model_Type *type, Eina_Model *model, unsigned int start, unsigned int count) +{ + Eina_Iterator *(*child_reversed_iterator_get)(const Eina_Model *, unsigned int, unsigned int); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + child_reversed_iterator_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_reversed_iterator_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_reversed_iterator_get, NULL); + + return child_reversed_iterator_get(model, start, count); +} + +EAPI Eina_Iterator * +eina_model_type_child_sorted_iterator_get(const Eina_Model_Type *type, Eina_Model *model, unsigned int start, unsigned int count, Eina_Compare_Cb compare) +{ + Eina_Iterator *(*child_sorted_iterator_get)(const Eina_Model *, unsigned int, unsigned int, Eina_Compare_Cb); + + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, NULL); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + child_sorted_iterator_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_sorted_iterator_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_sorted_iterator_get, NULL); + + return child_sorted_iterator_get(model, start, count, compare); +} + +EAPI Eina_Iterator * +eina_model_type_child_filtered_iterator_get(const Eina_Model_Type *type, Eina_Model *model, unsigned int start, unsigned int count, Eina_Each_Cb match, const void *data) +{ + Eina_Iterator *(*child_filtered_iterator_get)(const Eina_Model *, unsigned int, unsigned int, Eina_Each_Cb, const void *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(match, NULL); + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + child_filtered_iterator_get = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, child_filtered_iterator_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_filtered_iterator_get, NULL); + + return child_filtered_iterator_get(model, start, count, match, data); +} + +EAPI char * +eina_model_type_to_string(const Eina_Model_Type *type, const Eina_Model *model) +{ + char *(*to_string)(const Eina_Model *); + + EINA_MODEL_TYPE_INSTANCE_CHECK_VAL(type, model, NULL); + + to_string = _eina_model_type_find_offset + (type, offsetof(Eina_Model_Type, to_string)); + EINA_SAFETY_ON_NULL_RETURN_VAL(to_string, NULL); + + return to_string(model); +} + +EAPI Eina_Bool +eina_model_type_subclass_check(const Eina_Model_Type *type, const Eina_Model_Type *self_or_parent) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(type, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(self_or_parent, EINA_FALSE); + + for (; type != NULL; type = type->parent) + { + if (type == self_or_parent) + return EINA_TRUE; + } + + return EINA_FALSE; +} + +static inline const Eina_Model_Interface * +_eina_model_type_interface_get(const Eina_Model_Type *type, const char *name, Eina_Bool ptr_cmp) +{ + const Eina_Model_Interface **itr; + + if (!type) + return NULL; + + if (!type->interfaces) + return _eina_model_type_interface_get(type->parent, name, ptr_cmp); + + if (ptr_cmp) + { + for (itr = type->interfaces; itr != NULL; itr++) + if ((*itr)->name == name) + return *itr; + } + else + { + for (itr = type->interfaces; itr != NULL; itr++) + if (strcmp((*itr)->name, name) == 0) + return *itr; + } + + return NULL; +} + +static inline Eina_Bool +_eina_model_interface_check(const Eina_Model_Interface *iface) +{ + EINA_SAFETY_ON_FALSE_RETURN_VAL + (iface->version == EINA_MODEL_INTERFACE_VERSION, EINA_FALSE); + return EINA_TRUE; +} + +EAPI const Eina_Model_Interface * +eina_model_type_interface_get(const Eina_Model_Type *type, const char *name) +{ + const Eina_Model_Interface *iface; + + EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), NULL); + + /* search for pointer, make speed-aware users fast */ + iface = _eina_model_type_interface_get(type, name, EINA_TRUE); + + if (!iface) + { + /* search using strcmp(), slow users don't care */ + iface = _eina_model_type_interface_get(type, name, EINA_FALSE); + } + else if (!_eina_model_interface_check(iface)) + iface = NULL; + + return iface; +} + +EAPI void * +eina_model_type_private_data_get(const Eina_Model *model, const Eina_Model_Type *type) +{ + const Eina_Model_Description *desc; + unsigned int i; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(type, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_type_check(type), NULL); + + desc = model->desc; + + for (i = 0; i < desc->total.types; i++) + if (desc->cache.types[i] == type) + return model->privates[i]; + + CRITICAL("Model %p (%s) is not an instance of type %p (%s)", + model, desc->cache.types[0]->name, + type, type->name); + return NULL; +} + +/* interface functions ************************************************/ + +EAPI Eina_Bool +eina_model_interface_check(const Eina_Model_Interface *iface) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, EINA_FALSE); + return _eina_model_interface_check(iface); +} + +EAPI void * +eina_model_interface_private_data_get(const Eina_Model *model, const Eina_Model_Interface *iface) +{ + const Eina_Model_Description *desc; + unsigned int i; + + EINA_MODEL_INSTANCE_CHECK_VAL(model, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_interface_check(iface), NULL); + + desc = model->desc; + + for (i = 0; i < desc->total.ifaces; i++) + if (desc->cache.ifaces[i] == iface) + return model->privates[desc->total.types + i]; + + CRITICAL("Model %p (%s) does not implement interface %p (%s)", + model, desc->cache.types[0]->name, + iface, iface->name); + return NULL; +} + +static Eina_Bool +_eina_model_interface_implemented(const Eina_Model *model, const Eina_Model_Interface *iface) +{ + const Eina_Model_Interface **itr, **itr_end; + + itr = model->desc->cache.ifaces; + itr_end = itr + model->desc->total.ifaces; + + for (; itr < itr_end; itr++) + if (*itr == iface) + return EINA_TRUE; + + return EINA_FALSE; +} + +EAPI Eina_Bool +eina_model_interface_implemented(const Eina_Model *model, const Eina_Model_Interface *iface) +{ + EINA_MODEL_INSTANCE_CHECK_VAL(model, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_interface_check(iface), + EINA_FALSE); + return _eina_model_interface_implemented(model, iface); +} + +#define EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK(iface, model) \ + EINA_SAFETY_ON_NULL_RETURN(iface); \ + EINA_SAFETY_ON_FALSE_RETURN(_eina_model_interface_check(iface)); \ + EINA_MODEL_INSTANCE_CHECK(model); \ + EINA_SAFETY_ON_FALSE_RETURN(_eina_model_interface_implemented(model, iface)); + +#define EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, retval) \ + EINA_SAFETY_ON_NULL_RETURN_VAL(iface, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_interface_check(iface), retval); \ + EINA_MODEL_INSTANCE_CHECK_VAL(model, retval); \ + EINA_SAFETY_ON_FALSE_RETURN_VAL(_eina_model_interface_implemented(model, iface), retval); + + +EAPI Eina_Bool +eina_model_interface_constructor(const Eina_Model_Interface *iface, Eina_Model *model) +{ + Eina_Bool (*constructor)(Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + constructor = _eina_model_interface_find_offset + (iface, offsetof(Eina_Model_Interface, constructor)); + EINA_SAFETY_ON_NULL_RETURN_VAL(constructor, EINA_FALSE); + return constructor(model); +} + +EAPI Eina_Bool +eina_model_interface_destructor(const Eina_Model_Interface *iface, Eina_Model *model) +{ + Eina_Bool (*destructor)(Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + destructor = _eina_model_interface_find_offset + (iface, offsetof(Eina_Model_Interface, destructor)); + EINA_SAFETY_ON_NULL_RETURN_VAL(destructor, EINA_FALSE); + return destructor(model); +} + +EAPI Eina_Bool +eina_model_interface_copy(const Eina_Model_Interface *iface, const Eina_Model *src, Eina_Model *dst) +{ + Eina_Bool (*copy)(const Eina_Model *, Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, src, EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, dst, EINA_FALSE); + + copy = _eina_model_interface_find_offset + (iface, offsetof(Eina_Model_Interface, copy)); + EINA_SAFETY_ON_NULL_RETURN_VAL(copy, EINA_FALSE); + return copy(src, dst); +} + +EAPI Eina_Bool +eina_model_interface_deep_copy(const Eina_Model_Interface *iface, const Eina_Model *src, Eina_Model *dst) +{ + Eina_Bool (*deep_copy)(const Eina_Model *, Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, src, EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, dst, EINA_FALSE); + + deep_copy = _eina_model_interface_find_offset + (iface, offsetof(Eina_Model_Interface, deep_copy)); + EINA_SAFETY_ON_NULL_RETURN_VAL(deep_copy, EINA_FALSE); + return deep_copy(src, dst); +} + + +/* Eina_Model_Interface_Properties ************************************/ + +EAPI Eina_Bool +eina_model_interface_properties_compare(const Eina_Model_Interface *iface, const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + Eina_Bool (*compare)(const Eina_Model *, const Eina_Model *, int *cmp); + + EINA_SAFETY_ON_NULL_RETURN_VAL(cmp, EINA_FALSE); + + *cmp = 0; + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, a, EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, b, EINA_FALSE); + + compare = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, compare)); + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, EINA_FALSE); + return compare(a, b, cmp); +} + +EAPI Eina_Bool +eina_model_interface_properties_load(const Eina_Model_Interface *iface, Eina_Model *model) +{ + Eina_Bool (*load)(Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + load = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, load)); + EINA_SAFETY_ON_NULL_RETURN_VAL(load, EINA_FALSE); + return load(model); +} + +EAPI Eina_Bool +eina_model_interface_properties_unload(const Eina_Model_Interface *iface, Eina_Model *model) +{ + Eina_Bool (*unload)(Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + unload = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, unload)); + EINA_SAFETY_ON_NULL_RETURN_VAL(unload, EINA_FALSE); + return unload(model); +} + +EAPI Eina_Bool +eina_model_interface_properties_get(const Eina_Model_Interface *iface, const Eina_Model *model, const char *name, Eina_Value *value) +{ + Eina_Bool (*get)(const Eina_Model *, const char *, Eina_Value *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + get = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(get, EINA_FALSE); + return get(model, name, value); +} + +EAPI Eina_Bool +eina_model_interface_properties_set(const Eina_Model_Interface *iface, Eina_Model *model, const char *name, const Eina_Value *value) +{ + Eina_Bool (*set)(Eina_Model *, const char *, const Eina_Value *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(eina_value_type_check(value->type), EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + set = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, set)); + EINA_SAFETY_ON_NULL_RETURN_VAL(set, EINA_FALSE); + return set(model, name, value); +} + +EAPI Eina_Bool +eina_model_interface_properties_del(const Eina_Model_Interface *iface, Eina_Model *model, const char *name) +{ + Eina_Bool (*del)(Eina_Model *, const char *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(name, EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + del = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, del)); + EINA_SAFETY_ON_NULL_RETURN_VAL(del, EINA_FALSE); + return del(model, name); +} + +EAPI Eina_List * +eina_model_interface_properties_names_list_get(const Eina_Model_Interface *iface, const Eina_Model *model) +{ + Eina_List *(*names_list_get)(const Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, NULL); + + names_list_get = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Properties, names_list_get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(names_list_get, NULL); + return names_list_get(model); +} + +/* Eina_Model_Interface_Children **************************************/ + +EAPI Eina_Bool +eina_model_interface_children_compare(const Eina_Model_Interface *iface, const Eina_Model *a, const Eina_Model *b, int *cmp) +{ + Eina_Bool (*compare)(const Eina_Model *, const Eina_Model *, int *); + + EINA_SAFETY_ON_NULL_RETURN_VAL(cmp, EINA_FALSE); + + *cmp = 0; + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, a, EINA_FALSE); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, b, EINA_FALSE); + + compare = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, compare)); + EINA_SAFETY_ON_NULL_RETURN_VAL(compare, EINA_FALSE); + return compare(a, b, cmp); +} + +EAPI Eina_Bool +eina_model_interface_children_load(const Eina_Model_Interface *iface, Eina_Model *model) +{ + Eina_Bool (*load)(Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + load = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, load)); + EINA_SAFETY_ON_NULL_RETURN_VAL(load, EINA_FALSE); + return load(model); +} + +EAPI Eina_Bool +eina_model_interface_children_unload(const Eina_Model_Interface *iface, Eina_Model *model) +{ + Eina_Bool (*unload)(Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + unload = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, unload)); + EINA_SAFETY_ON_NULL_RETURN_VAL(unload, EINA_FALSE); + return unload(model); +} + +EAPI int +eina_model_interface_children_count(const Eina_Model_Interface *iface, const Eina_Model *model) +{ + int (*count)(const Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, -1); + + count = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, count)); + EINA_SAFETY_ON_NULL_RETURN_VAL(count, -1); + return count(model); +} + +EAPI Eina_Model * +eina_model_interface_children_get(const Eina_Model_Interface *iface, const Eina_Model *model, unsigned int position) +{ + Eina_Model *(*get)(const Eina_Model *, unsigned int); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, NULL); + + get = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, get)); + EINA_SAFETY_ON_NULL_RETURN_VAL(get, NULL); + return get(model, position); +} + +EAPI Eina_Bool eina_model_interface_children_set(const Eina_Model_Interface *iface, Eina_Model *model, unsigned int position, Eina_Model *child) +{ + Eina_Bool (*set)(const Eina_Model *, unsigned int, Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + EINA_MODEL_INSTANCE_CHECK_VAL(child, EINA_FALSE); + + set = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, set)); + EINA_SAFETY_ON_NULL_RETURN_VAL(set, EINA_FALSE); + return set(model, position, child); +} + +EAPI Eina_Bool +eina_model_interface_children_del(const Eina_Model_Interface *iface, Eina_Model *model, unsigned int position) +{ + Eina_Bool (*del)(Eina_Model *, unsigned int); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + + del = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, del)); + EINA_SAFETY_ON_NULL_RETURN_VAL(del, EINA_FALSE); + return del(model, position); +} + + +EAPI Eina_Bool +eina_model_interface_children_insert_at(const Eina_Model_Interface *iface, Eina_Model *model, unsigned int position, Eina_Model *child) +{ + Eina_Bool (*insert_at)(const Eina_Model *, unsigned int, Eina_Model *); + + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK_VAL(iface, model, EINA_FALSE); + EINA_MODEL_INSTANCE_CHECK_VAL(child, EINA_FALSE); + + insert_at = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, insert_at)); + EINA_SAFETY_ON_NULL_RETURN_VAL(insert_at, EINA_FALSE); + return insert_at(model, position, child); +} + +EAPI void +eina_model_interface_children_sort(const Eina_Model_Interface *iface, Eina_Model *model, Eina_Compare_Cb compare) +{ + void (*sort)(const Eina_Model *, Eina_Compare_Cb); + + EINA_SAFETY_ON_NULL_RETURN(compare); + EINA_MODEL_INTERFACE_IMPLEMENTED_CHECK(iface, model); + + sort = _eina_model_interface_value_find_offset + (iface, offsetof(Eina_Model_Interface_Children, sort)); + EINA_SAFETY_ON_NULL_RETURN(sort); + return sort(model, compare); +} diff --git a/legacy/eina/src/lib/eina_private.h b/legacy/eina/src/lib/eina_private.h index e31ab476de..49df565780 100644 --- a/legacy/eina/src/lib/eina_private.h +++ b/legacy/eina/src/lib/eina_private.h @@ -95,6 +95,8 @@ #define EINA_MAGIC_INARRAY_ITERATOR 0x98761271 #define EINA_MAGIC_INARRAY_ACCESSOR 0x98761272 +#define EINA_MAGIC_MODEL 0x98761280 + #define EINA_MAGIC_CLASS 0x9877CB30 /* undef the following, we want out version */ diff --git a/legacy/eina/src/tests/Makefile.am b/legacy/eina/src/tests/Makefile.am index 7e05ba9aa1..cf500f5d22 100644 --- a/legacy/eina/src/tests/Makefile.am +++ b/legacy/eina/src/tests/Makefile.am @@ -66,7 +66,8 @@ eina_test_strbuf.c \ eina_test_str.c \ eina_test_quadtree.c \ eina_test_simple_xml_parser.c \ -eina_test_value.c +eina_test_value.c \ +eina_test_model.c eina_suite_LDADD = @CHECK_LIBS@ $(top_builddir)/src/lib/libeina.la -lm diff --git a/legacy/eina/src/tests/eina_suite.c b/legacy/eina/src/tests/eina_suite.c index 648a717613..2014e6c240 100644 --- a/legacy/eina/src/tests/eina_suite.c +++ b/legacy/eina/src/tests/eina_suite.c @@ -68,6 +68,7 @@ static const Eina_Test_Case etc[] = { { "Sched", eina_test_sched }, { "Simple Xml Parser", eina_test_simple_xml_parser}, { "Value", eina_test_value }, + { "Model", eina_test_model }, { NULL, NULL } }; diff --git a/legacy/eina/src/tests/eina_suite.h b/legacy/eina/src/tests/eina_suite.h index 6eaaec7178..d399298128 100644 --- a/legacy/eina/src/tests/eina_suite.h +++ b/legacy/eina/src/tests/eina_suite.h @@ -56,5 +56,6 @@ void eina_test_fp(TCase *tc); void eina_test_sched(TCase *tc); void eina_test_simple_xml_parser(TCase *tc); void eina_test_value(TCase *tc); +void eina_test_model(TCase *tc); #endif /* EINA_SUITE_H_ */ diff --git a/legacy/eina/src/tests/eina_test_model.c b/legacy/eina/src/tests/eina_test_model.c new file mode 100644 index 0000000000..d6a848377f --- /dev/null +++ b/legacy/eina/src/tests/eina_test_model.c @@ -0,0 +1,717 @@ +/* EINA - EFL data type library + * Copyright (C) 2012 ProFUSION embedded systems + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; + * if not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "eina_suite.h" +#include "Eina.h" + +static void +_eina_test_model_check_safety_null(const Eina_Log_Domain *d, Eina_Log_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args) +{ + Eina_Bool *ck = data; + + if ((level == EINA_LOG_LEVEL_ERR) && (strcmp(fmt, "%s") == 0)) + { + const char *str; + va_list cp_args; + + va_copy(cp_args, args); + str = va_arg(cp_args, const char *); + va_end(cp_args); + if (eina_str_has_prefix(str, "safety check failed: ") && + eina_str_has_suffix(str, " == NULL")) + { + *ck = EINA_TRUE; + return; + } + } + *ck = EINA_FALSE; + eina_log_print_cb_stderr(d, level, file, fnc, line, fmt, NULL, args); +} + +static void +_eina_test_model_cb_count(void *data, Eina_Model *model __UNUSED__, const Eina_Model_Event_Description *desc __UNUSED__, void *event_info __UNUSED__) +{ + unsigned *count = data; + (*count)++; + //printf("%p %s\n", model, desc->name); +} + +START_TEST(eina_model_test_properties) +{ + unsigned int count_del = 0, count_pset = 0, count_pdel = 0; + Eina_Model *m; + Eina_Value inv, outv; + int i; + char *s; + Eina_List *lst; + Eina_Bool ck; + + eina_init(); + + m = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(m != NULL); + + eina_model_event_callback_add + (m, "deleted", _eina_test_model_cb_count, &count_del); + eina_model_event_callback_add + (m, "property,set", _eina_test_model_cb_count, &count_pset); + eina_model_event_callback_add + (m, "property,deleted", _eina_test_model_cb_count, &count_pdel); + + fail_unless(eina_value_setup(&inv, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&inv, 1234)); + fail_unless(eina_value_get(&inv, &i)); + fail_unless(i == 1234); + + fail_unless(eina_model_property_set(m, "abc", &inv)); + + fail_unless(eina_value_set(&inv, 5678)); + fail_unless(eina_model_property_set(m, "xyz", &inv)); + + fail_unless(eina_value_set(&inv, 171)); + fail_unless(eina_model_property_set(m, "value", &inv)); + + lst = eina_model_properties_names_list_get(m); + fail_unless(eina_list_count(lst) == 3); + + lst = eina_list_sort(lst, 0, EINA_COMPARE_CB(strcmp)); + fail_unless(strcmp("abc", eina_list_nth(lst, 0)) == 0); + fail_unless(strcmp("value", eina_list_nth(lst, 1)) == 0); + fail_unless(strcmp("xyz", eina_list_nth(lst, 2)) == 0); + + eina_model_properties_names_list_free(lst); + + fail_unless(eina_model_property_get(m, "abc", &outv)); + fail_unless(eina_value_get(&outv, &i)); + fail_unless(i == 1234); + eina_value_flush(&outv); + + fail_unless(eina_model_property_get(m, "xyz", &outv)); + fail_unless(eina_value_get(&outv, &i)); + fail_unless(i == 5678); + eina_value_flush(&outv); + + fail_unless(eina_model_property_get(m, "value", &outv)); + fail_unless(eina_value_get(&outv, &i)); + fail_unless(i == 171); + eina_value_flush(&outv); + + fail_unless(eina_value_set(&inv, 666)); + fail_unless(eina_model_property_set(m, "value", &inv)); + fail_unless(eina_model_property_get(m, "value", &outv)); + fail_unless(eina_value_get(&outv, &i)); + fail_unless(i == 666); + + eina_value_flush(&outv); + eina_value_flush(&inv); + + fail_unless(eina_value_setup(&inv, EINA_VALUE_TYPE_STRING)); + fail_unless(eina_value_set(&inv, "Hello world!")); + fail_unless(eina_model_property_set(m, "string", &inv)); + + fail_unless(eina_model_property_get(m, "string", &outv)); + fail_unless(eina_value_get(&outv, &s)); + fail_unless(s != NULL); + fail_unless(strcmp(s, "Hello world!") == 0); + + eina_value_flush(&outv); + eina_value_flush(&inv); + + fail_unless(eina_value_setup(&inv, EINA_VALUE_TYPE_STRINGSHARE)); + fail_unless(eina_value_set(&inv, "Hello world-STRINGSHARED!")); + fail_unless(eina_model_property_set(m, "stringshare", &inv)); + /* set twice to see if references drop to zero before new add, shouldn't */ + fail_unless(eina_model_property_set(m, "stringshare", &inv)); + + fail_unless(eina_model_property_get(m, "stringshare", &outv)); + fail_unless(eina_value_get(&outv, &s)); + fail_unless(s != NULL); + fail_unless(strcmp(s, "Hello world-STRINGSHARED!") == 0); + + eina_value_flush(&outv); + eina_value_flush(&inv); + + s = eina_model_to_string(m); + fail_unless(s != NULL); + fail_unless(strcmp(s, "Eina_Model_Type_Generic({abc: 1234, string: Hello world!, stringshare: Hello world-STRINGSHARED!, value: 666, xyz: 5678}, [])") == 0); + free(s); + + fail_unless(eina_model_property_del(m, "value")); + + /* negative test (check safety was displayed by using print_cb) */ + eina_log_print_cb_set(_eina_test_model_check_safety_null, &ck); + + ck = EINA_FALSE; + fail_if(eina_model_property_get(m, "non-existent", &outv)); + fail_unless(ck == EINA_TRUE); + + ck = EINA_FALSE; + fail_if(eina_model_property_get(m, NULL, &outv)); + fail_unless(ck == EINA_TRUE); + + ck = EINA_FALSE; + fail_if(eina_model_property_del(m, "value")); + fail_unless(ck == EINA_TRUE); + + /* revert print_cb to default */ + eina_log_print_cb_set(eina_log_print_cb_stderr, NULL); + + fail_unless(eina_model_refcount(m) == 1); + + eina_model_unref(m); + fail_unless(count_del == 1); + fail_unless(count_pset == 7); + fail_unless(count_pdel == 1); + eina_shutdown(); +} +END_TEST + +static int +eina_model_test_children_reverse_cmp(const Eina_Model *a, const Eina_Model *b) +{ + return - eina_model_compare(a, b); +} + +START_TEST(eina_model_test_children) +{ + unsigned int count_del = 0, count_cset = 0, count_cins = 0, count_cdel = 0; + Eina_Model *m, *c; + char *s; + int i; + + eina_init(); + + m = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(m != NULL); + + eina_model_event_callback_add + (m, "deleted", _eina_test_model_cb_count, &count_del); + eina_model_event_callback_add + (m, "child,set", _eina_test_model_cb_count, &count_cset); + eina_model_event_callback_add + (m, "child,inserted", _eina_test_model_cb_count, &count_cins); + eina_model_event_callback_add + (m, "child,deleted", _eina_test_model_cb_count, &count_cdel); + + for (i = 0; i < 10; i++) + { + Eina_Value val; + + c = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(c != NULL); + + eina_model_event_callback_add + (c, "deleted", _eina_test_model_cb_count, &count_del); + eina_model_event_callback_add + (c, "child,set", _eina_test_model_cb_count, &count_cset); + eina_model_event_callback_add + (c, "child,inserted", _eina_test_model_cb_count, &count_cins); + eina_model_event_callback_add + (c, "child,deleted", _eina_test_model_cb_count, &count_cdel); + + fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&val, i)); + fail_unless(eina_model_property_set(c, "value", &val)); + + fail_unless(eina_model_child_append(m, c) >= 0); + fail_unless(eina_model_refcount(c) == 2); + + eina_value_flush(&val); + eina_model_unref(c); + } + + fail_unless(eina_model_child_count(m) == 10); + + for (i = 0; i < 10; i++) + { + Eina_Value val; + int x; + + c = eina_model_child_get(m, i); + fail_unless(c != NULL); + fail_unless(eina_model_refcount(c) == 2); + + fail_unless(eina_model_property_get(c, "value", &val)); + fail_unless(eina_value_get(&val, &x)); + fail_unless(x == i); + + eina_value_flush(&val); + eina_model_unref(c); + } + + eina_model_child_sort(m, EINA_COMPARE_CB(eina_model_test_children_reverse_cmp)); + + for (i = 0; i < 10; i++) + { + Eina_Value val; + int x; + + c = eina_model_child_get(m, i); + fail_unless(c != NULL); + fail_unless(eina_model_refcount(c) == 2); + + fail_unless(eina_model_property_get(c, "value", &val)); + fail_unless(eina_value_get(&val, &x)); + fail_unless(x == 10 - i - 1); + + eina_value_flush(&val); + eina_model_unref(c); + } + + eina_model_child_sort(m, EINA_COMPARE_CB(eina_model_compare)); + + s = eina_model_to_string(m); + fail_unless(s != NULL); + fail_unless(strcmp(s, "Eina_Model_Type_Generic({}, [Eina_Model_Type_Generic({value: 0}, []), Eina_Model_Type_Generic({value: 1}, []), Eina_Model_Type_Generic({value: 2}, []), Eina_Model_Type_Generic({value: 3}, []), Eina_Model_Type_Generic({value: 4}, []), Eina_Model_Type_Generic({value: 5}, []), Eina_Model_Type_Generic({value: 6}, []), Eina_Model_Type_Generic({value: 7}, []), Eina_Model_Type_Generic({value: 8}, []), Eina_Model_Type_Generic({value: 9}, [])])") == 0); + free(s); + + c = eina_model_child_get(m, 0); + eina_model_child_set(m, 1, c); + eina_model_unref(c); + + eina_model_child_del(m, 0); + eina_model_child_del(m, 8); + + s = eina_model_to_string(m); + fail_unless(s != NULL); + fail_unless(strcmp(s, "Eina_Model_Type_Generic({}, [Eina_Model_Type_Generic({value: 0}, []), Eina_Model_Type_Generic({value: 2}, []), Eina_Model_Type_Generic({value: 3}, []), Eina_Model_Type_Generic({value: 4}, []), Eina_Model_Type_Generic({value: 5}, []), Eina_Model_Type_Generic({value: 6}, []), Eina_Model_Type_Generic({value: 7}, []), Eina_Model_Type_Generic({value: 8}, [])])") == 0); + free(s); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + + fail_unless(count_del == 11); + fail_unless(count_cins == 10); + fail_unless(count_cset == 1); + fail_unless(count_cdel == 2); + + eina_shutdown(); +} +END_TEST + +START_TEST(eina_model_test_copy) +{ + unsigned int count_del = 0; + Eina_Model *m, *cp; + char *s1, *s2; + int i; + + eina_init(); + + m = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(m != NULL); + + eina_model_event_callback_add + (m, "deleted", _eina_test_model_cb_count, &count_del); + + for (i = 0; i < 5; i++) + { + Eina_Value val; + char name[2] = {'a'+ i, 0}; + fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&val, i)); + fail_unless(eina_model_property_set(m, name, &val)); + eina_value_flush(&val); + } + + for (i = 0; i < 5; i++) + { + Eina_Value val; + Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(c != NULL); + fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&val, i)); + fail_unless(eina_model_property_set(c, "x", &val)); + + eina_model_event_callback_add + (c, "deleted", _eina_test_model_cb_count, &count_del); + + fail_unless(eina_model_child_append(m, c) >= 0); + eina_model_unref(c); + eina_value_flush(&val); + } + + s1 = eina_model_to_string(m); + fail_unless(s1 != NULL); + fail_unless(strcmp(s1, "Eina_Model_Type_Generic({a: 0, b: 1, c: 2, d: 3, e: 4}, [Eina_Model_Type_Generic({x: 0}, []), Eina_Model_Type_Generic({x: 1}, []), Eina_Model_Type_Generic({x: 2}, []), Eina_Model_Type_Generic({x: 3}, []), Eina_Model_Type_Generic({x: 4}, [])])") == 0); + + cp = eina_model_copy(m); + fail_unless(cp != NULL); + fail_unless(cp != m); + + eina_model_event_callback_add + (cp, "deleted", _eina_test_model_cb_count, &count_del); + + s2 = eina_model_to_string(cp); + fail_unless(s2 != NULL); + fail_unless(strcmp(s1, s2) == 0); + + for (i = 0; i < 5; i++) + { + Eina_Model *c1 = eina_model_child_get(m, i); + Eina_Model *c2 = eina_model_child_get(cp, i); + + fail_unless(c1 != NULL); + fail_unless(c1 == c2); + fail_unless(eina_model_refcount(c1) == 4); + + eina_model_unref(c1); + eina_model_unref(c2); + } + + free(s1); + free(s2); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + + fail_unless(eina_model_refcount(cp) == 1); + eina_model_unref(cp); + + fail_unless(count_del == 2 + 5); + + eina_shutdown(); +} +END_TEST + +START_TEST(eina_model_test_deep_copy) +{ + unsigned int count_del = 0; + Eina_Model *m, *cp; + char *s1, *s2; + int i; + + eina_init(); + + m = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(m != NULL); + + eina_model_event_callback_add + (m, "deleted", _eina_test_model_cb_count, &count_del); + + for (i = 0; i < 5; i++) + { + Eina_Value val; + char name[2] = {'a'+ i, 0}; + fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&val, i)); + fail_unless(eina_model_property_set(m, name, &val)); + eina_value_flush(&val); + } + + for (i = 0; i < 5; i++) + { + Eina_Value val; + Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(c != NULL); + fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&val, i)); + fail_unless(eina_model_property_set(c, "x", &val)); + + eina_model_event_callback_add + (c, "deleted", _eina_test_model_cb_count, &count_del); + + fail_unless(eina_model_child_append(m, c) >= 0); + eina_model_unref(c); + eina_value_flush(&val); + } + + s1 = eina_model_to_string(m); + fail_unless(s1 != NULL); + fail_unless(strcmp(s1, "Eina_Model_Type_Generic({a: 0, b: 1, c: 2, d: 3, e: 4}, [Eina_Model_Type_Generic({x: 0}, []), Eina_Model_Type_Generic({x: 1}, []), Eina_Model_Type_Generic({x: 2}, []), Eina_Model_Type_Generic({x: 3}, []), Eina_Model_Type_Generic({x: 4}, [])])") == 0); + + cp = eina_model_deep_copy(m); + fail_unless(cp != NULL); + fail_unless(cp != m); + + eina_model_event_callback_add + (cp, "deleted", _eina_test_model_cb_count, &count_del); + + s2 = eina_model_to_string(cp); + fail_unless(s2 != NULL); + fail_unless(strcmp(s1, s2) == 0); + + for (i = 0; i < 5; i++) + { + Eina_Model *c1 = eina_model_child_get(m, i); + Eina_Model *c2 = eina_model_child_get(cp, i); + + fail_unless(c1 != NULL); + fail_unless(c1 != c2); + fail_unless(eina_model_refcount(c1) == 2); + fail_unless(eina_model_refcount(c2) == 2); + + eina_model_event_callback_add + (c2, "deleted", _eina_test_model_cb_count, &count_del); + + eina_model_unref(c1); + eina_model_unref(c2); + } + + free(s1); + free(s2); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + + fail_unless(eina_model_refcount(cp) == 1); + eina_model_unref(cp); + + fail_unless(count_del == 2 + 10); + + eina_shutdown(); +} +END_TEST + +static Eina_Model * +eina_model_test_iterator_setup(unsigned int *count_del) +{ + Eina_Model *m; + int i; + + m = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(m != NULL); + + eina_model_event_callback_add + (m, "deleted", _eina_test_model_cb_count, count_del); + + for (i = 0; i < 5; i++) + { + Eina_Value val; + Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC); + fail_unless(c != NULL); + fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT)); + fail_unless(eina_value_set(&val, i)); + fail_unless(eina_model_property_set(c, "x", &val)); + + eina_model_event_callback_add + (c, "deleted", _eina_test_model_cb_count, count_del); + + fail_unless(eina_model_child_append(m, c) >= 0); + eina_model_unref(c); + eina_value_flush(&val); + } + + return m; +} + +START_TEST(eina_model_test_child_iterator) +{ + unsigned int count_del = 0; + Eina_Iterator *it; + Eina_Model *m, *c; + int i = 0; + + eina_init(); + + m = eina_model_test_iterator_setup(&count_del); + + it = eina_model_child_iterator_get(m); + fail_unless(it != NULL); + EINA_ITERATOR_FOREACH(it, c) + { + Eina_Value tmp; + int x; + + fail_unless(eina_model_refcount(c) == 2); + fail_unless(eina_model_property_get(c, "x", &tmp)); + fail_unless(eina_value_get(&tmp, &x)); + fail_unless(x == i); + + eina_model_unref(c); + i++; + } + fail_unless(i == 5); + eina_iterator_free(it); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + fail_unless(count_del == 6); + eina_shutdown(); +} +END_TEST + +START_TEST(eina_model_test_child_reversed_iterator) +{ + unsigned int count_del = 0; + Eina_Iterator *it; + Eina_Model *m, *c; + int i = 4; + + eina_init(); + + m = eina_model_test_iterator_setup(&count_del); + + it = eina_model_child_reversed_iterator_get(m); + fail_unless(it != NULL); + EINA_ITERATOR_FOREACH(it, c) + { + Eina_Value tmp; + int x; + + fail_unless(eina_model_refcount(c) == 2); + fail_unless(eina_model_property_get(c, "x", &tmp)); + fail_unless(eina_value_get(&tmp, &x)); + fail_unless(x == i); + + eina_model_unref(c); + i--; + } + fail_unless(i == -1); + eina_iterator_free(it); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + fail_unless(count_del == 6); + eina_shutdown(); +} +END_TEST + +START_TEST(eina_model_test_child_sorted_iterator) +{ + unsigned int count_del = 0; + Eina_Iterator *it; + Eina_Model *m, *c; + int i = 4; + + eina_init(); + + m = eina_model_test_iterator_setup(&count_del); + + it = eina_model_child_sorted_iterator_get + (m, EINA_COMPARE_CB(eina_model_test_children_reverse_cmp)); + fail_unless(it != NULL); + EINA_ITERATOR_FOREACH(it, c) + { + Eina_Value tmp; + int x; + + /* 3 because sort takes an extra reference for its temp array */ + fail_unless(eina_model_refcount(c) == 3); + fail_unless(eina_model_property_get(c, "x", &tmp)); + fail_unless(eina_value_get(&tmp, &x)); + fail_unless(x == i); + + eina_model_unref(c); + i--; + } + fail_unless(i == -1); + eina_iterator_free(it); + + it = eina_model_child_sorted_iterator_get + (m, EINA_COMPARE_CB(eina_model_compare)); + fail_unless(it != NULL); + i = 0; + EINA_ITERATOR_FOREACH(it, c) + { + Eina_Value tmp; + int x; + + /* 3 because sort takes an extra reference for its temp array */ + fail_unless(eina_model_refcount(c) == 3); + fail_unless(eina_model_property_get(c, "x", &tmp)); + fail_unless(eina_value_get(&tmp, &x)); + fail_unless(x == i); + + eina_model_unref(c); + i++; + } + fail_unless(i == 5); + eina_iterator_free(it); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + fail_unless(count_del == 6); + eina_shutdown(); +} +END_TEST + +static Eina_Bool +eina_model_test_filter_event(const void *m, void *c, void *fdata) +{ + Eina_Value tmp; + int x; + fail_unless(m == fdata); + fail_unless(eina_model_property_get(c, "x", &tmp)); + fail_unless(eina_value_get(&tmp, &x)); + eina_value_flush(&tmp); + return x % 2 == 0; +} + +START_TEST(eina_model_test_child_filtered_iterator) +{ + unsigned int count_del = 0; + Eina_Iterator *it; + Eina_Model *m; + int i = 0, idx; + + eina_init(); + + m = eina_model_test_iterator_setup(&count_del); + + it = eina_model_child_filtered_iterator_get + (m, eina_model_test_filter_event, m); + fail_unless(it != NULL); + EINA_ITERATOR_FOREACH(it, idx) + { + Eina_Model *c; + Eina_Value tmp; + int x; + + fail_unless(idx % 2 == 0); + fail_unless(idx == i); + + c = eina_model_child_get(m, idx); + fail_unless(c != NULL); + fail_unless(eina_model_refcount(c) == 2); + fail_unless(eina_model_property_get(c, "x", &tmp)); + fail_unless(eina_value_get(&tmp, &x)); + fail_unless(x == i); + + eina_model_unref(c); + i += 2; + } + fail_unless(i == 6); + eina_iterator_free(it); + + fail_unless(eina_model_refcount(m) == 1); + eina_model_unref(m); + fail_unless(count_del == 6); + eina_shutdown(); +} +END_TEST + +void +eina_test_model(TCase *tc) +{ + tcase_add_test(tc, eina_model_test_properties); + tcase_add_test(tc, eina_model_test_children); + tcase_add_test(tc, eina_model_test_copy); + tcase_add_test(tc, eina_model_test_deep_copy); + tcase_add_test(tc, eina_model_test_child_iterator); + tcase_add_test(tc, eina_model_test_child_reversed_iterator); + tcase_add_test(tc, eina_model_test_child_sorted_iterator); + tcase_add_test(tc, eina_model_test_child_filtered_iterator); +}