implemented closure chaining.
[platform/upstream/glib.git] / gobject / gvalue.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  * FIXME: MT-safety
22  */
23
24 #include <string.h>
25
26 #include "gvalue.h"
27 #include "gvaluecollector.h"
28 #include "gbsearcharray.h"
29
30
31 /* --- typedefs & structures --- */
32 typedef struct {
33   GType src_type;
34   GType dest_type;
35   GValueTransform func;
36 } TransformEntry;
37
38
39 /* --- prototypes --- */
40 static gint     transform_entries_cmp   (gconstpointer bsearch_node1,
41                                          gconstpointer bsearch_node2);
42
43
44 /* --- variables --- */
45 static GBSearchArray *transform_array = NULL;
46 static GBSearchConfig transform_bconfig = G_STATIC_BCONFIG (sizeof (TransformEntry),
47                                                             transform_entries_cmp,
48                                                             0);
49
50
51 /* --- functions --- */
52 void
53 g_value_c_init (void)   /* sync with gtype.c */
54 {
55   transform_array = g_bsearch_array_new (&transform_bconfig);
56 }
57
58 static inline void              /* keep this function in sync with gvaluecollector.h and gboxed.c */
59 value_meminit (GValue *value,
60                GType   value_type)
61 {
62   value->g_type = value_type;
63   memset (value->data, 0, sizeof (value->data));
64 }
65
66 GValue*
67 g_value_init (GValue *value,
68               GType   g_type)
69 {
70   /* g_return_val_if_fail (G_TYPE_IS_VALUE (g_type), NULL);     be more elaborate below */
71   g_return_val_if_fail (value != NULL, NULL);
72   /* g_return_val_if_fail (G_VALUE_TYPE (value) == 0, NULL);    be more elaborate below */
73
74   if (G_TYPE_IS_VALUE (g_type) && G_VALUE_TYPE (value) == 0)
75     {
76       GTypeValueTable *value_table = g_type_value_table_peek (g_type);
77
78       /* setup and init */
79       value_meminit (value, g_type);
80       value_table->value_init (value);
81     }
82   else if (G_VALUE_TYPE (value))
83     g_warning ("%s: cannot initialize GValue with type `%s', the value has already been initialized as `%s'",
84                G_STRLOC,
85                g_type_name (g_type),
86                g_type_name (G_VALUE_TYPE (value)));
87   else /* !G_TYPE_IS_VALUE (g_type) */
88     g_warning ("%s: cannot initialize GValue with type `%s', %s",
89                G_STRLOC,
90                g_type_name (g_type),
91                g_type_value_table_peek (g_type) ?
92                "this type is abstract with regards to GValue use, use a more specific (derived) type" :
93                "this type has no GTypeValueTable implementation");
94   return value;
95 }
96
97 void
98 g_value_copy (const GValue *src_value,
99               GValue       *dest_value)
100 {
101   g_return_if_fail (G_IS_VALUE (src_value));
102   g_return_if_fail (G_IS_VALUE (dest_value));
103   g_return_if_fail (g_value_type_compatible (G_VALUE_TYPE (src_value), G_VALUE_TYPE (dest_value)));
104   
105   if (src_value != dest_value)
106     {
107       GType dest_type = G_VALUE_TYPE (dest_value);
108       GTypeValueTable *value_table = g_type_value_table_peek (dest_type);
109
110       /* make sure dest_value's value is free()d */
111       if (value_table->value_free)
112         value_table->value_free (dest_value);
113
114       /* setup and copy */
115       value_meminit (dest_value, dest_type);
116       value_table->value_copy (src_value, dest_value);
117     }
118 }
119
120 GValue*
121 g_value_reset (GValue *value)
122 {
123   GTypeValueTable *value_table;
124   GType g_type;
125   
126   g_return_val_if_fail (G_IS_VALUE (value), NULL);
127   
128   g_type = G_VALUE_TYPE (value);
129   value_table = g_type_value_table_peek (g_type);
130
131   /* make sure value's value is free()d */
132   if (value_table->value_free)
133     value_table->value_free (value);
134
135   /* setup and init */
136   value_meminit (value, g_type);
137   value_table->value_init (value);
138
139   return value;
140 }
141
142 void
143 g_value_unset (GValue *value)
144 {
145   GTypeValueTable *value_table;
146   
147   g_return_if_fail (G_IS_VALUE (value));
148
149   value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
150
151   if (value_table->value_free)
152     value_table->value_free (value);
153   memset (value, 0, sizeof (*value));
154 }
155
156 gboolean
157 g_value_fits_pointer (const GValue *value)
158 {
159   GTypeValueTable *value_table;
160
161   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
162
163   value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
164
165   return value_table->value_peek_pointer != NULL;
166 }
167
168 gpointer
169 g_value_peek_pointer (const GValue *value)
170 {
171   GTypeValueTable *value_table;
172
173   g_return_val_if_fail (G_IS_VALUE (value), NULL);
174
175   value_table = g_type_value_table_peek (G_VALUE_TYPE (value));
176   if (!value_table->value_peek_pointer)
177     g_return_val_if_fail (g_value_fits_pointer (value) == TRUE, NULL);
178
179   return value_table->value_peek_pointer (value);
180 }
181
182 void
183 g_value_set_instance (GValue  *value,
184                       gpointer instance)
185 {
186   GType g_type;
187   GTypeValueTable *value_table;
188   GTypeCValue cvalue;
189   gchar *error_msg;
190   
191   g_return_if_fail (G_IS_VALUE (value));
192   if (instance)
193     {
194       g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
195       g_return_if_fail (g_value_type_compatible (G_TYPE_FROM_INSTANCE (instance), G_VALUE_TYPE (value)));
196     }
197   
198   g_type = G_VALUE_TYPE (value);
199   value_table = g_type_value_table_peek (g_type);
200   
201   g_return_if_fail (strcmp (value_table->collect_format, "p") == 0);
202   
203   memset (&cvalue, 0, sizeof (cvalue));
204   cvalue.v_pointer = instance;
205   
206   /* make sure value's value is free()d */
207   if (value_table->value_free)
208     value_table->value_free (value);
209
210   /* setup and collect */
211   value_meminit (value, g_type);
212   error_msg = value_table->collect_value (value, 1, &cvalue, 0);
213   if (error_msg)
214     {
215       g_warning ("%s: %s", G_STRLOC, error_msg);
216       g_free (error_msg);
217       
218       /* we purposely leak the value here, it might not be
219        * in a sane state if an error condition occoured
220        */
221       value_meminit (value, g_type);
222       value_table->value_init (value);
223     }
224 }
225
226 static GValueTransform
227 transform_func_lookup (GType src_type,
228                        GType dest_type)
229 {
230   TransformEntry entry;
231
232   entry.src_type = src_type;
233   do
234     {
235       entry.dest_type = dest_type;
236       do
237         {
238           TransformEntry *e;
239           
240           e = g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry);
241           if (e)
242             {
243               /* need to check that there hasn't been a change in value handling */
244               if (g_type_value_table_peek (entry.dest_type) == g_type_value_table_peek (dest_type) &&
245                   g_type_value_table_peek (entry.src_type) == g_type_value_table_peek (src_type))
246                 return e->func;
247             }
248           entry.dest_type = g_type_parent (entry.dest_type);
249         }
250       while (entry.dest_type);
251       
252       entry.src_type = g_type_parent (entry.src_type);
253     }
254   while (entry.src_type);
255
256   return NULL;
257 }
258
259 static gint
260 transform_entries_cmp (gconstpointer bsearch_node1,
261                        gconstpointer bsearch_node2)
262 {
263   const TransformEntry *e1 = bsearch_node1;
264   const TransformEntry *e2 = bsearch_node2;
265   gint cmp = G_BSEARCH_ARRAY_CMP (e1->src_type, e2->src_type);
266
267   if (cmp)
268     return cmp;
269   else
270     return G_BSEARCH_ARRAY_CMP (e1->dest_type, e2->dest_type);
271 }
272
273 void
274 g_value_register_transform_func (GType           src_type,
275                                  GType           dest_type,
276                                  GValueTransform transform_func)
277 {
278   TransformEntry entry;
279
280   g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (src_type));
281   g_return_if_fail (G_TYPE_HAS_VALUE_TABLE (dest_type));
282   g_return_if_fail (transform_func != NULL);
283
284   entry.src_type = src_type;
285   entry.dest_type = dest_type;
286   
287   if (g_bsearch_array_lookup (transform_array, &transform_bconfig, &entry))
288     g_warning ("reregistering value transformation function (%p) for `%s' to `%s'",
289                transform_func,
290                g_type_name (src_type),
291                g_type_name (dest_type));
292
293   entry.func = transform_func;
294
295   transform_array = g_bsearch_array_insert (transform_array, &transform_bconfig, &entry, TRUE);
296 }
297
298 gboolean
299 g_value_type_transformable (GType src_type,
300                             GType dest_type)
301 {
302   g_return_val_if_fail (G_TYPE_IS_VALUE (src_type), FALSE);
303   g_return_val_if_fail (G_TYPE_IS_VALUE (dest_type), FALSE);
304
305   return (g_value_type_compatible (src_type, dest_type) ||
306           transform_func_lookup (src_type, dest_type) != NULL);
307 }
308
309 gboolean
310 g_value_type_compatible (GType src_type,
311                          GType dest_type)
312 {
313   g_return_val_if_fail (G_TYPE_IS_VALUE (src_type), FALSE);
314   g_return_val_if_fail (G_TYPE_IS_VALUE (dest_type), FALSE);
315
316   return (g_type_is_a (src_type, dest_type) &&
317           g_type_value_table_peek (dest_type) == g_type_value_table_peek (src_type));
318 }
319
320 gboolean
321 g_value_transform (const GValue *src_value,
322                    GValue       *dest_value)
323 {
324   GType dest_type;
325
326   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
327   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
328
329   dest_type = G_VALUE_TYPE (dest_value);
330   if (g_value_type_compatible (G_VALUE_TYPE (src_value), dest_type))
331     {
332       g_value_copy (src_value, dest_value);
333       
334       return TRUE;
335     }
336   else
337     {
338       GValueTransform transform = transform_func_lookup (G_VALUE_TYPE (src_value), dest_type);
339
340       if (transform)
341         {
342           g_value_unset (dest_value);
343           
344           /* setup and transform */
345           value_meminit (dest_value, dest_type);
346           transform (src_value, dest_value);
347           
348           return TRUE;
349         }
350     }
351   return FALSE;
352 }