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