]> git.street.me.uk Git - andy/viking.git/blob - src/vikgotoxmltool.c
gtk_object_sink has been deprecated since gtk version 2.10, use g_object_ref_sink...
[andy/viking.git] / src / vikgotoxmltool.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2009, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #ifdef HAVE_MATH_H
29 #include "math.h"
30 #endif
31 #include <glib.h>
32 #include <glib/gstdio.h>
33 #include <glib/gprintf.h>
34 #include <glib/gi18n.h>
35
36 #include "viking.h"
37
38 #include "vikgotoxmltool.h"
39
40
41 static void _goto_xml_tool_class_init ( VikGotoXmlToolClass *klass );
42 static void _goto_xml_tool_init ( VikGotoXmlTool *self );
43
44 static void _goto_xml_tool_finalize ( GObject *gob );
45
46 static gchar *_goto_xml_tool_get_url_format ( VikGotoTool *self );
47 static gboolean _goto_xml_tool_parse_file_for_latlon(VikGotoTool *self, gchar *filename, struct LatLon *ll);
48
49 typedef struct _VikGotoXmlToolPrivate VikGotoXmlToolPrivate;
50
51 struct _VikGotoXmlToolPrivate
52 {
53   gchar *url_format;
54   gchar *lat_path;
55   gchar *lat_attr;
56   gchar *lon_path;
57   gchar *lon_attr;
58   
59   struct LatLon ll;
60 };
61
62 #define GOTO_XML_TOOL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
63                                         VIK_GOTO_XML_TOOL_TYPE,          \
64                                         VikGotoXmlToolPrivate))
65
66 GType vik_goto_xml_tool_get_type()
67 {
68   static GType w_type = 0;
69
70   if (!w_type)
71   {
72     static const GTypeInfo w_info = 
73     {
74       sizeof (VikGotoXmlToolClass),
75       NULL, /* base_init */
76       NULL, /* base_finalize */
77       (GClassInitFunc) _goto_xml_tool_class_init,
78       NULL, /* class_finalize */
79       NULL, /* class_data */
80       sizeof (VikGotoXmlTool),
81       0,
82       (GInstanceInitFunc) _goto_xml_tool_init,
83     };
84     w_type = g_type_register_static ( VIK_GOTO_TOOL_TYPE, "VikGotoXmlTool", &w_info, 0 );
85   }
86
87   return w_type;
88 }
89
90 enum
91 {
92   PROP_0,
93
94   PROP_URL_FORMAT,
95   PROP_LAT_PATH,
96   PROP_LAT_ATTR,
97   PROP_LON_PATH,
98   PROP_LON_ATTR,
99 };
100
101 static void
102 _goto_xml_tool_set_property (GObject      *object,
103                              guint         property_id,
104                              const GValue *value,
105                              GParamSpec   *pspec)
106 {
107   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (object);
108   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
109   gchar **splitted = NULL;
110
111   switch (property_id)
112     {
113     case PROP_URL_FORMAT:
114       g_free (priv->url_format);
115       priv->url_format = g_value_dup_string (value);
116       break;
117
118     case PROP_LAT_PATH:
119       splitted = g_strsplit (g_value_get_string (value), "@", 2);
120       g_free (priv->lat_path);
121       priv->lat_path = splitted[0];
122       if (splitted[1])
123       {
124         g_object_set (object, "lat-attr", splitted[1], NULL);
125         g_free (splitted[1]);
126       }
127       /* only free the tab, not the strings */
128       g_free (splitted);
129       splitted = NULL;
130       break;
131
132     case PROP_LAT_ATTR:
133       /* Avoid to overwrite XPATH value */
134       /* NB: This disable future overwriting,
135          but as property is CONSTRUCT_ONLY there is no matter */
136       if (!priv->lat_attr || g_value_get_string (value))
137       {
138         g_free (priv->lat_attr);
139         priv->lat_attr = g_value_dup_string (value);
140       }
141       break;
142
143     case PROP_LON_PATH:
144       splitted = g_strsplit (g_value_get_string (value), "@", 2);
145       g_free (priv->lon_path);
146       priv->lon_path = splitted[0];
147       if (splitted[1])
148       {
149         g_object_set (object, "lon-attr", splitted[1], NULL);
150         g_free (splitted[1]);
151       }
152       /* only free the tab, not the strings */
153       g_free (splitted);
154       splitted = NULL;
155       break;
156
157     case PROP_LON_ATTR:
158       /* Avoid to overwrite XPATH value */
159       /* NB: This disable future overwriting,
160          but as property is CONSTRUCT_ONLY there is no matter */
161       if (!priv->lon_attr || g_value_get_string (value))
162       {
163         g_free (priv->lon_attr);
164         priv->lon_attr = g_value_dup_string (value);
165       }
166       break;
167
168     default:
169       /* We don't have any other property... */
170       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
171       break;
172     }
173 }
174
175 static void
176 _goto_xml_tool_get_property (GObject    *object,
177                              guint       property_id,
178                              GValue     *value,
179                              GParamSpec *pspec)
180 {
181   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (object);
182   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
183
184   switch (property_id)
185     {
186     case PROP_URL_FORMAT:
187       g_value_set_string (value, priv->url_format);
188       break;
189
190     case PROP_LAT_PATH:
191       g_value_set_string (value, priv->lat_path);
192       break;
193
194     case PROP_LAT_ATTR:
195       g_value_set_string (value, priv->lat_attr);
196       break;
197
198     case PROP_LON_PATH:
199       g_value_set_string (value, priv->lon_path);
200       break;
201
202     case PROP_LON_ATTR:
203       g_value_set_string (value, priv->lon_attr);
204       break;
205
206     default:
207       /* We don't have any other property... */
208       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
209       break;
210     }
211 }
212
213 static void _goto_xml_tool_class_init ( VikGotoXmlToolClass *klass )
214 {
215   GObjectClass *object_class;
216   VikGotoToolClass *parent_class;
217   GParamSpec *pspec;
218
219   object_class = G_OBJECT_CLASS (klass);
220
221   object_class->finalize = _goto_xml_tool_finalize;
222   object_class->set_property = _goto_xml_tool_set_property;
223   object_class->get_property = _goto_xml_tool_get_property;
224
225
226   pspec = g_param_spec_string ("url-format",
227                                "URL format",
228                                "The format of the URL",
229                                "<no-set>" /* default value */,
230                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
231   g_object_class_install_property (object_class,
232                                    PROP_URL_FORMAT,
233                                    pspec);
234
235   pspec = g_param_spec_string ("lat-path",
236                                "Latitude path",
237                                "XPath of the latitude",
238                                "<no-set>" /* default value */,
239                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
240   g_object_class_install_property (object_class,
241                                    PROP_LAT_PATH,
242                                    pspec);
243
244   pspec = g_param_spec_string ("lat-attr",
245                                "Latitude attribute",
246                                "XML attribute of the latitude",
247                                NULL /* default value */,
248                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
249   g_object_class_install_property (object_class,
250                                    PROP_LAT_ATTR,
251                                    pspec);
252
253   pspec = g_param_spec_string ("lon-path",
254                                "Longitude path",
255                                "XPath of the longitude",
256                                "<no-set>" /* default value */,
257                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
258   g_object_class_install_property (object_class,
259                                    PROP_LON_PATH,
260                                    pspec);
261
262   pspec = g_param_spec_string ("lon-attr",
263                                "Longitude attribute",
264                                "XML attribute of the longitude",
265                                NULL /* default value */,
266                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
267   g_object_class_install_property (object_class,
268                                    PROP_LON_ATTR,
269                                    pspec);
270
271   parent_class = VIK_GOTO_TOOL_CLASS (klass);
272
273   parent_class->get_url_format = _goto_xml_tool_get_url_format;
274   parent_class->parse_file_for_latlon = _goto_xml_tool_parse_file_for_latlon;
275
276   g_type_class_add_private (klass, sizeof (VikGotoXmlToolPrivate));
277 }
278
279 VikGotoXmlTool *vik_goto_xml_tool_new ()
280 {
281   return VIK_GOTO_XML_TOOL ( g_object_new ( VIK_GOTO_XML_TOOL_TYPE, "label", "Google", NULL ) );
282 }
283
284 static void _goto_xml_tool_init ( VikGotoXmlTool *self )
285 {
286   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
287   priv->url_format = NULL;
288   priv->lat_path = NULL;
289   priv->lat_attr = NULL;
290   priv->lon_path = NULL;
291   priv->lon_attr = NULL;
292   // 
293   priv->ll.lat = NAN;
294   priv->ll.lon = NAN;
295 }
296
297 static void _goto_xml_tool_finalize ( GObject *gob )
298 {
299   G_OBJECT_GET_CLASS(gob)->finalize(gob);
300 }
301
302 static gboolean
303 stack_is_path (const GSList *stack,
304                const gchar  *path)
305 {
306   gboolean equal = TRUE;
307   int stack_len = g_list_length((GList *)stack);
308   int i = 0;
309   i = stack_len - 1;
310   while (equal == TRUE && i >= 0)
311   {
312     if (*path != '/')
313       equal = FALSE;
314     else
315       path++;
316     const gchar *current = g_list_nth_data((GList *)stack, i);
317     size_t len = strlen(current);
318     if (strncmp(path, current, len) != 0 )
319       equal = FALSE;
320     else
321     {
322       path += len;
323     }
324     i--;
325   }
326   if (*path != '\0')
327     equal = FALSE;
328   return equal;
329 }
330
331 /* Called for open tags <foo bar="baz"> */
332 static void
333 _start_element (GMarkupParseContext *context,
334                 const gchar         *element_name,
335                 const gchar        **attribute_names,
336                 const gchar        **attribute_values,
337                 gpointer             user_data,
338                 GError             **error)
339 {
340   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (user_data);
341   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
342   const GSList *stack = g_markup_parse_context_get_element_stack (context);
343   /* Longitude */
344   if (priv->lon_attr != NULL && isnan(priv->ll.lon) && stack_is_path (stack, priv->lon_path))
345         {
346                 int i=0;
347                 while (attribute_names[i] != NULL)
348                 {
349                         if (strcmp (attribute_names[i], priv->lon_attr) == 0)
350                         {
351                                 priv->ll.lon = g_ascii_strtod(attribute_values[i], NULL);
352                         }
353                         i++;
354                 }
355         }
356   /* Latitude */
357   if (priv->lat_attr != NULL && isnan(priv->ll.lat) && stack_is_path (stack, priv->lat_path))
358         {
359                 int i=0;
360                 while (attribute_names[i] != NULL)
361                 {
362                         if (strcmp (attribute_names[i], priv->lat_attr) == 0)
363                         {
364                                 priv->ll.lat = g_ascii_strtod(attribute_values[i], NULL);
365                         }
366                         i++;
367                 }
368         }
369 }
370
371 /* Called for character data */
372 /* text is not nul-terminated */
373 static void
374 _text (GMarkupParseContext *context,
375        const gchar         *text,
376        gsize                text_len,  
377        gpointer             user_data,
378        GError             **error)
379 {
380   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (user_data);
381   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
382   const GSList *stack = g_markup_parse_context_get_element_stack (context);
383   gchar *textl = g_strndup(text, text_len);
384   /* Store only first result */
385         if (priv->lat_attr == NULL && isnan(priv->ll.lat) && stack_is_path (stack, priv->lat_path))
386         {
387     priv->ll.lat = g_ascii_strtod(textl, NULL);
388         }
389         if (priv->lon_attr == NULL && isnan(priv->ll.lon) && stack_is_path (stack, priv->lon_path))
390         {
391     priv->ll.lon = g_ascii_strtod(textl, NULL);
392         }
393   g_free(textl);
394 }
395
396 static gboolean
397 _goto_xml_tool_parse_file_for_latlon(VikGotoTool *self, gchar *filename, struct LatLon *ll)
398 {
399         GMarkupParser xml_parser;
400         GMarkupParseContext *xml_context = NULL;
401         GError *error = NULL;
402         VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
403   g_return_val_if_fail(priv != NULL, FALSE);
404
405   g_debug ("%s: %s@%s, %s@%s",
406            __FUNCTION__,
407            priv->lat_path, priv->lat_attr,
408            priv->lon_path, priv->lon_attr);
409
410         FILE *file = g_fopen (filename, "r");
411         if (file == NULL)
412                 /* TODO emit warning */
413                 return FALSE;
414         
415         /* setup context parse (ie callbacks) */
416         if (priv->lat_attr != NULL || priv->lon_attr != NULL)
417     // At least one coordinate uses an attribute
418     xml_parser.start_element = &_start_element;
419   else
420     xml_parser.start_element = NULL;
421         xml_parser.end_element = NULL;
422         if (priv->lat_attr == NULL || priv->lon_attr == NULL)
423     // At least one coordinate uses a raw element
424     xml_parser.text = &_text;
425   else
426     xml_parser.text = NULL;
427         xml_parser.passthrough = NULL;
428         xml_parser.error = NULL;
429         
430         xml_context = g_markup_parse_context_new(&xml_parser, 0, self, NULL);
431
432         /* setup result */
433         priv->ll.lat = NAN;
434         priv->ll.lon = NAN;
435         
436         gchar buff[BUFSIZ];
437         size_t nb;
438         while (xml_context &&
439                (nb = fread (buff, sizeof(gchar), BUFSIZ, file)) > 0)
440         {
441                 if (!g_markup_parse_context_parse(xml_context, buff, nb, &error))
442                 {
443                         fprintf(stderr, "%s: parsing error: %s.\n",
444                                 __FUNCTION__, error->message);
445                         g_markup_parse_context_free(xml_context);
446                         xml_context = NULL;
447                 }
448                 g_clear_error (&error);
449         }
450         /* cleanup */
451         if (xml_context &&
452             !g_markup_parse_context_end_parse(xml_context, &error))
453                 fprintf(stderr, "%s: errors occurred while reading file: %s.\n",
454                         __FUNCTION__, error->message);
455         g_clear_error (&error);
456         
457         if (xml_context)
458                 g_markup_parse_context_free(xml_context);
459         xml_context = NULL;
460         fclose (file);
461   
462   if (ll != NULL)
463   {
464     *ll = priv->ll;
465   }
466   
467   if (isnan(priv->ll.lat) || isnan(priv->ll.lat))
468                 /* At least one coordinate not found */
469                 return FALSE;
470         else
471                 return TRUE;
472 }
473
474 static gchar *
475 _goto_xml_tool_get_url_format ( VikGotoTool *self )
476 {
477   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
478   g_return_val_if_fail(priv != NULL, NULL);
479   return priv->url_format;
480 }