efl/legacy/eina/src/lib/eina_model.c

4849 lines
147 KiB
C

/* 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=" FMT_UCHAR,
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=" FMT_UCHAR,
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=" FMT_UCHAR,
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=" FMT_UCHAR,
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;
el->deleted = EINA_FALSE;
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);
}