[Multi-valued V2 (grilo) 11/12] core: Merge GrlDataMulti into GrlData



Makes GrlData a truly multi-valued container, keeping the old API for those
applications that are not actually interested in the multi-valued but in the
single-valued data.

Applications using the singled-value API are actually using the first value of
multi-valued elements.

Signed-off-by: Juan A. Suarez Romero <jasuarez igalia com>
---
 src/Makefile.am           |    2 -
 src/data/grl-data-multi.c |  521 -----------------------------------
 src/data/grl-data-multi.h |  112 --------
 src/data/grl-data.c       |  665 +++++++++++++++++++++++++++++++++++++--------
 src/data/grl-data.h       |   25 ++
 src/data/grl-media.c      |    2 +-
 src/data/grl-media.h      |    6 +-
 7 files changed, 587 insertions(+), 746 deletions(-)
 delete mode 100644 src/data/grl-data-multi.c
 delete mode 100644 src/data/grl-data-multi.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 0b37616..cff0f27 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,7 +51,6 @@ lib@GRL_NAME@_la_SOURCES =					\
 
 data_c_sources =		\
 	data/grl-data.c		\
-	data/grl-data-multi.c	\
 	data/grl-property.c	\
 	data/grl-media.c	\
 	data/grl-media-audio.c	\
@@ -80,7 +79,6 @@ lib@GRL_NAME@inc_HEADERS =	\
 
 data_h_headers =		\
 	data/grl-data.h		\
-	data/grl-data-multi.h	\
 	data/grl-property.h	\
 	data/grl-media.h	\
 	data/grl-media-box.h	\
