b4a3e8eb994a654da73e3f87eff80d4035f72ffe
[gnome.gobject-introspection] / girepository / gdump.c
1 /* GObject introspection: Dump introspection data
2  *
3  * Copyright (C) 2008 Colin Walters <walters@verbum.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <stdlib.h>
22
23 #include <glib.h>
24 #include <glib-object.h>
25 #include <gio/gio.h>
26
27 #include "girepository.h"
28 #include "config.h"
29
30 #include <string.h>
31
32 static void
33 escaped_printf (GOutputStream *out, const char *fmt, ...)
34 {
35   char *str;
36   va_list args;
37   gsize written;
38   GError *error = NULL;
39
40   va_start (args, fmt);
41
42   str = g_markup_vprintf_escaped (fmt, args);
43   if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
44     {
45       g_critical ("failed to write to iochannel: %s", error->message);
46       g_clear_error (&error);
47     }
48   g_free (str);
49
50   va_end (args);
51 }
52
53 static void
54 goutput_write (GOutputStream *out, const char *str)
55 {
56   gsize written;
57   GError *error = NULL;
58   if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
59     {
60       g_critical ("failed to write to iochannel: %s", error->message);
61       g_clear_error (&error);
62     }
63 }
64
65 typedef GType (*GetTypeFunc)(void);
66
67 static GType
68 invoke_get_type (GModule *self, const char *symbol, GError **error)
69 {
70   GetTypeFunc sym;
71
72   if (!g_module_symbol (self, symbol, (void**)&sym))
73     {
74       g_set_error (error,
75                    G_IO_ERROR,
76                    G_IO_ERROR_FAILED,
77                    "Failed to find symbol '%s'", symbol);
78       return G_TYPE_INVALID;
79     }
80
81   return sym ();
82 }
83
84 static void
85 dump_properties (GType type, GOutputStream *out)
86 {
87   guint i;
88   guint n_properties;
89   GParamSpec **props;
90
91   if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
92     {
93       GObjectClass *klass;
94       klass = g_type_class_ref (type);
95       props = g_object_class_list_properties (klass, &n_properties);
96     }
97   else
98     {
99       void *klass;
100       klass = g_type_default_interface_ref (type);
101       props = g_object_interface_list_properties (klass, &n_properties);
102     }
103
104   for (i = 0; i < n_properties; i++)
105     {
106       GParamSpec *prop;
107
108       prop = props[i];
109       if (prop->owner_type != type)
110         continue;
111
112       escaped_printf (out, "    <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
113                       prop->name, g_type_name (prop->value_type), prop->flags);
114     }
115   g_free (props);
116 }
117
118 static void
119 dump_signals (GType type, GOutputStream *out)
120 {
121   guint i;
122   guint n_sigs;
123   guint *sig_ids;
124
125   sig_ids = g_signal_list_ids (type, &n_sigs);
126   for (i = 0; i < n_sigs; i++)
127     {
128       guint sigid;
129       GSignalQuery query;
130       guint j;
131
132       sigid = sig_ids[i];
133       g_signal_query (sigid, &query);
134
135       escaped_printf (out, "    <signal name=\"%s\" return=\"%s\">\n",
136                       query.signal_name, g_type_name (query.return_type));
137
138       for (j = 0; j < query.n_params; j++)
139         {
140           escaped_printf (out, "      <param type=\"%s\"/>\n",
141                           g_type_name (query.param_types[j]));
142         }
143       goutput_write (out, "    </signal>\n");
144     }
145 }
146
147 static void
148 dump_object_type (GType type, const char *symbol, GOutputStream *out)
149 {
150   guint n_interfaces;
151   guint i;
152   GType *interfaces;
153
154   escaped_printf (out, "  <class name=\"%s\" get-type=\"%s\"",
155                   g_type_name (type), symbol);
156   if (type != G_TYPE_OBJECT)
157     {
158       GString *parent_str;
159       GType parent;
160       gboolean first = TRUE;
161       
162       parent = type;
163       parent_str = g_string_new ("");
164       do
165         {
166           parent = g_type_parent (parent);
167           if (first)
168             first = FALSE;
169           else
170             g_string_append_c (parent_str, ',');
171           g_string_append (parent_str, g_type_name (parent));
172         } while (parent != G_TYPE_OBJECT && parent != G_TYPE_INVALID);
173    
174       escaped_printf (out, " parents=\"%s\"", parent_str->str);
175       
176       g_string_free (parent_str, TRUE);
177     }
178
179   if (G_TYPE_IS_ABSTRACT (type))
180     escaped_printf (out, " abstract=\"1\"");
181   goutput_write (out, ">\n");
182
183   interfaces = g_type_interfaces (type, &n_interfaces);
184   for (i = 0; i < n_interfaces; i++)
185     {
186       GType itype = interfaces[i];
187       escaped_printf (out, "    <implements name=\"%s\"/>\n",
188                       g_type_name (itype));
189     }
190   dump_properties (type, out);
191   dump_signals (type, out);
192   goutput_write (out, "  </class>\n");
193 }
194
195 static void
196 dump_interface_type (GType type, const char *symbol, GOutputStream *out)
197 {
198   guint n_interfaces;
199   guint i;
200   GType *interfaces;
201
202   escaped_printf (out, "  <interface name=\"%s\" get-type=\"%s\">\n",
203                   g_type_name (type), symbol);
204
205   interfaces = g_type_interface_prerequisites (type, &n_interfaces);
206   for (i = 0; i < n_interfaces; i++)
207     {
208       GType itype = interfaces[i];
209       if (itype == G_TYPE_OBJECT)
210         {
211           /* Treat this as implicit for now; in theory GInterfaces are
212            * supported on things like GstMiniObject, but right now
213            * the introspection system only supports GObject.
214            * http://bugzilla.gnome.org/show_bug.cgi?id=559706
215            */
216           continue;
217         }
218       escaped_printf (out, "    <prerequisite name=\"%s\"/>\n",
219                       g_type_name (itype));
220     }
221   dump_properties (type, out);
222   dump_signals (type, out);
223   goutput_write (out, "  </interface>\n");
224 }
225
226 static void
227 dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
228 {
229   escaped_printf (out, "  <boxed name=\"%s\" get-type=\"%s\"/>\n",
230                   g_type_name (type), symbol);
231 }
232
233 static void
234 dump_flags_type (GType type, const char *symbol, GOutputStream *out)
235 {
236   guint i;
237   GFlagsClass *klass;
238
239   klass = g_type_class_ref (type);
240   escaped_printf (out, "  <flags name=\"%s\" get-type=\"%s\">\n",
241                   g_type_name (type), symbol);
242
243   for (i = 0; i < klass->n_values; i++)
244     {
245       GFlagsValue *value = &(klass->values[i]);
246
247       escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
248                       value->value_name, value->value_nick, value->value);
249     }
250   goutput_write (out, "  </flags>\n");
251 }
252
253 static void
254 dump_enum_type (GType type, const char *symbol, GOutputStream *out)
255 {
256   guint i;
257   GEnumClass *klass;
258
259   klass = g_type_class_ref (type);
260   escaped_printf (out, "  <enum name=\"%s\" get-type=\"%s\">\n",
261                   g_type_name (type), symbol);
262
263   for (i = 0; i < klass->n_values; i++)
264     {
265       GEnumValue *value = &(klass->values[i]);
266
267       escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
268                       value->value_name, value->value_nick, value->value);
269     }
270   goutput_write (out, "  </enum>");
271 }
272
273 static void
274 dump_type (GType type, const char *symbol, GOutputStream *out)
275 {
276   switch (g_type_fundamental (type))
277     {
278     case G_TYPE_OBJECT:
279       dump_object_type (type, symbol, out);
280       break;
281     case G_TYPE_INTERFACE:
282       dump_interface_type (type, symbol, out);
283       break;
284     case G_TYPE_BOXED:
285       dump_boxed_type (type, symbol, out);
286       break;
287     case G_TYPE_FLAGS:
288       dump_flags_type (type, symbol, out);
289       break;
290     case G_TYPE_ENUM:
291       dump_enum_type (type, symbol, out);
292       break;
293     case G_TYPE_POINTER:
294       /* GValue, etc.  Just skip them. */
295       break;
296     default:
297       /* Other fundamental types such as the once GStreamer and Clutter registers
298        * are not yet interesting from an introspection perspective and should be
299        * ignored
300        */
301       break;
302     }
303 }
304
305 /**
306  * g_irepository_dump:
307  * @arg: Comma-separated pair of input and output filenames
308  * @error: a %GError
309  *
310  * Argument specified is a comma-separated pair of filenames; i.e. of
311  * the form "input.txt,output.xml".  The input file should be a
312  * UTF-8 Unix-line-ending text file, with each line containing the name
313  * of a GType _get_type function.
314  *
315  * The output file should already exist, but be empty.  This function will
316  * overwrite its contents.
317  *
318  * Returns: %TRUE on success, %FALSE on error
319  */
320 gboolean
321 g_irepository_dump (const char *arg, GError **error)
322 {
323   GHashTable *output_types;
324   char **args;
325   GFile *input_file;
326   GFile *output_file;
327   GFileInputStream *input;
328   GFileOutputStream *output;
329   GDataInputStream *in;
330   GModule *self;
331   gboolean caught_error = FALSE;
332
333   self = g_module_open (NULL, 0);
334   if (!self)
335     {
336       g_set_error (error,
337                    G_IO_ERROR,
338                    G_IO_ERROR_FAILED,
339                    "failed to open self: %s",
340                    g_module_error ());
341       return FALSE;
342     }
343
344   args = g_strsplit (arg, ",", 2);
345
346   input_file = g_file_new_for_path (args[0]);
347   output_file = g_file_new_for_path (args[1]);
348
349   input = g_file_read (input_file, NULL, error);
350   if (input == NULL)
351     return FALSE;
352
353   output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
354   if (output == NULL)
355     {
356       g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
357       return FALSE;
358     }
359
360   goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
361   goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
362
363   output_types = g_hash_table_new (NULL, NULL);
364
365   in = g_data_input_stream_new (G_INPUT_STREAM (input));
366   g_object_unref (input);
367
368   while (TRUE)
369     {
370       gsize len;
371       char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
372       GType type;
373
374       if (line == NULL || *line == '\0')
375         {
376           g_free (line);
377           break;
378         }
379
380       g_strchomp (line);
381       type = invoke_get_type (self, line, error);
382
383       if (type == G_TYPE_INVALID)
384         {
385           g_printerr ("Invalid GType: '%s'\n", line);
386           caught_error = TRUE;
387           g_free (line);
388           break;
389         }
390
391       if (g_hash_table_lookup (output_types, (gpointer) type))
392         goto next;
393       g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
394
395       dump_type (type, line, G_OUTPUT_STREAM (output));
396
397     next:
398       g_free (line);
399     }
400
401   g_hash_table_destroy (output_types);
402
403   goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
404
405   {
406     GError **ioerror;
407     /* Avoid overwriting an earlier set error */
408     if (caught_error)
409       ioerror = NULL;
410     else
411       ioerror = error;
412     if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
413       return FALSE;
414     if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
415       return FALSE;
416   }
417
418   return !caught_error;
419 }