/* * Copyright (c) 2015 Marcus Lundblad * * GNOME Maps is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * GNOME Maps is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with GNOME Maps; if not, see * * Author: Marcus Lundblad */ #include "maps-osm-object.h" struct _MapsOSMObjectPrivate { guint64 id; guint version; guint64 changeset; GHashTable *tags; }; enum { PROP_0, PROP_ID, PROP_VERSION, PROP_CHANGESET }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MapsOSMObject, maps_osm_object, G_TYPE_OBJECT) static void maps_osm_object_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MapsOSMObject *osm_object = MAPS_OSMOBJECT (object); MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (osm_object); switch (property_id) { case PROP_ID: priv->id = g_value_get_uint64 (value); break; case PROP_VERSION: priv->version = g_value_get_uint (value); break; case PROP_CHANGESET: priv->changeset = g_value_get_uint64 (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void maps_osm_object_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MapsOSMObject *osm_object = MAPS_OSMOBJECT (object); MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (osm_object); switch (property_id) { case PROP_ID: g_value_set_uint64 (value, priv->id); break; case PROP_VERSION: g_value_set_uint (value, priv->version); break; case PROP_CHANGESET: g_value_set_uint64 (value, priv->changeset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void maps_osm_object_dispose (GObject *object) { MapsOSMObject *osm_object = MAPS_OSMOBJECT (object); MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (osm_object); g_hash_table_destroy (priv->tags); priv->tags = NULL; G_OBJECT_CLASS (maps_osm_object_parent_class)->dispose (object); } /* base implementation returning no object-specific XML attributes */ static GHashTable * maps_osm_object_get_xml_attributes (const MapsOSMObject *object) { return NULL; } /* base implementation return no object-specific child XML nodes */ static xmlNodePtr maps_osm_object_get_xml_child_nodes (const MapsOSMObject *object) { return NULL; } static void maps_osm_object_class_init (MapsOSMObjectClass *klass) { GObjectClass *maps_class = G_OBJECT_CLASS (klass); MapsOSMObjectClass *object_class = MAPS_OSMOBJECT_CLASS (klass); GParamSpec *pspec; maps_class->dispose = maps_osm_object_dispose; maps_class->get_property = maps_osm_object_get_property; maps_class->set_property = maps_osm_object_set_property; object_class->get_xml_attributes = maps_osm_object_get_xml_attributes; object_class->get_xml_child_nodes = maps_osm_object_get_xml_child_nodes; /** * MapsOSMObject:id: * * The OSM id of the object. */ pspec = g_param_spec_uint64 ("id", "ID", "ID", 0, G_MAXUINT64, 0, G_PARAM_READWRITE); g_object_class_install_property (maps_class, PROP_ID, pspec); /** * MapsOSMObject:version: * * The latest OSM version of the object. */ pspec = g_param_spec_uint("version", "Version", "Version", 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_property (maps_class, PROP_VERSION, pspec); /** * MapsOSMObject:changeset: * * The OSM changeset for the current upload of the object. */ pspec = g_param_spec_uint64 ("changeset", "Changeset", "Changeset", 0, G_MAXUINT64, 0, G_PARAM_READWRITE); g_object_class_install_property (maps_class, PROP_CHANGESET, pspec); } static void maps_osm_object_init (MapsOSMObject *object) { MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object); priv->tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); } const char * maps_osm_object_get_tag (const MapsOSMObject *object, const char *key) { MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object); g_return_val_if_fail (key != NULL, NULL); return g_hash_table_lookup (priv->tags, key); } void maps_osm_object_set_tag (MapsOSMObject *object, const char *key, const char *value) { MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object); g_return_if_fail (key != NULL); g_hash_table_insert (priv->tags, g_strdup (key), g_strdup (value)); } void maps_osm_object_delete_tag (MapsOSMObject *object, const char *key) { MapsOSMObjectPrivate *priv = maps_osm_object_get_instance_private (object); g_return_if_fail (key != NULL); g_hash_table_remove (priv->tags, key); } static void maps_osm_object_foreach_tag (gpointer key, gpointer value, gpointer user_data) { const char *name = (const char *) key; const char *val = (const char *) value; xmlNodePtr object_node = (xmlNodePtr) user_data; /* skip tag if it has an empty placeholder value */ if (val) { xmlNodePtr tag_node; tag_node = xmlNewNode (NULL, "tag"); xmlNewProp (tag_node, "k", key); xmlNewProp (tag_node, "v", val); xmlAddChild (object_node, tag_node); } } static void maps_osm_object_foreach_type_attr (gpointer key, gpointer value, gpointer user_data) { const char *name = (const char *) key; const char *val = (const char *) value; xmlNodePtr object_node = (xmlNodePtr) user_data; xmlNewProp (object_node, name, val); } xmlDocPtr maps_osm_object_to_xml (const MapsOSMObject *object) { MapsOSMObjectPrivate *priv; xmlDocPtr doc; xmlNodePtr osm_node; xmlNodePtr object_node; const char *type; GHashTable *type_attrs; xmlNodePtr type_sub_nodes; doc = xmlNewDoc ("1.0"); osm_node = xmlNewNode (NULL, "osm"); priv = (MapsOSMObjectPrivate *) maps_osm_object_get_instance_private (object); type = MAPS_OSMOBJECT_GET_CLASS (object)->get_xml_tag_name (); object_node = xmlNewNode (NULL, type); /* add common OSM attributes */ if (priv->id != 0) { char buf[32]; g_snprintf (buf, 32, "%" G_GUINT64_FORMAT, priv->id); xmlNewProp (object_node, "id", buf); } if (priv->version != 0) { char buf[16]; g_snprintf (buf, 16, "%d", priv->version); xmlNewProp (object_node, "version", buf); } if (priv->changeset != 0) { char buf[32]; g_snprintf (buf, 32, "%" G_GUINT64_FORMAT, priv->changeset); xmlNewProp (object_node, "changeset", buf); } /* add OSM tags */ g_hash_table_foreach (priv->tags, maps_osm_object_foreach_tag, object_node); /* add type-specific attributes */ type_attrs = MAPS_OSMOBJECT_GET_CLASS (object)->get_xml_attributes (object); if (type_attrs) { g_hash_table_foreach (type_attrs, maps_osm_object_foreach_type_attr, object_node); g_hash_table_destroy (type_attrs); } /* add type-specific sub-nodes */ type_sub_nodes = MAPS_OSMOBJECT_GET_CLASS (object)->get_xml_child_nodes (object); if (type_sub_nodes) xmlAddChildList (object_node, type_sub_nodes); /* add type node to top node */ xmlAddChild (osm_node, object_node); xmlDocSetRootElement (doc, osm_node); return doc; } char * maps_osm_object_serialize (const MapsOSMObject *object) { xmlDocPtr doc; xmlChar *result; int size; doc = maps_osm_object_to_xml (object); xmlDocDumpMemory (doc, &result, &size); xmlFreeDoc (doc); return result; }