diff --git a/src/data/grl-data-multi.c b/src/data/grl-data-multi.c
deleted file mode 100644
index d595bff..0000000
--- a/src/data/grl-data-multi.c
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * Copyright (C) 2011 Igalia S.L.
- *
- * Contact: Iago Toral Quiroga <itoral igalia com>
- *
- * Authors: Juan A. Suarez Romero <jasuarez igalia com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-/**
- * SECTION:grl-data-multi
- * @short_description: Low-level class to store multivalued data
- * @see_also: #GrlMedia, #GrlMediaBox, #GrlMediaVideo, #GrlMediaAudio, #GrlMediaImage
- *
- * This class acts as dictionary where keys and their values can be stored. It
- * is suggested to better high level classes, like #GrlMedia, which
- * provides functions to access known properties.
- */
-
-#include "grl-data-multi.h"
-#include "grl-log.h"
-#include "grl-plugin-registry.h"
-
-struct _GrlDataMultiPrivate {
-  GHashTable *extended_data;
-};
-
-static void grl_data_multi_finalize (GObject *object);
-
-#define GRL_DATA_MULTI_GET_PRIVATE(o)                                   \
-  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GRL_TYPE_DATA_MULTI, GrlDataMultiPrivate))
-
-static void free_list_values (GrlKeyID key, GList *values, gpointer user_data);
-
-/* ================ GrlDataMulti GObject ================ */
-
-G_DEFINE_TYPE (GrlDataMulti, grl_data_multi, GRL_TYPE_DATA);
-
-static void
-grl_data_multi_class_init (GrlDataMultiClass *klass)
-{
-  GObjectClass *gobject_class = (GObjectClass *)klass;
-
-  gobject_class->finalize = grl_data_multi_finalize;
-
-  g_type_class_add_private (klass, sizeof (GrlDataMultiPrivate));
-}
-
-static void
-grl_data_multi_init (GrlDataMulti *self)
-{
-  self->priv = GRL_DATA_MULTI_GET_PRIVATE (self);
-  self->priv->extended_data = g_hash_table_new_full (g_direct_hash,
-                                                     g_direct_equal,
-                                                     NULL,
-                                                     NULL);
-}
-
-static void
-grl_data_multi_finalize (GObject *object)
-{
-  GrlDataMulti *mdata = GRL_DATA_MULTI (object);
-
-  g_hash_table_foreach (mdata->priv->extended_data,
-                        (GHFunc) free_list_values,
-                        NULL);
-  g_hash_table_unref (mdata->priv->extended_data);
-
-  G_OBJECT_CLASS (grl_data_multi_parent_class)->finalize (object);
-}
-
-/* ================ Utitilies ================ */
-
-static void
-free_list_values (GrlKeyID key, GList *values, gpointer user_data)
-{
-  g_list_foreach (values, (GFunc) g_object_unref, NULL);
-  g_list_free (values);
-}
-
-/* ================ API ================ */
-
-/**
- * grl_data_multi_new:
- *
- * Creates a new multivalued data object.
- *
- * Returns: a new multivalued data object.
- **/
-GrlDataMulti *
-grl_data_multi_new (void)
-{
-  return g_object_new (GRL_TYPE_DATA_MULTI,
-		       NULL);
-}
-
-/**
- * grl_data_multi_add:
- * @mdata: a multivalued data
- * @prop: a set of related properties with their values
- *
- * Adds a new set of values.
- *
- * All keys in prop must be related among them.
- *
- * mdata will take the ownership of prop, so do not modify it.
- **/
-void
-grl_data_multi_add (GrlDataMulti *mdata,
-                    GrlProperty *prop)
-{
-  GList *key;
-  GList *keys;
-  GList *values;
-  GrlPluginRegistry *registry;
-  const GList *related_keys;
-  guint l;
-
-  g_return_if_fail (GRL_IS_DATA_MULTI (mdata));
-  g_return_if_fail (GRL_IS_PROPERTY (prop));
-
-  keys = grl_data_get_keys (GRL_DATA (prop));
-  if (!keys) {
-    /* Ignore empty properties */
-    GRL_WARNING ("Empty set of values");
-    g_object_unref (prop);
-    return;
-  }
-
-  /* It is assumed that this property only contains values for related properties.
-     Check if it must be inserted in data or in extended_data */
-
-  l = grl_data_multi_length (mdata, keys->data);
-
-  if (l > 0) {
-    /* Get the representative element of key */
-    registry = grl_plugin_registry_get_default ();
-    related_keys =
-      grl_plugin_registry_lookup_metadata_key_relation (registry,
-                                                        keys->data);
-
-    if (!related_keys) {
-      GRL_WARNING ("Related keys not found for key: %s",
-                   grl_metadata_key_get_name (related_keys->data));
-      g_list_free (keys);
-      return;
-    }
-
-    values = g_hash_table_lookup (mdata->priv->extended_data,
-                                  related_keys->data);
-    values = g_list_append (values, prop);
-    g_hash_table_insert (mdata->priv->extended_data,
-                         related_keys->data,
-                         values);
-  } else {
-    /* Insert it as single valued data */
-    for (key = keys; key; key = g_list_next (key)) {
-      grl_data_set (GRL_DATA (mdata),
-                    key->data,
-                    grl_data_get (GRL_DATA (prop), key->data));
-    }
-    g_object_unref (prop);
-  }
-  g_list_free (keys);
-}
-
-/**
- * grl_data_multi_length:
- * @mdata: a multivalued data
- * @key: a metadata key
- *
- * Returns how many values key has in mdata.
- *
- * Returns: number of values
- **/
-guint
-grl_data_multi_length (GrlDataMulti *mdata,
-                       GrlKeyID key)
-{
-  GrlPluginRegistry *registry;
-  const GList *related_keys;
-  gboolean found = FALSE;
-  guint length;
-
-  g_return_val_if_fail (GRL_IS_DATA_MULTI (mdata), 0);
-
-  /* Get the representative element of key */
-  registry = grl_plugin_registry_get_default ();
-  related_keys = grl_plugin_registry_lookup_metadata_key_relation (registry,
-                                                                   key);
-
-  if (!related_keys) {
-    GRL_WARNING ("Related keys not found for key: %s",
-                 grl_metadata_key_get_name (related_keys->data));
-    return 0;
-  }
-
-  /* Check first extended data */
-  length = g_list_length (g_hash_table_lookup (mdata->priv->extended_data,
-                                               related_keys->data));
-  if (length == 0) {
-    /* Check if we can find the information in data. It is a success if there is
-       at least one value for one of the related keys */
-    while (related_keys && !found) {
-      found = grl_data_key_is_known (GRL_DATA (mdata), related_keys->data);
-      related_keys = g_list_next (related_keys);
-    }
-
-    if (found) {
-      return 1;
-    } else {
-      return 0;
-    }
-  } else {
-    return length + 1;
-  }
-}
-
-/**
- * grl_data_multi_get:
- * @mdata: a multivalued data
- * @key: a metadata key
- * @pos: element to retrieve, starting at 0
- *
- * Returns a set containing the values for key and related keys at position specified.
- *
- * Returns: a new #GrlProperty. It must be freed with #g_object_unref()
- **/
-GrlProperty *
-grl_data_multi_get (GrlDataMulti *mdata,
-                    GrlKeyID key,
-                    guint pos)
-{
-  GrlData *collect_from = NULL;
-  GrlPluginRegistry *registry;
-  GrlProperty *prop = NULL;
-  const GList *related_keys;
-  GList *list_el;
-
-  g_return_val_if_fail (GRL_IS_DATA_MULTI (mdata), NULL);
-
-  /* Get the representative element of key */
-  registry = grl_plugin_registry_get_default ();
-  related_keys = grl_plugin_registry_lookup_metadata_key_relation (registry,
-                                                                   key);
-
-  if (!related_keys) {
-    GRL_WARNING ("Related keys not found for key: %s",
-                 grl_metadata_key_get_name (related_keys->data));
-    return NULL;
-  }
-
-  if (pos == 0) {
-    collect_from = GRL_DATA (mdata);
-  } else {
-    list_el = g_list_nth (g_hash_table_lookup (mdata->priv->extended_data,
-                                               related_keys->data),
-                          pos - 1);
-    if (list_el) {
-      collect_from = GRL_DATA (list_el->data);
-    } else {
-      GRL_WARNING ("Wrong position %u to get data", pos);
-    }
-  }
-
-  if (collect_from) {
-    prop = grl_property_new ();
-    while (related_keys) {
-      grl_data_set (GRL_DATA (prop),
-                    related_keys->data,
-                    grl_data_get (collect_from, related_keys->data));
-      related_keys = g_list_next (related_keys);
-    }
-  }
-
-  return prop;
-}
-
-/**
- * grl_data_multi_get_all_single:
- * @mdata: a multivalued data
- * @key: a metadata key
- *
- * Returns all non-NULL values for specified key. This ignores completely the
- * related keys.
- *
- * Returns: (element-type GObject.Value) (transfer container): a #GList with
- * values. Free it with #g_list_free.
- **/
-GList *
-grl_data_multi_get_all_single (GrlDataMulti *mdata,
-                               GrlKeyID key)
-{
-  GList *list_values;
-  GList *result = NULL;
-  GValue *value;
-  GrlPluginRegistry *registry;
-  const GList *related_keys;
-
-  g_return_val_if_fail (GRL_IS_DATA_MULTI (mdata), NULL);
-
-  /* Get the representative element of key */
-  registry = grl_plugin_registry_get_default ();
-  related_keys = grl_plugin_registry_lookup_metadata_key_relation (registry,
-                                                                   key);
-  if (!related_keys) {
-    GRL_WARNING ("Not found related keys");
-    return result;
-  }
-
-  /* Get the first value */
-  value = (GValue *) grl_data_get (GRL_DATA (mdata), key);
-  if (value) {
-    result = g_list_prepend (result, value);
-  }
-
-  /* Get the remaining list of values */
-  list_values = g_hash_table_lookup (mdata->priv->extended_data,
-                                     related_keys->data);
-  while (list_values) {
-    value = (GValue *) grl_data_get (GRL_DATA (list_values->data), key);
-    if (value) {
-      result = g_list_prepend (result, value);
-    }
-    list_values = g_list_next (list_values);
-  }
-
-  return g_list_reverse (result);
-}
-
-/**
- * grl_data_multi_get_all_single_string:
- * @mdata: a multivalued data
- * @key: a metadata key
- *
- * Returns all non-NULL values for specified key of type string. This ignores
- * completely the related keys.
- *
- * Returns: (element-type utf8) (transfer container): a #GList with
- * values. Free it with #g_list_free.
- **/
-GList *
-grl_data_multi_get_all_single_string (GrlDataMulti *mdata,
-                                      GrlKeyID key)
-{
-  GList *list_strings = NULL;
-  GList *list_values;
-  GList *value;
-  const gchar *string_value;
-
-  /* Verify key is of type string */
-  if (GRL_METADATA_KEY_GET_TYPE (key) != G_TYPE_STRING) {
-    GRL_WARNING ("Wrong type (not string)");
-    return NULL;
-  }
-
-  list_values = grl_data_multi_get_all_single (mdata, key);
-  for (value = list_values; value; value = g_list_next (value)) {
-    string_value = g_value_get_string (value->data);
-    if (string_value) {
-      list_strings = g_list_prepend (list_strings, (gpointer) string_value);
-    }
-  }
-
-  g_list_free (list_values);
-
-  return g_list_reverse (list_strings);
-}
-
-/**
- * grl_data_multi_remove:
- * @mdata: a multivalued data
- * @key: a metadata key
- * @pos: position of key to be removed, starting at 0
- *
- * Removes key and related keys from position in mdata.
- **/
-void
-grl_data_multi_remove (GrlDataMulti *mdata,
-                       GrlKeyID key,
-                       guint pos)
-{
-  GList *list_values;
-  GList *to_remove;
-  GrlPluginRegistry *registry;
-  GrlProperty *prop_at_1;
-  const GList *rel_key;
-  const GList *related_keys;
-
-  g_return_if_fail (GRL_IS_DATA_MULTI (mdata));
-
-  /* Get the representative element of key */
-  registry = grl_plugin_registry_get_default ();
-  related_keys = grl_plugin_registry_lookup_metadata_key_relation (registry,
-                                                                   key);
-
-  if (!related_keys) {
-    GRL_WARNING ("Related keys not found for key: %s",
-                 grl_metadata_key_get_name (related_keys->data));
-    return;
-  }
-
-  /* If element to remove is in position 0 (single-data), then we can replace it
-     by value in pos 1 (extended-data) and then remove 1. If element to remove
-     is >0 then we must remove it and shift values the empty position */
-
-  if (pos == 0) {
-    prop_at_1 = grl_data_multi_get (mdata, key, 1);
-    if (prop_at_1) {
-      /* Replace related keys in pos 0 */
-      for (rel_key = related_keys; rel_key; rel_key = g_list_next (rel_key)) {
-        grl_data_set (GRL_DATA (mdata),
-                      rel_key->data,
-                      grl_data_get (GRL_DATA (prop_at_1), rel_key->data));
-      }
-      pos = 1;
-    } else {
-      /* There is no multivalues; remove data */
-      for (rel_key = related_keys; rel_key; rel_key = g_list_next (rel_key)) {
-        grl_data_remove (GRL_DATA (mdata), rel_key->data);
-      }
-    }
-  }
-
-  if (pos > 0) {
-    list_values = g_hash_table_lookup (mdata->priv->extended_data,
-                                       related_keys->data);
-    to_remove = g_list_nth (list_values, pos - 1);
-    if (!to_remove) {
-      /* Wrong index; ignore */
-      return;
-    }
-
-    g_object_unref (to_remove->data);
-    list_values = g_list_delete_link (list_values, to_remove);
-    g_hash_table_insert (mdata->priv->extended_data,
-                         related_keys->data,
-                         list_values);
-  }
-}
-
-/**
- * grl_data_multi_update:
- * @mdata: a multivalued data
- * @prop: a set of related keys
- * @pos: position to be updated
- *
- * Updates the values at position in mdata with values in prop.
- *
- * GrlDataMulti will take ownership of prop, so do not free it after invoking
- * this function.
- **/
-void
-grl_data_multi_update (GrlDataMulti *mdata,
-                       GrlProperty *prop,
-                       guint pos)
-{
-  GList *keys;
-  GList *list_val;
-  GrlPluginRegistry *registry;
-  const GList *related_keys;
-
-  g_return_if_fail (GRL_IS_DATA_MULTI (mdata));
-  g_return_if_fail (GRL_IS_PROPERTY (prop));
-
-  keys = grl_data_get_keys (GRL_DATA (prop));
-  if (!keys) {
-    GRL_WARNING ("Empty properties");
-    g_object_unref (prop);
-    return;
-  }
-
-  /* Get the representative element of key */
-  registry = grl_plugin_registry_get_default ();
-  related_keys = grl_plugin_registry_lookup_metadata_key_relation (registry,
-                                                                   keys->data);
-
-  if (!related_keys) {
-    GRL_WARNING ("Related keys not found for key: %s",
-                 grl_metadata_key_get_name (related_keys->data));
-    return;
-  }
-
-  if (pos == 0) {
-    while (related_keys) {
-      grl_data_set (GRL_DATA (mdata),
-                    related_keys->data,
-                    grl_data_get (GRL_DATA (prop), related_keys->data));
-      related_keys = g_list_next (related_keys);
-    }
-    g_object_unref (prop);
-  } else {
-    /* Replace the entire element */
-    list_val = g_list_nth (g_hash_table_lookup (mdata->priv->extended_data,
-                                                related_keys->data),
-                           pos - 1);
-    if (!list_val) {
-      GRL_WARNING ("%s: index %u out of range", __FUNCTION__, pos);
-      g_object_unref (prop);
-      return;
-    }
-
-    g_object_unref (list_val->data);
-    list_val->data = prop;
-  }
-}
diff --git a/src/data/grl-data-multi.h b/src/data/grl-data-multi.h
deleted file mode 100644
index 88aed98..0000000
--- a/src/data/grl-data-multi.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2011 Igalia S.L.
- *
- * Contact: Iago Toral Quiroga <itoral igalia com>
- *
- * Authors: Juan A. Suarez Romero <jasuarez igalia com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#if !defined (_GRILO_H_INSIDE_) && !defined (GRILO_COMPILATION)
-#error "Only <grilo.h> can be included directly."
-#endif
-
-#ifndef _GRL_DATA_MULTI_H_
-#define _GRL_DATA_MULTI_H_
-
-#include <grl-data.h>
-#include <grl-property.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define GRL_TYPE_DATA_MULTI                     \
-  (grl_data_multi_get_type())
-
-#define GRL_DATA_MULTI(obj)                           \
-  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                 \
-                               GRL_TYPE_DATA_MULTI,   \
-                               GrlDataMulti))
-
-#define GRL_DATA_MULTI_CLASS(klass)                   \
-  (G_TYPE_CHECK_CLASS_CAST ((klass),                  \
-                            GRL_TYPE_DATA_MULTI,      \
-                            GrlDataMultiClass))
-
-#define GRL_IS_DATA_MULTI(obj)                          \
-  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                   \
-                               GRL_TYPE_DATA_MULTI))
-
-#define GRL_IS_DATA_MULTI_CLASS(klass)                  \
-  (G_TYPE_CHECK_CLASS_TYPE ((klass),                    \
-                            GRL_TYPE_DATA_MULTI))
-
-#define GRL_DATA_MULTI_GET_CLASS(obj)                   \
-  (G_TYPE_INSTANCE_GET_CLASS ((obj),                    \
-                              GRL_TYPE_DATA_MULTI,      \
-                              GrlDataMultiClass))
-
-typedef struct _GrlDataMulti        GrlDataMulti;
-typedef struct _GrlDataMultiPrivate GrlDataMultiPrivate;
-typedef struct _GrlDataMultiClass   GrlDataMultiClass;
-
-/**
- * GrlDataMultiClass:
- * @parent_class: the parent class structure
- *
- * Grilo Data Multivalued class
- */
-struct _GrlDataMultiClass
-{
-  GrlDataClass parent_class;
-
-  /*< private >*/
-  gpointer _grl_reserved[GRL_PADDING];
-};
-
-struct _GrlDataMulti
-{
-  GrlData parent;
-
-  /*< private >*/
-  GrlDataMultiPrivate *priv;
-
-  gpointer _grl_reserved[GRL_PADDING_SMALL];
-};
-
-GType grl_data_multi_get_type (void) G_GNUC_CONST;
-
-GrlDataMulti *grl_data_multi_new (void);
-
-void grl_data_multi_add (GrlDataMulti *mdata, GrlProperty *prop);
-
-guint grl_data_multi_length (GrlDataMulti *mdata, GrlKeyID key);
-
-GrlProperty *grl_data_multi_get (GrlDataMulti *mdata, GrlKeyID key, guint pos);
-
-GList *grl_data_multi_get_all_single (GrlDataMulti *mdata, GrlKeyID key);
-
-GList *grl_data_multi_get_all_single_string (GrlDataMulti *mdata, GrlKeyID key);
-
-void grl_data_multi_remove (GrlDataMulti *mdata, GrlKeyID key, guint pos);
-
-void grl_data_multi_update (GrlDataMulti *mdata, GrlProperty *prop, guint pos);
-
-G_END_DECLS
-
-#endif /* _GRL_DATA_MULTI_H_ */
diff --git a/src/data/grl-data.c b/src/data/grl-data.c
index 116319c..6dcee80 100644
--- a/src/data/grl-data.c
+++ b/src/data/grl-data.c
@@ -34,6 +34,10 @@
 
 #include "grl-data.h"
 #include "grl-log.h"
