Eobj: Use xref.
[profile/ivi/eobj.git] / lib / eobj.c
1 #include <Eina.h>
2
3 #include "Eobj.h"
4 #include "eobj_private.h"
5
6 #include "config.h"
7
8 typedef int Eobj_Class_Id;
9
10 int _eobj_log_dom = -1;
11
12 static Eobj_Class **_eobj_classes;
13 static Eobj_Class_Id _eobj_classes_last_id;
14 static Eina_Bool _eobj_init_count = 0;
15
16 static void _eobj_callback_remove_all(Eobj *obj);
17 static void eobj_class_constructor(Eobj *obj, const Eobj_Class *klass);
18 static void eobj_class_destructor(Eobj *obj, const Eobj_Class *klass);
19 static void eobj_constructor_error_unset(Eobj *obj);
20
21 typedef struct _Eobj_Callback_Description Eobj_Callback_Description;
22
23 struct _Eobj {
24      EINA_MAGIC
25      Eobj *parent;
26      const Eobj_Class *klass;
27      void *data_blob;
28      int refcount;
29 #ifndef NDEBUG
30      Eina_Inlist *xrefs;
31 #endif
32
33      Eina_List *composite_objects;
34
35      Eina_Inlist *callbacks;
36      int walking_list;
37
38      Eina_Inlist *kls_itr;
39
40      Eina_Bool delete:1;
41      Eina_Bool construct_error:1;
42 };
43
44 /* Start of Dich */
45 /* Dich search, split to 0xff 0xff 0xffff */
46
47 #define DICH_CHAIN1_MASK (0xff)
48 #define DICH_CHAIN2_MASK (0xff)
49 #define DICH_CHAIN_LAST_MASK (0xffff)
50 #define DICH_CHAIN1_SIZE (DICH_CHAIN1_MASK + 1)
51 #define DICH_CHAIN2_SIZE (DICH_CHAIN2_MASK + 1)
52 #define DICH_CHAIN_LAST_SIZE (DICH_CHAIN_LAST_MASK + 1)
53 #define DICH_CHAIN1(x) (((x) >> 24) & DICH_CHAIN1_MASK)
54 #define DICH_CHAIN2(x) (((x) >> 16) & DICH_CHAIN2_MASK)
55 #define DICH_CHAIN_LAST(x) ((x) & DICH_CHAIN_LAST_MASK)
56
57 #define OP_CLASS_OFFSET 16
58 #define OP_CLASS_OFFSET_GET(x) (((x) >> OP_CLASS_OFFSET) & 0xffff)
59 #define OP_CLASS_GET(op) ({ \
60       Eobj_Class_Id tmp = OP_CLASS_OFFSET_GET(op); \
61       (Eobj_Class *) ((tmp <= _eobj_classes_last_id) && (tmp > 0)) ? \
62       (_eobj_classes[tmp - 1]) : NULL; \
63       })
64 #define OP_SUB_ID_GET(op) ((op) & 0xffff)
65
66 /* Structure of Eobj_Op is:
67  * 16bit: class
68  * 16bit: op.
69  */
70
71 typedef struct _Dich_Chain1 Dich_Chain1;
72
73 typedef struct
74 {
75    eobj_op_func_type func;
76 } op_type_funcs;
77
78 typedef struct
79 {
80    op_type_funcs *funcs;
81 } Dich_Chain2;
82
83 struct _Dich_Chain1
84 {
85    Dich_Chain2 *chain;
86 };
87
88 typedef struct
89 {
90      EINA_INLIST;
91      const Eobj_Class *klass;
92 } Eobj_Extension_Node;
93
94 struct _Eobj_Class
95 {
96    Eobj_Class_Id class_id;
97    const Eobj_Class *parent;
98    const Eobj_Class_Description *desc;
99    Dich_Chain1 chain[DICH_CHAIN1_SIZE];
100    Eina_Inlist *extensions;
101
102    const Eobj_Class **mro;
103
104    size_t data_offset; /* < Offset of the data within object data. */
105
106    Eina_Bool constructed : 1;
107 };
108
109 static inline eobj_op_func_type
110 dich_func_get(const Eobj_Class *klass, Eobj_Op op)
111 {
112    const Dich_Chain1 *chain1 = &klass->chain[DICH_CHAIN1(op)];
113    if (!chain1) return NULL;
114    if (!chain1->chain) return NULL;
115    Dich_Chain2 *chain2 = &chain1->chain[DICH_CHAIN2(op)];
116    if (!chain2) return NULL;
117    if (!chain2->funcs) return NULL;
118
119    /* num_ops is calculated from the class. */
120    const Eobj_Class *op_klass = OP_CLASS_GET(op);
121    if (!op_klass || (DICH_CHAIN_LAST(op) >= op_klass->desc->ops.count))
122       return NULL;
123
124    return chain2->funcs[DICH_CHAIN_LAST(op)].func;
125 }
126
127 static inline void
128 dich_func_set(Eobj_Class *klass, Eobj_Op op, eobj_op_func_type func)
129 {
130    const Eobj_Class *op_klass = OP_CLASS_GET(op);
131    size_t num_ops;
132
133    /* Verify op is valid. */
134    if (op_klass)
135      {
136         /* num_ops is calculated from the class. */
137         num_ops = op_klass->desc->ops.count;
138         if (DICH_CHAIN_LAST(op) >= num_ops)
139           {
140              ERR("OP %x is too big for the domain '%s', expected value < %x.",
141                    op, op_klass->desc->name, op_klass->desc->ops.count);
142           }
143      }
144    else
145      {
146         ERR("OP %x is from an illegal class.", op);
147         return;
148      }
149
150    Dich_Chain1 *chain1 = &klass->chain[DICH_CHAIN1(op)];
151    if (!chain1->chain)
152      {
153         klass->chain[DICH_CHAIN1(op)].chain =
154            chain1->chain =
155            calloc(DICH_CHAIN2_SIZE, sizeof(*(chain1->chain)));
156      }
157
158    Dich_Chain2 *chain2 = &chain1->chain[DICH_CHAIN2(op)];
159    if (!chain2->funcs)
160      {
161         chain2->funcs = chain1->chain[DICH_CHAIN2(op)].funcs =
162            calloc(num_ops, sizeof(*(chain2->funcs)));
163      }
164
165    chain2->funcs[DICH_CHAIN_LAST(op)].func = func;
166 }
167
168 static inline void
169 dich_func_clean_all(Eobj_Class *klass)
170 {
171    int i;
172    Dich_Chain1 *chain1 = klass->chain;
173
174    for (i = 0 ; i < DICH_CHAIN1_SIZE ; i++, chain1++)
175      {
176         int j;
177         Dich_Chain2 *chain2 = chain1->chain;
178
179         if (!chain2)
180            continue;
181
182         for (j = 0 ; j < DICH_CHAIN2_SIZE ; j++, chain2++)
183           {
184              free(chain2->funcs);
185           }
186         free(chain1->chain);
187         chain1->chain = NULL;
188      }
189 }
190
191 /* END OF DICH */
192
193 typedef struct
194 {
195    EINA_INLIST;
196    Eobj_Op op;
197    const Eobj_Class **kls_itr;
198 } Eobj_Kls_Itr_Node;
199
200 static inline Eina_Bool
201 _eobj_kls_itr_init(Eobj *obj, Eobj_Op op)
202 {
203    if (obj->kls_itr)
204      {
205         Eobj_Kls_Itr_Node *node =
206            EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
207         if (node->op == op)
208           {
209              return EINA_FALSE;
210           }
211      }
212
213
214      {
215         Eobj_Kls_Itr_Node *node = calloc(1, sizeof(*node));
216         node->op = op;
217         node->kls_itr = obj->klass->mro;
218         obj->kls_itr = eina_inlist_prepend(obj->kls_itr,
219               EINA_INLIST_GET(node));
220
221         return EINA_TRUE;
222      }
223 }
224
225 static inline void
226 _eobj_kls_itr_end(Eobj *obj, Eobj_Op op)
227 {
228    Eobj_Kls_Itr_Node *node =
229       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
230
231    if (node->op != op)
232       return;
233
234    obj->kls_itr = eina_inlist_remove(obj->kls_itr, obj->kls_itr);
235    free(node);
236 }
237
238 static inline const Eobj_Class *
239 _eobj_kls_itr_get(Eobj *obj)
240 {
241    Eobj_Kls_Itr_Node *node =
242       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
243
244    return (node) ? *(node->kls_itr) : NULL;
245 }
246
247 static inline const Eobj_Class *
248 _eobj_kls_itr_next(Eobj *obj)
249 {
250    Eobj_Kls_Itr_Node *node =
251       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
252    const Eobj_Class **kls_itr = node->kls_itr;
253    if (*kls_itr)
254      {
255         kls_itr++;
256         node->kls_itr = kls_itr;
257         return *kls_itr;
258      }
259    else
260      {
261         return NULL;
262      }
263 }
264
265 static inline Eina_Bool
266 _eobj_kls_itr_reached_end(const Eobj *obj)
267 {
268    Eobj_Kls_Itr_Node *node =
269       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
270    const Eobj_Class **kls_itr = node->kls_itr;
271    return !(*kls_itr && *(kls_itr + 1));
272 }
273
274 static const Eobj_Op_Description *
275 _eobj_op_id_desc_get(Eobj_Op op)
276 {
277    const Eobj_Class *klass = OP_CLASS_GET(op);
278    Eobj_Op sub_id = OP_SUB_ID_GET(op);
279
280    if (klass && (sub_id < klass->desc->ops.count))
281       return klass->desc->ops.descs + sub_id;
282
283    return NULL;
284 }
285
286 static Eina_Bool
287 _eobj_op_internal(Eobj *obj, Eobj_Op op, va_list *p_list)
288 {
289    const Eobj_Class *klass;
290    Eina_Bool ret = EINA_FALSE;
291    Eina_Bool _itr_init;
292
293    _itr_init = _eobj_kls_itr_init(obj, op);
294    klass = _eobj_kls_itr_get(obj);
295    while (klass)
296      {
297         eobj_op_func_type func = dich_func_get(klass, op);
298
299         if (func)
300           {
301              func(obj, eobj_data_get(obj, klass), p_list);
302              ret = EINA_TRUE;
303              goto end;
304           }
305
306         klass = _eobj_kls_itr_next(obj);
307      }
308
309    /* Try composite objects */
310      {
311         Eina_List *itr;
312         Eobj *emb_obj;
313         EINA_LIST_FOREACH(obj->composite_objects, itr, emb_obj)
314           {
315              if (_eobj_op_internal(emb_obj, op, p_list))
316                {
317                   ret = EINA_TRUE;
318                   goto end;
319                }
320           }
321      }
322
323 end:
324
325    if (_itr_init) _eobj_kls_itr_end(obj, op);
326    return ret;
327 }
328
329 static inline Eina_Bool
330 _eobj_ops_internal(Eobj *obj, va_list *p_list)
331 {
332    Eina_Bool ret = EINA_TRUE;
333    Eobj_Op op = 0;
334
335    op = va_arg(*p_list, Eobj_Op);
336    while (op)
337      {
338         if (!_eobj_op_internal(obj, op, p_list))
339           {
340              const Eobj_Op_Description *desc = _eobj_op_id_desc_get(op);
341              const char *_id_name = (desc) ? desc->name : NULL;
342              const Eobj_Class *op_klass = OP_CLASS_GET(op);
343              const char *_dom_name = (op_klass) ? op_klass->desc->name : NULL;
344              ERR("Can't find func for op %x ('%s' of domain '%s') for class '%s'. Aborting.",
345                    op, _id_name, _dom_name,
346                    obj->klass->desc->name);
347              ret = EINA_FALSE;
348              break;
349           }
350         op = va_arg(*p_list, Eobj_Op);
351      }
352
353    return ret;
354 }
355
356 EAPI Eina_Bool
357 eobj_do_internal(Eobj *obj, ...)
358 {
359    Eina_Bool ret;
360    va_list p_list;
361    eobj_ref(obj);
362    va_start(p_list, obj);
363    ret = _eobj_ops_internal(obj, &p_list);
364    va_end(p_list);
365    eobj_unref(obj);
366    return ret;
367 }
368
369 EAPI Eina_Bool
370 eobj_do_super(Eobj *obj, Eobj_Op op, ...)
371 {
372    const Eobj_Class *obj_klass;
373    Eina_Bool ret = EINA_TRUE;
374    va_list p_list;
375
376    va_start(p_list, op);
377
378    /* Advance the kls itr. */
379    obj_klass = _eobj_kls_itr_next(obj);
380    if (!_eobj_op_internal(obj, op, &p_list))
381      {
382         const Eobj_Op_Description *desc = _eobj_op_id_desc_get(op);
383         const char *_id_name = (desc) ? desc->name : NULL;
384         const Eobj_Class *op_klass = OP_CLASS_GET(op);
385         const char *_dom_name = (op_klass) ? op_klass->desc->name : NULL;
386         ERR("Can't find func for op %x ('%s' of domain '%s') for class '%s'. Aborting.",
387               op, _id_name, _dom_name,
388               (obj_klass) ? obj_klass->desc->name : NULL);
389         ret = EINA_FALSE;
390      }
391    va_end(p_list);
392
393    return ret;
394 }
395
396 EAPI const Eobj_Class *
397 eobj_class_get(const Eobj *obj)
398 {
399    return obj->klass;
400 }
401
402 EAPI const char *
403 eobj_class_name_get(const Eobj_Class *klass)
404 {
405    return klass->desc->name;
406 }
407
408 static void
409 _eobj_class_base_op_init(Eobj_Class *klass)
410 {
411    const Eobj_Class_Description *desc = klass->desc;
412    if (!desc || !desc->ops.base_op_id)
413       return;
414
415    /* FIXME: Depends on values defined above! */
416    *(desc->ops.base_op_id) = klass->class_id << OP_CLASS_OFFSET;
417 }
418
419 static Eina_List *
420 _eobj_class_mro_add(Eina_List *mro, const Eobj_Class *klass)
421 {
422    if (!klass)
423       return mro;
424
425    mro = eina_list_append(mro, klass);
426
427      {
428         Eobj_Extension_Node *extn;
429         EINA_INLIST_FOREACH(klass->extensions, extn)
430           {
431              mro = _eobj_class_mro_add(mro, extn->klass);
432           }
433      }
434
435    mro = _eobj_class_mro_add(mro, klass->parent);
436
437    return mro;
438 }
439
440 static void
441 _eobj_class_mro_init(Eobj_Class *klass)
442 {
443    Eina_List *mro = NULL;
444
445    DBG("Started creating MRO for class '%s'", klass->desc->name);
446    mro = _eobj_class_mro_add(mro, klass);
447
448      {
449         Eina_List *itr1, *itr2, *itr2n;
450
451         itr1 = eina_list_last(mro);
452         while (itr1)
453           {
454              itr2 = eina_list_prev(itr1);
455
456              while (itr2)
457                {
458                   itr2n = eina_list_prev(itr2);
459
460                   if (eina_list_data_get(itr1) == eina_list_data_get(itr2))
461                     {
462                        mro = eina_list_remove_list(mro, itr2);
463                     }
464
465                   itr2 = itr2n;
466                }
467
468              itr1 = eina_list_prev(itr1);
469           }
470      }
471
472    /* Copy the mro and free the list. */
473      {
474         const Eobj_Class *kls_itr;
475         const Eobj_Class **mro_itr;
476         klass->mro = calloc(sizeof(*klass->mro), eina_list_count(mro) + 1);
477
478         mro_itr = klass->mro;
479
480         EINA_LIST_FREE(mro, kls_itr)
481           {
482              *(mro_itr++) = kls_itr;
483
484              DBG("Added '%s' to MRO", kls_itr->desc->name);
485           }
486         *(mro_itr) = NULL;
487      }
488
489    DBG("Finished creating MRO for class '%s'", klass->desc->name);
490 }
491
492 static void
493 _eobj_class_constructor(Eobj_Class *klass)
494 {
495    if (klass->constructed)
496       return;
497
498    klass->constructed = EINA_TRUE;
499
500    if (klass->desc->class_constructor)
501       klass->desc->class_constructor(klass);
502
503    _eobj_class_mro_init(klass);
504 }
505
506 EAPI void
507 eobj_class_funcs_set(Eobj_Class *klass, const Eobj_Op_Func_Description *func_descs)
508 {
509    const Eobj_Op_Func_Description *itr;
510    itr = func_descs;
511    if (itr)
512      {
513         for ( ; itr->op != 0 ; itr++)
514           {
515              dich_func_set(klass, itr->op, itr->func);
516           }
517      }
518 }
519
520 void
521 eobj_class_free(Eobj_Class *klass)
522 {
523    if (klass->constructed)
524      {
525         if (klass->desc->class_destructor)
526            klass->desc->class_destructor(klass);
527
528         dich_func_clean_all(klass);
529      }
530
531      {
532         Eina_Inlist *itrn;
533         Eobj_Extension_Node *extn;
534         EINA_INLIST_FOREACH_SAFE(klass->extensions, itrn, extn)
535           {
536              free(extn);
537           }
538      }
539
540    free(klass->mro);
541
542    free(klass);
543 }
544
545 /* DEVCHECK */
546 static Eina_Bool
547 _eobj_class_check_op_descs(const Eobj_Class *klass)
548 {
549    const Eobj_Class_Description *desc = klass->desc;
550    const Eobj_Op_Description *itr;
551    size_t i;
552
553    if (desc->ops.count > 0)
554      {
555         if (!desc->ops.base_op_id)
556           {
557              ERR("Class '%s' has a non-zero ops count, but base_id is NULL.",
558                    desc->name);
559              return EINA_FALSE;
560           }
561
562         if (!desc->ops.descs)
563           {
564              ERR("Class '%s' has a non-zero ops count, but there are no descs.",
565                    desc->name);
566              return EINA_FALSE;
567           }
568      }
569
570    itr = desc->ops.descs;
571    for (i = 0 ; i < desc->ops.count ; i++, itr++)
572      {
573         if (itr->sub_op != i)
574           {
575              if (itr->name)
576                {
577                   ERR("Wrong order in Ops description for class '%s'. Expected %d and got %d", desc->name, i, itr->sub_op);
578                }
579              else
580                {
581                   ERR("Found too few Ops description for class '%s'. Expected %d descriptions, but found %d.", desc->name, desc->ops.count, i);
582                }
583              return EINA_FALSE;
584           }
585      }
586
587    if (itr && itr->name)
588      {
589         ERR("Found extra Ops description for class '%s'. Expected %d descriptions, but found more.", desc->name, desc->ops.count);
590         return EINA_FALSE;
591      }
592
593    return EINA_TRUE;
594 }
595
596 EAPI const Eobj_Class *
597 eobj_class_new(const Eobj_Class_Description *desc, const Eobj_Class *parent, ...)
598 {
599    Eobj_Class *klass;
600    va_list p_list;
601
602    va_start(p_list, parent);
603
604 #define _CLS_NEW_CHECK(x) \
605    do \
606      { \
607         if (!x) \
608           { \
609              ERR("%s must not be NULL! Aborting.", #x); \
610              return NULL; \
611           } \
612      } \
613    while(0)
614
615    _CLS_NEW_CHECK(desc);
616    _CLS_NEW_CHECK(desc->name);
617
618    klass = calloc(1, sizeof(Eobj_Class));
619    klass->parent = parent;
620
621    /* Handle class extensions */
622      {
623         Eobj_Class *extn = NULL;
624
625         extn = va_arg(p_list, Eobj_Class *);
626         while (extn)
627           {
628              switch (extn->desc->type)
629                {
630                 case EOBJ_CLASS_TYPE_REGULAR:
631                 case EOBJ_CLASS_TYPE_REGULAR_NO_INSTANT:
632                    /* Use it like an interface. */
633                 case EOBJ_CLASS_TYPE_INTERFACE:
634                    break;
635                 case EOBJ_CLASS_TYPE_MIXIN:
636                      {
637                         Eobj_Extension_Node *node = calloc(1, sizeof(*node));
638                         node->klass = extn;
639                         klass->extensions =
640                            eina_inlist_append(klass->extensions,
641                                  EINA_INLIST_GET(node));
642                      }
643                    break;
644                }
645
646              extn = va_arg(p_list, Eobj_Class *);
647           }
648      }
649
650    klass->desc = desc;
651
652    /* Handle the inheritance */
653    if (klass->parent)
654      {
655         /* Verify the inheritance is allowed. */
656         switch (klass->desc->type)
657           {
658            case EOBJ_CLASS_TYPE_REGULAR:
659            case EOBJ_CLASS_TYPE_REGULAR_NO_INSTANT:
660               if ((klass->parent->desc->type != EOBJ_CLASS_TYPE_REGULAR) &&
661                     (klass->parent->desc->type != EOBJ_CLASS_TYPE_REGULAR_NO_INSTANT))
662                 {
663                    ERR("Regular classes ('%s') aren't allowed to inherit from non-regular classes ('%s').", klass->desc->name, klass->parent->desc->name);
664                    goto cleanup;
665                 }
666               break;
667            case EOBJ_CLASS_TYPE_INTERFACE:
668            case EOBJ_CLASS_TYPE_MIXIN:
669               if ((klass->parent->desc->type != EOBJ_CLASS_TYPE_INTERFACE) &&
670                     (klass->parent->desc->type != EOBJ_CLASS_TYPE_MIXIN))
671                 {
672                    ERR("Non-regular classes ('%s') aren't allowed to inherit from regular classes ('%s').", klass->desc->name, klass->parent->desc->name);
673                    goto cleanup;
674                 }
675               break;
676           }
677
678
679         /* Update the current offset. */
680         /* FIXME: Make sure this alignment is enough. */
681         klass->data_offset = klass->parent->data_offset +
682            klass->parent->desc->data_size +
683            (sizeof(void *) -
684                   (klass->parent->desc->data_size % sizeof(void *)));
685      }
686
687    if (!_eobj_class_check_op_descs(klass))
688      {
689         goto cleanup;
690      }
691
692    klass->class_id = ++_eobj_classes_last_id;
693      {
694         /* FIXME: Handle errors. */
695         Eobj_Class **tmp;
696         tmp = realloc(_eobj_classes, _eobj_classes_last_id * sizeof(*_eobj_classes));
697         _eobj_classes = tmp;
698         _eobj_classes[klass->class_id - 1] = klass;
699      }
700
701    _eobj_class_base_op_init(klass);
702
703    /* FIXME: Shouldn't be called here - should be called from eobj_add. */
704    _eobj_class_constructor(klass);
705
706    va_end(p_list);
707
708    return klass;
709
710 cleanup:
711    eobj_class_free(klass);
712    return NULL;
713 }
714 #undef _CLS_NEW_CHECK
715
716 EAPI Eobj *
717 eobj_add(const Eobj_Class *klass, Eobj *parent)
718 {
719    if (klass->desc->type != EOBJ_CLASS_TYPE_REGULAR)
720      {
721         ERR("Class '%s' is not instantiate-able. Aborting.", klass->desc->name);
722         return NULL;
723      }
724
725    Eobj *obj = calloc(1, sizeof(*obj));
726    obj->klass = klass;
727    obj->parent = parent;
728
729    obj->refcount++;
730
731    obj->data_blob = calloc(1, klass->data_offset + klass->desc->data_size);
732
733    _eobj_kls_itr_init(obj, EOBJ_NOOP);
734    eobj_constructor_error_unset(obj);
735
736    eobj_ref(obj);
737    eobj_class_constructor(obj, klass);
738
739    if (eobj_constructor_error_get(obj))
740      {
741         ERR("Type '%s' - One of the object constructors have failed.", klass->desc->name);
742         goto fail;
743      }
744
745    if (!_eobj_kls_itr_reached_end(obj))
746      {
747         ERR("Type '%s' - Not all of the object constructors have been executed.", klass->desc->name);
748         goto fail;
749      }
750    _eobj_kls_itr_end(obj, EOBJ_NOOP);
751    eobj_unref(obj);
752
753    return obj;
754
755 fail:
756    /* Unref twice, once for the ref above, and once for the basic object ref. */
757    eobj_unref(obj);
758    eobj_unref(obj);
759    return NULL;
760 }
761
762 typedef struct
763 {
764    EINA_INLIST;
765    const Eobj *ref_obj;
766    const char *file;
767    int line;
768 } Eobj_Xref_Node;
769
770 EAPI Eobj *
771 eobj_xref_internal(Eobj *obj, const Eobj *ref_obj, const char *file, int line)
772 {
773    eobj_ref(obj);
774
775 #ifndef NDEBUG
776    Eobj_Xref_Node *xref = calloc(1, sizeof(*xref));
777    xref->ref_obj = ref_obj;
778    xref->file = file;
779    xref->line = line;
780
781    /* FIXME: Make it sorted. */
782    obj->xrefs = eina_inlist_prepend(obj->xrefs, EINA_INLIST_GET(xref));
783 #else
784    (void) ref_obj;
785    (void) file;
786    (void) line;
787 #endif
788
789    return obj;
790 }
791
792 EAPI void
793 eobj_xunref(Eobj *obj, const Eobj *ref_obj)
794 {
795 #ifndef NDEBUG
796    Eobj_Xref_Node *xref = NULL;
797    EINA_INLIST_FOREACH(obj->xrefs, xref)
798      {
799         if (xref->ref_obj == ref_obj)
800            break;
801      }
802
803    if (xref)
804      {
805         obj->xrefs = eina_inlist_remove(obj->xrefs, EINA_INLIST_GET(xref));
806         free(xref);
807      }
808    else
809      {
810         ERR("ref_obj (%p) does not reference obj (%p). Aborting unref.", ref_obj, obj);
811         return;
812      }
813 #else
814    (void) ref_obj;
815 #endif
816    eobj_unref(obj);
817 }
818
819 EAPI Eobj *
820 eobj_ref(Eobj *obj)
821 {
822    obj->refcount++;
823    return obj;
824 }
825
826 EAPI void
827 eobj_unref(Eobj *obj)
828 {
829    if (--(obj->refcount) == 0)
830      {
831         /* We need that for the event callbacks that may ref/unref. */
832         obj->refcount++;
833
834         if (!obj->delete)
835           {
836              eobj_event_callback_call(obj, EOBJ_EV_DEL, NULL);
837              obj->delete = EINA_TRUE;
838           }
839         eobj_event_callback_call(obj, EOBJ_EV_FREE, NULL);
840
841         obj->refcount--;
842
843         const Eobj_Class *klass = eobj_class_get(obj);
844         _eobj_kls_itr_init(obj, EOBJ_NOOP);
845         eobj_constructor_error_unset(obj);
846         eobj_class_destructor(obj, klass);
847         if (eobj_constructor_error_get(obj))
848           {
849              ERR("Type '%s' - One of the object destructors have failed.", klass->desc->name);
850           }
851
852         if (!_eobj_kls_itr_reached_end(obj))
853           {
854              ERR("Type '%s' - Not all of the object destructors have been executed.", klass->desc->name);
855           }
856         _eobj_kls_itr_end(obj, EOBJ_NOOP);
857         /*FIXME: add eobj_class_unref(klass) ? - just to clear the caches. */
858
859         /* If for some reason it's not empty, clear it. */
860         while (obj->kls_itr)
861           {
862              WRN("Kls_Itr is not empty, possibly a bug, please report. - An error will be reported for each kls_itr in the stack.");
863              Eina_Inlist *nitr = nitr->next;
864              free(EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node));
865              obj->kls_itr = nitr;
866           }
867
868 #ifndef NDEBUG
869         /* If for some reason it's not empty, clear it. */
870         while (obj->xrefs)
871           {
872              WRN("obj->xrefs is not empty, possibly a bug, please report. - An error will be reported for each xref in the stack.");
873              Eina_Inlist *nitr = nitr->next;
874              free(EINA_INLIST_CONTAINER_GET(obj->xrefs, Eobj_Kls_Itr_Node));
875              obj->xrefs = nitr;
876           }
877 #endif
878
879         Eina_List *itr, *itr_n;
880         Eobj *emb_obj;
881         EINA_LIST_FOREACH_SAFE(obj->composite_objects, itr, itr_n, emb_obj)
882           {
883              eobj_composite_object_detach(obj, emb_obj);
884           }
885
886         _eobj_callback_remove_all(obj);
887
888         if (obj->data_blob)
889            free(obj->data_blob);
890
891         free(obj);
892      }
893 }
894
895 EAPI int
896 eobj_ref_get(const Eobj *obj)
897 {
898    return obj->refcount;
899 }
900
901 /* Weak reference. */
902 Eina_Bool
903 _eobj_weak_ref_cb(void *data, Eobj *obj __UNUSED__, const Eobj_Event_Description *desc __UNUSED__, void *event_info __UNUSED__)
904 {
905    Eobj_Weak_Ref *wref = data;
906    wref->obj = NULL;
907
908    return EINA_TRUE;
909 }
910
911 EAPI Eobj_Weak_Ref *
912 eobj_weak_ref_new(const Eobj *_obj)
913 {
914    Eobj *obj = (Eobj *) _obj;
915    Eobj_Weak_Ref *wref = calloc(1, sizeof(*wref));
916    wref->obj = obj;
917    eobj_event_callback_add(obj, EOBJ_EV_DEL, _eobj_weak_ref_cb, wref);
918
919    return wref;
920 }
921
922 EAPI void
923 eobj_weak_ref_free(Eobj_Weak_Ref *wref)
924 {
925    if (wref->obj)
926      {
927         eobj_event_callback_del_full(wref->obj, EOBJ_EV_DEL, _eobj_weak_ref_cb,
928               wref);
929      }
930    free(wref);
931 }
932
933 /* EOF Weak reference. */
934
935 EAPI void
936 eobj_del(Eobj *obj)
937 {
938    if (!obj->delete)
939      {
940         eobj_event_callback_call(obj, EOBJ_EV_DEL, NULL);
941         obj->delete = EINA_TRUE;
942      }
943    eobj_unref(obj);
944 }
945
946 EAPI Eobj *
947 eobj_parent_get(Eobj *obj)
948 {
949    return obj->parent;
950 }
951
952 EAPI void
953 eobj_constructor_error_set(Eobj *obj)
954 {
955    obj->construct_error = EINA_TRUE;
956 }
957
958 static void
959 eobj_constructor_error_unset(Eobj *obj)
960 {
961    obj->construct_error = EINA_FALSE;
962 }
963
964 EAPI Eina_Bool
965 eobj_constructor_error_get(const Eobj *obj)
966 {
967    return obj->construct_error;
968 }
969
970 static inline void
971 _eobj_constructor_default(Eobj *obj)
972 {
973    eobj_constructor_super(obj);
974 }
975
976 static inline void
977 _eobj_destructor_default(Eobj *obj)
978 {
979    eobj_destructor_super(obj);
980 }
981
982 static void
983 eobj_class_constructor(Eobj *obj, const Eobj_Class *klass)
984 {
985    if (!klass)
986       return;
987
988    if (klass->desc->constructor)
989       klass->desc->constructor(obj, eobj_data_get(obj, klass));
990    else
991       _eobj_constructor_default(obj);
992 }
993
994 static void
995 eobj_class_destructor(Eobj *obj, const Eobj_Class *klass)
996 {
997    if (!klass)
998       return;
999
1000    if (klass->desc->destructor)
1001       klass->desc->destructor(obj, eobj_data_get(obj, klass));
1002    else
1003       _eobj_destructor_default(obj);
1004 }
1005
1006 EAPI void
1007 eobj_constructor_super(Eobj *obj)
1008 {
1009    eobj_class_constructor(obj, _eobj_kls_itr_next(obj));
1010 }
1011
1012 EAPI void
1013 eobj_destructor_super(Eobj *obj)
1014 {
1015    eobj_class_destructor(obj, _eobj_kls_itr_next(obj));
1016 }
1017
1018 EAPI void *
1019 eobj_data_get(Eobj *obj, const Eobj_Class *klass)
1020 {
1021    /* FIXME: Add a check that this is of the right klass and we don't seg.
1022     * Probably just return NULL. */
1023    if (klass->desc->data_size > 0)
1024       return ((char *) obj->data_blob) + klass->data_offset;
1025    else
1026       return NULL;
1027 }
1028
1029 EAPI Eina_Bool
1030 eobj_init(void)
1031 {
1032    const char *log_dom = "eobj";
1033    if (_eobj_init_count++ > 0)
1034       return EINA_TRUE;
1035
1036    eina_init();
1037
1038    _eobj_classes = NULL;
1039    _eobj_classes_last_id = 0;
1040    _eobj_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE);
1041    if (_eobj_log_dom < 0)
1042      {
1043         EINA_LOG_ERR("Could not register log domain: %s", log_dom);
1044         return EINA_FALSE;
1045      }
1046
1047    return EINA_TRUE;
1048 }
1049
1050 EAPI Eina_Bool
1051 eobj_shutdown(void)
1052 {
1053    int i;
1054    Eobj_Class **cls_itr = _eobj_classes;
1055
1056    if (--_eobj_init_count > 0)
1057       return EINA_TRUE;
1058
1059    for (i = 0 ; i < _eobj_classes_last_id ; i++, cls_itr++)
1060      {
1061         if (*cls_itr)
1062            eobj_class_free(*cls_itr);
1063      }
1064
1065    if (_eobj_classes)
1066       free(_eobj_classes);
1067
1068    eina_log_domain_unregister(_eobj_log_dom);
1069    _eobj_log_dom = -1;
1070
1071    eina_shutdown();
1072    return EINA_TRUE;
1073 }
1074
1075 EAPI void
1076 eobj_composite_object_attach(Eobj *obj, Eobj *emb_obj)
1077 {
1078    eobj_xref(emb_obj, obj);
1079    obj->composite_objects = eina_list_prepend(obj->composite_objects, emb_obj);
1080 }
1081
1082 EAPI void
1083 eobj_composite_object_detach(Eobj *obj, Eobj *emb_obj)
1084 {
1085    obj->composite_objects = eina_list_remove(obj->composite_objects, emb_obj);
1086    eobj_xunref(emb_obj, obj);
1087 }
1088
1089 EAPI Eina_Bool
1090 eobj_composite_is(Eobj *emb_obj)
1091 {
1092    Eobj *obj = eobj_parent_get(emb_obj);
1093    Eina_List *itr;
1094    Eobj *tmp;
1095
1096    if (!obj)
1097       return EINA_FALSE;
1098
1099    EINA_LIST_FOREACH(obj->composite_objects, itr, tmp)
1100      {
1101         if (tmp == emb_obj)
1102            return EINA_TRUE;
1103      }
1104
1105    return EINA_FALSE;
1106 }
1107
1108 /* Callbacks */
1109 struct _Eobj_Callback_Description
1110 {
1111    EINA_INLIST;
1112    const Eobj_Event_Description *event;
1113    Eobj_Event_Cb func;
1114    void *func_data;
1115    Eobj_Callback_Priority priority;
1116    Eina_Bool delete_me : 1;
1117 };
1118
1119 /* Actually remove, doesn't care about walking list, or delete_me */
1120 static void
1121 _eobj_callback_remove(Eobj *obj, Eobj_Callback_Description *cb)
1122 {
1123    obj->callbacks = eina_inlist_remove(obj->callbacks,
1124          EINA_INLIST_GET(cb));
1125    free(cb);
1126 }
1127
1128 /* Actually remove, doesn't care about walking list, or delete_me */
1129 static void
1130 _eobj_callback_remove_all(Eobj *obj)
1131 {
1132    Eina_Inlist *initr;
1133    Eobj_Callback_Description *cb;
1134    EINA_INLIST_FOREACH_SAFE(obj->callbacks, initr, cb)
1135      {
1136         _eobj_callback_remove(obj, cb);
1137      }
1138 }
1139
1140 static void
1141 _eobj_callbacks_clear(Eobj *obj)
1142 {
1143    Eina_Inlist *itn;
1144    Eobj_Callback_Description *cb;
1145
1146    /* Abort if we are currently walking the list. */
1147    if (obj->walking_list > 0)
1148       return;
1149
1150    EINA_INLIST_FOREACH_SAFE(obj->callbacks, itn, cb)
1151      {
1152         if (cb->delete_me)
1153           {
1154              _eobj_callback_remove(obj, cb);
1155           }
1156      }
1157 }
1158
1159 static int
1160 _callback_priority_cmp(const void *_a, const void *_b)
1161 {
1162    const Eobj_Callback_Description *a, *b;
1163    a = (const Eobj_Callback_Description *) _a;
1164    b = (const Eobj_Callback_Description *) _b;
1165    if (a->priority < b->priority)
1166       return -1;
1167    else
1168       return 1;
1169 }
1170
1171 EAPI Eina_Bool
1172 eobj_event_callback_priority_add(Eobj *obj,
1173       const Eobj_Event_Description *desc,
1174       Eobj_Callback_Priority priority,
1175       Eobj_Event_Cb func,
1176       const void *data)
1177 {
1178    Eobj_Callback_Description *cb = calloc(1, sizeof(*cb));
1179    cb->event = desc;
1180    cb->func = func;
1181    cb->func_data = (void *) data;
1182    cb->priority = priority;
1183    obj->callbacks = eina_inlist_sorted_insert(obj->callbacks,
1184          EINA_INLIST_GET(cb), _callback_priority_cmp);
1185
1186    eobj_event_callback_call(obj, EOBJ_EV_CALLBACK_ADD, desc);
1187
1188    return EINA_TRUE;
1189 }
1190
1191 EAPI void *
1192 eobj_event_callback_del(Eobj *obj, const Eobj_Event_Description *desc, Eobj_Event_Cb func)
1193 {
1194    void *ret = NULL;
1195    Eobj_Callback_Description *cb;
1196    EINA_INLIST_FOREACH(obj->callbacks, cb)
1197      {
1198         if ((cb->event == desc) && (cb->func == func))
1199           {
1200              void *data;
1201
1202              data = cb->func_data;
1203              cb->delete_me = EINA_TRUE;
1204              _eobj_callbacks_clear(obj);
1205              ret = data;
1206              goto found;
1207           }
1208      }
1209
1210    return NULL;
1211
1212 found:
1213    eobj_event_callback_call(obj, EOBJ_EV_CALLBACK_DEL, desc);
1214    return ret;
1215 }
1216
1217 EAPI void *
1218 eobj_event_callback_del_full(Eobj *obj, const Eobj_Event_Description *desc, Eobj_Event_Cb func, const void *user_data)
1219 {
1220    void *ret = NULL;
1221    Eobj_Callback_Description *cb;
1222    EINA_INLIST_FOREACH(obj->callbacks, cb)
1223      {
1224         if ((cb->event == desc) && (cb->func == func) &&
1225               (cb->func_data == user_data))
1226           {
1227              void *data;
1228
1229              data = cb->func_data;
1230              cb->delete_me = EINA_TRUE;
1231              _eobj_callbacks_clear(obj);
1232              ret = data;
1233              goto found;
1234           }
1235      }
1236
1237    return NULL;
1238
1239 found:
1240    eobj_event_callback_call(obj, EOBJ_EV_CALLBACK_DEL, desc);
1241    return ret;
1242 }
1243
1244 EAPI Eina_Bool
1245 eobj_event_callback_call(Eobj *obj, const Eobj_Event_Description *desc,
1246       const void *event_info)
1247 {
1248    Eina_Bool ret = EINA_TRUE;
1249    Eobj_Callback_Description *cb;
1250
1251    eobj_ref(obj);
1252    obj->walking_list++;
1253
1254    EINA_INLIST_FOREACH(obj->callbacks, cb)
1255      {
1256         if (!cb->delete_me  && (cb->event == desc))
1257           {
1258              /* Abort callback calling if the func says so. */
1259              if (!cb->func((void *) cb->func_data, obj, desc,
1260                       (void *) event_info))
1261                {
1262                   ret = EINA_FALSE;
1263                   break;
1264                }
1265           }
1266         if (obj->delete)
1267           break;
1268      }
1269    obj->walking_list--;
1270    _eobj_callbacks_clear(obj);
1271    eobj_unref(obj);
1272
1273    return ret;
1274 }
1275
1276 static Eina_Bool
1277 _eobj_event_forwarder_callback(void *data, Eobj *obj, const Eobj_Event_Description *desc, void *event_info)
1278 {
1279    (void) obj;
1280    Eobj *new_obj = (Eobj *) data;
1281    return eobj_event_callback_call(new_obj, desc, event_info);
1282 }
1283
1284 /* FIXME: Change default priority? Maybe call later? */
1285 EAPI Eina_Bool
1286 eobj_event_callback_forwarder_add(Eobj *obj, const Eobj_Event_Description *desc, Eobj *new_obj)
1287 {
1288    return eobj_event_callback_add(obj, desc, _eobj_event_forwarder_callback, new_obj);
1289 }
1290
1291 EAPI Eina_Bool
1292 eobj_event_callback_forwarder_del(Eobj *obj, const Eobj_Event_Description *desc, Eobj *new_obj)
1293 {
1294    eobj_event_callback_del_full(obj, desc, _eobj_event_forwarder_callback, new_obj);
1295    return EINA_TRUE;
1296 }
1297