Properly determine match type to pass to header_match.
[platform/upstream/evolution-data-server.git] / camel / camel-object.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-object.c: Base class for Camel */
3
4 /*
5  * Author:
6  *  Dan Winship <danw@ximian.com>
7  *
8  * Copyright 2000 Ximian, Inc. (www.ximian.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23  * USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <string.h>
31 #include "camel-object.h"
32
33 /* I just mashed the keyboard for these... */
34 #define CAMEL_OBJECT_MAGIC_VALUE           0x77A344EF
35 #define CAMEL_OBJECT_CLASS_MAGIC_VALUE     0xEE26A990
36 #define CAMEL_OBJECT_FINALIZED_VALUE       0x84AC3656
37 #define CAMEL_OBJECT_CLASS_FINALIZED_VALUE 0x7621ABCD
38
39 #define DEFAULT_PREALLOCS 8
40
41 #define BAST_CASTARD 1          /* Define to return NULL when casts fail */
42
43 #define NULL_PREP_VALUE ((gpointer)make_global_classfuncs)      /* See camel_object_class_declare_event */
44
45 /* ** Quickie type system ************************************************* */
46
47 typedef struct _CamelTypeInfo
48 {
49         CamelType self;
50         CamelType parent;
51         const gchar *name;
52
53         size_t instance_size;
54         GMemChunk *instance_chunk;
55         CamelObjectInitFunc instance_init;
56         CamelObjectFinalizeFunc instance_finalize;
57         GList *free_instances;
58
59         size_t classfuncs_size;
60         CamelObjectClassInitFunc class_init;
61         CamelObjectClassFinalizeFunc class_finalize;
62         CamelObjectClass *global_classfuncs;
63 }
64 CamelTypeInfo;
65
66 typedef struct _CamelHookPair
67 {
68         CamelObjectEventHookFunc func;
69         gpointer user_data;
70 }
71 CamelHookPair;
72
73 /* ************************************************************************ */
74
75 static void camel_type_lock_up (void);
76 static void camel_type_lock_down (void);
77
78 static void obj_init (CamelObject * obj);
79 static void obj_finalize (CamelObject * obj);
80 static void obj_class_init (CamelObjectClass * class);
81 static void obj_class_finalize (CamelObjectClass * class);
82
83 static gboolean shared_is_of_type (CamelObjectShared * sh, CamelType ctype,
84                                    gboolean is_obj);
85 static void make_global_classfuncs (CamelTypeInfo * type_info);
86
87 /* ************************************************************************ */
88
89 G_LOCK_DEFINE_STATIC (type_system);
90 G_LOCK_DEFINE_STATIC (type_system_level);
91 static GPrivate *type_system_locklevel = NULL;
92
93 G_LOCK_DEFINE_STATIC (refcount);
94
95 static gboolean type_system_initialized = FALSE;
96 static GHashTable *ctype_to_typeinfo = NULL;
97 static const CamelType camel_object_type = 1;
98 static CamelType cur_max_type = CAMEL_INVALID_TYPE;
99
100 /* ************************************************************************ */
101
102 #define LOCK_VAL (GPOINTER_TO_INT (g_private_get (type_system_locklevel)))
103 #define LOCK_SET( val ) g_private_set (type_system_locklevel, GINT_TO_POINTER (val))
104
105 static void
106 camel_type_lock_up (void)
107 {
108         G_LOCK (type_system_level);
109
110         if (type_system_locklevel == NULL)
111                 type_system_locklevel = g_private_new (GINT_TO_POINTER (0));
112
113         if (LOCK_VAL == 0) {
114                 G_UNLOCK (type_system_level);
115                 G_LOCK (type_system);
116                 G_LOCK (type_system_level);
117         }
118
119         LOCK_SET (LOCK_VAL + 1);
120
121         G_UNLOCK (type_system_level);
122 }
123
124 static void
125 camel_type_lock_down (void)
126 {
127         G_LOCK (type_system_level);
128
129         if (type_system_locklevel == NULL) {
130                 g_warning
131                         ("camel_type_lock_down: lock down before a lock up?");
132                 type_system_locklevel = g_private_new (GINT_TO_POINTER (0));
133                 G_UNLOCK (type_system_level);
134                 return;
135         }
136
137         LOCK_SET (LOCK_VAL - 1);
138
139         if (LOCK_VAL == 0)
140                 G_UNLOCK (type_system);
141
142         G_UNLOCK (type_system_level);
143 }
144
145 void
146 camel_type_init (void)
147 {
148         CamelTypeInfo *obj_info;
149
150         camel_type_lock_up ();
151
152         if (type_system_initialized) {
153                 g_warning
154                         ("camel_type_init: type system already initialized.");
155                 camel_type_lock_down ();
156                 return;
157         }
158
159         type_system_initialized = TRUE;
160         ctype_to_typeinfo = g_hash_table_new (g_direct_hash, g_direct_equal);
161
162         obj_info = g_new (CamelTypeInfo, 1);
163         obj_info->self = camel_object_type;
164         obj_info->parent = CAMEL_INVALID_TYPE;
165         obj_info->name = "CamelObject";
166
167         obj_info->instance_size = sizeof (CamelObject);
168         obj_info->instance_chunk =
169                 g_mem_chunk_create (CamelObject, DEFAULT_PREALLOCS,
170                                     G_ALLOC_ONLY);
171         obj_info->instance_init = obj_init;
172         obj_info->instance_finalize = obj_finalize;
173         obj_info->free_instances = NULL;
174
175         obj_info->classfuncs_size = sizeof (CamelObjectClass);
176         obj_info->class_init = obj_class_init;
177         obj_info->class_finalize = obj_class_finalize;
178
179         g_hash_table_insert (ctype_to_typeinfo,
180                              GINT_TO_POINTER (CAMEL_INVALID_TYPE), NULL);
181         g_hash_table_insert (ctype_to_typeinfo,
182                              GINT_TO_POINTER (camel_object_type), obj_info);
183
184         /* Sigh. Ugly */
185         make_global_classfuncs (obj_info);
186
187         cur_max_type = camel_object_type;
188
189         camel_type_lock_down ();
190 }
191
192 CamelType
193 camel_type_register (CamelType parent, const gchar * name,
194                      size_t instance_size, size_t classfuncs_size,
195                      CamelObjectClassInitFunc class_init,
196                      CamelObjectClassFinalizeFunc class_finalize,
197                      CamelObjectInitFunc instance_init,
198                      CamelObjectFinalizeFunc instance_finalize)
199 {
200         CamelTypeInfo *parent_info;
201         CamelTypeInfo *obj_info;
202         gchar *chunkname;
203
204         g_return_val_if_fail (parent != CAMEL_INVALID_TYPE,
205                               CAMEL_INVALID_TYPE);
206         g_return_val_if_fail (name, CAMEL_INVALID_TYPE);
207         g_return_val_if_fail (instance_size, CAMEL_INVALID_TYPE);
208         g_return_val_if_fail (classfuncs_size, CAMEL_INVALID_TYPE);
209
210         camel_type_lock_up ();
211
212         if (type_system_initialized == FALSE) {
213                 G_UNLOCK (type_system);
214                 camel_type_init ();
215                 G_LOCK (type_system);
216         }
217
218         parent_info =
219                 g_hash_table_lookup (ctype_to_typeinfo,
220                                      GINT_TO_POINTER (parent));
221
222         if (parent_info == NULL) {
223                 g_warning
224                         ("camel_type_register: no such parent type %d of class `%s'",
225                          parent, name);
226                 camel_type_lock_down ();
227                 return CAMEL_INVALID_TYPE;
228         }
229
230         if (parent_info->instance_size > instance_size) {
231                 g_warning
232                         ("camel_type_register: instance of class `%s' would be smaller than parent `%s'",
233                          name, parent_info->name);
234                 camel_type_lock_down ();
235                 return CAMEL_INVALID_TYPE;
236         }
237
238         if (parent_info->classfuncs_size > classfuncs_size) {
239                 g_warning
240                         ("camel_type_register: classfuncs of class `%s' would be smaller than parent `%s'",
241                          name, parent_info->name);
242                 camel_type_lock_down ();
243                 return CAMEL_INVALID_TYPE;
244         }
245
246         cur_max_type++;
247
248         obj_info = g_new (CamelTypeInfo, 1);
249         obj_info->self = cur_max_type;
250         obj_info->parent = parent;
251         obj_info->name = name;
252
253         obj_info->instance_size = instance_size;
254         chunkname =
255                 g_strdup_printf ("chunk for instances of Camel type `%s'",
256                                  name);
257         obj_info->instance_chunk =
258                 g_mem_chunk_new (chunkname, instance_size,
259                                  instance_size * DEFAULT_PREALLOCS,
260                                  G_ALLOC_ONLY);
261         g_free (chunkname);
262         obj_info->instance_init = instance_init;
263         obj_info->instance_finalize = instance_finalize;
264         obj_info->free_instances = NULL;
265
266         obj_info->classfuncs_size = classfuncs_size;
267         obj_info->class_init = class_init;
268         obj_info->class_finalize = class_finalize;
269
270         g_hash_table_insert (ctype_to_typeinfo,
271                              GINT_TO_POINTER (obj_info->self), obj_info);
272
273         /* Sigh. Ugly. */
274         make_global_classfuncs (obj_info);
275
276         camel_type_lock_down ();
277         return obj_info->self;
278 }
279
280 CamelObjectClass *
281 camel_type_get_global_classfuncs (CamelType type)
282 {
283         CamelTypeInfo *type_info;
284
285         g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL);
286
287         camel_type_lock_up ();
288         type_info =
289                 g_hash_table_lookup (ctype_to_typeinfo,
290                                      GINT_TO_POINTER (type));
291         camel_type_lock_down ();
292
293         g_return_val_if_fail (type_info != NULL, NULL);
294
295         return type_info->global_classfuncs;
296 }
297
298 const gchar *
299 camel_type_to_name (CamelType type)
300 {
301         CamelTypeInfo *type_info;
302
303         g_return_val_if_fail (type != CAMEL_INVALID_TYPE,
304                               "(the invalid type)");
305
306         camel_type_lock_up ();
307         type_info =
308                 g_hash_table_lookup (ctype_to_typeinfo,
309                                      GINT_TO_POINTER (type));
310         camel_type_lock_down ();
311
312         g_return_val_if_fail (type_info != NULL,
313                               "(a bad type parameter was specified)");
314
315         return type_info->name;
316 }
317
318 /* ** The CamelObject ***************************************************** */
319
320 static void
321 obj_init (CamelObject * obj)
322 {
323         obj->s.magic = CAMEL_OBJECT_MAGIC_VALUE;
324         obj->ref_count = 1;
325         obj->event_to_hooklist = NULL;
326         obj->in_event = 0;
327         obj->destroying = 0;
328 }
329
330 static void
331 obj_finalize (CamelObject * obj)
332 {
333         g_return_if_fail (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE);
334         g_return_if_fail (obj->ref_count == 0);
335         g_return_if_fail (obj->in_event == 0);
336
337         obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE;
338
339         if (obj->event_to_hooklist) {
340                 g_hash_table_foreach (obj->event_to_hooklist, (GHFunc) g_free, NULL);
341                 g_hash_table_destroy (obj->event_to_hooklist);
342                 obj->event_to_hooklist = NULL;
343         }
344 }
345
346 static void
347 obj_class_init (CamelObjectClass * class)
348 {
349         class->s.magic = CAMEL_OBJECT_CLASS_MAGIC_VALUE;
350
351         camel_object_class_declare_event (class, "finalize", NULL);
352 }
353
354 static void
355 obj_class_finalize (CamelObjectClass * class)
356 {
357         g_return_if_fail (class->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE);
358
359         class->s.magic = CAMEL_OBJECT_CLASS_FINALIZED_VALUE;
360
361         if (class->event_to_preplist) {
362                 g_hash_table_foreach (class->event_to_preplist,
363                                       (GHFunc) g_free, NULL);
364                 g_hash_table_destroy (class->event_to_preplist);
365                 class->event_to_preplist = NULL;
366         }
367 }
368
369 CamelType
370 camel_object_get_type (void)
371 {
372         if (type_system_initialized == FALSE)
373                 camel_type_init ();
374
375         return camel_object_type;
376 }
377
378 CamelObject *
379 camel_object_new (CamelType type)
380 {
381         CamelTypeInfo *type_info;
382         GSList *parents = NULL;
383         GSList *head = NULL;
384         CamelObject *instance;
385
386         g_return_val_if_fail (type != CAMEL_INVALID_TYPE, NULL);
387
388         /* Look up the type */
389
390         camel_type_lock_up ();
391
392         type_info =
393                 g_hash_table_lookup (ctype_to_typeinfo,
394                                      GINT_TO_POINTER (type));
395
396         if (type_info == NULL) {
397                 g_warning
398                         ("camel_object_new: trying to create object of invalid type %d",
399                          type);
400                 camel_type_lock_down ();
401                 return NULL;
402         }
403
404         /* Grab an instance out of the freed ones if possible, alloc otherwise */
405
406         if (type_info->free_instances) {
407                 GList *first;
408
409                 first = g_list_first (type_info->free_instances);
410                 instance = first->data;
411                 type_info->free_instances =
412                         g_list_remove_link (type_info->free_instances, first);
413                 g_list_free_1 (first);
414         } else {
415                 instance = g_mem_chunk_alloc0 (type_info->instance_chunk);
416         }
417
418         /* Init the instance and classfuncs a bit */
419
420         instance->s.type = type;
421         instance->classfuncs = type_info->global_classfuncs;
422
423         /* Loop through the parents in simplest -> most complex order, initing the class and instance.
424
425          * When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL
426          * because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh?
427          */
428
429         while (type_info) {
430                 parents = g_slist_prepend (parents, type_info);
431                 type_info =
432                         g_hash_table_lookup (ctype_to_typeinfo,
433                                              GINT_TO_POINTER (type_info->
434                                                               parent));
435         }
436
437         head = parents;
438
439         for (; parents && parents->data; parents = parents->next) {
440                 CamelTypeInfo *thisinfo;
441
442                 thisinfo = parents->data;
443                 if (thisinfo->instance_init)
444                         (thisinfo->instance_init) (instance);
445         }
446
447         g_slist_free (head);
448
449         camel_type_lock_down ();
450         return instance;
451 }
452
453 void
454 camel_object_ref (CamelObject * obj)
455 {
456         g_return_if_fail (CAMEL_IS_OBJECT (obj));
457
458         G_LOCK (refcount);
459         obj->ref_count++;
460         G_UNLOCK (refcount);
461 }
462
463 void
464 camel_object_unref (CamelObject * obj)
465 {
466         CamelTypeInfo *type_info;
467         CamelTypeInfo *iter;
468         GSList *parents = NULL;
469         GSList *head = NULL;
470
471         g_return_if_fail (CAMEL_IS_OBJECT (obj));
472
473         G_LOCK (refcount);
474         obj->ref_count--;
475
476         if (obj->ref_count > 0) {
477                 G_UNLOCK (refcount);
478                 return;
479         }
480
481         G_UNLOCK (refcount);
482
483         /* If the object already had its last unref, do not begin the
484          * destruction process again. This can happen if, for example,
485          * the object sends an event in its finalize handler (vfolders
486          * do this).
487          */
488
489         if (obj->destroying)
490                 return;
491         
492         obj->destroying = 1;
493
494         /* Send the finalize event */
495
496         camel_object_trigger_event (obj, "finalize", NULL);
497
498         /* Destroy it! hahaha! */
499
500         camel_type_lock_up ();
501
502         type_info =
503                 g_hash_table_lookup (ctype_to_typeinfo,
504                                      GINT_TO_POINTER (obj->s.type));
505
506         if (type_info == NULL) {
507                 g_warning
508                         ("camel_object_unref: seemingly valid object has a bad type %d",
509                          obj->s.type);
510                 camel_type_lock_down ();
511                 return;
512         }
513
514         /* Loop through the parents in most complex -> simplest order, finalizing the class 
515          * and instance.
516          *
517          * When parent = CAMEL_INVALID_TYPE and we're at the end of the line, _lookup returns NULL
518          * because we inserted it as corresponding to CAMEL_INVALID_TYPE. Clever, eh?
519          *
520          * Use iter to preserve type_info for free_{instance,classfunc}s
521          */
522
523         iter = type_info;
524
525         while (iter) {
526                 parents = g_slist_prepend (parents, iter);
527                 iter =
528                         g_hash_table_lookup (ctype_to_typeinfo,
529                                              GINT_TO_POINTER (iter->parent));
530         }
531
532         parents = g_slist_reverse (parents);
533         head = parents;
534
535         for (; parents && parents->data; parents = parents->next) {
536                 CamelTypeInfo *thisinfo;
537
538                 thisinfo = parents->data;
539                 if (thisinfo->instance_finalize)
540                         (thisinfo->instance_finalize) (obj);
541         }
542
543         g_slist_free (head);
544
545         /* Sanity check */
546
547         if (obj->ref_count != 0)
548                 g_warning ("camel_object_unref: destroyed object %s at %p somehow got"
549                            " referenced in destruction chain.",
550                            camel_type_to_name (obj->s.type),
551                            obj);
552
553         /* A little bit of cleaning up.
554
555          * Don't erase the type, so we can peek at it if a finalized object
556          * is check_cast'ed somewhere.
557          */
558
559         memset (obj, 0, type_info->instance_size);
560         obj->s.type = type_info->self;
561         obj->s.magic = CAMEL_OBJECT_FINALIZED_VALUE;
562
563         /* Tuck away the pointer for use in a new object */
564
565         type_info->free_instances =
566                 g_list_prepend (type_info->free_instances, obj);
567
568         camel_type_lock_down ();
569 }
570
571 gboolean
572 camel_object_is_of_type (CamelObject * obj, CamelType ctype)
573 {
574         return shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE);
575 }
576
577 gboolean
578 camel_object_class_is_of_type (CamelObjectClass * class, CamelType ctype)
579 {
580         return shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE);
581 }
582
583 #ifdef BAST_CASTARD
584 #define ERRVAL NULL
585 #else
586 #define ERRVAL obj
587 #endif
588
589 CamelObject *
590 camel_object_check_cast (CamelObject * obj, CamelType ctype)
591 {
592         if (shared_is_of_type ((CamelObjectShared *) obj, ctype, TRUE))
593                 return obj;
594         return ERRVAL;
595 }
596
597 CamelObjectClass *
598 camel_object_class_check_cast (CamelObjectClass * class, CamelType ctype)
599 {
600         if (shared_is_of_type ((CamelObjectShared *) class, ctype, FALSE))
601                 return class;
602         return ERRVAL;
603 }
604
605 #undef ERRVAL
606
607 gchar *
608 camel_object_describe (CamelObject * obj)
609 {
610         if (obj == NULL)
611                 return g_strdup ("a NULL pointer");
612
613         if (obj->s.magic == CAMEL_OBJECT_MAGIC_VALUE) {
614                 return g_strdup_printf ("an instance of `%s' at %p",
615                                         camel_type_to_name (obj->s.type),
616                                         obj);
617         } else if (obj->s.magic == CAMEL_OBJECT_FINALIZED_VALUE) {
618                 return g_strdup_printf ("a finalized instance of `%s' at %p",
619                                         camel_type_to_name (obj->s.type),
620                                         obj);
621         } else if (obj->s.magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
622                 return g_strdup_printf ("the classfuncs of `%s' at %p",
623                                         camel_type_to_name (obj->s.type),
624                                         obj);
625         } else if (obj->s.magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) {
626                 return
627                         g_strdup_printf
628                         ("the finalized classfuncs of `%s' at %p",
629                          camel_type_to_name (obj->s.type), obj);
630         }
631
632         return g_strdup ("not a CamelObject");
633 }
634
635 /* This is likely to be called in the class_init callback,
636  * and the type will likely be somewhat uninitialized. 
637  * Is this a problem? We'll see....
638  */
639 void
640 camel_object_class_declare_event (CamelObjectClass * class,
641                                   const gchar * name,
642                                   CamelObjectEventPrepFunc prep)
643 {
644         g_return_if_fail (CAMEL_IS_OBJECT_CLASS (class));
645         g_return_if_fail (name);
646
647         if (class->event_to_preplist == NULL)
648                 class->event_to_preplist =
649                         g_hash_table_new (g_str_hash, g_str_equal);
650         else if (g_hash_table_lookup (class->event_to_preplist, name) != NULL) {
651                 g_warning
652                         ("camel_object_class_declare_event: event `%s' already declared for `%s'",
653                          name, camel_type_to_name (class->s.type));
654                 return;
655         }
656
657         /* AIEEEEEEEEEEEEEEEEEEEEEE
658
659          * I feel so naughty. Since it's valid to declare an event and not
660          * provide a hook, it should be valid to insert a NULL value into
661          * the table. However, then our lookup in trigger_event would be
662          * ambiguous, not telling us whether the event is undefined or whether
663          * it merely has no hook.
664          *
665          * So we create an 'NULL prep' value that != NULL... specifically, it
666          * equals the address of one of our static functions , because that
667          * can't possibly be your hook.
668          *
669          * Just don't forget to check for the 'evil value' and it'll work,
670          * I promise.
671          */
672
673         if (prep == NULL)
674                 prep = NULL_PREP_VALUE;
675
676         g_hash_table_insert (class->event_to_preplist, g_strdup (name), prep);
677 }
678
679 void
680 camel_object_hook_event (CamelObject * obj, const gchar * name,
681                          CamelObjectEventHookFunc hook, gpointer user_data)
682 {
683         GSList *hooklist;
684         CamelHookPair *pair;
685         gpointer old_name, old_hooklist;
686
687         g_return_if_fail (CAMEL_IS_OBJECT (obj));
688         g_return_if_fail (name);
689         g_return_if_fail (hook);
690
691         if (obj->event_to_hooklist == NULL)
692                 obj->event_to_hooklist =
693                         g_hash_table_new (g_str_hash, g_str_equal);
694
695         pair = g_new (CamelHookPair, 1);
696         pair->func = hook;
697         pair->user_data = user_data;
698
699         if (g_hash_table_lookup_extended (obj->event_to_hooklist, name,
700                                           &old_name, &old_hooklist)) {
701                 hooklist = g_slist_prepend (old_hooklist, pair);
702                 g_hash_table_insert (obj->event_to_hooklist, old_name,
703                                      hooklist);
704         } else {
705                 hooklist = g_slist_prepend (NULL, pair);
706                 g_hash_table_insert (obj->event_to_hooklist, g_strdup (name),
707                                      hooklist);
708         }
709 }
710
711 void
712 camel_object_unhook_event (CamelObject * obj, const gchar * name,
713                            CamelObjectEventHookFunc hook, gpointer user_data)
714 {
715         GSList *hooklist;
716         GSList *head;
717
718         g_return_if_fail (CAMEL_IS_OBJECT (obj));
719         g_return_if_fail (name);
720         g_return_if_fail (hook);
721
722         if (obj->event_to_hooklist == NULL) {
723                 g_warning
724                         ("camel_object_unhook_event: trying to unhook `%s' from an instance "
725                          "of `%s' with no hooks attached", name,
726                          camel_type_to_name (obj->s.type));
727                 return;
728         }
729
730         hooklist = g_hash_table_lookup (obj->event_to_hooklist, name);
731
732         if (hooklist == NULL) {
733                 g_warning
734                         ("camel_object_unhook_event: trying to unhook `%s' from an instance "
735                          "of `%s' with no hooks attached to that event.",
736                          name, camel_type_to_name (obj->s.type));
737                 return;
738         }
739
740         head = hooklist;
741
742         while (hooklist) {
743                 CamelHookPair *pair = (CamelHookPair *) hooklist->data;
744
745                 if (pair->func == hook && pair->user_data == user_data) {
746                         g_free (hooklist->data);
747                         head = g_slist_remove_link (head, hooklist);
748                         g_slist_free_1 (hooklist);
749                         g_hash_table_insert (obj->event_to_hooklist, (char *) name,
750                                              head);
751                         return;
752                 }
753
754                 hooklist = hooklist->next;
755         }
756
757         g_warning
758                 ("camel_object_unhook_event: cannot find hook/data pair %p/%p in an "
759                  "instance of `%s' attached to `%s'", hook, user_data,
760                  camel_type_to_name (obj->s.type), name);
761 }
762
763 void
764 camel_object_trigger_event (CamelObject * obj, const gchar * name,
765                             gpointer event_data)
766 {
767         GSList *hooklist;
768         CamelHookPair *pair;
769         CamelObjectEventPrepFunc prep;
770
771         g_return_if_fail (CAMEL_IS_OBJECT (obj));
772         g_return_if_fail (name);
773
774         if (obj->in_event) {
775                 g_warning
776                         ("camel_object_trigger_event: trying to trigger `%s' in class "
777                          "`%s' while already triggering another event", name,
778                          camel_type_to_name (obj->s.type));
779                 return;
780         }
781
782         if (obj->classfuncs->event_to_preplist == NULL) {
783                 g_warning
784                         ("camel_object_trigger_event: trying to trigger `%s' in class "
785                          "`%s' with no defined events.", name,
786                          camel_type_to_name (obj->s.type));
787                 return;
788         }
789
790         prep = g_hash_table_lookup (obj->classfuncs->event_to_preplist, name);
791
792         if (prep == NULL) {
793                 g_warning
794                         ("camel_object_trigger_event: trying to trigger undefined "
795                          "event `%s' in class `%s'.", name,
796                          camel_type_to_name (obj->s.type));
797                 return;
798         }
799
800         /* Ref so that it can't get destroyed in the event, which would
801          * be Bad. And it's a valid ref anyway...
802          */
803
804         camel_object_ref (obj);
805         obj->in_event = 1;
806
807         if ((prep != NULL_PREP_VALUE && !prep (obj, event_data))
808             || obj->event_to_hooklist == NULL) {
809                 obj->in_event = 0;
810                 camel_object_unref (obj);
811                 return;
812         }
813
814         hooklist = g_hash_table_lookup (obj->event_to_hooklist, name);
815
816         while (hooklist && hooklist->data) {
817                 pair = hooklist->data;
818                 (pair->func) (obj, event_data, pair->user_data);
819                 hooklist = hooklist->next;
820         }
821
822         obj->in_event = 0;
823         camel_object_unref (obj);
824 }
825
826 /* ** Static helpers ****************************************************** */
827
828 static gboolean
829 shared_is_of_type (CamelObjectShared * sh, CamelType ctype, gboolean is_obj)
830 {
831         CamelTypeInfo *type_info;
832         gchar *targtype;
833
834         if (is_obj)
835                 targtype = "instance";
836         else
837                 targtype = "classdata";
838
839         if (ctype == CAMEL_INVALID_TYPE) {
840                 g_warning
841                         ("shared_is_of_type: trying to cast to CAMEL_INVALID_TYPE");
842                 return FALSE;
843         }
844
845         if (sh == NULL) {
846                 g_warning
847                         ("shared_is_of_type: trying to cast NULL to %s of `%s'",
848                          targtype, camel_type_to_name (ctype));
849                 return FALSE;
850         }
851
852         if (sh->magic == CAMEL_OBJECT_FINALIZED_VALUE) {
853                 g_warning
854                         ("shared_is_of_type: trying to cast finalized instance "
855                          "of `%s' into %s of `%s'",
856                          camel_type_to_name (sh->type), targtype,
857                          camel_type_to_name (ctype));
858                 return FALSE;
859         }
860
861         if (sh->magic == CAMEL_OBJECT_CLASS_FINALIZED_VALUE) {
862                 g_warning
863                         ("shared_is_of_type: trying to cast finalized classdata "
864                          "of `%s' into %s of `%s'",
865                          camel_type_to_name (sh->type), targtype,
866                          camel_type_to_name (ctype));
867                 return FALSE;
868         }
869
870         if (is_obj) {
871                 if (sh->magic == CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
872                         g_warning
873                                 ("shared_is_of_type: trying to cast classdata "
874                                  "of `%s' into instance of `%s'",
875                                  camel_type_to_name (sh->type),
876                                  camel_type_to_name (ctype));
877                         return FALSE;
878                 }
879
880                 if (sh->magic != CAMEL_OBJECT_MAGIC_VALUE) {
881                         g_warning
882                                 ("shared_is_of_type: trying to cast junk data "
883                                  "into instance of `%s'",
884                                  camel_type_to_name (ctype));
885                         return FALSE;
886                 }
887         } else {
888                 if (sh->magic == CAMEL_OBJECT_MAGIC_VALUE) {
889                         g_warning
890                                 ("shared_is_of_type: trying to cast instance "
891                                  "of `%s' into classdata of `%s'",
892                                  camel_type_to_name (sh->type),
893                                  camel_type_to_name (ctype));
894                         return FALSE;
895                 }
896
897                 if (sh->magic != CAMEL_OBJECT_CLASS_MAGIC_VALUE) {
898                         g_warning
899                                 ("shared_is_of_type: trying to cast junk data "
900                                  "into classdata of `%s'",
901                                  camel_type_to_name (ctype));
902                         return FALSE;
903                 }
904         }
905
906         camel_type_lock_up ();
907
908         type_info =
909                 g_hash_table_lookup (ctype_to_typeinfo,
910                                      GINT_TO_POINTER (sh->type));
911
912         if (type_info == NULL) {
913                 g_warning ("shared_is_of_type: seemingly valid %s has "
914                            "bad type %d.", targtype, sh->type);
915                 camel_type_lock_down ();
916                 return FALSE;
917         }
918
919         while (type_info) {
920                 if (type_info->self == ctype) {
921                         camel_type_lock_down ();
922                         return TRUE;
923                 }
924
925                 type_info =
926                         g_hash_table_lookup (ctype_to_typeinfo,
927                                              GINT_TO_POINTER (type_info->
928                                                               parent));
929         }
930
931         /* this isn't an error, e.g. CAMEL_IS_FOLDER(folder), its upto  the
932            caller to handle the false case */
933         /*g_warning
934                 ("shared_is_of_type: %s of `%s' (@%p) is not also %s of `%s'",
935                  targtype, camel_type_to_name (sh->type), sh, targtype,
936                  camel_type_to_name (ctype));*/
937
938         camel_type_lock_down ();
939         return FALSE;
940 }
941
942 static void
943 make_global_classfuncs (CamelTypeInfo * type_info)
944 {
945         CamelObjectClass *funcs;
946         GSList *parents;
947         GSList *head;
948
949         g_assert (type_info);
950
951         funcs = g_malloc0 (type_info->classfuncs_size);
952         funcs->s.type = type_info->self;
953
954         type_info->global_classfuncs = funcs;
955
956         parents = NULL;
957         while (type_info) {
958                 parents = g_slist_prepend (parents, type_info);
959                 type_info =
960                         g_hash_table_lookup (ctype_to_typeinfo,
961                                              GINT_TO_POINTER (type_info->
962                                                               parent));
963         }
964
965         head = parents;
966
967         for (; parents && parents->data; parents = parents->next) {
968                 CamelTypeInfo *thisinfo;
969
970                 thisinfo = parents->data;
971                 if (thisinfo->class_init)
972                         (thisinfo->class_init) (funcs);
973         }
974
975         g_slist_free (head);
976 }