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