welcome eina_model.
authorbarbieri <barbieri>
Sat, 21 Jan 2012 01:30:09 +0000 (01:30 +0000)
committerbarbieri <barbieri@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Sat, 21 Jan 2012 01:30:09 +0000 (01:30 +0000)
This is a simple to use generic hierarchical data access. It includes
properties, children, reference counting and notifications (callbacks).

It can be understood in the same lines as GObject or PyObject, but I
believe the best usage is just to provide data models, not generic
Object Oriented Programming in C.

It misses most of documentation, although the important bits are
there. Need help here :-/

git-svn-id: http://svn.enlightenment.org/svn/e/trunk/eina@67405 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

12 files changed:
ChangeLog
NEWS
src/include/Eina.h
src/include/eina_model.h [new file with mode: 0644]
src/lib/Makefile.am
src/lib/eina_main.c
src/lib/eina_model.c [new file with mode: 0644]
src/lib/eina_private.h
src/tests/Makefile.am
src/tests/eina_suite.c
src/tests/eina_suite.h
src/tests/eina_test_model.c [new file with mode: 0644]

index dfecb61..19bfb3f 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 2012-01-19  Shinwoo Kim
 
        * Fix compilation of eina_semaphore_lock() (Windows port)
+
+2012-01-20  Gustavo Barbieri
+
+       * Add eina_model data type (generic hierarchy data access).
diff --git a/NEWS b/NEWS
index 8669aa7..328f974 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Additions:
     * Deprecated eina_array_count_get(), use eina_array_count() instead.
     * Added eina_inarray data type
     * Added eina_value data type (generic value storage)
+    * Added eina_model data type (generic hierarchy data access)
 
 Eina 1.1.0
 
index 757fb3b..f535822 100644 (file)
@@ -196,6 +196,7 @@ extern "C" {
 #include "eina_mmap.h"
 #include "eina_xattr.h"
 #include "eina_value.h"
+#include "eina_model.h"
 
 #ifdef __cplusplus
 }
diff --git a/src/include/eina_model.h b/src/include/eina_model.h
new file mode 100644 (file)
index 0000000..958b31c
--- /dev/null
@@ -0,0 +1,887 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2012 ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <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
index d8c9d20..e2ac704 100644 (file)
@@ -51,7 +51,8 @@ eina_unicode.c \
 eina_ustrbuf.c \
 eina_ustringshare.c \
 eina_value.c \
-eina_xattr.c
+eina_xattr.c \
+eina_model.c
 
 # Will be back for developper after 1.1
 # eina_object.c
index 79f8a36..f233929 100644 (file)
@@ -69,6 +69,7 @@
 #include "eina_inlist.h"
 #include "eina_inarray.h"
 #include "eina_value.h"
+#include "eina_model.h"
 
 /*============================================================================*
 *                                  Local                                     *
@@ -153,6 +154,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
    S(file);
    S(prefix);
    S(value);
+   S(model);
 #undef S
 
 struct eina_desc_setup
@@ -189,7 +191,8 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
    S(simple_xml),
    S(file),
    S(prefix),
-   S(value)
+   S(value),
+   S(model)
 #undef S
 };
 static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) /
diff --git a/src/lib/eina_model.c b/src/lib/eina_model.c
new file mode 100644 (file)
index 0000000..02ffd4c
--- /dev/null
@@ -0,0 +1,4847 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2012 ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <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);
+}
index e31ab47..49df565 100644 (file)
@@ -95,6 +95,8 @@
 #define EINA_MAGIC_INARRAY_ITERATOR 0x98761271
 #define EINA_MAGIC_INARRAY_ACCESSOR 0x98761272
 
+#define EINA_MAGIC_MODEL 0x98761280
+
 #define EINA_MAGIC_CLASS 0x9877CB30
 
 /* undef the following, we want out version */
index 7e05ba9..cf500f5 100644 (file)
@@ -66,7 +66,8 @@ eina_test_strbuf.c    \
 eina_test_str.c                \
 eina_test_quadtree.c   \
 eina_test_simple_xml_parser.c \
-eina_test_value.c
+eina_test_value.c \
+eina_test_model.c
 
 eina_suite_LDADD = @CHECK_LIBS@ $(top_builddir)/src/lib/libeina.la -lm
 