+#include <grl-plugin-registry.h>
+
+#define GRL_LOG_DOMAIN_DEFAULT data_log_domain
+GRL_LOG_DOMAIN(data_log_domain);
 
 enum {
   PROP_0,
@@ -45,39 +49,35 @@ struct _GrlDataPrivate {
   gboolean overwrite;
 };
 
-static void grl_data_set_property (GObject *object,
-                                   guint prop_id,
-                                   const GValue *value,
-                                   GParamSpec *pspec);
+static void grl_data_set_gobject_property (GObject *object,
+                                           guint prop_id,
+                                           const GValue *value,
+                                           GParamSpec *pspec);
 
-static void grl_data_get_property (GObject *object,
-                                   guint prop_id,
-                                   GValue *value,
-                                   GParamSpec *pspec);
+static void grl_data_get_gobject_property (GObject *object,
+                                           guint prop_id,
+                                           GValue *value,
+                                           GParamSpec *pspec);
 
 static void grl_data_finalize (GObject *object);
+static void free_list_values (GrlKeyID key, GList *values, gpointer user_data);
 
 #define GRL_DATA_GET_PRIVATE(o)                                         \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), GRL_TYPE_DATA, GrlDataPrivate))
 
-G_DEFINE_TYPE (GrlData, grl_data, G_TYPE_OBJECT);
+static void free_list_values (GrlKeyID key, GList *values, gpointer user_data);
 
-static void
-free_val (GValue *val)
-{
-  if (val) {
-    g_value_unset (val);
-    g_free (val);
-  }
-}
+/* ================ GrlData GObject ================ */
+
+G_DEFINE_TYPE (GrlData, grl_data, G_TYPE_OBJECT);
 
 static void
 grl_data_class_init (GrlDataClass *klass)
 {
   GObjectClass *gobject_class = (GObjectClass *)klass;
 
-  gobject_class->set_property = grl_data_set_property;
-  gobject_class->get_property = grl_data_get_property;
+  gobject_class->set_property = grl_data_set_gobject_property;
+  gobject_class->get_property = grl_data_get_gobject_property;
   gobject_class->finalize = grl_data_finalize;
 
   g_type_class_add_private (klass, sizeof (GrlDataPrivate));
@@ -98,21 +98,28 @@ grl_data_init (GrlData *self)
   self->priv->data = g_hash_table_new_full (g_direct_hash,
                                             g_direct_equal,
                                             NULL,
-                                            (GDestroyNotify) free_val);
+                                            NULL);
 }
 
 static void
 grl_data_finalize (GObject *object)
 {
+  GrlData *data = GRL_DATA (object);
+
   g_signal_handlers_destroy (object);
+  g_hash_table_foreach (data->priv->data,
+                        (GHFunc) free_list_values,
+                        NULL);
+  g_hash_table_unref (data->priv->data);
+
   G_OBJECT_CLASS (grl_data_parent_class)->finalize (object);
 }
 
 static void
