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