--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EINA_MODEL_H_
+#define EINA_MODEL_H_
+
+#include "eina_types.h"
+#include "eina_value.h"
+#include <stdarg.h>
+
+/**
+ * @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
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#elif defined __GNUC__
+# define alloca __builtin_alloca
+#elif defined _AIX
+# define alloca __alloca
+#elif defined _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+#else
+# include <stddef.h>
+# 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);
+}