-grl_data_set_property (GObject *object,
-                       guint prop_id,
-                       const GValue *value,
-                       GParamSpec *pspec)
+grl_data_set_gobject_property (GObject *object,
+                               guint prop_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
 {
   GrlData *self = GRL_DATA (object);
 
@@ -128,10 +135,10 @@ grl_data_set_property (GObject *object,
 }
 
 static void
-grl_data_get_property (GObject *object,
-                       guint prop_id,
-                       GValue *value,
-                       GParamSpec *pspec)
+grl_data_get_gobject_property (GObject *object,
+                               guint prop_id,
+                               GValue *value,
+                               GParamSpec *pspec)
 {
   GrlData *self = GRL_DATA (object);
 
@@ -146,6 +153,66 @@ grl_data_get_property (GObject *object,
   }
 }
 
+/* ================ Utitilies ================ */
+
+/* Free the list of values, which are of type #GrlProperty */
+static void
+free_list_values (GrlKeyID key, GList *values, gpointer user_data)
+{
+  g_list_foreach (values, (GFunc) g_object_unref, NULL);
+  g_list_free (values);
+}
+
+/* Returns the sample key that represents the set of keys related with @key */
+static GrlKeyID
+get_sample_key (GrlKeyID key)
+{
+  GrlPluginRegistry *registry;
+  const GList *related_keys;
+
+  registry = grl_plugin_registry_get_default ();
+  related_keys =
+    grl_plugin_registry_lookup_metadata_key_relation (registry, key);
+
+  if (!related_keys) {
+    GRL_WARNING ("Related keys not found for key \"%s\"",
+                 grl_metadata_key_get_name (related_keys->data));
+    return NULL;
+  } else {
+    return related_keys->data;
+  }
+}
+
+/* Sets the first value of data. If it already has a value and overwrite is
+   %TRUE it is replaced */
+static void
+data_set (GrlData *data, GrlKeyID key, const GValue *value, gboolean overwrite)
+{
+  GrlProperty *prop = NULL;
+
+  /* Get the right property */
+  if (grl_data_length (data, key) > 0) {
+    prop = grl_data_get_property (data, key, 0);
+  }
+
+  if (!prop) {
+    /* No property; add it */
+    prop = grl_property_new_for_key (key);
+    grl_property_set (prop, key, value);
+    grl_data_add_property (data, prop);
+  } else {
+    if (grl_property_key_is_known (prop, key) && !overwrite) {
+      /* Property already has a value, and we can not overwrite it */
+      return;
+    } else {
+      /* Set the new value */
+      grl_property_set (prop, key, value);
+    }
+  }
+}
+
+/* ================ API ================ */
+
 /**
  * grl_data_new:
  *
@@ -165,10 +232,9 @@ grl_data_new (void)
 /**
  * grl_data_get:
  * @data: data to retrieve value
- * @key: (type GObject.ParamSpec): key to look up.
+ * @key: (type Grl.KeyID): key to look up.
  *
- * Get the value associated with the key. If it does not contain any value, NULL
- * will be returned.
+ * Get the first value associated with the key.
  *
  * Returns: (transfer none): a #GValue. This value should not be modified nor freed by user.
  *
@@ -177,20 +243,31 @@ grl_data_new (void)
 const GValue *
 grl_data_get (GrlData *data, GrlKeyID key)
 {
+  GrlProperty *prop = NULL;
+
   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
   g_return_val_if_fail (key, NULL);
 
-  return g_hash_table_lookup (data->priv->data, key);
+  if (grl_data_length (data, key) > 0) {
+    prop = grl_data_get_property (data, key, 0);
+  }
+
+  if (!prop) {
+    return NULL;
+  }
+
+  return grl_property_get (prop, key);
 }
 
 /**
  * grl_data_set:
  * @data: data to modify
- * @key: (type GObject.ParamSpec): key to change or add
+ * @key: (type Grl.KeyID): key to change or add
  * @value: the new value
  *
- * Sets the value associated with the key. If key already has a value and
- * #overwrite is TRUE, old value is freed and the new one is set.
+ * Sets the first value associated with the key. If key already has a value and
+ * #overwrite is %TRUE, old value is freed and the new one is set. Else the new
+ * one is assigned.
  *
  * Also, checks that value is compliant with the key specification, modifying it
  * accordingly. For instance, if the key requires a number between 0 and 10, but
@@ -201,42 +278,20 @@ grl_data_get (GrlData *data, GrlKeyID key)
 void
 grl_data_set (GrlData *data, GrlKeyID key, const GValue *value)
 {
-  GValue *copy = NULL;
-
   g_return_if_fail (GRL_IS_DATA (data));
   g_return_if_fail (key);
 
-  if (data->priv->overwrite ||
-      g_hash_table_lookup (data->priv->data, key) == NULL) {
-    /* Dup value */
-    if (value) {
-      if (G_VALUE_TYPE (value) == GRL_METADATA_KEY_GET_TYPE (key)) {
-        copy = g_new0 (GValue, 1);
-        g_value_init (copy, G_VALUE_TYPE (value));
-        g_value_copy (value, copy);
-      } else {
-        GRL_WARNING ("value has type %s, but expected %s",
-                     g_type_name (G_VALUE_TYPE (value)),
-                     g_type_name (GRL_METADATA_KEY_GET_TYPE (key)));
-      }
-    }
-
-    if (copy && g_param_value_validate (key, copy)) {
-      GRL_WARNING ("'%s' value invalid, adjusting",
-                   GRL_METADATA_KEY_GET_NAME (key));
-    }
-    g_hash_table_insert (data->priv->data, key, copy);
-  }
+  data_set (data, key, value, data->priv->overwrite);
 }
 
 /**
  * grl_data_set_string:
  * @data: data to modify
- * @key: (type GObject.ParamSpec): key to change or add
+ * @key: (type Grl.KeyID): key to change or add
  * @strvalue: the new value
  *
- * Sets the value associated with the key. If key already has a value and
- * #overwrite is TRUE, old value is freed and the new one is set.
+ * Sets the first value associated with the key. If key already has a value and
+ * #overwrite is %TRUE, old value is freed and the new one is set.
  *
  * Since: 0.1.4
  **/
