Don't copy strings which are marked as static. Don't allow non-canonical
[platform/upstream/glib.git] / gobject / gparam.c
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2  * Copyright (C) 1997-1999, 2000-2001 Tim Janik and Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * MT safe
22  */
23
24 #include        "gparam.h"
25 #include        "gparamspecs.h"
26
27 #include        "gvaluecollector.h"
28 #include        "gobjectalias.h"
29 #include        <string.h>
30
31
32
33 /* --- defines --- */
34 #define G_PARAM_USER_MASK                       (~0 << G_PARAM_USER_SHIFT)
35 #define PSPEC_APPLIES_TO_VALUE(pspec, value)    (G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec)))
36 #define G_SLOCK(mutex)                          g_static_mutex_lock (mutex)
37 #define G_SUNLOCK(mutex)                        g_static_mutex_unlock (mutex)
38
39
40 /* --- prototypes --- */
41 static void     g_param_spec_class_base_init     (GParamSpecClass       *class);
42 static void     g_param_spec_class_base_finalize (GParamSpecClass       *class);
43 static void     g_param_spec_class_init          (GParamSpecClass       *class,
44                                                   gpointer               class_data);
45 static void     g_param_spec_init                (GParamSpec            *pspec,
46                                                   GParamSpecClass       *class);
47 static void     g_param_spec_finalize            (GParamSpec            *pspec);
48 static void     value_param_init                (GValue         *value);
49 static void     value_param_free_value          (GValue         *value);
50 static void     value_param_copy_value          (const GValue   *src_value,
51                                                  GValue         *dest_value);
52 static void     value_param_transform_value     (const GValue   *src_value,
53                                                  GValue         *dest_value);
54 static gpointer value_param_peek_pointer        (const GValue   *value);
55 static gchar*   value_param_collect_value       (GValue         *value,
56                                                  guint           n_collect_values,
57                                                  GTypeCValue    *collect_values,
58                                                  guint           collect_flags);
59 static gchar*   value_param_lcopy_value         (const GValue   *value,
60                                                  guint           n_collect_values,
61                                                  GTypeCValue    *collect_values,
62                                                  guint           collect_flags);
63
64
65 /* --- variables --- */
66 static GQuark quark_floating = 0;
67 G_LOCK_DEFINE_STATIC (pspec_ref_count);
68
69
70 /* --- functions --- */
71 void
72 g_param_type_init (void)
73 {
74   static const GTypeFundamentalInfo finfo = {
75     (G_TYPE_FLAG_CLASSED |
76      G_TYPE_FLAG_INSTANTIATABLE |
77      G_TYPE_FLAG_DERIVABLE |
78      G_TYPE_FLAG_DEEP_DERIVABLE),
79   };
80   static const GTypeValueTable param_value_table = {
81     value_param_init,           /* value_init */
82     value_param_free_value,     /* value_free */
83     value_param_copy_value,     /* value_copy */
84     value_param_peek_pointer,   /* value_peek_pointer */
85     "p",                        /* collect_format */
86     value_param_collect_value,  /* collect_value */
87     "p",                        /* lcopy_format */
88     value_param_lcopy_value,    /* lcopy_value */
89   };
90   static const GTypeInfo param_spec_info = {
91     sizeof (GParamSpecClass),
92
93     (GBaseInitFunc) g_param_spec_class_base_init,
94     (GBaseFinalizeFunc) g_param_spec_class_base_finalize,
95     (GClassInitFunc) g_param_spec_class_init,
96     (GClassFinalizeFunc) NULL,
97     NULL,       /* class_data */
98
99     sizeof (GParamSpec),
100     0,          /* n_preallocs */
101     (GInstanceInitFunc) g_param_spec_init,
102
103     &param_value_table,
104   };
105   GType type;
106
107   type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", &param_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT);
108   g_assert (type == G_TYPE_PARAM);
109   g_value_register_transform_func (G_TYPE_PARAM, G_TYPE_PARAM, value_param_transform_value);
110 }
111
112 static void
113 g_param_spec_class_base_init (GParamSpecClass *class)
114 {
115 }
116
117 static void
118 g_param_spec_class_base_finalize (GParamSpecClass *class)
119 {
120 }
121
122 static void
123 g_param_spec_class_init (GParamSpecClass *class,
124                          gpointer         class_data)
125 {
126   quark_floating = g_quark_from_static_string ("GParamSpec-floating");
127
128   class->value_type = G_TYPE_NONE;
129   class->finalize = g_param_spec_finalize;
130   class->value_set_default = NULL;
131   class->value_validate = NULL;
132   class->values_cmp = NULL;
133 }
134
135 static void
136 g_param_spec_init (GParamSpec      *pspec,
137                    GParamSpecClass *class)
138 {
139   pspec->name = NULL;
140   pspec->_nick = NULL;
141   pspec->_blurb = NULL;
142   pspec->flags = 0;
143   pspec->value_type = class->value_type;
144   pspec->owner_type = 0;
145   pspec->qdata = NULL;
146   pspec->ref_count = 1;
147   pspec->param_id = 0;
148   g_datalist_id_set_data (&pspec->qdata, quark_floating, GUINT_TO_POINTER (TRUE));
149 }
150
151 static void
152 g_param_spec_finalize (GParamSpec *pspec)
153 {
154   g_datalist_clear (&pspec->qdata);
155
156   if (!(pspec->flags & G_PARAM_STATIC_NAME))
157     g_free (pspec->name);
158   
159   if (!(pspec->flags & G_PARAM_STATIC_NICK))
160     g_free (pspec->_nick);
161
162   if (!(pspec->flags & G_PARAM_STATIC_BLURB))
163     g_free (pspec->_blurb);
164
165   g_type_free_instance ((GTypeInstance*) pspec);
166 }
167
168 GParamSpec*
169 g_param_spec_ref (GParamSpec *pspec)
170 {
171   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
172
173   G_LOCK (pspec_ref_count);
174   if (pspec->ref_count > 0)
175     {
176       pspec->ref_count += 1;
177       G_UNLOCK (pspec_ref_count);
178     }
179   else
180     {
181       G_UNLOCK (pspec_ref_count);
182       g_return_val_if_fail (pspec->ref_count > 0, NULL);
183     }
184   
185   return pspec;
186 }
187
188 void
189 g_param_spec_unref (GParamSpec *pspec)
190 {
191   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
192
193   G_LOCK (pspec_ref_count);
194   if (pspec->ref_count > 0)
195     {
196       gboolean need_finalize;
197
198       /* sync with _sink */
199       pspec->ref_count -= 1;
200       need_finalize = pspec->ref_count == 0;
201       G_UNLOCK (pspec_ref_count);
202       if (need_finalize)
203         G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
204     }
205   else
206     {
207       G_UNLOCK (pspec_ref_count);
208       g_return_if_fail (pspec->ref_count > 0);
209     }
210 }
211
212 void
213 g_param_spec_sink (GParamSpec *pspec)
214 {
215   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
216
217   G_LOCK (pspec_ref_count);
218   if (pspec->ref_count > 0)
219     {
220       if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating))
221         {
222           /* sync with _unref */
223           if (pspec->ref_count > 1)
224             pspec->ref_count -= 1;
225           else
226             {
227               G_UNLOCK (pspec_ref_count);
228               g_param_spec_unref (pspec);
229
230               return;
231             }
232         }
233       G_UNLOCK (pspec_ref_count);
234     }
235   else
236     {
237       G_UNLOCK (pspec_ref_count);
238       g_return_if_fail (pspec->ref_count > 0);
239     }
240 }
241
242 G_CONST_RETURN gchar*
243 g_param_spec_get_name (GParamSpec *pspec)
244 {
245   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
246
247   return pspec->name;
248 }
249
250 G_CONST_RETURN gchar*
251 g_param_spec_get_nick (GParamSpec *pspec)
252 {
253   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
254
255   if (pspec->_nick)
256     return pspec->_nick;
257   else
258     {
259       GParamSpec *redirect_target;
260
261       redirect_target = g_param_spec_get_redirect_target (pspec);
262       if (redirect_target && redirect_target->_nick)
263         return redirect_target->_nick;
264     }
265
266   return pspec->name;
267 }
268
269 G_CONST_RETURN gchar*
270 g_param_spec_get_blurb (GParamSpec *pspec)
271 {
272   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
273
274   if (pspec->_blurb)
275     return pspec->_blurb;
276   else
277     {
278       GParamSpec *redirect_target;
279
280       redirect_target = g_param_spec_get_redirect_target (pspec);
281       if (redirect_target && redirect_target->_blurb)
282         return redirect_target->_blurb;
283     }
284
285   return NULL;
286 }
287
288 static void
289 canonicalize_key (gchar *key)
290 {
291   gchar *p;
292   
293   for (p = key; *p != 0; p++)
294     {
295       gchar c = *p;
296       
297       if (c != '-' &&
298           (c < '0' || c > '9') &&
299           (c < 'A' || c > 'Z') &&
300           (c < 'a' || c > 'z'))
301         *p = '-';
302     }
303 }
304
305 static gboolean
306 is_canonical (gchar *key)
307 {
308   gchar *p;
309
310   for (p = key; *p != 0; p++)
311     {
312       gchar c = *p;
313       
314       if (c != '-' &&
315           (c < '0' || c > '9') &&
316           (c < 'A' || c > 'Z') &&
317           (c < 'a' || c > 'z'))
318         return FALSE;
319     }
320
321   return TRUE;
322 }
323
324 gpointer
325 g_param_spec_internal (GType        param_type,
326                        const gchar *name,
327                        const gchar *nick,
328                        const gchar *blurb,
329                        GParamFlags  flags)
330 {
331   GParamSpec *pspec;
332   
333   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
334   g_return_val_if_fail (name != NULL, NULL);
335   g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
336   g_return_val_if_fail (!(flags & G_PARAM_STATIC_NAME) || is_canonical (name), NULL);
337   
338   pspec = (gpointer) g_type_create_instance (param_type);
339
340   if ((flags & G_PARAM_STATIC_NAME))
341     pspec->name = name;
342   else
343     {
344       pspec->name = g_strdup (name);
345       canonicalize_key (pspec->name);
346     }
347
348   if (flags & G_PARAM_STATIC_NICK)
349     pspec->_nick = nick;
350   else
351     pspec->_nick = g_strdup (nick);
352
353   if (flags & G_PARAM_STATIC_BLURB)
354     pspec->_blurb = blurb;
355   else
356     pspec->_blurb = g_strdup (blurb);
357
358   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
359   
360   return pspec;
361 }
362
363 gpointer
364 g_param_spec_get_qdata (GParamSpec *pspec,
365                         GQuark      quark)
366 {
367   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
368   
369   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
370 }
371
372 void
373 g_param_spec_set_qdata (GParamSpec *pspec,
374                         GQuark      quark,
375                         gpointer    data)
376 {
377   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
378   g_return_if_fail (quark > 0);
379
380   g_datalist_id_set_data (&pspec->qdata, quark, data);
381 }
382
383 void
384 g_param_spec_set_qdata_full (GParamSpec    *pspec,
385                              GQuark         quark,
386                              gpointer       data,
387                              GDestroyNotify destroy)
388 {
389   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
390   g_return_if_fail (quark > 0);
391
392   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
393 }
394
395 gpointer
396 g_param_spec_steal_qdata (GParamSpec *pspec,
397                           GQuark      quark)
398 {
399   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
400   g_return_val_if_fail (quark > 0, NULL);
401   
402   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
403 }
404
405 GParamSpec*
406 g_param_spec_get_redirect_target (GParamSpec *pspec)
407 {
408   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
409
410   if (G_IS_PARAM_SPEC_OVERRIDE (pspec))
411     {
412       GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
413
414       return ospec->overridden;
415     }
416   else
417     return NULL;
418 }
419
420 void
421 g_param_value_set_default (GParamSpec *pspec,
422                            GValue     *value)
423 {
424   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
425   g_return_if_fail (G_IS_VALUE (value));
426   g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
427
428   g_value_reset (value);
429   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
430 }
431
432 gboolean
433 g_param_value_defaults (GParamSpec *pspec,
434                         GValue     *value)
435 {
436   GValue dflt_value = { 0, };
437   gboolean defaults;
438
439   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
440   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
441   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
442
443   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
444   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
445   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
446   g_value_unset (&dflt_value);
447
448   return defaults;
449 }
450
451 gboolean
452 g_param_value_validate (GParamSpec *pspec,
453                         GValue     *value)
454 {
455   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
456   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
457   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
458
459   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
460     {
461       GValue oval = *value;
462
463       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
464           memcmp (&oval.data, &value->data, sizeof (oval.data)))
465         return TRUE;
466     }
467
468   return FALSE;
469 }
470
471 gboolean
472 g_param_value_convert (GParamSpec   *pspec,
473                        const GValue *src_value,
474                        GValue       *dest_value,
475                        gboolean      strict_validation)
476 {
477   GValue tmp_value = { 0, };
478
479   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
480   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
481   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
482   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);
483
484   /* better leave dest_value untouched when returning FALSE */
485
486   g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
487   if (g_value_transform (src_value, &tmp_value) &&
488       (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
489     {
490       g_value_unset (dest_value);
491       
492       /* values are relocatable */
493       memcpy (dest_value, &tmp_value, sizeof (tmp_value));
494       
495       return TRUE;
496     }
497   else
498     {
499       g_value_unset (&tmp_value);
500       
501       return FALSE;
502     }
503 }
504
505 gint
506 g_param_values_cmp (GParamSpec   *pspec,
507                     const GValue *value1,
508                     const GValue *value2)
509 {
510   gint cmp;
511
512   /* param_values_cmp() effectively does: value1 - value2
513    * so the return values are:
514    * -1)  value1 < value2
515    *  0)  value1 == value2
516    *  1)  value1 > value2
517    */
518   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
519   g_return_val_if_fail (G_IS_VALUE (value1), 0);
520   g_return_val_if_fail (G_IS_VALUE (value2), 0);
521   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
522   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
523
524   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
525
526   return CLAMP (cmp, -1, 1);
527 }
528
529 static void
530 value_param_init (GValue *value)
531 {
532   value->data[0].v_pointer = NULL;
533 }
534
535 static void
536 value_param_free_value (GValue *value)
537 {
538   if (value->data[0].v_pointer)
539     g_param_spec_unref (value->data[0].v_pointer);
540 }
541
542 static void
543 value_param_copy_value (const GValue *src_value,
544                         GValue       *dest_value)
545 {
546   if (src_value->data[0].v_pointer)
547     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
548   else
549     dest_value->data[0].v_pointer = NULL;
550 }
551
552 static void
553 value_param_transform_value (const GValue *src_value,
554                              GValue       *dest_value)
555 {
556   if (src_value->data[0].v_pointer &&
557       g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
558     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
559   else
560     dest_value->data[0].v_pointer = NULL;
561 }
562
563 static gpointer
564 value_param_peek_pointer (const GValue *value)
565 {
566   return value->data[0].v_pointer;
567 }
568
569 static gchar*
570 value_param_collect_value (GValue      *value,
571                            guint        n_collect_values,
572                            GTypeCValue *collect_values,
573                            guint        collect_flags)
574 {
575   if (collect_values[0].v_pointer)
576     {
577       GParamSpec *param = collect_values[0].v_pointer;
578
579       if (param->g_type_instance.g_class == NULL)
580         return g_strconcat ("invalid unclassed param spec pointer for value type `",
581                             G_VALUE_TYPE_NAME (value),
582                             "'",
583                             NULL);
584       else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
585         return g_strconcat ("invalid param spec type `",
586                             G_PARAM_SPEC_TYPE_NAME (param),
587                             "' for value type `",
588                             G_VALUE_TYPE_NAME (value),
589                             "'",
590                             NULL);
591       value->data[0].v_pointer = g_param_spec_ref (param);
592     }
593   else
594     value->data[0].v_pointer = NULL;
595
596   return NULL;
597 }
598
599 static gchar*
600 value_param_lcopy_value (const GValue *value,
601                          guint         n_collect_values,
602                          GTypeCValue  *collect_values,
603                          guint         collect_flags)
604 {
605   GParamSpec **param_p = collect_values[0].v_pointer;
606
607   if (!param_p)
608     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
609
610   if (!value->data[0].v_pointer)
611     *param_p = NULL;
612   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
613     *param_p = value->data[0].v_pointer;
614   else
615     *param_p = g_param_spec_ref (value->data[0].v_pointer);
616
617   return NULL;
618 }
619
620
621 /* --- param spec pool --- */
622 struct _GParamSpecPool
623 {
624   GStaticMutex smutex;
625   gboolean     type_prefixing;
626   GHashTable  *hash_table;
627 };
628
629 static guint
630 param_spec_pool_hash (gconstpointer key_spec)
631 {
632   const GParamSpec *key = key_spec;
633   const gchar *p;
634   guint h = key->owner_type;
635
636   for (p = key->name; *p; p++)
637     h = (h << 5) - h + *p;
638
639   return h;
640 }
641
642 static gboolean
643 param_spec_pool_equals (gconstpointer key_spec_1,
644                         gconstpointer key_spec_2)
645 {
646   const GParamSpec *key1 = key_spec_1;
647   const GParamSpec *key2 = key_spec_2;
648
649   return (key1->owner_type == key2->owner_type &&
650           strcmp (key1->name, key2->name) == 0);
651 }
652
653 GParamSpecPool*
654 g_param_spec_pool_new (gboolean type_prefixing)
655 {
656   static GStaticMutex init_smutex = G_STATIC_MUTEX_INIT;
657   GParamSpecPool *pool = g_new (GParamSpecPool, 1);
658
659   memcpy (&pool->smutex, &init_smutex, sizeof (init_smutex));
660   pool->type_prefixing = type_prefixing != FALSE;
661   pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);
662
663   return pool;
664 }
665
666 void
667 g_param_spec_pool_insert (GParamSpecPool *pool,
668                           GParamSpec     *pspec,
669                           GType           owner_type)
670 {
671   gchar *p;
672   
673   if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
674     {
675       G_SLOCK (&pool->smutex);
676       for (p = pspec->name; *p; p++)
677         {
678           if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
679             {
680               g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
681               G_SUNLOCK (&pool->smutex);
682               return;
683             }
684         }
685       
686       pspec->owner_type = owner_type;
687       g_param_spec_ref (pspec);
688       g_hash_table_insert (pool->hash_table, pspec, pspec);
689       G_SUNLOCK (&pool->smutex);
690     }
691   else
692     {
693       g_return_if_fail (pool != NULL);
694       g_return_if_fail (pspec);
695       g_return_if_fail (owner_type > 0);
696       g_return_if_fail (pspec->owner_type == 0);
697     }
698 }
699
700 void
701 g_param_spec_pool_remove (GParamSpecPool *pool,
702                           GParamSpec     *pspec)
703 {
704   if (pool && pspec)
705     {
706       G_SLOCK (&pool->smutex);
707       if (g_hash_table_remove (pool->hash_table, pspec))
708         g_param_spec_unref (pspec);
709       else
710         g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
711       G_SUNLOCK (&pool->smutex);
712     }
713   else
714     {
715       g_return_if_fail (pool != NULL);
716       g_return_if_fail (pspec);
717     }
718 }
719
720 static inline GParamSpec*
721 param_spec_ht_lookup (GHashTable  *hash_table,
722                       const gchar *param_name,
723                       GType        owner_type,
724                       gboolean     walk_ancestors)
725 {
726   GParamSpec key, *pspec;
727
728   key.owner_type = owner_type;
729   key.name = (gchar*) param_name;
730   if (walk_ancestors)
731     do
732       {
733         pspec = g_hash_table_lookup (hash_table, &key);
734         if (pspec)
735           return pspec;
736         key.owner_type = g_type_parent (key.owner_type);
737       }
738     while (key.owner_type);
739   else
740     pspec = g_hash_table_lookup (hash_table, &key);
741
742   if (!pspec)
743     {
744       /* try canonicalized form */
745       key.name = g_strdup (param_name);
746       key.owner_type = owner_type;
747       
748       canonicalize_key (key.name);
749       if (walk_ancestors)
750         do
751           {
752             pspec = g_hash_table_lookup (hash_table, &key);
753             if (pspec)
754               {
755                 g_free (key.name);
756                 return pspec;
757               }
758             key.owner_type = g_type_parent (key.owner_type);
759           }
760         while (key.owner_type);
761       else
762         pspec = g_hash_table_lookup (hash_table, &key);
763       g_free (key.name);
764     }
765
766   return pspec;
767 }
768
769 GParamSpec*
770 g_param_spec_pool_lookup (GParamSpecPool *pool,
771                           const gchar    *param_name,
772                           GType           owner_type,
773                           gboolean        walk_ancestors)
774 {
775   GParamSpec *pspec;
776   gchar *delim;
777
778   if (!pool || !param_name)
779     {
780       g_return_val_if_fail (pool != NULL, NULL);
781       g_return_val_if_fail (param_name != NULL, NULL);
782     }
783
784   G_SLOCK (&pool->smutex);
785
786   delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
787
788   /* try quick and away, i.e. without prefix */
789   if (!delim)
790     {
791       pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
792       G_SUNLOCK (&pool->smutex);
793
794       return pspec;
795     }
796
797   /* strip type prefix */
798   if (pool->type_prefixing && delim[1] == ':')
799     {
800       guint l = delim - param_name;
801       gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
802       GType type;
803       
804       strncpy (buffer, param_name, delim - param_name);
805       buffer[l] = 0;
806       type = g_type_from_name (buffer);
807       if (l >= 32)
808         g_free (buffer);
809       if (type)         /* type==0 isn't a valid type pefix */
810         {
811           /* sanity check, these cases don't make a whole lot of sense */
812           if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
813             {
814               G_SUNLOCK (&pool->smutex);
815
816               return NULL;
817             }
818           owner_type = type;
819           param_name += l + 2;
820           pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
821           G_SUNLOCK (&pool->smutex);
822
823           return pspec;
824         }
825     }
826   /* malformed param_name */
827
828   G_SUNLOCK (&pool->smutex);
829
830   return NULL;
831 }
832
833 static void
834 pool_list (gpointer key,
835            gpointer value,
836            gpointer user_data)
837 {
838   GParamSpec *pspec = value;
839   gpointer *data = user_data;
840   GType owner_type = (GType) data[1];
841
842   if (owner_type == pspec->owner_type)
843     data[0] = g_list_prepend (data[0], pspec);
844 }
845
846 GList*
847 g_param_spec_pool_list_owned (GParamSpecPool *pool,
848                               GType           owner_type)
849 {
850   gpointer data[2];
851
852   g_return_val_if_fail (pool != NULL, NULL);
853   g_return_val_if_fail (owner_type > 0, NULL);
854   
855   G_SLOCK (&pool->smutex);
856   data[0] = NULL;
857   data[1] = (gpointer) owner_type;
858   g_hash_table_foreach (pool->hash_table, pool_list, &data);
859   G_SUNLOCK (&pool->smutex);
860
861   return data[0];
862 }
863
864 static gint
865 pspec_compare_id (gconstpointer a,
866                   gconstpointer b)
867 {
868   const GParamSpec *pspec1 = a, *pspec2 = b;
869
870   return pspec1->param_id < pspec2->param_id ? -1 : pspec1->param_id > pspec2->param_id;
871 }
872
873 static inline GSList*
874 pspec_list_remove_overridden_and_redirected (GSList     *plist,
875                                              GHashTable *ht,
876                                              GType       owner_type,
877                                              guint      *n_p)
878 {
879   GSList *rlist = NULL;
880
881   while (plist)
882     {
883       GSList *tmp = plist->next;
884       GParamSpec *pspec = plist->data;
885       GParamSpec *found;
886       gboolean remove = FALSE;
887
888       /* Remove paramspecs that are redirected, and also paramspecs
889        * that have are overridden by non-redirected properties.
890        * The idea is to get the single paramspec for each name that
891        * best corresponds to what the application sees.
892        */
893       if (g_param_spec_get_redirect_target (pspec))
894         remove = TRUE;
895       else
896         {
897           found = param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE);
898           if (found != pspec)
899             {
900               GParamSpec *redirect = g_param_spec_get_redirect_target (found);
901               if (redirect != pspec)
902                 remove = TRUE;
903             }
904         }
905
906       if (remove)
907         {
908           g_slist_free_1 (plist);
909         }
910       else
911         {
912           plist->next = rlist;
913           rlist = plist;
914           *n_p += 1;
915         }
916       plist = tmp;
917     }
918   return rlist;
919 }
920
921 static void
922 pool_depth_list (gpointer key,
923                  gpointer value,
924                  gpointer user_data)
925 {
926   GParamSpec *pspec = value;
927   gpointer *data = user_data;
928   GSList **slists = data[0];
929   GType owner_type = (GType) data[1];
930
931   if (g_type_is_a (owner_type, pspec->owner_type))
932     {
933       if (G_TYPE_IS_INTERFACE (pspec->owner_type))
934         {
935           slists[0] = g_slist_prepend (slists[0], pspec);
936         }
937       else
938         {
939           guint d = g_type_depth (pspec->owner_type);
940
941           slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
942         }
943     }
944 }
945
946 /* We handle interfaces specially since we don't want to
947  * count interface prerequsites like normal inheritance;
948  * the property comes from the direct inheritance from
949  * the prerequisite class, not from the interface that
950  * prerequires it.
951  * 
952  * also 'depth' isn't a meaningful concept for interface
953  * prerequites.
954  */
955 static void
956 pool_depth_list_for_interface (gpointer key,
957                                gpointer value,
958                                gpointer user_data)
959 {
960   GParamSpec *pspec = value;
961   gpointer *data = user_data;
962   GSList **slists = data[0];
963   GType owner_type = (GType) data[1];
964
965   if (pspec->owner_type == owner_type)
966     slists[0] = g_slist_prepend (slists[0], pspec);
967 }
968
969 GParamSpec** /* free result */
970 g_param_spec_pool_list (GParamSpecPool *pool,
971                         GType           owner_type,
972                         guint          *n_pspecs_p)
973 {
974   GParamSpec **pspecs, **p;
975   GSList **slists, *node;
976   gpointer data[2];
977   guint d, i;
978
979   g_return_val_if_fail (pool != NULL, NULL);
980   g_return_val_if_fail (owner_type > 0, NULL);
981   g_return_val_if_fail (n_pspecs_p != NULL, NULL);
982   
983   G_SLOCK (&pool->smutex);
984   *n_pspecs_p = 0;
985   d = g_type_depth (owner_type);
986   slists = g_new0 (GSList*, d);
987   data[0] = slists;
988   data[1] = (gpointer) owner_type;
989
990   g_hash_table_foreach (pool->hash_table,
991                         G_TYPE_IS_INTERFACE (owner_type) ?
992                            pool_depth_list_for_interface :
993                            pool_depth_list,
994                         &data);
995   
996   for (i = 0; i < d; i++)
997     slists[i] = pspec_list_remove_overridden_and_redirected (slists[i], pool->hash_table, owner_type, n_pspecs_p);
998   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
999   p = pspecs;
1000   for (i = 0; i < d; i++)
1001     {
1002       slists[i] = g_slist_sort (slists[i], pspec_compare_id);
1003       for (node = slists[i]; node; node = node->next)
1004         *p++ = node->data;
1005       g_slist_free (slists[i]);
1006     }
1007   *p++ = NULL;
1008   g_free (slists);
1009   G_SUNLOCK (&pool->smutex);
1010
1011   return pspecs;
1012 }
1013
1014
1015 /* --- auxillary functions --- */
1016 typedef struct
1017 {
1018   /* class portion */
1019   GType           value_type;
1020   void          (*finalize)             (GParamSpec   *pspec);
1021   void          (*value_set_default)    (GParamSpec   *pspec,
1022                                          GValue       *value);
1023   gboolean      (*value_validate)       (GParamSpec   *pspec,
1024                                          GValue       *value);
1025   gint          (*values_cmp)           (GParamSpec   *pspec,
1026                                          const GValue *value1,
1027                                          const GValue *value2);
1028 } ParamSpecClassInfo;
1029
1030 static void
1031 param_spec_generic_class_init (gpointer g_class,
1032                                gpointer class_data)
1033 {
1034   GParamSpecClass *class = g_class;
1035   ParamSpecClassInfo *info = class_data;
1036
1037   class->value_type = info->value_type;
1038   if (info->finalize)
1039     class->finalize = info->finalize;                   /* optional */
1040   class->value_set_default = info->value_set_default;
1041   if (info->value_validate)
1042     class->value_validate = info->value_validate;       /* optional */
1043   class->values_cmp = info->values_cmp;
1044   g_free (class_data);
1045 }
1046
1047 static void
1048 default_value_set_default (GParamSpec *pspec,
1049                            GValue     *value)
1050 {
1051   /* value is already zero initialized */
1052 }
1053
1054 static gint
1055 default_values_cmp (GParamSpec   *pspec,
1056                     const GValue *value1,
1057                     const GValue *value2)
1058 {
1059   return memcmp (&value1->data, &value2->data, sizeof (value1->data));
1060 }
1061
1062 GType
1063 g_param_type_register_static (const gchar              *name,
1064                               const GParamSpecTypeInfo *pspec_info)
1065 {
1066   GTypeInfo info = {
1067     sizeof (GParamSpecClass),      /* class_size */
1068     NULL,                          /* base_init */
1069     NULL,                          /* base_destroy */
1070     param_spec_generic_class_init, /* class_init */
1071     NULL,                          /* class_destroy */
1072     NULL,                          /* class_data */
1073     0,                             /* instance_size */
1074     16,                            /* n_preallocs */
1075     NULL,                          /* instance_init */
1076   };
1077   ParamSpecClassInfo *cinfo;
1078
1079   g_return_val_if_fail (name != NULL, 0);
1080   g_return_val_if_fail (pspec_info != NULL, 0);
1081   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
1082   g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
1083   g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
1084   /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
1085   /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
1086   /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */
1087
1088   info.instance_size = pspec_info->instance_size;
1089   info.n_preallocs = pspec_info->n_preallocs;
1090   info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
1091   cinfo = g_new (ParamSpecClassInfo, 1);
1092   cinfo->value_type = pspec_info->value_type;
1093   cinfo->finalize = pspec_info->finalize;
1094   cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
1095   cinfo->value_validate = pspec_info->value_validate;
1096   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
1097   info.class_data = cinfo;
1098
1099   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
1100 }
1101
1102 void
1103 g_value_set_param (GValue     *value,
1104                    GParamSpec *param)
1105 {
1106   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1107   if (param)
1108     g_return_if_fail (G_IS_PARAM_SPEC (param));
1109
1110   if (value->data[0].v_pointer)
1111     g_param_spec_unref (value->data[0].v_pointer);
1112   value->data[0].v_pointer = param;
1113   if (value->data[0].v_pointer)
1114     g_param_spec_ref (value->data[0].v_pointer);
1115 }
1116
1117 void
1118 g_value_set_param_take_ownership (GValue     *value,
1119                                   GParamSpec *param)
1120 {
1121   g_value_take_param (value, param);
1122 }
1123
1124 void
1125 g_value_take_param (GValue     *value,
1126                     GParamSpec *param)
1127 {
1128   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1129   if (param)
1130     g_return_if_fail (G_IS_PARAM_SPEC (param));
1131
1132   if (value->data[0].v_pointer)
1133     g_param_spec_unref (value->data[0].v_pointer);
1134   value->data[0].v_pointer = param; /* we take over the reference count */
1135 }
1136
1137 GParamSpec*
1138 g_value_get_param (const GValue *value)
1139 {
1140   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1141
1142   return value->data[0].v_pointer;
1143 }
1144
1145 GParamSpec*
1146 g_value_dup_param (const GValue *value)
1147 {
1148   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1149
1150   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
1151 }
1152
1153 #define __G_PARAM_C__
1154 #include "gobjectaliasdef.c"