index 648a717..2014e6c 100644 (file)
@@ -68,6 +68,7 @@ static const Eina_Test_Case etc[] = {
    { "Sched", eina_test_sched },
    { "Simple Xml Parser", eina_test_simple_xml_parser},
    { "Value", eina_test_value },
+   { "Model", eina_test_model },
    { NULL, NULL }
 };
 
index 6eaaec7..d399298 100644 (file)
@@ -56,5 +56,6 @@ void eina_test_fp(TCase *tc);
 void eina_test_sched(TCase *tc);
 void eina_test_simple_xml_parser(TCase *tc);
 void eina_test_value(TCase *tc);
+void eina_test_model(TCase *tc);
 
 #endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina_test_model.c b/src/tests/eina_test_model.c
new file mode 100644 (file)
index 0000000..d6a8483
--- /dev/null
@@ -0,0 +1,717 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2012 ProFUSION embedded systems
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "eina_suite.h"
+#include "Eina.h"
+
+static void
+_eina_test_model_check_safety_null(const Eina_Log_Domain *d, Eina_Log_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args)
+{
+   Eina_Bool *ck = data;
+
+   if ((level == EINA_LOG_LEVEL_ERR) && (strcmp(fmt, "%s") == 0))
+     {
+        const char *str;
+        va_list cp_args;
+
+        va_copy(cp_args, args);
+        str = va_arg(cp_args, const char *);
+        va_end(cp_args);
+        if (eina_str_has_prefix(str, "safety check failed: ") &&
+            eina_str_has_suffix(str, " == NULL"))
+          {
+             *ck = EINA_TRUE;
+             return;
+          }
+     }
+   *ck = EINA_FALSE;
+   eina_log_print_cb_stderr(d, level, file, fnc, line, fmt, NULL, args);
+}
+
+static void
+_eina_test_model_cb_count(void *data, Eina_Model *model __UNUSED__, const Eina_Model_Event_Description *desc __UNUSED__, void *event_info __UNUSED__)
+{
+   unsigned *count = data;
+   (*count)++;
+   //printf("%p %s\n", model, desc->name);
+}
+
+START_TEST(eina_model_test_properties)
+{
+   unsigned int count_del = 0, count_pset = 0, count_pdel = 0;
+   Eina_Model *m;
+   Eina_Value inv, outv;
+   int i;
+   char *s;
+   Eina_List *lst;
+   Eina_Bool ck;
+
+   eina_init();
+
+   m = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+   fail_unless(m != NULL);
+
+   eina_model_event_callback_add
+     (m, "deleted", _eina_test_model_cb_count, &count_del);
+   eina_model_event_callback_add
+     (m, "property,set", _eina_test_model_cb_count, &count_pset);
+   eina_model_event_callback_add
+     (m, "property,deleted", _eina_test_model_cb_count, &count_pdel);
+
+   fail_unless(eina_value_setup(&inv, EINA_VALUE_TYPE_INT));
+   fail_unless(eina_value_set(&inv, 1234));
+   fail_unless(eina_value_get(&inv, &i));
+   fail_unless(i == 1234);
+
+   fail_unless(eina_model_property_set(m, "abc", &inv));
+
+   fail_unless(eina_value_set(&inv, 5678));
+   fail_unless(eina_model_property_set(m, "xyz", &inv));
+
+   fail_unless(eina_value_set(&inv, 171));
+   fail_unless(eina_model_property_set(m, "value", &inv));
+
+   lst = eina_model_properties_names_list_get(m);
+   fail_unless(eina_list_count(lst) == 3);
+
+   lst = eina_list_sort(lst, 0, EINA_COMPARE_CB(strcmp));
+   fail_unless(strcmp("abc", eina_list_nth(lst, 0)) == 0);
+   fail_unless(strcmp("value", eina_list_nth(lst, 1)) == 0);
+   fail_unless(strcmp("xyz", eina_list_nth(lst, 2)) == 0);
+
+   eina_model_properties_names_list_free(lst);
+
+   fail_unless(eina_model_property_get(m, "abc", &outv));
+   fail_unless(eina_value_get(&outv, &i));
+   fail_unless(i == 1234);
+   eina_value_flush(&outv);
+
+   fail_unless(eina_model_property_get(m, "xyz", &outv));
+   fail_unless(eina_value_get(&outv, &i));
+   fail_unless(i == 5678);
+   eina_value_flush(&outv);
+
+   fail_unless(eina_model_property_get(m, "value", &outv));
+   fail_unless(eina_value_get(&outv, &i));
+   fail_unless(i == 171);
+   eina_value_flush(&outv);
+
+   fail_unless(eina_value_set(&inv, 666));
+   fail_unless(eina_model_property_set(m, "value", &inv));
+   fail_unless(eina_model_property_get(m, "value", &outv));
+   fail_unless(eina_value_get(&outv, &i));
+   fail_unless(i == 666);
+
+   eina_value_flush(&outv);
+   eina_value_flush(&inv);
+
+   fail_unless(eina_value_setup(&inv, EINA_VALUE_TYPE_STRING));
+   fail_unless(eina_value_set(&inv, "Hello world!"));
+   fail_unless(eina_model_property_set(m, "string", &inv));
+
+   fail_unless(eina_model_property_get(m, "string", &outv));
+   fail_unless(eina_value_get(&outv, &s));
+   fail_unless(s != NULL);
+   fail_unless(strcmp(s, "Hello world!") == 0);
+
+   eina_value_flush(&outv);
+   eina_value_flush(&inv);
+
+   fail_unless(eina_value_setup(&inv, EINA_VALUE_TYPE_STRINGSHARE));
+   fail_unless(eina_value_set(&inv, "Hello world-STRINGSHARED!"));
+   fail_unless(eina_model_property_set(m, "stringshare", &inv));
+   /* set twice to see if references drop to zero before new add, shouldn't */
+   fail_unless(eina_model_property_set(m, "stringshare", &inv));
+
+   fail_unless(eina_model_property_get(m, "stringshare", &outv));
+   fail_unless(eina_value_get(&outv, &s));
+   fail_unless(s != NULL);
+   fail_unless(strcmp(s, "Hello world-STRINGSHARED!") == 0);
+
+   eina_value_flush(&outv);
+   eina_value_flush(&inv);
+
+   s = eina_model_to_string(m);
+   fail_unless(s != NULL);
+   fail_unless(strcmp(s, "Eina_Model_Type_Generic({abc: 1234, string: Hello world!, stringshare: Hello world-STRINGSHARED!, value: 666, xyz: 5678}, [])") == 0);
+   free(s);
+
+   fail_unless(eina_model_property_del(m, "value"));
+
+   /* negative test (check safety was displayed by using print_cb) */
+   eina_log_print_cb_set(_eina_test_model_check_safety_null, &ck);
+
+   ck = EINA_FALSE;
+   fail_if(eina_model_property_get(m, "non-existent", &outv));
+   fail_unless(ck == EINA_TRUE);
+
+   ck = EINA_FALSE;
+   fail_if(eina_model_property_get(m, NULL, &outv));
+   fail_unless(ck == EINA_TRUE);
+
+   ck = EINA_FALSE;
+   fail_if(eina_model_property_del(m, "value"));
+   fail_unless(ck == EINA_TRUE);
+
+   /* revert print_cb to default */
+   eina_log_print_cb_set(eina_log_print_cb_stderr, NULL);
+
+   fail_unless(eina_model_refcount(m) == 1);
+
+   eina_model_unref(m);
+   fail_unless(count_del == 1);
+   fail_unless(count_pset == 7);
+   fail_unless(count_pdel == 1);
+   eina_shutdown();
+}
+END_TEST
+
+static int
+eina_model_test_children_reverse_cmp(const Eina_Model *a, const Eina_Model *b)
+{
+   return - eina_model_compare(a, b);
+}
+
+START_TEST(eina_model_test_children)
+{
+   unsigned int count_del = 0, count_cset = 0, count_cins = 0, count_cdel = 0;
+   Eina_Model *m, *c;
+   char *s;
+   int i;
+
+   eina_init();
+
+   m = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+   fail_unless(m != NULL);
+
+   eina_model_event_callback_add
+     (m, "deleted", _eina_test_model_cb_count, &count_del);
+   eina_model_event_callback_add
+     (m, "child,set", _eina_test_model_cb_count, &count_cset);
+   eina_model_event_callback_add
+     (m, "child,inserted", _eina_test_model_cb_count, &count_cins);
+   eina_model_event_callback_add
+     (m, "child,deleted", _eina_test_model_cb_count, &count_cdel);
+
+   for (i = 0; i < 10; i++)
+     {
+        Eina_Value val;
+
+        c = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+        fail_unless(c != NULL);
+
+        eina_model_event_callback_add
+          (c, "deleted", _eina_test_model_cb_count, &count_del);
+        eina_model_event_callback_add
+          (c, "child,set", _eina_test_model_cb_count, &count_cset);
+        eina_model_event_callback_add
+          (c, "child,inserted", _eina_test_model_cb_count, &count_cins);
+        eina_model_event_callback_add
+          (c, "child,deleted", _eina_test_model_cb_count, &count_cdel);
+
+        fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT));
+        fail_unless(eina_value_set(&val, i));
+        fail_unless(eina_model_property_set(c, "value", &val));
+
+        fail_unless(eina_model_child_append(m, c) >= 0);
+        fail_unless(eina_model_refcount(c) == 2);
+
+        eina_value_flush(&val);
+        eina_model_unref(c);
+     }
+
+   fail_unless(eina_model_child_count(m) == 10);
+
+   for (i = 0; i < 10; i++)
+     {
+        Eina_Value val;
+        int x;
+
+        c = eina_model_child_get(m, i);
+        fail_unless(c != NULL);
+        fail_unless(eina_model_refcount(c) == 2);
+
+        fail_unless(eina_model_property_get(c, "value", &val));
+        fail_unless(eina_value_get(&val, &x));
+        fail_unless(x == i);
+
+        eina_value_flush(&val);
+        eina_model_unref(c);
+     }
+
+   eina_model_child_sort(m, EINA_COMPARE_CB(eina_model_test_children_reverse_cmp));
+
+   for (i = 0; i < 10; i++)
+     {
+        Eina_Value val;
+        int x;
+
+        c = eina_model_child_get(m, i);
+        fail_unless(c != NULL);
+        fail_unless(eina_model_refcount(c) == 2);
+
+        fail_unless(eina_model_property_get(c, "value", &val));
+        fail_unless(eina_value_get(&val, &x));
+        fail_unless(x == 10 - i - 1);
+
+        eina_value_flush(&val);
+        eina_model_unref(c);
+     }
+
+   eina_model_child_sort(m, EINA_COMPARE_CB(eina_model_compare));
+
+   s = eina_model_to_string(m);
+   fail_unless(s != NULL);
+   fail_unless(strcmp(s, "Eina_Model_Type_Generic({}, [Eina_Model_Type_Generic({value: 0}, []), Eina_Model_Type_Generic({value: 1}, []), Eina_Model_Type_Generic({value: 2}, []), Eina_Model_Type_Generic({value: 3}, []), Eina_Model_Type_Generic({value: 4}, []), Eina_Model_Type_Generic({value: 5}, []), Eina_Model_Type_Generic({value: 6}, []), Eina_Model_Type_Generic({value: 7}, []), Eina_Model_Type_Generic({value: 8}, []), Eina_Model_Type_Generic({value: 9}, [])])") == 0);
+   free(s);
+
+   c = eina_model_child_get(m, 0);
+   eina_model_child_set(m, 1, c);
+   eina_model_unref(c);
+
+   eina_model_child_del(m, 0);
+   eina_model_child_del(m, 8);
+
+   s = eina_model_to_string(m);
+   fail_unless(s != NULL);
+   fail_unless(strcmp(s, "Eina_Model_Type_Generic({}, [Eina_Model_Type_Generic({value: 0}, []), Eina_Model_Type_Generic({value: 2}, []), Eina_Model_Type_Generic({value: 3}, []), Eina_Model_Type_Generic({value: 4}, []), Eina_Model_Type_Generic({value: 5}, []), Eina_Model_Type_Generic({value: 6}, []), Eina_Model_Type_Generic({value: 7}, []), Eina_Model_Type_Generic({value: 8}, [])])") == 0);
+   free(s);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+
+   fail_unless(count_del == 11);
+   fail_unless(count_cins == 10);
+   fail_unless(count_cset == 1);
+   fail_unless(count_cdel == 2);
+
+   eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_model_test_copy)
+{
+   unsigned int count_del = 0;
+   Eina_Model *m, *cp;
+   char *s1, *s2;
+   int i;
+
+   eina_init();
+
+   m = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+   fail_unless(m != NULL);
+
+   eina_model_event_callback_add
+     (m, "deleted", _eina_test_model_cb_count, &count_del);
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Value val;
+        char name[2] = {'a'+ i, 0};
+        fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT));
+        fail_unless(eina_value_set(&val, i));
+        fail_unless(eina_model_property_set(m, name, &val));
+        eina_value_flush(&val);
+     }
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Value val;
+        Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+        fail_unless(c != NULL);
+        fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT));
+        fail_unless(eina_value_set(&val, i));
+        fail_unless(eina_model_property_set(c, "x", &val));
+
+        eina_model_event_callback_add
+          (c, "deleted", _eina_test_model_cb_count, &count_del);
+
+        fail_unless(eina_model_child_append(m, c) >= 0);
+        eina_model_unref(c);
+        eina_value_flush(&val);
+     }
+
+   s1 = eina_model_to_string(m);
+   fail_unless(s1 != NULL);
+   fail_unless(strcmp(s1, "Eina_Model_Type_Generic({a: 0, b: 1, c: 2, d: 3, e: 4}, [Eina_Model_Type_Generic({x: 0}, []), Eina_Model_Type_Generic({x: 1}, []), Eina_Model_Type_Generic({x: 2}, []), Eina_Model_Type_Generic({x: 3}, []), Eina_Model_Type_Generic({x: 4}, [])])") == 0);
+
+   cp = eina_model_copy(m);
+   fail_unless(cp != NULL);
+   fail_unless(cp != m);
+
+   eina_model_event_callback_add
+     (cp, "deleted", _eina_test_model_cb_count, &count_del);
+
+   s2 = eina_model_to_string(cp);
+   fail_unless(s2 != NULL);
+   fail_unless(strcmp(s1, s2) == 0);
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Model *c1 = eina_model_child_get(m, i);
+        Eina_Model *c2 = eina_model_child_get(cp, i);
+
+        fail_unless(c1 != NULL);
+        fail_unless(c1 == c2);
+        fail_unless(eina_model_refcount(c1) == 4);
+
+        eina_model_unref(c1);
+        eina_model_unref(c2);
+     }
+
+   free(s1);
+   free(s2);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+
+   fail_unless(eina_model_refcount(cp) == 1);
+   eina_model_unref(cp);
+
+   fail_unless(count_del == 2 + 5);
+
+   eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_model_test_deep_copy)
+{
+   unsigned int count_del = 0;
+   Eina_Model *m, *cp;
+   char *s1, *s2;
+   int i;
+
+   eina_init();
+
+   m = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+   fail_unless(m != NULL);
+
+   eina_model_event_callback_add
+     (m, "deleted", _eina_test_model_cb_count, &count_del);
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Value val;
+        char name[2] = {'a'+ i, 0};
+        fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT));
+        fail_unless(eina_value_set(&val, i));
+        fail_unless(eina_model_property_set(m, name, &val));
+        eina_value_flush(&val);
+     }
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Value val;
+        Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+        fail_unless(c != NULL);
+        fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT));
+        fail_unless(eina_value_set(&val, i));
+        fail_unless(eina_model_property_set(c, "x", &val));
+
+        eina_model_event_callback_add
+          (c, "deleted", _eina_test_model_cb_count, &count_del);
+
+        fail_unless(eina_model_child_append(m, c) >= 0);
+        eina_model_unref(c);
+        eina_value_flush(&val);
+     }
+
+   s1 = eina_model_to_string(m);
+   fail_unless(s1 != NULL);
+   fail_unless(strcmp(s1, "Eina_Model_Type_Generic({a: 0, b: 1, c: 2, d: 3, e: 4}, [Eina_Model_Type_Generic({x: 0}, []), Eina_Model_Type_Generic({x: 1}, []), Eina_Model_Type_Generic({x: 2}, []), Eina_Model_Type_Generic({x: 3}, []), Eina_Model_Type_Generic({x: 4}, [])])") == 0);
+
+   cp = eina_model_deep_copy(m);
+   fail_unless(cp != NULL);
+   fail_unless(cp != m);
+
+   eina_model_event_callback_add
+     (cp, "deleted", _eina_test_model_cb_count, &count_del);
+
+   s2 = eina_model_to_string(cp);
+   fail_unless(s2 != NULL);
+   fail_unless(strcmp(s1, s2) == 0);
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Model *c1 = eina_model_child_get(m, i);
+        Eina_Model *c2 = eina_model_child_get(cp, i);
+
+        fail_unless(c1 != NULL);
+        fail_unless(c1 != c2);
+        fail_unless(eina_model_refcount(c1) == 2);
+        fail_unless(eina_model_refcount(c2) == 2);
+
+        eina_model_event_callback_add
+          (c2, "deleted", _eina_test_model_cb_count, &count_del);
+
+        eina_model_unref(c1);
+        eina_model_unref(c2);
+     }
+
+   free(s1);
+   free(s2);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+
+   fail_unless(eina_model_refcount(cp) == 1);
+   eina_model_unref(cp);
+
+   fail_unless(count_del == 2 + 10);
+
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Model *
+eina_model_test_iterator_setup(unsigned int *count_del)
+{
+   Eina_Model *m;
+   int i;
+
+   m = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+   fail_unless(m != NULL);
+
+   eina_model_event_callback_add
+     (m, "deleted", _eina_test_model_cb_count, count_del);
+
+   for (i = 0; i < 5; i++)
+     {
+        Eina_Value val;
+        Eina_Model *c = eina_model_new(EINA_MODEL_TYPE_GENERIC);
+        fail_unless(c != NULL);
+        fail_unless(eina_value_setup(&val, EINA_VALUE_TYPE_INT));
+        fail_unless(eina_value_set(&val, i));
+        fail_unless(eina_model_property_set(c, "x", &val));
+
+        eina_model_event_callback_add
+          (c, "deleted", _eina_test_model_cb_count, count_del);
+
+        fail_unless(eina_model_child_append(m, c) >= 0);
+        eina_model_unref(c);
+        eina_value_flush(&val);
+     }
+
+   return m;
+}
+
+START_TEST(eina_model_test_child_iterator)
+{
+   unsigned int count_del = 0;
+   Eina_Iterator *it;
+   Eina_Model *m, *c;
+   int i = 0;
+
+   eina_init();
+
+   m = eina_model_test_iterator_setup(&count_del);
+
+   it = eina_model_child_iterator_get(m);
+   fail_unless(it != NULL);
+   EINA_ITERATOR_FOREACH(it, c)
+     {
+        Eina_Value tmp;
+        int x;
+
+        fail_unless(eina_model_refcount(c) == 2);
+        fail_unless(eina_model_property_get(c, "x", &tmp));
+        fail_unless(eina_value_get(&tmp, &x));
+        fail_unless(x == i);
+
+        eina_model_unref(c);
+        i++;
+     }
+   fail_unless(i == 5);
+   eina_iterator_free(it);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+   fail_unless(count_del == 6);
+   eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_model_test_child_reversed_iterator)
+{
+   unsigned int count_del = 0;
+   Eina_Iterator *it;
+   Eina_Model *m, *c;
+   int i = 4;
+
+   eina_init();
+
+   m = eina_model_test_iterator_setup(&count_del);
+
+   it = eina_model_child_reversed_iterator_get(m);
+   fail_unless(it != NULL);
+   EINA_ITERATOR_FOREACH(it, c)
+     {
+        Eina_Value tmp;
+        int x;
+
+        fail_unless(eina_model_refcount(c) == 2);
+        fail_unless(eina_model_property_get(c, "x", &tmp));
+        fail_unless(eina_value_get(&tmp, &x));
+        fail_unless(x == i);
+
+        eina_model_unref(c);
+        i--;
+     }
+   fail_unless(i == -1);
+   eina_iterator_free(it);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+   fail_unless(count_del == 6);
+   eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_model_test_child_sorted_iterator)
+{
+   unsigned int count_del = 0;
+   Eina_Iterator *it;
+   Eina_Model *m, *c;
+   int i = 4;
+
+   eina_init();
+
+   m = eina_model_test_iterator_setup(&count_del);
+
+   it = eina_model_child_sorted_iterator_get
+     (m, EINA_COMPARE_CB(eina_model_test_children_reverse_cmp));
+   fail_unless(it != NULL);
+   EINA_ITERATOR_FOREACH(it, c)
+     {
+        Eina_Value tmp;
+        int x;
+
+        /* 3 because sort takes an extra reference for its temp array */
+        fail_unless(eina_model_refcount(c) == 3);
+        fail_unless(eina_model_property_get(c, "x", &tmp));
+        fail_unless(eina_value_get(&tmp, &x));
+        fail_unless(x == i);
+
+        eina_model_unref(c);
+        i--;
+     }
+   fail_unless(i == -1);
+   eina_iterator_free(it);
+
+   it = eina_model_child_sorted_iterator_get
+     (m, EINA_COMPARE_CB(eina_model_compare));
+   fail_unless(it != NULL);
+   i = 0;
+   EINA_ITERATOR_FOREACH(it, c)
+     {
+        Eina_Value tmp;
+        int x;
+
+        /* 3 because sort takes an extra reference for its temp array */
+        fail_unless(eina_model_refcount(c) == 3);
+        fail_unless(eina_model_property_get(c, "x", &tmp));
+        fail_unless(eina_value_get(&tmp, &x));
+        fail_unless(x == i);
+
+        eina_model_unref(c);
+        i++;
+     }
+   fail_unless(i == 5);
+   eina_iterator_free(it);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+   fail_unless(count_del == 6);
+   eina_shutdown();
+}
+END_TEST
+
+static Eina_Bool
+eina_model_test_filter_event(const void *m, void *c, void *fdata)
+{
+   Eina_Value tmp;
+   int x;
+   fail_unless(m == fdata);
+   fail_unless(eina_model_property_get(c, "x", &tmp));
+   fail_unless(eina_value_get(&tmp, &x));
+   eina_value_flush(&tmp);
+   return x % 2 == 0;
+}
+
+START_TEST(eina_model_test_child_filtered_iterator)
+{
+   unsigned int count_del = 0;
+   Eina_Iterator *it;
+   Eina_Model *m;
+   int i = 0, idx;
+
+   eina_init();
+
+   m = eina_model_test_iterator_setup(&count_del);
+
+   it = eina_model_child_filtered_iterator_get
+     (m, eina_model_test_filter_event, m);
+   fail_unless(it != NULL);
+   EINA_ITERATOR_FOREACH(it, idx)
+     {
+        Eina_Model *c;
+        Eina_Value tmp;
+        int x;
+
+        fail_unless(idx % 2 == 0);
+        fail_unless(idx == i);
+
+        c = eina_model_child_get(m, idx);
+        fail_unless(c != NULL);
+        fail_unless(eina_model_refcount(c) == 2);
+        fail_unless(eina_model_property_get(c, "x", &tmp));
+        fail_unless(eina_value_get(&tmp, &x));
+        fail_unless(x == i);
+
+        eina_model_unref(c);
+        i += 2;
+     }
+   fail_unless(i == 6);
+   eina_iterator_free(it);
+
+   fail_unless(eina_model_refcount(m) == 1);
+   eina_model_unref(m);
+   fail_unless(count_del == 6);
+   eina_shutdown();
+}
+END_TEST
+
+void
+eina_test_model(TCase *tc)
+{
+   tcase_add_test(tc, eina_model_test_properties);
+   tcase_add_test(tc, eina_model_test_children);
+   tcase_add_test(tc, eina_model_test_copy);
+   tcase_add_test(tc, eina_model_test_deep_copy);
+   tcase_add_test(tc, eina_model_test_child_iterator);
+   tcase_add_test(tc, eina_model_test_child_reversed_iterator);
+   tcase_add_test(tc, eina_model_test_child_sorted_iterator);
+   tcase_add_test(tc, eina_model_test_child_filtered_iterator);
+}