/*
* 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-related-keys
* @short_description: A class where to store related metadata keys.
* @see_also: #GrlRegistry, #GrlData
*
* When handling media keys, like artist, URI, mime-type, and so on, some of
* these keys are somewhat related: they do not make sense if they are not
* accompanied by other keys.
*
* For instance, media URI and and mime-type are related keys: mime-type does
* not make sense if it is not accompanied by an URI. Moreover, for each URI
* value, there is a corresponding mime-type value.
*
* #GrlRelatedKeys stores related keys and their values in one place, so user
* can handle them in one shot.
*/
#include "grl-related-keys.h"
#include "grl-log.h"
#include "grl-registry.h"
struct _GrlRelatedKeysPrivate {
GHashTable *data;
};
static void grl_related_keys_finalize (GObject *object);
static void free_value (GValue *val);
/* ================ GrlRelatedKeys GObject ================ */
G_DEFINE_TYPE_WITH_PRIVATE (GrlRelatedKeys, grl_related_keys, G_TYPE_OBJECT);
static void
grl_related_keys_class_init (GrlRelatedKeysClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *)klass;
gobject_class->finalize = grl_related_keys_finalize;
}
static void
grl_related_keys_init (GrlRelatedKeys *self)
{
self->priv = grl_related_keys_get_instance_private (self);
self->priv->data = g_hash_table_new_full (g_direct_hash,
g_direct_equal,
NULL,
(GDestroyNotify) free_value);
}
static void
grl_related_keys_finalize (GObject *object)
{
g_hash_table_unref (GRL_RELATED_KEYS (object)->priv->data);
G_OBJECT_CLASS (grl_related_keys_parent_class)->finalize (object);
}
/* ================ Utitilies ================ */
static void
free_value (GValue *val)
{
if (val) {
g_value_unset (val);
g_free (val);
}
}
/* ================ API ================ */
/**
* grl_related_keys_new:
*
* Creates a new #GrlRelatedKeys instance that can be used to store related
* keys and their values.
*
* Returns: a new object.
*
* Since: 0.1.10
**/
GrlRelatedKeys *
grl_related_keys_new (void)
{
return g_object_new (GRL_TYPE_RELATED_KEYS, NULL);
}
/**
* grl_related_keys_new_valist:
* @key: first key
* @args: #va_list of value, followed by (key,value) pairs to insert
*
* Creates a new #GrlRelatedKeys containing pairs of (key, value). Finish the
* list with %NULL.
*
* In case of a binary-type key, the expected element is (key, value, size).
*
* value type will be extracted from key information.
*
* Returns: a new #GrlRelatedKeys
*
* Since: 0.1.10
**/
GrlRelatedKeys *
grl_related_keys_new_valist (GrlKeyID key,
va_list args)
{
GType key_type;
GrlKeyID next_key;
GrlRelatedKeys *prop;
gpointer next_value;
prop = grl_related_keys_new ();
next_key = key;
while (next_key) {
key_type = GRL_METADATA_KEY_GET_TYPE (next_key);
if (key_type == G_TYPE_STRING) {
grl_related_keys_set_string (prop, next_key, va_arg (args, gchar *));
} else if (key_type == G_TYPE_INT) {
grl_related_keys_set_int (prop, next_key, va_arg (args, gint));
} else if (key_type == G_TYPE_FLOAT) {
grl_related_keys_set_float (prop, next_key, va_arg (args, double));
} else if (key_type == G_TYPE_BOOLEAN) {
grl_related_keys_set_boolean (prop, next_key, va_arg (args, gboolean));
} else if (key_type == G_TYPE_BYTE_ARRAY) {
next_value = va_arg (args, gpointer);
grl_related_keys_set_binary (prop,
next_key,
next_value,
va_arg (args, gsize));
} else {
GRL_WARNING ("related key type '%s' not handled",
g_type_name (key_type));
}
next_key = va_arg (args, GrlKeyID);
}
return prop;
}
/**
* grl_related_keys_new_with_keys: (skip)
* @key: first key
* @...: value, following by list of (key, value)
*
* Creates a initial #GrlRelatedKeys containing the list of (key, value)
* pairs. Finish the list with %NULL.
*
* For more information see #grl_related_keys_new_valist.
*
* Returns: a new #GrlRelatedKeys
*
* Since: 0.1.10
**/
GrlRelatedKeys *
grl_related_keys_new_with_keys (GrlKeyID key,
...)
{
GrlRelatedKeys *prop;
va_list args;
va_start (args, key);
prop = grl_related_keys_new_valist (key, args);
va_end (args);
return prop;
}
/**
* grl_related_keys_get:
* @relkeys: set of related keys to retrieve value
* @key: (type GrlKeyID): key to look up.
*
* Get the value associated with @key from @relkeys. If it does not contain any
* value, %NULL will be returned.
*
* Returns: (transfer none): a #GValue. This value should not be modified nor
* freed by user.
*
* Since: 0.1.10
**/
const GValue *
grl_related_keys_get (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
g_return_val_if_fail (key, NULL);
return g_hash_table_lookup (relkeys->priv->data, GRLKEYID_TO_POINTER (key));
}
/**
* grl_related_keys_set:
* @relkeys: set of related keys to modify
* @key: (type GrlKeyID): key to change or add
* @value: the new value
*
* Sets the value associated with @key into @relkeys. Old value is freed and
* the new one is set.
*
* Also, checks that @value is compliant with @key specification, modifying it
* accordingly. For instance, if @key requires a number between 0 and 10, but
* value is outside this range, it will be adapted accordingly.
*
* Since: 0.1.10
**/
void
grl_related_keys_set (GrlRelatedKeys *relkeys,
GrlKeyID key,
const GValue *value)
{
GValue *copy = NULL;
GrlRegistry *registry;
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_return_if_fail (key);
if (!value) {
return;
}
/* Dup value */
if (G_VALUE_TYPE (value) != GRL_METADATA_KEY_GET_TYPE (key)) {
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)));
return;
}
copy = g_new0 (GValue, 1);
g_value_init (copy, G_VALUE_TYPE (value));
g_value_copy (value, copy);
registry = grl_registry_get_default ();
if (!grl_registry_metadata_key_validate (registry, key, copy)) {
GRL_WARNING ("'%s' value invalid, adjusting",
GRL_METADATA_KEY_GET_NAME (key));
}
g_hash_table_insert (relkeys->priv->data, GRLKEYID_TO_POINTER (key), copy);
}
/**
* grl_related_keys_set_string:
* @relkeys: set of related keys to modify
* @key: (type GrlKeyID): key to change or add
* @strvalue: the new value
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as a strying-type key. Old value is freed and the new one is set.
*
* Since: 0.1.10
**/
void
grl_related_keys_set_string (GrlRelatedKeys *relkeys,
GrlKeyID key,
const gchar *strvalue)
{
GValue value = { 0 };
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
if (strvalue) {
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, strvalue);
grl_related_keys_set (relkeys, key, &value);
g_value_unset (&value);
}
}
/**
* grl_related_keys_get_string:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to use
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* or value is not string, or @key is not in @relkeys, then %NULL is returned.
*
* Returns: string associated with @key, or %NULL in other case. Caller should
* not change nor free the value.
*
* Since: 0.1.10
**/
const gchar *
grl_related_keys_get_string (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_STRING (value)) {
return NULL;
} else {
return g_value_get_string (value);
}
}
/**
* grl_related_keys_set_int:
* @relkeys: set of related keys to change
* @key: (type GrlKeyID): key to change or add
* @intvalue: the new value
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as an int-type key. Old value is replaced by the new one.
*
* Since: 0.1.10
**/
void
grl_related_keys_set_int (GrlRelatedKeys *relkeys,
GrlKeyID key,
gint intvalue)
{
GValue value = { 0 };
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, intvalue);
grl_related_keys_set (relkeys, key, &value);
}
/**
* grl_related_keys_get_int:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to use
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* or value is not a gint, or @key is not in @relkeys, then 0 is returned.
*
* Returns: int value associated with @key, or 0 in other case.
*
* Since: 0.1.10
**/
gint
grl_related_keys_get_int (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), 0);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_INT (value)) {
return 0;
} else {
return g_value_get_int (value);
}
}
/**
* grl_related_keys_set_float:
* @relkeys: set of related keys to change
* @key: (type GrlKeyID): key to change or add
* @floatvalue: the new value
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as a float-type key. Old value is replaced by the new one.
*
* Since: 0.1.10
**/
void
grl_related_keys_set_float (GrlRelatedKeys *relkeys,
GrlKeyID key,
float floatvalue)
{
GValue value = { 0 };
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_value_init (&value, G_TYPE_FLOAT);
g_value_set_float (&value, floatvalue);
grl_related_keys_set (relkeys, key, &value);
}
/**
* grl_related_keys_get_float:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to use
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* or value is not a gfloat, or @key is not in @relkeys, then 0 is returned.
*
* Returns: float value associated with @key, or 0 in other case.
*
* Since: 0.1.10
**/
gfloat
grl_related_keys_get_float (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), 0.0);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_FLOAT (value)) {
return 0;
} else {
return g_value_get_float (value);
}
}
/**
* grl_related_keys_set_boolean:
* @relkeys: set of related keys to change
* @key: (type GrlKeyID): key to change or add
* @booleanvalue: the new value
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as a boolean-type key. Old value is replaced by the new one.
*
* Since: 0.2.3
**/
void
grl_related_keys_set_boolean (GrlRelatedKeys *relkeys,
GrlKeyID key,
gboolean booleanvalue)
{
GValue value = { 0 };
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, booleanvalue);
grl_related_keys_set (relkeys, key, &value);
}
/**
* grl_related_keys_get_boolean:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to use
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* or value is not a gboolean, or @key is not in @relkeys, then %FALSE is
* returned.
*
* Returns: float value associated with @key, or %FALSE in other case.
*
* Since: 0.2.3
**/
gboolean
grl_related_keys_get_boolean (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), FALSE);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_BOOLEAN (value)) {
return FALSE;
} else {
return g_value_get_boolean (value);
}
}
/**
* grl_related_keys_set_binary:
* @relkeys: set of related keys to change
* @key: (type GrlKeyID): key to change or add
* @buf: buffer holding the relkeys
* @size: size of the buffer
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as a binary-type key. Old value is replaced by the new one.
*
* Since: 0.1.10
**/
void
grl_related_keys_set_binary (GrlRelatedKeys *relkeys,
GrlKeyID key,
const guint8 *buf,
gsize size)
{
GValue v = { 0 };
GByteArray *array;
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
if (!buf || !size) {
return;
}
array = g_byte_array_append (g_byte_array_sized_new(size),
buf,
size);
g_value_init (&v, g_byte_array_get_type ());
g_value_take_boxed (&v, array);
grl_related_keys_set (relkeys, key, &v);
g_value_unset (&v);
}
/**
* grl_related_keys_get_binary:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to use
* @size: (out): location to store the buffer size
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* or value is not a binary, or @key is not in @relkeys, then 0 is returned.
*
* Returns: buffer location associated with @key, or %NULL in other case. If
* successful @size will be set to the buffer size.
*
* Since: 0.1.10
**/
const guint8 *
grl_related_keys_get_binary (GrlRelatedKeys *relkeys,
GrlKeyID key,
gsize *size)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
g_return_val_if_fail (size, NULL);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_BOXED (value)) {
return NULL;
} else {
GByteArray * array;
array = g_value_get_boxed (value);
*size = array->len;
return (const guint8 *) array->data;
}
}
/**
* grl_related_keys_set_boxed:
* @relkeys: set of related keys to modify
* @key: key to change or add
* @boxed: the new value
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as a boxed-type key. Old value is freed and the new one is set.
*
* Since: 0.2.0
*/
void
grl_related_keys_set_boxed (GrlRelatedKeys *relkeys,
GrlKeyID key,
gconstpointer boxed)
{
GValue value = { 0 };
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_return_if_fail (boxed != NULL);
g_value_init (&value, grl_metadata_key_get_type (key));
g_value_set_boxed (&value, boxed);
grl_related_keys_set (relkeys, key, &value);
g_value_unset (&value);
}
/**
* grl_related_keys_get_boxed:
* @relkeys: set of related keys to inspect
* @key: key to use
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* the value is not of a boxed type, or @key is not in @relkeys, then %NULL is
* returned.
*
* Returns: (transfer none): the #GBoxed value associated with @key if
* possible, or %NULL in other case. The caller should not change nor free the
* value.
*
* Since: 0.2.0
*/
gconstpointer
grl_related_keys_get_boxed (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_BOXED (value)) {
return NULL;
} else {
return g_value_get_boxed (value);
}
}
/**
* grl_related_keys_set_int64:
* @relkeys: set of related keys to change
* @key: (type GrlKeyID): key to change or add
* @intvalue: the new value
*
* Sets the value associated with @key into @relkeys. @key must have been
* registered as a int64-type key. Old value is replaced by the new one.
*
* Since: 0.2.12
**/
void
grl_related_keys_set_int64 (GrlRelatedKeys *relkeys,
GrlKeyID key,
gint64 intvalue)
{
GValue value = { 0 };
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_value_init (&value, G_TYPE_INT64);
g_value_set_int64 (&value, intvalue);
grl_related_keys_set (relkeys, key, &value);
}
/**
* grl_related_keys_get_int64:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to use
*
* Returns the value associated with @key from @relkeys. If @key has no value,
* or value is not a gint64, or @key is not in @relkeys, then 0 is returned.
*
* Returns: int64 value associated with @key, or 0 in other case.
*
* Since: 0.2.12
**/
gint64
grl_related_keys_get_int64 (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
const GValue *value;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), 0);
value = grl_related_keys_get (relkeys, key);
if (!value || !G_VALUE_HOLDS_INT64 (value)) {
return 0;
} else {
return g_value_get_int64 (value);
}
}
/**
* grl_related_keys_remove:
* @relkeys: set of related keys
* @key: (type GrlKeyID): key to remove
*
* Removes @key from @relkeys set.
*
* Since: 0.2.3
**/
void
grl_related_keys_remove (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
g_return_if_fail (key != GRL_METADATA_KEY_INVALID);
g_hash_table_remove (relkeys->priv->data, GRLKEYID_TO_POINTER (key));
}
/**
* grl_related_keys_has_key:
* @relkeys: set of related keys to inspect
* @key: (type GrlKeyID): key to search
*
* Checks if @key is in @relkeys.
*
* Returns: %TRUE if @key is in @relkeys, %FALSE in other case.
*
* Since: 0.1.10
**/
gboolean
grl_related_keys_has_key (GrlRelatedKeys *relkeys,
GrlKeyID key)
{
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), FALSE);
return g_hash_table_lookup_extended (relkeys->priv->data,
GRLKEYID_TO_POINTER (key),
NULL, NULL);
}
/**
* grl_related_keys_get_keys:
* @relkeys: set of related keys to inspect
*
* Returns a list with keys contained in @relkeys.
*
* Returns: (transfer container) (element-type GrlKeyID): a list 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.13
**/
GList *
grl_related_keys_get_keys (GrlRelatedKeys *relkeys)
{
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
return g_hash_table_get_keys (relkeys->priv->data);
}
/**
* grl_related_keys_dup:
* @relkeys: set of related keys to duplicate
*
* Makes a deep copy of @relkeys and its contents.
*
* Returns: (transfer full): a new #GrlRelatedKeys.
* Free it with #g_object_unref.
*
* Since: 0.1.10
**/
GrlRelatedKeys *
grl_related_keys_dup (GrlRelatedKeys *relkeys)
{
GList *keys, *key;
const GValue *value;
GValue *value_copy;
GrlRelatedKeys *dup_relkeys;
g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
dup_relkeys = grl_related_keys_new ();
keys = grl_related_keys_get_keys (relkeys);
for (key = keys; key; key = g_list_next (key)) {
value = grl_related_keys_get (relkeys, GRLPOINTER_TO_KEYID (key->data));
value_copy = g_new0 (GValue, 1);
g_value_init (value_copy, G_VALUE_TYPE (value));
g_value_copy (value, value_copy);
g_hash_table_insert (dup_relkeys->priv->data, key->data, value_copy);
}
g_list_free (keys);
return dup_relkeys;
}