Bug 559706 - interface prequisites
[platform/upstream/gobject-introspection.git] / 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     escaped_printf (out, " parent=\"%s\"", g_type_name (g_type_parent (type)));
158
159   if (G_TYPE_IS_ABSTRACT (type))
160     escaped_printf (out, " abstract=\"1\"");
161   goutput_write (out, ">\n");
162
163   interfaces = g_type_interfaces (type, &n_interfaces);
164   for (i = 0; i < n_interfaces; i++)
165     {
166       GType itype = interfaces[i];
167       escaped_printf (out, "    <implements name=\"%s\"/>\n",
168                       g_type_name (itype));
169     }
170   dump_properties (type, out);
171   dump_signals (type, out);
172   goutput_write (out, "  </class>\n");
173 }
174
175 static void
176 dump_interface_type (GType type, const char *symbol, GOutputStream *out)
177 {
178   guint n_interfaces;
179   guint i;
180   GType *interfaces;
181
182   escaped_printf (out, "  <interface name=\"%s\" get-type=\"%s\">\n",
183                   g_type_name (type), symbol);
184
185   interfaces = g_type_interface_prerequisites (type, &n_interfaces);
186   for (i = 0; i < n_interfaces; i++)
187     {
188       GType itype = interfaces[i];
189       if (itype == G_TYPE_OBJECT)
190         {
191           /* This is implicit */
192           continue;
193         }
194       escaped_printf (out, "    <prerequisite name=\"%s\"/>\n",
195                       g_type_name (itype));
196     }
197   dump_properties (type, out);
198   dump_signals (type, out);
199   goutput_write (out, "  </interface>\n");
200 }
201
202 static void
203 dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
204 {
205   escaped_printf (out, "  <boxed name=\"%s\" get-type=\"%s\"/>\n",
206                   g_type_name (type), symbol);
207 }
208
209 static void
210 dump_flags_type (GType type, const char *symbol, GOutputStream *out)
211 {
212   guint i;
213   GFlagsClass *klass;
214
215   klass = g_type_class_ref (type);
216   escaped_printf (out, "  <flags name=\"%s\" get-type=\"%s\">\n",
217                   g_type_name (type), symbol);
218
219   for (i = 0; i < klass->n_values; i++)
220     {
221       GFlagsValue *value = &(klass->values[i]);
222
223       escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
224                       value->value_name, value->value_nick, value->value);
225     }
226   goutput_write (out, "  </flags>\n");
227 }
228
229 static void
230 dump_enum_type (GType type, const char *symbol, GOutputStream *out)
231 {
232   guint i;
233   GEnumClass *klass;
234
235   klass = g_type_class_ref (type);
236   escaped_printf (out, "  <enum name=\"%s\" get-type=\"%s\">\n",
237                   g_type_name (type), symbol);
238
239   for (i = 0; i < klass->n_values; i++)
240     {
241       GEnumValue *value = &(klass->values[i]);
242
243       escaped_printf (out, "    <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
244                       value->value_name, value->value_nick, value->value);
245     }
246   goutput_write (out, "  </enum>");
247 }
248
249 static void
250 dump_type (GType type, const char *symbol, GOutputStream *out)
251 {
252   switch (g_type_fundamental (type))
253     {
254     case G_TYPE_OBJECT:
255       dump_object_type (type, symbol, out);
256       break;
257     case G_TYPE_INTERFACE:
258       dump_interface_type (type, symbol, out);
259       break;
260     case G_TYPE_BOXED:
261       dump_boxed_type (type, symbol, out);
262       break;
263     case G_TYPE_FLAGS:
264       dump_flags_type (type, symbol, out);
265       break;
266     case G_TYPE_ENUM:
267       dump_enum_type (type, symbol, out);
268       break;
269     case G_TYPE_POINTER:
270       /* GValue, etc.  Just skip them. */
271       break;
272     default:
273       g_warning ("unhandled gtype %s", g_type_name (type));
274     }
275 }
276
277 /**
278  * g_irepository_dump:
279  * @arg: Comma-separated pair of input and output filenames
280  * @error: a %GError
281  *
282  * Argument specified is a comma-separated pair of filenames; i.e. of
283  * the form "input.txt,output.xml".  The input file should be a
284  * UTF-8 Unix-line-ending text file, with each line containing the name
285  * of a GType _get_type function.
286  *
287  * The output file should already exist, but be empty.  This function will
288  * overwrite its contents.
289  *
290  * Returns: %TRUE on success, %FALSE on error
291  */
292 gboolean
293 g_irepository_dump (const char *arg, GError **error)
294 {
295   GHashTable *output_types;
296   char **args;
297   GFile *input_file;
298   GFile *output_file;
299   GFileInputStream *input;
300   GFileOutputStream *output;
301   GDataInputStream *in;
302   GModule *self;
303   gboolean caught_error = FALSE;
304
305   self = g_module_open (NULL, 0);
306   if (!self)
307     {
308       g_set_error (error,
309                    G_IO_ERROR,
310                    G_IO_ERROR_FAILED,
311                    "failed to open self: %s",
312                    g_module_error ());
313       return FALSE;
314     }
315
316   args = g_strsplit (arg, ",", 2);
317
318   input_file = g_file_new_for_path (args[0]);
319   output_file = g_file_new_for_path (args[1]);
320
321   input = g_file_read (input_file, NULL, error);
322   if (input == NULL)
323     return FALSE;
324
325   output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
326   if (output == NULL)
327     {
328       g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
329       return FALSE;
330     }
331
332   goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
333   goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
334
335   output_types = g_hash_table_new (NULL, NULL);
336
337   in = g_data_input_stream_new (G_INPUT_STREAM (input));
338   g_object_unref (input);
339
340   while (TRUE)
341     {
342       gsize len;
343       char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
344       GType type;
345
346       if (line == NULL || *line == '\0')
347         {
348           g_free (line);
349           break;
350         }
351
352       g_strchomp (line);
353       type = invoke_get_type (self, line, error);
354
355       if (type == G_TYPE_INVALID)
356         {
357           caught_error = TRUE;
358           g_free (line);
359           break;
360         }
361
362       if (g_hash_table_lookup (output_types, (gpointer) type))
363         goto next;
364       g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
365
366       dump_type (type, line, G_OUTPUT_STREAM (output));
367
368     next:
369       g_free (line);
370     }
371
372   g_hash_table_destroy (output_types);
373
374   goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
375
376   {
377     GError **ioerror;
378     /* Avoid overwriting an earlier set error */
379     if (caught_error)
380       ioerror = NULL;
381     else
382       ioerror = error;
383     if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
384       return FALSE;
385     if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
386       return FALSE;
387   }
388
389   return !caught_error;
390 }