1 /*
2  * Copyright (C) 2010, 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-data
27  * @short_description: Low-level class for storing metadata information about
28  * media resources provided by #GrlSource objects.
29  * @see_also: #GrlMedia
30  *
31  * This class acts as a generic metadata dictionary where metadata keys and
32  * their values can be stored.  Usually, application and plugin developers would
33  * interact with specific subclass of #GrlData, #GrlMedia, which provide
34  * specific API to manipulate well known attributes of these media types.
35  */
36 
37 #include "grl-data.h"
38 #include "grl-log.h"
39 #include "grl-registry-priv.h"
40 
41 #define GRL_LOG_DOMAIN_DEFAULT data_log_domain
42 GRL_LOG_DOMAIN(data_log_domain);
43 
44 struct _GrlDataPrivate {
45   GHashTable *data;
46 };
47 
48 static void grl_data_finalize (GObject *object);
49 static void free_list_values (GrlKeyID key, GList *values, gpointer user_data);
50 
51 static void free_list_values (GrlKeyID key, GList *values, gpointer user_data);
52 
53 /* ================ GrlData GObject ================ */
54 
55 G_DEFINE_TYPE_WITH_PRIVATE (GrlData, grl_data, G_TYPE_OBJECT);
56 
57 static void
grl_data_class_init(GrlDataClass * klass)58 grl_data_class_init (GrlDataClass *klass)
59 {
60   GObjectClass *gobject_class = (GObjectClass *)klass;
61 
62   gobject_class->finalize = grl_data_finalize;
63 }
64 
65 static void
grl_data_init(GrlData * self)66 grl_data_init (GrlData *self)
67 {
68   self->priv = grl_data_get_instance_private (self);
69   self->priv->data = g_hash_table_new_full (g_direct_hash,
70                                             g_direct_equal,
71                                             NULL,
72                                             NULL);
73 }
74 
75 static void
grl_data_finalize(GObject * object)76 grl_data_finalize (GObject *object)
77 {
78   GrlData *data = GRL_DATA (object);
79 
80   g_signal_handlers_destroy (object);
81   g_hash_table_foreach (data->priv->data,
82                         (GHFunc) free_list_values,
83                         NULL);
84   g_hash_table_unref (data->priv->data);
85 
86   G_OBJECT_CLASS (grl_data_parent_class)->finalize (object);
87 }
88 
89 /* ================ Utitilies ================ */
90 
91 /* Free the list of values, which are of type #GrlRelatedKeys */
92 static void
free_list_values(GrlKeyID key,GList * values,gpointer user_data)93 free_list_values (GrlKeyID key, GList *values, gpointer user_data)
94 {
95   g_list_free_full (values, g_object_unref);
96 }
97 
98 /* Returns the sample key that represents the set of keys related with @key */
99 static GrlKeyID
get_sample_key(GrlKeyID key)100 get_sample_key (GrlKeyID key)
101 {
102   GrlRegistry *registry;
103   const GList *related_keys;
104 
105   registry = grl_registry_get_default ();
106   related_keys =
107     grl_registry_lookup_metadata_key_relation (registry, key);
108 
109   if (!related_keys) {
110     GRL_WARNING ("Related keys not found for key \"%s\"",
111                  grl_metadata_key_get_name (key));
112     return GRL_METADATA_KEY_INVALID;
113   } else {
114     return GRLPOINTER_TO_KEYID (related_keys->data);
115   }
116 }
117 
118 /* ================ API ================ */
119 
120 /**
121  * grl_data_new:
122  *
123  * Creates a new data object.
124  *
125  * Returns: a new data object.
126  *
127  * Since: 0.1.4
128  **/
129 GrlData *
grl_data_new(void)130 grl_data_new (void)
131 {
132   return g_object_new (GRL_TYPE_DATA,
133 		       NULL);
134 }
135 
136 /**
137  * grl_data_get:
138  * @data: data to retrieve value
139  * @key: (type GrlKeyID): key to look up.
140  *
141  * Get the first value from @data associated with @key.
142  *
143  * Returns: (transfer none): a #GValue. This value should not be modified nor
144  * freed by user.
145  *
146  * Since: 0.1.4
147  **/
148 const GValue *
grl_data_get(GrlData * data,GrlKeyID key)149 grl_data_get (GrlData *data, GrlKeyID key)
150 {
151   GrlRelatedKeys *relkeys = NULL;
152 
153   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
154   g_return_val_if_fail (key, NULL);
155 
156   if (grl_data_length (data, key) > 0) {
157     relkeys = grl_data_get_related_keys (data, key, 0);
158   }
159 
160   if (!relkeys) {
161     return NULL;
162   }
163 
164   return grl_related_keys_get (relkeys, key);
165 }
166 
167 /**
168  * grl_data_set:
169  * @data: data to modify
170  * @key: (type GrlKeyID): key to change or add
171  * @value: the new value
172  *
173  * Sets the first value associated with @key in @data. If key already has a
174  * value old value is freed and the new one is set.
175  *
176  * Also, checks that @value is compliant with @key specification, modifying it
177  * accordingly. For instance, if @key requires a number between 0 and 10, but
178  * @value is outside this range, it will be adapted accordingly.
179  *
180  * Since: 0.1.4
181  **/
182 void
grl_data_set(GrlData * data,GrlKeyID key,const GValue * value)183 grl_data_set (GrlData *data, GrlKeyID key, const GValue *value)
184 {
185   GrlRelatedKeys *relkeys = NULL;
186 
187   g_return_if_fail (GRL_IS_DATA (data));
188   g_return_if_fail (key);
189 
190   if (!value) {
191     return;
192   }
193 
194   /* Get the right set of related keys */
195   if (grl_data_length (data, key) > 0) {
196     relkeys = grl_data_get_related_keys (data, key, 0);
197   }
198 
199   if (!relkeys) {
200     /* No related keys; add them */
201     relkeys = grl_related_keys_new ();
202     grl_related_keys_set (relkeys, key, value);
203     grl_data_add_related_keys (data, relkeys);
204   } else {
205     /* Set the new value */
206     grl_related_keys_set (relkeys, key, value);
207   }
208 }
209 
210 /**
211  * grl_data_set_string:
212  * @data: data to modify
213  * @key: (type GrlKeyID): key to change or add
214  * @strvalue: the new value
215  *
216  * Sets the first string value associated with @key in @data. If @key already
217  * has a value old value is freed and the new one is set.
218  *
219  * Since: 0.1.4
220  **/
221 void
grl_data_set_string(GrlData * data,GrlKeyID key,const gchar * strvalue)222 grl_data_set_string (GrlData *data,
223                      GrlKeyID key,
224                      const gchar *strvalue)
225 {
226   GValue value = { 0 };
227 
228   g_return_if_fail (GRL_IS_DATA (data));
229   g_return_if_fail (key);
230 
231   if (strvalue) {
232     g_value_init (&value, G_TYPE_STRING);
233     g_value_set_string (&value, strvalue);
234     grl_data_set (data, key, &value);
235     g_value_unset (&value);
236   }
237 }
238 
239 /**
240  * grl_data_get_string:
241  * @data: data to inspect
242  * @key: (type GrlKeyID): key to use
243  *
244  * Returns the first string value associated with @key from @data. If @key has
245  * no first value, or value is not string, or @key is not in @data, then %NULL
246  * is returned.
247  *
248  * Returns: string associated with @key, or %NULL in other case. Caller should
249  * not change nor free the value.
250  *
251  * Since: 0.1.4
252  **/
253 const gchar *
grl_data_get_string(GrlData * data,GrlKeyID key)254 grl_data_get_string (GrlData *data, GrlKeyID key)
255 {
256   const GValue *value;
257 
258   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
259   g_return_val_if_fail (key, NULL);
260 
261   value = grl_data_get (data, key);
262 
263   if (!value || !G_VALUE_HOLDS_STRING (value)) {
264     return NULL;
265   } else {
266     return g_value_get_string (value);
267   }
268 }
269 
270 /**
271  * grl_data_set_int:
272  * @data: data to change
273  * @key: (type GrlKeyID): key to change or add
274  * @intvalue: the new value
275  *
276  * Sets the first int value associated with @key in @data. If @key already has a
277  * first value old value is replaced by the new one.
278  *
279  * Since: 0.1.4
280  **/
281 void
grl_data_set_int(GrlData * data,GrlKeyID key,gint intvalue)282 grl_data_set_int (GrlData *data, GrlKeyID key, gint intvalue)
283 {
284   GValue value = { 0 };
285 
286   g_return_if_fail (GRL_IS_DATA (data));
287   g_return_if_fail (key);
288 
289   g_value_init (&value, G_TYPE_INT);
290   g_value_set_int (&value, intvalue);
291   grl_data_set (data, key, &value);
292 }
293 
294 /**
295  * grl_data_get_int:
296  * @data: data to inspect
297  * @key: (type GrlKeyID): key to use
298  *
299  * Returns the first int value associated with @key from @data. If @key has no
300  * first value, or value is not a gint, or @key is not in data, then 0 is
301  * returned.
302  *
303  * Returns: int value associated with @key, or 0 in other case.
304  *
305  * Since: 0.1.4
306  **/
307 gint
grl_data_get_int(GrlData * data,GrlKeyID key)308 grl_data_get_int (GrlData *data, GrlKeyID key)
309 {
310   const GValue *value;
311 
312   g_return_val_if_fail (GRL_IS_DATA (data), 0);
313   g_return_val_if_fail (key, 0);
314 
315   value = grl_data_get (data, key);
316 
317   if (!value || !G_VALUE_HOLDS_INT (value)) {
318     return 0;
319   } else {
320     return g_value_get_int (value);
321   }
322 }
323 
324 /**
325  * grl_data_set_float:
326  * @data: data to change
327  * @key: (type GrlKeyID): key to change or add
328  * @floatvalue: the new value
329  *
330  * Sets the first float value associated with @key in @data. If @key already has
331  * a first value old value is replaced by the new one.
332  *
333  * Since: 0.1.5
334  **/
335 void
grl_data_set_float(GrlData * data,GrlKeyID key,float floatvalue)336 grl_data_set_float (GrlData *data, GrlKeyID key, float floatvalue)
337 {
338   GValue value = { 0 };
339 
340   g_return_if_fail (GRL_IS_DATA (data));
341   g_return_if_fail (key);
342 
343   g_value_init (&value, G_TYPE_FLOAT);
344   g_value_set_float (&value, floatvalue);
345   grl_data_set (data, key, &value);
346 }
347 
348 /**
349  * grl_data_get_float:
350  * @data: data to inspect
351  * @key: (type GrlKeyID): key to use
352  *
353  * Returns the first float value associated with @key from @data. If @key has no
354  * first value, or value is not a gfloat, or @key is not in data, then 0 is
355  * returned.
356  *
357  * Returns: float value associated with @key, or 0 in other case.
358  *
359  * Since: 0.1.5
360  **/
361 gfloat
grl_data_get_float(GrlData * data,GrlKeyID key)362 grl_data_get_float (GrlData *data, GrlKeyID key)
363 {
364   const GValue *value;
365 
366   g_return_val_if_fail (GRL_IS_DATA (data), 0.0);
367   g_return_val_if_fail (key, 0.0);
368 
369   value = grl_data_get (data, key);
370 
371   if (!value || !G_VALUE_HOLDS_FLOAT (value)) {
372     return 0;
373   } else {
374     return g_value_get_float (value);
375   }
376 }
377 
378 /**
379  * grl_data_set_boolean:
380  * @data: data to change
381  * @key: (type GrlKeyID): key to change or add
382  * @boolvalue: the new value
383  *
384  * Sets the first boolean value associated with @key in @data. If @key already
385  * has a first value, old value is replaced by the new one.
386  *
387  * Since: 0.2.3
388  **/
389 void
grl_data_set_boolean(GrlData * data,GrlKeyID key,gboolean boolvalue)390 grl_data_set_boolean (GrlData *data, GrlKeyID key, gboolean boolvalue)
391 {
392   GValue value = { 0 };
393 
394   g_return_if_fail (GRL_IS_DATA (data));
395   g_return_if_fail (key);
396 
397   g_value_init (&value, G_TYPE_BOOLEAN);
398   g_value_set_boolean (&value, boolvalue);
399   grl_data_set (data, key, &value);
400 }
401 
402 /**
403  * grl_data_get_boolean:
404  * @data: data to inspect
405  * @key: (type GrlKeyID): key to use
406  *
407  * Returns: the first boolean value associated with @key from @data. If @key has
408  * no first value, or value is not a gboolean, or @key is not in the data, then
409  * FALSE is returned
410  *
411  * Since: 0.2.3
412  */
413 gboolean
grl_data_get_boolean(GrlData * data,GrlKeyID key)414 grl_data_get_boolean (GrlData *data, GrlKeyID key)
415 {
416   const GValue *value;
417 
418   g_return_val_if_fail (GRL_IS_DATA (data), FALSE);
419   g_return_val_if_fail (key, FALSE);
420 
421   value = grl_data_get (data, key);
422 
423   if (!value || !G_VALUE_HOLDS_BOOLEAN (value)) {
424     return FALSE;
425   } else {
426     return g_value_get_boolean (value);
427   }
428 }
429 
430 /**
431  * grl_data_set_binary:
432  * @data: data to change
433  * @key: (type GrlKeyID): key to change or add
434  * @buf: buffer holding the data
435  * @size: size of the buffer
436  *
437  * Sets the first binary value associated with @key in @data. If @key already
438  * has a first value old value is replaced by the new one.
439  *
440  * Since: 0.1.9
441  **/
442 void
grl_data_set_binary(GrlData * data,GrlKeyID key,const guint8 * buf,gsize size)443 grl_data_set_binary (GrlData *data, GrlKeyID key, const guint8 *buf, gsize size)
444 {
445   GValue v = { 0 };
446   GByteArray * array;
447 
448   g_return_if_fail (GRL_IS_DATA (data));
449   g_return_if_fail (key);
450 
451   if (!buf || !size) {
452     return;
453   }
454 
455   array = g_byte_array_append(g_byte_array_sized_new(size),
456 		              buf,
457 		              size);
458 
459   g_value_init (&v, g_byte_array_get_type());
460   g_value_take_boxed(&v, array);
461   grl_data_set(data, key, &v);
462   g_value_unset (&v);
463 }
464 
465 /**
466  * grl_data_get_binary:
467  * @data: data to inspect
468  * @key: (type GrlKeyID): key to use
469  * @size: (out): location to store the buffer size
470  *
471  * Returns the first binary value associated with @key from @data. If @key has
472  * no first value, or value is not a gfloat, or @key is not in data, then %NULL
473  * is returned.
474  *
475  * Returns: buffer location associated with the @key, or %NULL in other case. If
476  * successful @size will be set the to the buffer size.
477  *
478  * Since: 0.1.9
479  **/
480 const guint8 *
grl_data_get_binary(GrlData * data,GrlKeyID key,gsize * size)481 grl_data_get_binary(GrlData *data, GrlKeyID key, gsize *size)
482 {
483   const GValue *value;
484 
485   g_return_val_if_fail (size, NULL);
486   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
487   g_return_val_if_fail (key, NULL);
488 
489   value = grl_data_get (data, key);
490 
491   if (!value || !G_VALUE_HOLDS_BOXED (value)) {
492     return NULL;
493   } else {
494     GByteArray * array;
495 
496     array = g_value_get_boxed(value);
497     *size = array->len;
498     return (const guint8 *) array->data;
499   }
500 }
501 
502 /**
503  * grl_data_set_boxed:
504  * @data: data to modify
505  * @key: key to change or add
506  * @boxed: the new value
507  *
508  * Sets the first boxed value associated with @key in @data. If @key already
509  * has a value, the old value is freed and the new one is set.
510  *
511  * Since: 0.2.0
512  **/
513 void
grl_data_set_boxed(GrlData * data,GrlKeyID key,gconstpointer boxed)514 grl_data_set_boxed (GrlData *data, GrlKeyID key, gconstpointer boxed)
515 {
516   GValue value = { 0 };
517 
518   g_return_if_fail (GRL_IS_DATA (data));
519   g_return_if_fail (key);
520   g_return_if_fail (boxed != NULL);
521 
522   g_value_init (&value, GRL_METADATA_KEY_GET_TYPE (key));
523   g_value_set_boxed (&value, boxed);
524   grl_data_set (data, key, &value);
525   g_value_unset (&value);
526 }
527 
528 /**
529  * grl_data_get_boxed:
530  * @data: data to inspect
531  * @key: (type GrlKeyID): key to use
532  *
533  * Returns the first boxed value associated with @key from @data. If @key has
534  * no first value, that value is not of a boxed type, or @key is not in @data,
535  * then %NULL is returned.
536  *
537  * Returns: (transfer none): the boxed instance associated with @key if
538  * possible, or %NULL in other cases. The caller should not change nor free the
539  * value.
540  *
541  * Since: 0.2.0
542  **/
543 gpointer
grl_data_get_boxed(GrlData * data,GrlKeyID key)544 grl_data_get_boxed (GrlData *data, GrlKeyID key)
545 {
546   const GValue *value;
547 
548   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
549   g_return_val_if_fail (key, NULL);
550 
551   value = grl_data_get (data, key);
552 
553   if (!value || !G_VALUE_HOLDS_BOXED (value)) {
554     return NULL;
555   } else {
556     return g_value_get_boxed (value);
557   }
558 }
559 
560 /**
561  * grl_data_set_int64:
562  * @data: data to change
563  * @key: (type GrlKeyID): key to change or add
564  * @intvalue: the new value
565  *
566  * Sets the first int64 value associated with @key in @data. If @key already has a
567  * first value old value is replaced by the new one.
568  *
569  * Since: 0.2.12
570  **/
571 void
grl_data_set_int64(GrlData * data,GrlKeyID key,gint64 intvalue)572 grl_data_set_int64 (GrlData *data, GrlKeyID key, gint64 intvalue)
573 {
574   GValue value = { 0 };
575 
576   g_return_if_fail (GRL_IS_DATA (data));
577   g_return_if_fail (key);
578 
579   g_value_init (&value, G_TYPE_INT64);
580   g_value_set_int64 (&value, intvalue);
581   grl_data_set (data, key, &value);
582 }
583 
584 /**
585  * grl_data_get_int64:
586  * @data: data to inspect
587  * @key: (type GrlKeyID): key to use
588  *
589  * Returns the first int64 value associated with @key from @data. If @key has no
590  * first value, or value is not a gint, or @key is not in data, then 0 is
591  * returned.
592  *
593  * Returns: int value associated with @key, or 0 in other case.
594  *
595  * Since: 0.2.12
596  **/
597 gint64
grl_data_get_int64(GrlData * data,GrlKeyID key)598 grl_data_get_int64 (GrlData *data, GrlKeyID key)
599 {
600   const GValue *value;
601 
602   g_return_val_if_fail (GRL_IS_DATA (data), 0);
603   g_return_val_if_fail (key, 0);
604 
605   value = grl_data_get (data, key);
606 
607   if (!value || !G_VALUE_HOLDS_INT64 (value)) {
608     return 0;
609   } else {
610     return g_value_get_int64 (value);
611   }
612 }
613 
614 /**
615  * grl_data_set_for_id:
616  * @data: data to change
617  * @key_name: name of the key to change or add
618  * @value: the new value
619  *
620  * Sets the first value associated with @key_name in @data. This @key_name is used to create
621  * a new #GParamSpec instance, which is further used to create and register a key using
622  * grl_registry_register_metadata_key(). If @key_name already has a first @value, old
623  * value is replaced by the new one.
624  *
625  * A property key_name consists of segments consisting of ASCII letters and
626  * digits, separated by either the '-' or '_' character. The first
627  * character of a property key_name must be a letter. Key_names which violate these
628  * rules lead to undefined behaviour.
629  *
630  * Returns: TRUE if @value was set to @key_name, FALSE otherwise.
631  *
632  * Since: 0.3.6
633  **/
634 gboolean
grl_data_set_for_id(GrlData * data,const gchar * key_name,const GValue * value)635 grl_data_set_for_id (GrlData *data, const gchar *key_name, const GValue *value)
636 {
637   GrlRegistry *registry;
638   GrlKeyID key_id;
639 
640   registry = grl_registry_get_default ();
641   key_id = grl_registry_register_or_lookup_metadata_key (registry,
642                                                          key_name,
643                                                          value,
644                                                          GRL_METADATA_KEY_INVALID);
645   if (key_id == GRL_METADATA_KEY_INVALID) {
646     return FALSE;
647   }
648 
649   grl_data_set (data, key_id, value);
650   return TRUE;
651 }
652 
653 
654 /**
655  * grl_data_add_for_id:
656  * @data: data to change
657  * @key_name: name of the key to change or add
658  * @value: the new value
659  *
660  * Appends the value associated with @key_name to @data. This @key_name is used to create
661  * a new #GParamSpec instance, which is further used to create and register a key using
662  * grl_registry_register_metadata_key().
663  *
664  * A property key_name consists of segments consisting of ASCII letters and
665  * digits, separated by either the '-' or '_' character. The first
666  * character of a property key_name must be a letter. Key_names which violate these
667  * rules lead to undefined behaviour.
668  *
669  * Returns: TRUE if @value was added to @key_name, FALSE otherwise.
670  *
671  * Since: 0.3.6
672  **/
673 gboolean
grl_data_add_for_id(GrlData * data,const gchar * key_name,const GValue * value)674 grl_data_add_for_id (GrlData *data, const gchar *key_name, const GValue *value)
675 {
676   GrlRegistry *registry;
677   GrlKeyID key_id;
678   GType value_type;
679 
680   registry = grl_registry_get_default ();
681   key_id = grl_registry_register_or_lookup_metadata_key (registry,
682                                                          key_name,
683                                                          value,
684                                                          GRL_METADATA_KEY_INVALID);
685   if (key_id == GRL_METADATA_KEY_INVALID) {
686     return FALSE;
687   }
688 
689   value_type = G_VALUE_TYPE (value);
690 
691   switch (value_type) {
692   case G_TYPE_INT:
693     grl_data_add_int (data, key_id, g_value_get_int (value));
694     break;
695 
696   case G_TYPE_INT64:
697     grl_data_add_int64 (data, key_id, g_value_get_int64 (value));
698     break;
699 
700   case G_TYPE_FLOAT:
701     grl_data_add_float (data, key_id, g_value_get_float (value));
702     break;
703 
704   case G_TYPE_STRING:
705     grl_data_add_string (data, key_id, g_value_get_string (value));
706     break;
707 
708   default:
709     /* FIXME: We should add support to all GType supported by GrlMedia */
710     GRL_WARNING ("'%s' is being ignored as %s type is not being handled",
711                  key_name, g_type_name (value_type));
712     return FALSE;
713   }
714 
715   return TRUE;
716 }
717 
718 /**
719  * grl_data_remove:
720  * @data: data to change
721  * @key: (type GrlKeyID): key to remove
722  *
723  * Removes the first value for @key from @data. If there are other keys related
724  * to @key their values will also be removed from @data.
725  *
726  * Since: 0.1.4
727  **/
728 void
grl_data_remove(GrlData * data,GrlKeyID key)729 grl_data_remove (GrlData *data, GrlKeyID key)
730 {
731   g_return_if_fail (GRL_IS_DATA (data));
732   g_return_if_fail (key);
733 
734   grl_data_remove_nth (data, key, 0);
735 }
736 
737 /**
738  * grl_data_has_key:
739  * @data: data to inspect
740  * @key: (type GrlKeyID): key to search
741  *
742  * Checks if @key is in @data.
743  *
744  * Returns: %TRUE if @key is in @data, %FALSE in other case.
745  *
746  * Since: 0.1.4
747  **/
748 gboolean
grl_data_has_key(GrlData * data,GrlKeyID key)749 grl_data_has_key (GrlData *data, GrlKeyID key)
750 {
751   GList *related_keys;
752   GrlKeyID sample_key;
753   gboolean found = FALSE;
754 
755   g_return_val_if_fail (GRL_IS_DATA (data), FALSE);
756   g_return_val_if_fail (key, FALSE);
757 
758   sample_key = get_sample_key (key);
759   if (!sample_key) {
760     return FALSE;
761   }
762 
763   related_keys = g_hash_table_lookup (data->priv->data, GRLKEYID_TO_POINTER (sample_key));
764   while (related_keys && !found) {
765     found = grl_related_keys_has_key (related_keys->data, key);
766     related_keys = g_list_next (related_keys);
767   }
768 
769   return found;
770 }
771 
772 /**
773  * grl_data_get_keys:
774  * @data: data to inspect
775  *
776  * Returns a list with keys contained in @data.
777  *
778  * Returns: (transfer container) (element-type GrlKeyID): an array with the
779  * keys. The content of the list should not be modified or freed. Use
780  * g_list_free() when done using the list.
781  *
782  * Since: 0.1.4
783  **/
784 GList *
grl_data_get_keys(GrlData * data)785 grl_data_get_keys (GrlData *data)
786 {
787   GList *allkeys = NULL;
788   GList *keylist, *key;
789   GrlRegistry *registry;
790   const GList *relkeys;
791 
792   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
793 
794   keylist = g_hash_table_get_keys (data->priv->data);
795   registry = grl_registry_get_default ();
796 
797   for (key = keylist; key; key = g_list_next (key)) {
798     GrlKeyID key_id = GRLPOINTER_TO_KEYID (key->data);
799     relkeys =
800         grl_registry_lookup_metadata_key_relation (registry, key_id);
801     while (relkeys) {
802       if (grl_data_has_key (data, GRLPOINTER_TO_KEYID (relkeys->data))) {
803         allkeys = g_list_prepend (allkeys, relkeys->data);
804       }
805       relkeys = g_list_next (relkeys);
806     }
807   }
808 
809   g_list_free (keylist);
810 
811   return allkeys;
812 }
813 
814 /**
815  * grl_data_add_related_keys:
816  * @data: data to change
817  * @relkeys: (transfer full): a set of related properties with their values
818  *
819  * Adds a new set of values into @data.
820  *
821  * All keys in @prop must be related among them.
822  *
823  * @data will take the ownership of @relkeys, so do not modify it.
824  *
825  * Since: 0.1.10
826  **/
827 void
grl_data_add_related_keys(GrlData * data,GrlRelatedKeys * relkeys)828 grl_data_add_related_keys (GrlData *data,
829                            GrlRelatedKeys *relkeys)
830 {
831   GList *keys;
832   GList *list_relkeys;
833   GrlKeyID sample_key;
834 
835   g_return_if_fail (GRL_IS_DATA (data));
836   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
837 
838   keys = grl_related_keys_get_keys (relkeys);
839   if (!keys) {
840     /* Ignore empty set of related keys */
841     GRL_WARNING ("Trying to add an empty GrlRelatedKeys to GrlData");
842     g_object_unref (relkeys);
843     return;
844   }
845 
846   sample_key = get_sample_key (GRLPOINTER_TO_KEYID (keys->data));
847   g_list_free (keys);
848 
849   if (!sample_key) {
850     g_object_unref (relkeys);
851     return;
852   }
853 
854   list_relkeys = g_hash_table_lookup (data->priv->data,
855                                       GRLKEYID_TO_POINTER (sample_key));
856   list_relkeys = g_list_append (list_relkeys, relkeys);
857   g_hash_table_insert (data->priv->data,
858                        GRLKEYID_TO_POINTER (sample_key),
859                        list_relkeys);
860 }
861 
862 /**
863  * grl_data_add_string:
864  * @data: data to append
865  * @key: (type GrlKeyID): key to append
866  * @strvalue: the new value
867  *
868  * Appends a new string value for @key in @data.
869  *
870  * Since: 0.1.10
871  **/
872 void
grl_data_add_string(GrlData * data,GrlKeyID key,const gchar * strvalue)873 grl_data_add_string (GrlData *data,
874                      GrlKeyID key,
875                      const gchar *strvalue)
876 {
877   GrlRelatedKeys *relkeys;
878 
879   g_return_if_fail (GRL_IS_DATA (data));
880   g_return_if_fail (key);
881 
882   if (strvalue) {
883     relkeys = grl_related_keys_new ();
884     grl_related_keys_set_string (relkeys, key, strvalue);
885     grl_data_add_related_keys (data, relkeys);
886   }
887 }
888 
889 /**
890  * grl_data_add_int:
891  * @data: data to append
892  * @key: (type GrlKeyID): key to append
893  * @intvalue: the new value
894  *
895  * Appends a new int value for @key in @data.
896  *
897  * Since: 0.1.10
898  **/
899 void
grl_data_add_int(GrlData * data,GrlKeyID key,gint intvalue)900 grl_data_add_int (GrlData *data,
901                   GrlKeyID key,
902                   gint intvalue)
903 {
904   GrlRelatedKeys *relkeys;
905 
906   g_return_if_fail (GRL_IS_DATA (data));
907   g_return_if_fail (key);
908 
909   relkeys = grl_related_keys_new ();
910   grl_related_keys_set_int (relkeys, key, intvalue);
911   grl_data_add_related_keys (data, relkeys);
912 }
913 
914 /**
915  * grl_data_add_float:
916  * @data: data to append
917  * @key: (type GrlKeyID): key to append
918  * @floatvalue: the new value
919  *
920  * Appends a new float value for @key in @data.
921  *
922  * Since: 0.1.10
923  **/
924 void
grl_data_add_float(GrlData * data,GrlKeyID key,gfloat floatvalue)925 grl_data_add_float (GrlData *data,
926                     GrlKeyID key,
927                     gfloat floatvalue)
928 {
929   GrlRelatedKeys *relkeys;
930 
931   g_return_if_fail (GRL_IS_DATA (data));
932   g_return_if_fail (key);
933 
934   relkeys = grl_related_keys_new ();
935   grl_related_keys_set_float (relkeys, key, floatvalue);
936   grl_data_add_related_keys (data, relkeys);
937 }
938 
939 /**
940  * grl_data_add_binary:
941  * @data: data to append
942  * @key: (type GrlKeyID): key to append
943  * @buf: the buffer containing the new value
944  * @size: size of buffer
945  *
946  * Appends a new binary value for @key in @data.
947  *
948  * Since: 0.1.10
949  **/
950 void
grl_data_add_binary(GrlData * data,GrlKeyID key,const guint8 * buf,gsize size)951 grl_data_add_binary (GrlData *data,
952                      GrlKeyID key,
953                      const guint8 *buf,
954                      gsize size)
955 {
956   GrlRelatedKeys *relkeys;
957 
958   g_return_if_fail (GRL_IS_DATA (data));
959   g_return_if_fail (key);
960 
961   if (!buf || !size) {
962     return;
963   }
964 
965   relkeys = grl_related_keys_new ();
966   grl_related_keys_set_binary (relkeys, key, buf, size);
967   grl_data_add_related_keys (data, relkeys);
968 }
969 
970 /**
971  * grl_data_add_boxed:
972  * @data: data to append
973  * @key: (type GrlKeyID): key to append
974  * @boxed: the new value
975  *
976  * Appends a new boxed value for @key in @data.
977  *
978  * Since: 0.2.0
979  **/
980 void
grl_data_add_boxed(GrlData * data,GrlKeyID key,gconstpointer boxed)981 grl_data_add_boxed (GrlData *data,
982                     GrlKeyID key,
983                     gconstpointer boxed)
984 {
985   GrlRelatedKeys *relkeys;
986 
987   g_return_if_fail (GRL_IS_DATA (data));
988   g_return_if_fail (key);
989   g_return_if_fail (boxed != NULL);
990 
991   relkeys = grl_related_keys_new ();
992   grl_related_keys_set_boxed (relkeys, key, boxed);
993   grl_data_add_related_keys (data, relkeys);
994 }
995 
996 /**
997  * grl_data_add_int64:
998  * @data: data to append
999  * @key: (type GrlKeyID): key to append
1000  * @intvalue: the new value
1001  *
1002  * Appends a new int64 value for @key in @data.
1003  *
1004  * Since: 0.2.12
1005  **/
1006 void
grl_data_add_int64(GrlData * data,GrlKeyID key,gint64 intvalue)1007 grl_data_add_int64 (GrlData *data,
1008                     GrlKeyID key,
1009                     gint64 intvalue)
1010 {
1011   GrlRelatedKeys *relkeys;
1012 
1013   g_return_if_fail (GRL_IS_DATA (data));
1014   g_return_if_fail (key);
1015 
1016   relkeys = grl_related_keys_new ();
1017   grl_related_keys_set_int64 (relkeys, key, intvalue);
1018   grl_data_add_related_keys (data, relkeys);
1019 }
1020 
1021 /**
1022  * grl_data_length:
1023  * @data: a data
1024  * @key: a metadata key
1025  *
1026  * Returns how many values @key or related keys have in @data: if @key has no
1027  * value, but a related key has, then it is counted as positive.
1028  *
1029  * As example, let's think in three related keys, K1, K2 and K3, and then thinks
1030  * we have added several values for those keys, as:
1031  *
1032  *   (V10, V20, V30), (V11, NULL, V31), (V12, NULL, V32)
1033  *
1034  * Therefore, when invoking grl_data_length (data, K2) it will return 3:
1035  * considering K2 and the related keys (K1 and K3), there are 3 values.
1036  *
1037  * Returns: number of values
1038  *
1039  * Since: 0.1.10
1040  **/
1041 guint
grl_data_length(GrlData * data,GrlKeyID key)1042 grl_data_length (GrlData *data,
1043                  GrlKeyID key)
1044 {
1045   GrlKeyID sample_key;
1046 
1047   g_return_val_if_fail (GRL_IS_DATA (data), 0);
1048   g_return_val_if_fail (key, 0);
1049 
1050   sample_key = get_sample_key (key);
1051   if (!sample_key) {
1052     return 0;
1053   }
1054 
1055   return g_list_length (g_hash_table_lookup (data->priv->data,
1056                                              GRLKEYID_TO_POINTER (sample_key)));
1057 }
1058 
1059 /**
1060  * grl_data_get_related_keys:
1061  * @data: a data
1062  * @key: a metadata key
1063  * @index: element to retrieve, starting at 0
1064  *
1065  * Returns a set containing the values for @key and related keys at position
1066  * @index from @data.
1067  *
1068  * If user changes any of the values in the related keys, the changes will
1069  * become permanent.
1070  *
1071  * Returns: (transfer none): a #GrlRelatedKeys. Do not free it.
1072  *
1073  * Since: 0.1.10
1074  **/
1075 GrlRelatedKeys *
grl_data_get_related_keys(GrlData * data,GrlKeyID key,guint index)1076 grl_data_get_related_keys (GrlData *data,
1077                            GrlKeyID key,
1078                            guint index)
1079 {
1080   GList *relkeys_list;
1081   GrlKeyID sample_key;
1082   GrlRelatedKeys *relkeys;
1083 
1084   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
1085   g_return_val_if_fail (key, NULL);
1086 
1087   sample_key = get_sample_key (key);
1088   if (!sample_key) {
1089     return NULL;
1090   }
1091 
1092   relkeys_list = g_hash_table_lookup (data->priv->data,
1093                                       GRLKEYID_TO_POINTER (sample_key));
1094   relkeys = g_list_nth_data (relkeys_list, index);
1095 
1096   if (!relkeys) {
1097     GRL_WARNING ("%s: index %u out of range", __FUNCTION__, index);
1098     return NULL;
1099   }
1100 
1101   return relkeys;
1102 }
1103 
1104 /**
1105  * grl_data_get_single_values_for_key:
1106  * @data: a data
1107  * @key: a metadata key
1108  *
1109  * Returns all non-%NULL values for @key from @data. This ignores related keys.
1110  *
1111  * Returns: (element-type GObject.Value) (transfer container): a #GList with
1112  * values. Do not change or free the values. Free the list with #g_list_free.
1113  *
1114  * Since: 0.1.13
1115  */
1116 GList *
grl_data_get_single_values_for_key(GrlData * data,GrlKeyID key)1117 grl_data_get_single_values_for_key (GrlData *data,
1118                                     GrlKeyID key)
1119 {
1120   GList *related_keys;
1121   GList *values = NULL;
1122   GrlKeyID sample_key;
1123   const GValue *v;
1124 
1125   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
1126   g_return_val_if_fail (key, NULL);
1127 
1128   sample_key = get_sample_key (key);
1129   if (!sample_key) {
1130     return NULL;
1131   }
1132 
1133   related_keys = g_hash_table_lookup (data->priv->data,
1134                                       GRLKEYID_TO_POINTER (sample_key));
1135   while (related_keys) {
1136     v = grl_related_keys_get (related_keys->data, key);
1137     if (v) {
1138       values = g_list_prepend (values, (gpointer) v);
1139     }
1140     related_keys = g_list_next (related_keys);
1141   }
1142 
1143   return g_list_reverse (values);
1144 }
1145 
1146 /**
1147  * grl_data_get_single_values_for_key_string:
1148  * @data: a data
1149  * @key: a metadata key
1150  *
1151  * Returns all non-%NULL values for @key from @data. @key must have been
1152  * registered as a string-type key. This ignores related keys.
1153  *
1154  * Returns: (element-type utf8) (transfer container): a #GList with values. Do
1155  * not change or free the strings. Free the list with #g_list_free.
1156  *
1157  * Since: 0.1.13
1158  **/
1159 GList *
grl_data_get_single_values_for_key_string(GrlData * data,GrlKeyID key)1160 grl_data_get_single_values_for_key_string (GrlData *data,
1161                                            GrlKeyID key)
1162 {
1163   GList *list_strings = NULL;
1164   GList *list_values;
1165   GList *value;
1166   const gchar *string_value;
1167 
1168   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
1169   g_return_val_if_fail (key, NULL);
1170 
1171   /* Verify key is of type string */
1172   if (GRL_METADATA_KEY_GET_TYPE (key) != G_TYPE_STRING) {
1173     GRL_WARNING ("%s: requested key is not of type string", __FUNCTION__);
1174     return NULL;
1175   }
1176 
1177   list_values = grl_data_get_single_values_for_key (data, key);
1178   for (value = list_values; value; value = g_list_next (value)) {
1179     string_value = g_value_get_string (value->data);
1180     if (string_value) {
1181       list_strings = g_list_prepend (list_strings, (gpointer) string_value);
1182     }
1183   }
1184 
1185   g_list_free (list_values);
1186 
1187   return g_list_reverse (list_strings);
1188 }
1189 
1190 /**
1191  * grl_data_remove_nth:
1192  * @data: a data
1193  * @key: a metadata key
1194  * @index: index of key to be removed, starting at 0
1195  *
1196  * Removes the value at position @index for @key from @data. If there are other
1197  * keys related to @key, their values at position @index will also be removed
1198  * from @data.
1199  *
1200  * Since: 0.1.10
1201  **/
1202 void
grl_data_remove_nth(GrlData * data,GrlKeyID key,guint index)1203 grl_data_remove_nth (GrlData *data,
1204                      GrlKeyID key,
1205                      guint index)
1206 {
1207   GList *relkeys_element;
1208   GList *relkeys_list;
1209   GrlKeyID sample_key;
1210 
1211   g_return_if_fail (GRL_IS_DATA (data));
1212   g_return_if_fail (key);
1213 
1214   sample_key = get_sample_key (key);
1215   if (!sample_key) {
1216     return;
1217   }
1218 
1219   relkeys_list = g_hash_table_lookup (data->priv->data,
1220                                       GRLKEYID_TO_POINTER (sample_key));
1221   relkeys_element = g_list_nth (relkeys_list, index);
1222   if (!relkeys_element) {
1223     GRL_WARNING ("%s: index %u out of range", __FUNCTION__, index);
1224     return;
1225   }
1226 
1227   g_object_unref (relkeys_element->data);
1228   relkeys_list = g_list_delete_link (relkeys_list, relkeys_element);
1229   g_hash_table_insert (data->priv->data,
1230                        GRLKEYID_TO_POINTER (sample_key),
1231                        relkeys_list);
1232 }
1233 
1234 /**
1235  * grl_data_set_related_keys:
1236  * @data: a data
1237  * @relkeys: a set of related keys
1238  * @index: position to be updated, starting at 0
1239  *
1240  * Updates the values at position @index in @data with values in @relkeys.
1241  *
1242  * @data will take ownership of @relkeys, so do not free it after invoking this
1243  * function.
1244  *
1245  * Since: 0.1.10
1246  **/
1247 void
grl_data_set_related_keys(GrlData * data,GrlRelatedKeys * relkeys,guint index)1248 grl_data_set_related_keys (GrlData *data,
1249                            GrlRelatedKeys *relkeys,
1250                            guint index)
1251 {
1252   GList *keys;
1253   GList *relkeys_element;
1254   GList *relkeys_list;
1255   GrlKeyID sample_key;
1256 
1257   g_return_if_fail (GRL_IS_DATA (data));
1258   g_return_if_fail (GRL_IS_RELATED_KEYS (relkeys));
1259 
1260   keys = grl_related_keys_get_keys (relkeys);
1261   if (!keys) {
1262     GRL_WARNING ("Trying to set an empty GrlRelatedKeys into GrlData");
1263     g_object_unref (relkeys);
1264     return;
1265   }
1266 
1267   sample_key = get_sample_key (GRLPOINTER_TO_KEYID (keys->data));
1268   g_list_free (keys);
1269   if (!sample_key) {
1270     return;
1271   }
1272 
1273   relkeys_list = g_hash_table_lookup (data->priv->data,
1274                                       GRLKEYID_TO_POINTER (sample_key));
1275   relkeys_element = g_list_nth (relkeys_list, index);
1276   if (!relkeys_element) {
1277     GRL_WARNING ("%s: index %u out of range", __FUNCTION__, index);
1278     return;
1279   }
1280 
1281   g_object_unref (relkeys_element->data);
1282   relkeys_element->data = relkeys;
1283 }
1284 
1285 /**
1286  * grl_data_dup:
1287  * @data: data to duplicate
1288  *
1289  * Makes a deep copy of @data and all its contents.
1290  *
1291  * Returns: (transfer full): a new #GrlData. Free it with #g_object_unref.
1292  *
1293  * Since: 0.1.10
1294  **/
1295 GrlData *
grl_data_dup(GrlData * data)1296 grl_data_dup (GrlData *data)
1297 {
1298   GList *dup_relkeys_list;
1299   GList *key;
1300   GList *keys;
1301   GList *relkeys_list;
1302   GrlData *dup_data;
1303 
1304   g_return_val_if_fail (GRL_IS_DATA (data), NULL);
1305 
1306   dup_data = grl_data_new ();
1307   keys = g_hash_table_get_keys (data->priv->data);
1308   for (key = keys; key; key = g_list_next (key)) {
1309     dup_relkeys_list = NULL;
1310     relkeys_list = g_hash_table_lookup (data->priv->data, key->data);
1311     while (relkeys_list) {
1312       dup_relkeys_list =
1313         g_list_prepend (dup_relkeys_list,
1314                         grl_related_keys_dup (relkeys_list->data));
1315       relkeys_list = g_list_next (relkeys_list);
1316     }
1317     g_hash_table_insert (dup_data->priv->data,
1318                          key->data,
1319                          g_list_reverse (relkeys_list));
1320   }
1321 
1322   g_list_free (keys);
1323 
1324   return dup_data;
1325 }
1326