@@ -259,12 +314,13 @@ grl_data_set_string (GrlData *data,
 /**
  * grl_data_get_string:
  * @data: data to inspect
- * @key: (type GObject.ParamSpec): key to use
+ * @key: (type Grl.KeyID): key to use
  *
- * Returns the value associated with the key. If key has no value, or value is
- * not string, or key is not in data, then NULL is returned.
+ * Returns the first value associated with the key. If key has no first value,
+ * or value is not string, or key is not in data, then %NULL is returned.
  *
- * Returns: string associated with key, or NULL in other case. Caller should not change nor free the value.
+ * Returns: string associated with key, or %NULL in other case. Caller should
+ * not change nor free the value.
  *
  * Since: 0.1.4
  **/
@@ -273,7 +329,7 @@ grl_data_get_string (GrlData *data, GrlKeyID key)
 {
   const GValue *value = grl_data_get (data, key);
 
-  if (!value || !G_VALUE_HOLDS_STRING(value)) {
+  if (!value || !G_VALUE_HOLDS_STRING (value)) {
     return NULL;
   } else {
     return g_value_get_string (value);
@@ -283,11 +339,11 @@ grl_data_get_string (GrlData *data, GrlKeyID key)
 /**
  * grl_data_set_int:
  * @data: data to change
- * @key: (type GObject.ParamSpec): key to change or add
+ * @key: (type Grl.KeyID): key to change or add
  * @intvalue: the new value
  *
- * Sets the value associated with the key. If key already has a value and
- * #overwrite is TRUE, old value is replaced by the new one.
+ * Sets the first value associated with the key. If key already has a first
+ * value and #overwrite is %TRUE, old value is replaced by the new one.
  *
  * Since: 0.1.4
  **/
@@ -303,10 +359,10 @@ grl_data_set_int (GrlData *data, GrlKeyID key, gint intvalue)
 /**
  * grl_data_get_int:
  * @data: data to inspect
- * @key: (type GObject.ParamSpec): key to use
+ * @key: (type Grl.KeyID): key to use
  *
- * Returns the value associated with the key. If key has no value, or value is
- * not a gint, or key is not in data, then 0 is returned.
+ * Returns the first value associated with the key. If key has no first value,
+ * or value is not a gint, or key is not in data, then 0 is returned.
  *
  * Returns: int value associated with key, or 0 in other case.
  *
@@ -317,7 +373,7 @@ grl_data_get_int (GrlData *data, GrlKeyID key)
 {
   const GValue *value = grl_data_get (data, key);
 
-  if (!value || !G_VALUE_HOLDS_INT(value)) {
+  if (!value || !G_VALUE_HOLDS_INT (value)) {
     return 0;
   } else {
     return g_value_get_int (value);
@@ -327,11 +383,11 @@ grl_data_get_int (GrlData *data, GrlKeyID key)
 /**
  * grl_data_set_float:
  * @data: data to change
- * @key: (type GObject.ParamSpec): key to change or add
+ * @key: (type Grl.KeyID): key to change or add
  * @floatvalue: the new value
  *
- * Sets the value associated with the key. If key already has a value and
- * #overwrite is TRUE, old value is replaced by the new one.
+ * Sets the first value associated with the key. If key already has a first
+ * value and #overwrite is %TRUE, old value is replaced by the new one.
  *
  * Since: 0.1.5
  **/
@@ -343,13 +399,14 @@ grl_data_set_float (GrlData *data, GrlKeyID key, float floatvalue)
   g_value_set_float (&value, floatvalue);
   grl_data_set (data, key, &value);
 }
+
 /**
  * grl_data_get_float:
  * @data: data to inspect
- * @key: (type GObject.ParamSpec): key to use
+ * @key: (type Grl.KeyID): key to use
  *
- * Returns the value associated with the key. If key has no value, or value is
- * not a gfloat, or key is not in data, then 0 is returned.
+ * Returns the first value associated with the key. If key has no first value,
+ * or value is not a gfloat, or key is not in data, then 0 is returned.
  *
  * Returns: float value associated with key, or 0 in other case.
  *
@@ -360,7 +417,7 @@ grl_data_get_float (GrlData *data, GrlKeyID key)
 {
   const GValue *value = grl_data_get (data, key);
 
-  if (!value || !G_VALUE_HOLDS_FLOAT(value)) {
+  if (!value || !G_VALUE_HOLDS_FLOAT (value)) {
     return 0;
   } else {
     return g_value_get_float (value);
@@ -370,12 +427,12 @@ grl_data_get_float (GrlData *data, GrlKeyID key)
 /**
  * grl_data_set_binary:
  * @data: data to change
- * @key: (type GObject.ParamSpec): key to change or add
+ * @key: (type Grl.KeyID): key to change or add
  * @buf: buffer holding the data
  * @size: size of the buffer
  *
- * Sets the value associated with the key. If key already has a value and
- * #overwrite is TRUE, old value is replaced by the new one.
+ * Sets the first value associated with the key. If key already has a first
+ * value and #overwrite is %TRUE, old value is replaced by the new one.
  **/
 void
 grl_data_set_binary (GrlData *data, GrlKeyID key, const guint8 *buf, gsize size)
@@ -396,13 +453,13 @@ grl_data_set_binary (GrlData *data, GrlKeyID key, const guint8 *buf, gsize size)
 /**
  * grl_data_get_binary:
  * @data: data to inspect
- * @key: (type GObject.ParamSpec): key to use
+ * @key: (type Grl.KeyID): key to use
  * @size: location to store the buffer size
  *
- * Returns the value associated with the key. If key has no value, or value is
- * not a gfloat, or key is not in data, then 0 is returned.
+ * Returns the first value associated with the key. If key has no first value,
+ * or value is not a gfloat, or key is not in data, then 0 is returned.
  *
- * Returns: buffer location associated with the key, or NULL in other case. If
+ * Returns: buffer location associated with the key, or %NULL in other case. If
  * successful size will be set the to the buffer size.
  **/
 const guint8 *
@@ -412,7 +469,7 @@ grl_data_get_binary(GrlData *data, GrlKeyID key, gsize *size)
 
   const GValue *value = grl_data_get (data, key);
 
-  if (!value || !G_VALUE_HOLDS_BOXED(value)) {
+  if (!value || !G_VALUE_HOLDS_BOXED (value)) {
     return NULL;
   } else {
     GByteArray * array;
@@ -426,7 +483,7 @@ grl_data_get_binary(GrlData *data, GrlKeyID key, gsize *size)
 /**
  * grl_data_add:
  * @data: data to change
- * @key: (type GObject.ParamSpec): key to add
+ * @key: (type Grl.KeyID): key to add
  *
  * Adds a new key to data, with no value. If key already exists, it does
  * nothing.
@@ -444,10 +501,12 @@ grl_data_add (GrlData *data, GrlKeyID key)
 /**
  * grl_data_remove:
  * @data: data to change
- * @key: (type GObject.ParamSpec): key to remove
+ * @key: (type Grl.KeyID): key to remove
+ *
+ * Removes the first value for key from data. If key is not in data, or value is
+ * %NULL, hen it does nothing.
  *
- * Removes key from data, freeing its value. If key is not in data, then
- * it does nothing.
+ * Notice this function ignores the value of #overwrite property.
  *
  * Since: 0.1.4
  **/
@@ -455,18 +514,19 @@ void
 grl_data_remove (GrlData *data, GrlKeyID key)
 {
   g_return_if_fail (GRL_IS_DATA (data));
+  g_return_if_fail (key);
 
-  g_hash_table_remove (data->priv->data, key);
+  data_set (data, key, NULL, TRUE);
 }
 
 /**
  * grl_data_has_key:
  * @data: data to inspect
- * @key: (type GObject.ParamSpec): key to search
+ * @key: (type Grl.KeyID): key to search
  *
  * Checks if key is in data.
  *
- * Returns: TRUE if key is in data, FALSE in other case.
+ * Returns: %TRUE if key is in data, %FALSE in other case.
  *
  * Since: 0.1.4
  **/
@@ -484,9 +544,9 @@ grl_data_has_key (GrlData *data, GrlKeyID key)
  *
  * Returns a list with keys contained in data.
  *
- * Returns: (transfer container) (element-type GObject.ParamSpec): an array with
- * the keys. The content of the list should not be modified or freed. Use g_list_free()
- * when done using the list.
+ * Returns: (transfer container) (element-type Grl.KeyID): an array with the
+ * keys. The content of the list should not be modified or freed. Use
+ * g_list_free() when done using the list.
  *
  * Since: 0.1.4
  **/
@@ -505,35 +565,426 @@ grl_data_get_keys (GrlData *data)
 /**
  * grl_data_key_is_known:
  * @data: data to inspect
- * @key: (type GObject.ParamSpec): key to search
+ * @key: (type Grl.KeyID): key to search
  *
- * Checks if the key has a value.
+ * Checks if the key has a first value.
  *
- * Returns: TRUE if key has a value.
+ * Returns: %TRUE if key has a value.
  *
  * Since: 0.1.4
  **/
 gboolean
 grl_data_key_is_known (GrlData *data, GrlKeyID key)
 {
-  GValue *v;
+  const GValue *v;
 
   g_return_val_if_fail (GRL_IS_DATA (data), FALSE);
+  g_return_val_if_fail (key, FALSE);
 
-  v = g_hash_table_lookup (data->priv->data, key);
+  v = grl_data_get (data, key);
 
   if (!v) {
     return FALSE;
   }
 
   if (G_VALUE_HOLDS_STRING (v)) {
-    return g_value_get_string(v) != NULL;
+    return g_value_get_string (v) != NULL;
   }
 
   return TRUE;
 }
 
 /**
+ * grl_data_add_property:
+ * @data: data to change
+ * @prop: a set of related properties with their values
+ *
+ * Adds a new set of values.
+ *
+ * All keys in prop must be related among them.
+ *
+ * @data will take the ownership of prop, so do not modify it.
+ **/
+void
+grl_data_add_property (GrlData *data,
+                       GrlProperty *prop)
+{
+  GList *keys;
+  GList *list_prop;
+  GrlKeyID sample_key;
+
+  g_return_if_fail (GRL_IS_DATA (data));
+  g_return_if_fail (GRL_IS_PROPERTY (prop));
+
+  keys = grl_property_get_keys (prop, TRUE);
+  if (!keys) {
+    /* Ignore empty properties */
+    GRL_WARNING ("Empty set of values");
+    g_object_unref (prop);
+    return;
+  }
+
+  sample_key = get_sample_key (keys->data);
+  g_list_free (keys);
+
+  if (!sample_key) {
+    g_object_unref (prop);
+    return;
+  }
+
+  list_prop = g_hash_table_lookup (data->priv->data, sample_key);
+  list_prop = g_list_append (list_prop, prop);
+  g_hash_table_insert (data->priv->data, sample_key, list_prop);
+}
+
+/**
+ * grl_data_add_string:
+ * @data: data to append
+ * @key: (type Grl.KeyID): key to append
+ * @strvalue: the new value
+ *
+ * Appends a new string value for @key in @data. All related keys are set to
+ * %NULL.
+ **/
+void
+grl_data_add_string (GrlData *data,
+                     GrlKeyID key,
+                     const gchar *strvalue)
+{
+  GrlProperty *prop;
+
+  prop = grl_property_new_for_key (key);
+  grl_property_set_string (prop, key, strvalue);
+  grl_data_add_property (data, prop);
+}
+
+/**
+ * grl_data_add_int:
+ * @data: data to append
+ * @key: (type Grl.KeyID): key to append
+ * @intvalue: the new value
+ *
+ * Appends a new int value for @key in @data. All related keys are set to
+ * %NULL.
+ **/
+void
+grl_data_add_int (GrlData *data,
+                  GrlKeyID key,
+                  gint intvalue)
+{
+  GrlProperty *prop;
+
+  prop = grl_property_new_for_key (key);
+  grl_property_set_int (prop, key, intvalue);
+  grl_data_add_property (data, prop);
+}
+
+/**
+ * grl_data_add_float:
+ * @data: data to append
+ * @key: (type Grl.KeyID): key to append
+ * @floatvalue: the new value
+ *
+ * Appends a new float value for @key in @data. All related keys are set to
+ * %NULL.
+ **/
+void
+grl_data_add_float (GrlData *data,
+                    GrlKeyID key,
+                    gfloat floatvalue)
+{
+  GrlProperty *prop;
+
+  prop = grl_property_new_for_key (key);
+  grl_property_set_float (prop, key, floatvalue);
+  grl_data_add_property (data, prop);
+}
+
+/**
+ * grl_data_add_binary:
+ * @data: data to append
+ * @key: (type Grl.KeyID): key to append
+ * @buf: the buffer containing the new value
+ * @size: size of buffer
+ *
+ * Appends a new binary value for @key in @data. All related keys are set to
+ * %NULL.
+ **/
+void
+grl_data_add_binary (GrlData *data,
+                     GrlKeyID key,
+                     const guint8 *buf,
+                     gsize size)
+{
+  GrlProperty *prop;
+
+  prop = grl_property_new_for_key (key);
+  grl_property_set_binary (prop, key, buf, size);
+  grl_data_add_property (data, prop);
+}
+
+/**
+ * grl_data_length:
+ * @data: a data
+ * @key: a metadata key
+ *
+ * Returns how many values key has in mdata.
+ *
+ * Returns: number of values
+ **/
+guint
+grl_data_length (GrlData *data,
+                 GrlKeyID key)
+{
+  GrlKeyID sample_key;
+
+  g_return_val_if_fail (GRL_IS_DATA (data), 0);
+  g_return_val_if_fail (key, 0);
+
+  sample_key = get_sample_key (key);
+  if (!sample_key) {
+    return 0;
+  }
+
+  return g_list_length (g_hash_table_lookup (data->priv->data, sample_key));
+}
+
+/**
+ * grl_data_get_property:
+ * @mdata: a data
+ * @key: a metadata key
+ * @index: element to retrieve, starting at 0
+ *
+ * Returns a set containing the values for key and related keys at position specified.
+ *
+ * If user changes any of the values in the property, the changes will become permanent.
+ *
+ * Returns: a #GrlProperty. Do not free it.
+ **/
+GrlProperty *
+grl_data_get_property (GrlData *data,
+                       GrlKeyID key,
+                       guint index)
+{
+  GList *prop_list;
+  GrlKeyID sample_key;
+  GrlProperty *prop;
+
+  g_return_val_if_fail (GRL_IS_DATA (data), NULL);
+  g_return_val_if_fail (key, NULL);
+
+  sample_key = get_sample_key (key);
+  if (!sample_key) {
+    return NULL;
+  }
+
+  prop_list = g_hash_table_lookup (data->priv->data, sample_key);
+  prop = g_list_nth_data (prop_list, index);
+
+  if (!prop) {
+      /* GRL_WARNING ("%s: index %u out of range", __FUNCTION__, index); */
+    GRL_WARNING("oUT OF RANGE");
+      return NULL;
+  }
+
+  return prop;
+}
+
+/**
+ * grl_data_get_all_single_property:
+ * @mdata: a data
+ * @key: a metadata key
+ *
+ * Returns all non-%NULL values for specified key. This ignores completely the
+ * related keys.
+ *
+ * Returns: (element-type GObject.Value) (transfer container): a #GList with
+ * values. Do not change or free the values. Free the list with #g_list_free.
+ **/
+GList *
+grl_data_get_all_single_property (GrlData *data,
+                                  GrlKeyID key)
+{
+  GrlKeyID sample_key;
+
+  g_return_val_if_fail (GRL_IS_DATA (data), NULL);
+  g_return_val_if_fail (key, NULL);
+
+  sample_key = get_sample_key (key);
+  if (!sample_key) {
+    return NULL;
+  }
+
+  return g_list_copy (g_hash_table_lookup (data->priv->data, sample_key));
+}
+
+/**
+ * grl_data_get_all_single_property_string:
+ * @mdata: a data
+ * @key: a metadata key
+ *
+ * Returns all non-%NULL values for specified key of type string. This ignores
+ * completely the related keys.
+ *
+ * Returns: (element-type utf8) (transfer container): a #GList with values. Do
+ * not change or free the strings. Free the list with #g_list_free.
+ **/
+GList *
+grl_data_get_all_single_property_string (GrlData *data,
+                                         GrlKeyID key)
+{
+  GList *list_strings = NULL;
+  GList *list_values;
+  GList *value;
+  const gchar *string_value;
+
+  g_return_val_if_fail (GRL_IS_DATA (data), NULL);
+  g_return_val_if_fail (key, NULL);
+
+  /* Verify key is of type string */
+  if (GRL_METADATA_KEY_GET_TYPE (key) != G_TYPE_STRING) {
+    GRL_WARNING ("Wrong type (not string)");
+    return NULL;
+  }
+
+  list_values = grl_data_get_all_single_property (data, key);
+  for (value = list_values; value; value = g_list_next (value)) {
+    string_value = g_value_get_string (value->data);
+    if (string_value) {
+      list_strings = g_list_prepend (list_strings, (gpointer) string_value);
+    }
+  }
+
+  g_list_free (list_values);
+
+  return g_list_reverse (list_strings);
+}
+
+/**
+ * grl_data_remove_property:
+ * @data: a data
+ * @key: a metadata key
+ * @index: index of key to be removed, starting at 0
+ *
+ * Removes key and related keys from position in mdata.
+ **/
+void
+grl_data_remove_property (GrlData *data,
+                          GrlKeyID key,
+                          guint index)
+{
+  GList *prop_element;
+  GList *prop_list;
+  GrlKeyID sample_key;
+
+  g_return_if_fail (GRL_IS_DATA (data));
+  g_return_if_fail (key);
+
+  sample_key = get_sample_key (key);
+  if (!sample_key) {
+    return;
+  }
+
+  prop_list = g_hash_table_lookup (data->priv->data, sample_key);
+  prop_element = g_list_nth (prop_list, index);
+  if (!prop_element) {
+    GRL_WARNING ("%s: index %u out of range", __FUNCTION__, index);
+    return;
+  }
+
+  g_object_unref (prop_element->data);
+  prop_list = g_list_delete_link (prop_list, prop_element);
+  g_hash_table_insert (data->priv->data, sample_key, prop_list);
+}
+
+/**
+ * grl_data_set_property:
+ * @data: a data
+ * @prop: a set of related keys
+ * @index: position to be updated, starting at 0
+ *
+ * Updates the values at position in @data with values in @prop.
+ *
+ * @data will take ownership of @prop, so do not free it after invoking this
+ * function.
+ **/
+void
+grl_data_set_property (GrlData *data,
+                       GrlProperty *prop,
+                       guint index)
+{
+  GList *keys;
+  GList *prop_element;
+  GList *prop_list;
+  GrlKeyID sample_key;
+
+  g_return_if_fail (GRL_IS_DATA (data));
+  g_return_if_fail (GRL_IS_PROPERTY (prop));
+
+  keys = grl_property_get_keys (prop, TRUE);
+  if (!keys) {
+    GRL_WARNING ("Empty properties");
+    g_object_unref (prop);
+    return;
+  }
+
+  sample_key = get_sample_key (keys->data);
+  g_list_free (keys);
+  if (!sample_key) {
+    return;
+  }
+
+  prop_list = g_hash_table_lookup (data->priv->data, sample_key);
+  prop_element = g_list_nth (prop_list, index);
+  if (!prop_element) {
+    GRL_WARNING ("%s: index %u out of range", __FUNCTION__, index);
+    return;
+  }
+
+  g_object_unref (prop_element->data);
+  prop_element->data = prop;
+}
+
+/**
+ * grl_data_dup:
+ * @data: data to duplicate
+ *
+ * Makes a deep copy of @data and all its contents.
+ *
+ * Returns: a new #GrlData. Free it with #g_object_unref.
+ **/
+GrlData *
+grl_data_dup (GrlData *data)
+{
+  GList *dup_prop_list;
+  GList *key;
+  GList *keys;
+  GList *prop_list;
+  GrlData *dup_data;
+
+  g_return_val_if_fail (GRL_IS_DATA (data), NULL);
+
+  dup_data = grl_data_new ();
+  keys = g_hash_table_get_keys (data->priv->data);
+  for (key = keys; key; key = g_list_next (key)) {
+    dup_prop_list = NULL;
+    prop_list = g_hash_table_lookup (data->priv->data, key->data);
+    while (prop_list) {
+      dup_prop_list = g_list_prepend (dup_prop_list,
+                                      grl_property_dup (prop_list->data));
+      prop_list = g_list_next (prop_list);
+    }
+    g_hash_table_insert (dup_data->priv->data,
+                         key->data,
+                         g_list_reverse (prop_list));
+  }
+
+  g_list_free (keys);
+
+  return dup_data;
+}
+
+/**
  * grl_data_set_overwrite:
  * @data: data to change
  * @overwrite: if data can be overwritten
@@ -541,8 +992,8 @@ grl_data_key_is_known (GrlData *data, GrlKeyID key)
  * This controls if #grl_data_set will overwrite current value of a property
  * with the new one.
  *
- * Set it to TRUE so old values are overwritten, or FALSE in other case (default
- * is FALSE).
+ * Set it to %TRUE so old values are overwritten, or %FALSE in other case (default
+ * is %FALSE).
  *
  * Since: 0.1.4
  **/
@@ -563,7 +1014,7 @@ grl_data_set_overwrite (GrlData *data, gboolean overwrite)
  *
  * Checks if old values are replaced when calling #grl_data_set.
  *
- * Returns: TRUE if values will be overwritten.
+ * Returns: %TRUE if values will be overwritten.
  *
  * Since: 0.1.4
  **/
diff --git a/src/data/grl-data.h b/src/data/grl-data.h
index 6af3ad4..d358502 100644
--- a/src/data/grl-data.h
+++ b/src/data/grl-data.h
@@ -32,6 +32,7 @@
 #include <glib-object.h>
 #include <grl-metadata-key.h>
 #include <grl-definitions.h>
+#include <grl-property.h>
 
 G_BEGIN_DECLS
 
@@ -127,6 +128,30 @@ GList *grl_data_get_keys (GrlData *data);
 
 gboolean grl_data_key_is_known (GrlData *data, GrlKeyID key);
 
+void grl_data_add_property (GrlData *data, GrlProperty *prop);
+
+void grl_data_add_string (GrlData *data, GrlKeyID key, const gchar *strvalue);
+
+void grl_data_add_int (GrlData *data, GrlKeyID key, gint intvalue);
+
+void grl_data_add_float (GrlData *data, GrlKeyID key, gfloat floatvalue);
+
+void grl_data_add_binary (GrlData *data, GrlKeyID key, const guint8 *buf, gsize size);
+
+guint grl_data_length (GrlData *data, GrlKeyID key);
+
+GrlProperty *grl_data_get_property (GrlData *data, GrlKeyID key, guint index);
+
+GList *grl_data_get_all_single_property (GrlData *data, GrlKeyID key);
+
+GList *grl_data_get_all_single_property_string (GrlData *data, GrlKeyID key);
+
+void grl_data_remove_property (GrlData *data, GrlKeyID key, guint index);
+
+void grl_data_set_property (GrlData *data, GrlProperty *prop, guint index);
+
+GrlData *grl_data_dup (GrlData *data);
+
 void grl_data_set_overwrite (GrlData *data, gboolean overwrite);
 
 gboolean grl_data_get_overwrite (GrlData *data);
diff --git a/src/data/grl-media.c b/src/data/grl-media.c
index ad3cf63..d37bed3 100644
--- a/src/data/grl-media.c
+++ b/src/data/grl-media.c
@@ -44,7 +44,7 @@ GRL_LOG_DOMAIN(media_log_domain);
 static void grl_media_dispose (GObject *object);
 static void grl_media_finalize (GObject *object);
 
-G_DEFINE_TYPE (GrlMedia, grl_media, GRL_TYPE_DATA_MULTI);
+G_DEFINE_TYPE (GrlMedia, grl_media, GRL_TYPE_DATA);
 
 static void
 grl_media_class_init (GrlMediaClass *klass)
diff --git a/src/data/grl-media.h b/src/data/grl-media.h
index 347b48f..31c2928 100644
--- a/src/data/grl-media.h
+++ b/src/data/grl-media.h
@@ -29,7 +29,7 @@
 #ifndef _GRL_MEDIA_H_
 #define _GRL_MEDIA_H_
 
-#include <grl-data-multi.h>
+#include <grl-data.h>
 #include <grl-definitions.h>
 
 G_BEGIN_DECLS
@@ -85,7 +85,7 @@ typedef struct _GrlMediaClass GrlMediaClass;
  */
 struct _GrlMediaClass
 {
-  GrlDataMultiClass parent_class;
+  GrlDataClass parent_class;
 
   /*< private >*/
   gpointer _grl_reserved[GRL_PADDING];
@@ -93,7 +93,7 @@ struct _GrlMediaClass
 
 struct _GrlMedia
 {
-  GrlDataMulti parent;
+  GrlData parent;
 
   /*< private >*/
   gpointer _grl_reserved[GRL_PADDING_SMALL];
-- 
1.7.4



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]