1 /*
2  * Copyright (C) 2011 Igalia S.L.
3  *
4  * Contact: Iago Toral Quiroga <itoral@igalia.com>
5  *
6  * Authors: Juan A. Suarez Romero <jasuarez@igalia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**
26  * SECTION:grl-related-keys
27  * @short_description: A class where to store related metadata keys.
28  * @see_also: #GrlRegistry, #GrlData
29  *
30  * When handling media keys, like artist, URI, mime-type, and so on, some of
31  * these keys are somewhat related: they do not make sense if they are not
32  * accompanied by other keys.
33  *
34  * For instance, media URI and and mime-type are related keys: mime-type does
35  * not make sense if it is not accompanied by an URI. Moreover, for each URI
36  * value, there is a corresponding mime-type value.
37  *
38  * #GrlRelatedKeys stores related keys and their values in one place, so user
39  * can handle them in one shot.
40  */
41 
42 #include "grl-related-keys.h"
43 #include "grl-log.h"
44 #include "grl-registry-priv.h"
45 
46 struct _GrlRelatedKeysPrivate {
47   GHashTable *data;
48 };
49 
50 static void grl_related_keys_finalize (GObject *object);
51 static void free_value (GValue *val);
52 
53 /* ================ GrlRelatedKeys GObject ================ */
54 
55 G_DEFINE_TYPE_WITH_PRIVATE (GrlRelatedKeys, grl_related_keys, G_TYPE_OBJECT);
56 
57 static void
grl_related_keys_class_init(GrlRelatedKeysClass * klass)58 grl_related_keys_class_init (GrlRelatedKeysClass *klass)
59 {
60   GObjectClass *gobject_class = (GObjectClass *)klass;
61 
62   gobject_class->finalize = grl_related_keys_finalize;
63 }
64 
65 static void
grl_related_keys_init(GrlRelatedKeys * self)66 grl_related_keys_init (GrlRelatedKeys *self)
67 {
68   self->priv = grl_related_keys_get_instance_private (self);
69   self->priv->data = g_hash_table_new_full (g_direct_hash,
70                                             g_direct_equal,
71                                             NULL,
72                                             (GDestroyNotify) free_value);
73 }
74 
75 static void
grl_related_keys_finalize(GObject * object)76 grl_related_keys_finalize (GObject *object)
77 {
78   g_hash_table_unref (GRL_RELATED_KEYS (object)->priv->data);
79   G_OBJECT_CLASS (grl_related_keys_parent_class)->finalize (object);
80 }
81 
82 /* ================ Utitilies ================ */
83 
84 static void
free_value(GValue * val)85 free_value (GValue *val)
86 {
87   if (val) {
88     g_value_unset (val);
89     g_free (val);
90   }
91 }
92 
93 /* ================ API ================ */
94 
95 /**
96  * grl_related_keys_new:
97  *
98  * Creates a new #GrlRelatedKeys instance that can be used to store related
99  * keys and their values.
100  *
101  * Returns: a new object.
102  *
103  * Since: 0.1.10
104  **/
105 GrlRelatedKeys *
grl_related_keys_new(void)106 grl_related_keys_new (void)
107 {
108   return g_object_new (GRL_TYPE_RELATED_KEYS, NULL);
109 }
110 
111 /**
112  * grl_related_keys_new_valist:
113  * @key: first key
114  * @args: #va_list of value, followed by (key,value) pairs to insert
115  *
116  * Creates a new #GrlRelatedKeys containing pairs of (key, value). Finish the
117  * list with %NULL.
118  *
119  * In case of a binary-type key, the expected element is (key, value, size).
120  *
121  * value type will be extracted from key information.
122  *
123  * Returns: a new #GrlRelatedKeys
124  *
125  * Since: 0.1.10
126  **/
127 GrlRelatedKeys *
grl_related_keys_new_valist(GrlKeyID key,va_list args)128 grl_related_keys_new_valist (GrlKeyID key,
129                              va_list args)
130 {
131   GType key_type;
132   GrlKeyID next_key;
133   GrlRelatedKeys *prop;
134   gpointer next_value;
135 
136   prop = grl_related_keys_new ();
137 
138   next_key = key;
139   while (next_key) {
140     key_type = GRL_METADATA_KEY_GET_TYPE (next_key);
141     if (key_type == G_TYPE_STRING) {
142       grl_related_keys_set_string (prop, next_key, va_arg (args, gchar *));
143     } else if (key_type == G_TYPE_INT) {
144       grl_related_keys_set_int (prop, next_key, va_arg (args, gint));
145     } else if (key_type == G_TYPE_FLOAT) {
146       grl_related_keys_set_float (prop, next_key, va_arg (args, double));
147     } else if (key_type == G_TYPE_BOOLEAN) {
148       grl_related_keys_set_boolean (prop, next_key, va_arg (args, gboolean));
149     } else if (key_type == G_TYPE_BYTE_ARRAY) {
150       next_value = va_arg (args, gpointer);
151       grl_related_keys_set_binary (prop,
152                                    next_key,
153                                    next_value,
154                                    va_arg (args, gsize));
155     } else {
156       GRL_WARNING ("related key type '%s' not handled",
157                    g_type_name (key_type));
158     }
159     next_key = va_arg (args, GrlKeyID);
160   }
161 
162   return prop;
163 }
164 
165 /**
166  * grl_related_keys_new_with_keys: (skip)
167  * @key: first key
168  * @...: value, following by list of (key, value)
169  *
170  * Creates a initial #GrlRelatedKeys containing the list of (key, value)
171  * pairs. Finish the list with %NULL.
172  *
173  * For more information see #grl_related_keys_new_valist.
174  *
175  * Returns: a new #GrlRelatedKeys
176  *
177  * Since: 0.1.10
178  **/
179 GrlRelatedKeys *
grl_related_keys_new_with_keys(GrlKeyID key,...)180 grl_related_keys_new_with_keys (GrlKeyID key,
181                                 ...)
182 {
183   GrlRelatedKeys *prop;
184   va_list args;
185 
186   va_start (args, key);
187   prop = grl_related_keys_new_valist (key, args);
188   va_end (args);
189 
190   return prop;
191 }
192 
193 
194 /**
195  * grl_related_keys_get:
196  * @relkeys: set of related keys to retrieve value
197  * @key: (type GrlKeyID): key to look up.
198  *
199  * Get the value associated with @key from @relkeys. If it does not contain any
200  * value, %NULL will be returned.
201  *
202  * Returns: (transfer none): a #GValue. This value should not be modified nor
203  * freed by user.
204  *
205  * Since: 0.1.10
206  **/
207 const GValue *
grl_related_keys_get(GrlRelatedKeys * relkeys,GrlKeyID key)208 grl_related_keys_get (GrlRelatedKeys *relkeys,
209                       GrlKeyID key)
210 {
211   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
212   g_return_val_if_fail (key, NULL);
213 
214   return g_hash_table_lookup (relkeys->priv->data, GRLKEYID_TO_POINTER (key));
215 }
216 
217 /**
218  * grl_related_keys_set:
219  * @relkeys: set of related keys to modify
220  * @key: (type GrlKeyID): key to change or add
221  * @value: the new value
222  *
223  * Sets the value associated with @key into @relkeys. Old value is freed and
224  * the new one is set.
225  *
226  * Also, checks that @value is compliant with @key specification, modifying it
227  * accordingly. For instance, if @key requires a number between 0 and 10, but
228  * value is outside this range, it will be adapted accordingly.
229  *
230  * Since: 0.1.10
231  **/
232 void
grl_related_keys_set(GrlRelatedKeys * relkeys,GrlKeyID key,const GValue * value)233 grl_related_keys_set (GrlRelatedKeys *relkeys,
234                       GrlKeyID key,
235                       const GValue *value)
236 {
237   GValue *copy = NULL;
238   GrlRegistry *registry;
239   GType key_type, value_type;
240 
241   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
242   g_return_if_fail (key);
243 
244   if (!value) {
245     return;
246   }
247 
248   key_type = GRL_METADATA_KEY_GET_TYPE (key);
249   value_type = G_VALUE_TYPE (value);
250 
251   if (!g_value_type_transformable (value_type, key_type)) {
252     GRL_WARNING ("value has type %s, but expected %s",
253                  g_type_name (value_type),
254                  g_type_name (key_type));
255     return;
256   }
257 
258   /* Dup value */
259   copy = g_new0 (GValue, 1);
260   g_value_init (copy, key_type);
261   if (!g_value_transform (value, copy)) {
262     GRL_WARNING ("transforming value type %s to key's type %s failed",
263                  g_type_name (value_type),
264                  g_type_name (key_type));
265     g_free (copy);
266     return;
267   }
268 
269   registry = grl_registry_get_default ();
270 
271   if (!grl_registry_metadata_key_validate (registry, key, copy)) {
272     GRL_WARNING ("'%s' value invalid, adjusting",
273                  GRL_METADATA_KEY_GET_NAME (key));
274   }
275   g_hash_table_insert (relkeys->priv->data, GRLKEYID_TO_POINTER (key), copy);
276 }
277 
278 /**
279  * grl_related_keys_set_string:
280  * @relkeys: set of related keys to modify
281  * @key: (type GrlKeyID): key to change or add
282  * @strvalue: the new value
283  *
284  * Sets the value associated with @key into @relkeys. @key must have been
285  * registered as a strying-type key. Old value is freed and the new one is set.
286  *
287  * Since: 0.1.10
288  **/
289 void
grl_related_keys_set_string(GrlRelatedKeys * relkeys,GrlKeyID key,const gchar * strvalue)290 grl_related_keys_set_string (GrlRelatedKeys *relkeys,
291                              GrlKeyID key,
292                              const gchar *strvalue)
293 {
294   GValue value = { 0 };
295 
296   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
297 
298   if (strvalue) {
299     g_value_init (&value, G_TYPE_STRING);
300     g_value_set_string (&value, strvalue);
301     grl_related_keys_set (relkeys, key, &value);
302     g_value_unset (&value);
303   }
304 }
305 
306 /**
307  * grl_related_keys_get_string:
308  * @relkeys: set of related keys to inspect
309  * @key: (type GrlKeyID): key to use
310  *
311  * Returns the value associated with @key from @relkeys. If @key has no value,
312  * or value is not string, or @key is not in @relkeys, then %NULL is returned.
313  *
314  * Returns: string associated with @key, or %NULL in other case. Caller should
315  * not change nor free the value.
316  *
317  * Since: 0.1.10
318  **/
319 const gchar *
grl_related_keys_get_string(GrlRelatedKeys * relkeys,GrlKeyID key)320 grl_related_keys_get_string (GrlRelatedKeys *relkeys,
321                              GrlKeyID key)
322 {
323   const GValue *value;
324 
325   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
326 
327   value = grl_related_keys_get (relkeys, key);
328 
329   if (!value || !G_VALUE_HOLDS_STRING (value)) {
330     return NULL;
331   } else {
332     return g_value_get_string (value);
333   }
334 }
335 
336 /**
337  * grl_related_keys_set_int:
338  * @relkeys: set of related keys to change
339  * @key: (type GrlKeyID): key to change or add
340  * @intvalue: the new value
341  *
342  * Sets the value associated with @key into @relkeys. @key must have been
343  * registered as an int-type key. Old value is replaced by the new one.
344  *
345  * Since: 0.1.10
346  **/
347 void
grl_related_keys_set_int(GrlRelatedKeys * relkeys,GrlKeyID key,gint intvalue)348 grl_related_keys_set_int (GrlRelatedKeys *relkeys,
349                           GrlKeyID key,
350                           gint intvalue)
351 {
352   GValue value = { 0 };
353   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
354   g_value_init (&value, G_TYPE_INT);
355   g_value_set_int (&value, intvalue);
356   grl_related_keys_set (relkeys, key, &value);
357 }
358 
359 /**
360  * grl_related_keys_get_int:
361  * @relkeys: set of related keys to inspect
362  * @key: (type GrlKeyID): key to use
363  *
364  * Returns the value associated with @key from @relkeys. If @key has no value,
365  * or value is not a gint, or @key is not in @relkeys, then 0 is returned.
366  *
367  * Returns: int value associated with @key, or 0 in other case.
368  *
369  * Since: 0.1.10
370  **/
371 gint
grl_related_keys_get_int(GrlRelatedKeys * relkeys,GrlKeyID key)372 grl_related_keys_get_int (GrlRelatedKeys *relkeys,
373                           GrlKeyID key)
374 {
375   const GValue *value;
376 
377   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), 0);
378 
379   value = grl_related_keys_get (relkeys, key);
380 
381   if (!value || !G_VALUE_HOLDS_INT (value)) {
382     return 0;
383   } else {
384     return g_value_get_int (value);
385   }
386 }
387 
388 /**
389  * grl_related_keys_set_float:
390  * @relkeys: set of related keys to change
391  * @key: (type GrlKeyID): key to change or add
392  * @floatvalue: the new value
393  *
394  * Sets the value associated with @key into @relkeys. @key must have been
395  * registered as a float-type key. Old value is replaced by the new one.
396  *
397  * Since: 0.1.10
398  **/
399 void
grl_related_keys_set_float(GrlRelatedKeys * relkeys,GrlKeyID key,float floatvalue)400 grl_related_keys_set_float (GrlRelatedKeys *relkeys,
401                             GrlKeyID key,
402                             float floatvalue)
403 {
404   GValue value = { 0 };
405   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
406   g_value_init (&value, G_TYPE_FLOAT);
407   g_value_set_float (&value, floatvalue);
408   grl_related_keys_set (relkeys, key, &value);
409 }
410 
411 /**
412  * grl_related_keys_get_float:
413  * @relkeys: set of related keys to inspect
414  * @key: (type GrlKeyID): key to use
415  *
416  * Returns the value associated with @key from @relkeys. If @key has no value,
417  * or value is not a gfloat, or @key is not in @relkeys, then 0 is returned.
418  *
419  * Returns: float value associated with @key, or 0 in other case.
420  *
421  * Since: 0.1.10
422  **/
423 gfloat
grl_related_keys_get_float(GrlRelatedKeys * relkeys,GrlKeyID key)424 grl_related_keys_get_float (GrlRelatedKeys *relkeys,
425                             GrlKeyID key)
426 {
427   const GValue *value;
428 
429   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), 0.0);
430 
431   value = grl_related_keys_get (relkeys, key);
432 
433   if (!value || !G_VALUE_HOLDS_FLOAT (value)) {
434     return 0;
435   } else {
436     return g_value_get_float (value);
437   }
438 }
439 
440 /**
441  * grl_related_keys_set_boolean:
442  * @relkeys: set of related keys to change
443  * @key: (type GrlKeyID): key to change or add
444  * @booleanvalue: the new value
445  *
446  * Sets the value associated with @key into @relkeys. @key must have been
447  * registered as a boolean-type key. Old value is replaced by the new one.
448  *
449  * Since: 0.2.3
450  **/
451 void
grl_related_keys_set_boolean(GrlRelatedKeys * relkeys,GrlKeyID key,gboolean booleanvalue)452 grl_related_keys_set_boolean (GrlRelatedKeys *relkeys,
453                               GrlKeyID key,
454                               gboolean booleanvalue)
455 {
456   GValue value = { 0 };
457   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
458   g_value_init (&value, G_TYPE_BOOLEAN);
459   g_value_set_boolean (&value, booleanvalue);
460   grl_related_keys_set (relkeys, key, &value);
461 }
462 
463 /**
464  * grl_related_keys_get_boolean:
465  * @relkeys: set of related keys to inspect
466  * @key: (type GrlKeyID): key to use
467  *
468  * Returns the value associated with @key from @relkeys. If @key has no value,
469  * or value is not a gboolean, or @key is not in @relkeys, then %FALSE is
470  * returned.
471  *
472  * Returns: float value associated with @key, or %FALSE in other case.
473  *
474  * Since: 0.2.3
475  **/
476 gboolean
grl_related_keys_get_boolean(GrlRelatedKeys * relkeys,GrlKeyID key)477 grl_related_keys_get_boolean (GrlRelatedKeys *relkeys,
478                               GrlKeyID key)
479 {
480   const GValue *value;
481 
482   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), FALSE);
483 
484   value = grl_related_keys_get (relkeys, key);
485 
486   if (!value || !G_VALUE_HOLDS_BOOLEAN (value)) {
487     return FALSE;
488   } else {
489     return g_value_get_boolean (value);
490   }
491 }
492 
493 /**
494  * grl_related_keys_set_binary:
495  * @relkeys: set of related keys to change
496  * @key: (type GrlKeyID): key to change or add
497  * @buf: buffer holding the relkeys
498  * @size: size of the buffer
499  *
500  * Sets the value associated with @key into @relkeys. @key must have been
501  * registered as a binary-type key. Old value is replaced by the new one.
502  *
503  * Since: 0.1.10
504  **/
505 void
grl_related_keys_set_binary(GrlRelatedKeys * relkeys,GrlKeyID key,const guint8 * buf,gsize size)506 grl_related_keys_set_binary (GrlRelatedKeys *relkeys,
507                              GrlKeyID key,
508                              const guint8 *buf,
509                              gsize size)
510 {
511   GValue v = { 0 };
512   GByteArray *array;
513 
514   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
515 
516   if (!buf || !size) {
517     return;
518   }
519 
520   array = g_byte_array_append (g_byte_array_sized_new(size),
521                                buf,
522                                size);
523 
524   g_value_init (&v, g_byte_array_get_type ());
525   g_value_take_boxed (&v, array);
526   grl_related_keys_set (relkeys, key, &v);
527   g_value_unset (&v);
528 }
529 
530 /**
531  * grl_related_keys_get_binary:
532  * @relkeys: set of related keys to inspect
533  * @key: (type GrlKeyID): key to use
534  * @size: (out): location to store the buffer size
535  *
536  * Returns the value associated with @key from @relkeys. If @key has no value,
537  * or value is not a binary, or @key is not in @relkeys, then 0 is returned.
538  *
539  * Returns: buffer location associated with @key, or %NULL in other case. If
540  * successful @size will be set to the buffer size.
541  *
542  * Since: 0.1.10
543  **/
544 const guint8 *
grl_related_keys_get_binary(GrlRelatedKeys * relkeys,GrlKeyID key,gsize * size)545 grl_related_keys_get_binary (GrlRelatedKeys *relkeys,
546                              GrlKeyID key,
547                              gsize *size)
548 {
549   const GValue *value;
550 
551   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
552   g_return_val_if_fail (size, NULL);
553 
554   value = grl_related_keys_get (relkeys, key);
555 
556 
557   if (!value || !G_VALUE_HOLDS_BOXED (value)) {
558     return NULL;
559   } else {
560     GByteArray * array;
561 
562     array = g_value_get_boxed (value);
563     *size = array->len;
564     return (const guint8 *) array->data;
565   }
566 }
567 
568 /**
569  * grl_related_keys_set_boxed:
570  * @relkeys: set of related keys to modify
571  * @key: key to change or add
572  * @boxed: the new value
573  *
574  * Sets the value associated with @key into @relkeys. @key must have been
575  * registered as a boxed-type key. Old value is freed and the new one is set.
576  *
577  * Since: 0.2.0
578  */
579 void
grl_related_keys_set_boxed(GrlRelatedKeys * relkeys,GrlKeyID key,gconstpointer boxed)580 grl_related_keys_set_boxed (GrlRelatedKeys *relkeys,
581                             GrlKeyID key,
582                             gconstpointer boxed)
583 {
584   GValue value = { 0 };
585 
586   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
587   g_return_if_fail (boxed != NULL);
588 
589   g_value_init (&value, grl_metadata_key_get_type (key));
590   g_value_set_boxed (&value, boxed);
591   grl_related_keys_set (relkeys, key, &value);
592   g_value_unset (&value);
593 }
594 
595 /**
596  * grl_related_keys_get_boxed:
597  * @relkeys: set of related keys to inspect
598  * @key: key to use
599  *
600  * Returns the value associated with @key from @relkeys. If @key has no value,
601  * the value is not of a boxed type, or @key is not in @relkeys, then %NULL is
602  * returned.
603  *
604  * Returns: (transfer none): the #GBoxed value associated with @key if
605  * possible, or %NULL in other case. The caller should not change nor free the
606  * value.
607  *
608  * Since: 0.2.0
609  */
610 gconstpointer
grl_related_keys_get_boxed(GrlRelatedKeys * relkeys,GrlKeyID key)611 grl_related_keys_get_boxed (GrlRelatedKeys *relkeys,
612                             GrlKeyID key)
613 {
614   const GValue *value;
615 
616   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
617 
618   value = grl_related_keys_get (relkeys, key);
619 
620   if (!value || !G_VALUE_HOLDS_BOXED (value)) {
621     return NULL;
622   } else {
623     return g_value_get_boxed (value);
624   }
625 }
626 
627 /**
628  * grl_related_keys_set_int64:
629  * @relkeys: set of related keys to change
630  * @key: (type GrlKeyID): key to change or add
631  * @intvalue: the new value
632  *
633  * Sets the value associated with @key into @relkeys. @key must have been
634  * registered as a int64-type key. Old value is replaced by the new one.
635  *
636  * Since: 0.2.12
637  **/
638 void
grl_related_keys_set_int64(GrlRelatedKeys * relkeys,GrlKeyID key,gint64 intvalue)639 grl_related_keys_set_int64 (GrlRelatedKeys *relkeys,
640                             GrlKeyID key,
641                             gint64 intvalue)
642 {
643   GValue value = { 0 };
644   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
645   g_value_init (&value, G_TYPE_INT64);
646   g_value_set_int64 (&value, intvalue);
647   grl_related_keys_set (relkeys, key, &value);
648 }
649 
650 /**
651  * grl_related_keys_get_int64:
652  * @relkeys: set of related keys to inspect
653  * @key: (type GrlKeyID): key to use
654  *
655  * Returns the value associated with @key from @relkeys. If @key has no value,
656  * or value is not a gint64, or @key is not in @relkeys, then 0 is returned.
657  *
658  * Returns: int64 value associated with @key, or 0 in other case.
659  *
660  * Since: 0.2.12
661  **/
662 gint64
grl_related_keys_get_int64(GrlRelatedKeys * relkeys,GrlKeyID key)663 grl_related_keys_get_int64 (GrlRelatedKeys *relkeys,
664                             GrlKeyID key)
665 {
666   const GValue *value;
667 
668   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), 0);
669 
670   value = grl_related_keys_get (relkeys, key);
671 
672   if (!value || !G_VALUE_HOLDS_INT64 (value)) {
673     return 0;
674   } else {
675     return g_value_get_int64 (value);
676   }
677 }
678 
679 /**
680  * grl_related_keys_set_for_id:
681  * @relkeys: set of related keys to modify
682  * @key_name: name of the key to change or add
683  * @value: the new value
684  *
685  * Sets the value associated with @key_name in @relkeys. This @key_name is used to create
686  * a new #GParamSpec instance, which is further used to create and register a key using
687  * grl_registry_register_metadata_key(). If @key_name already has a @value, old value
688  * is replaced by the new one.
689  *
690  * A property key_name consists of segments consisting of ASCII letters and
691  * digits, separated by either the '-' or '_' character. The first
692  * character of a property key_name must be a letter. Key_names which violate these
693  * rules lead to undefined behaviour.
694  *
695  * Returns: TRUE if @value was set to @key_name, FALSE otherwise.
696  *
697  * Since: 0.3.13
698  **/
699 gboolean
grl_related_keys_set_for_id(GrlRelatedKeys * relkeys,const gchar * key_name,const GValue * value)700 grl_related_keys_set_for_id (GrlRelatedKeys *relkeys,
701                              const gchar *key_name,
702                              const GValue *value)
703 {
704   GList *keys;
705   GrlKeyID bind_key, key;
706   GrlRegistry *registry;
707 
708   keys = grl_related_keys_get_keys (relkeys);
709   if (keys) {
710     bind_key = GRLPOINTER_TO_KEYID (keys->data);
711     g_list_free (keys);
712   } else {
713     bind_key = GRL_METADATA_KEY_INVALID;
714   }
715 
716   registry = grl_registry_get_default ();
717   key = grl_registry_register_or_lookup_metadata_key (registry,
718                                                       key_name,
719                                                       value,
720                                                       bind_key);
721   if (key == GRL_METADATA_KEY_INVALID) {
722     return FALSE;
723   }
724 
725   grl_related_keys_set (relkeys, key, value);
726   return TRUE;
727 }
728 
729 /**
730  * grl_related_keys_remove:
731  * @relkeys: set of related keys
732  * @key: (type GrlKeyID): key to remove
733  *
734  * Removes @key from @relkeys set.
735  *
736  * Since: 0.2.3
737  **/
738 void
grl_related_keys_remove(GrlRelatedKeys * relkeys,GrlKeyID key)739 grl_related_keys_remove (GrlRelatedKeys *relkeys,
740                          GrlKeyID key)
741 {
742   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
743   g_return_if_fail (key != GRL_METADATA_KEY_INVALID);
744 
745   g_hash_table_remove (relkeys->priv->data, GRLKEYID_TO_POINTER (key));
746 }
747 
748 /**
749  * grl_related_keys_has_key:
750  * @relkeys: set of related keys to inspect
751  * @key: (type GrlKeyID): key to search
752  *
753  * Checks if @key is in @relkeys.
754  *
755  * Returns: %TRUE if @key is in @relkeys, %FALSE in other case.
756  *
757  * Since: 0.1.10
758  **/
759 gboolean
grl_related_keys_has_key(GrlRelatedKeys * relkeys,GrlKeyID key)760 grl_related_keys_has_key (GrlRelatedKeys *relkeys,
761                           GrlKeyID key)
762 {
763   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), FALSE);
764 
765   return g_hash_table_lookup_extended (relkeys->priv->data,
766                                        GRLKEYID_TO_POINTER (key),
767                                        NULL, NULL);
768 }
769 
770 /**
771  * grl_related_keys_get_keys:
772  * @relkeys: set of related keys to inspect
773  *
774  * Returns a list with keys contained in @relkeys.
775  *
776  * Returns: (transfer container) (element-type GrlKeyID): a list with
777  * the keys. The content of the list should not be modified or freed. Use
778  * g_list_free() when done using the list.
779  *
780  * Since: 0.1.13
781  **/
782 GList *
grl_related_keys_get_keys(GrlRelatedKeys * relkeys)783 grl_related_keys_get_keys (GrlRelatedKeys *relkeys)
784 {
785   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
786 
787   return g_hash_table_get_keys (relkeys->priv->data);
788 }
789 
790 /**
791  * grl_related_keys_dup:
792  * @relkeys: set of related keys to duplicate
793  *
794  * Makes a deep copy of @relkeys and its contents.
795  *
796  * Returns: (transfer full): a new #GrlRelatedKeys.
797  * Free it with #g_object_unref.
798  *
799  * Since: 0.1.10
800  **/
801 GrlRelatedKeys *
grl_related_keys_dup(GrlRelatedKeys * relkeys)802 grl_related_keys_dup (GrlRelatedKeys *relkeys)
803 {
804   GList *keys, *key;
805   const GValue *value;
806   GValue *value_copy;
807   GrlRelatedKeys *dup_relkeys;
808 
809   g_return_val_if_fail (GRL_IS_RELATED_KEYS (relkeys), NULL);
810 
811   dup_relkeys = grl_related_keys_new ();
812 
813   keys = grl_related_keys_get_keys (relkeys);
814   for (key = keys; key; key = g_list_next (key)) {
815     value = grl_related_keys_get (relkeys, GRLPOINTER_TO_KEYID (key->data));
816     value_copy = g_new0 (GValue, 1);
817     g_value_init (value_copy, G_VALUE_TYPE (value));
818     g_value_copy (value, value_copy);
819     g_hash_table_insert (dup_relkeys->priv->data, key->data, value_copy);
820   }
821 
822   g_list_free (keys);
823 
824   return dup_relkeys;
825 }
826