1 /*
2  * libosinfo:
3  *
4  * Copyright (C) 2009-2020 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <osinfo/osinfo.h>
22 #include <glib/gi18n-lib.h>
23 
24 /**
25  * SECTION:osinfo_entity
26  * @short_description: Abstract base class for metadata objects
27  * @see_also: #OsinfoList, #OsinfoDb
28  *
29  * #OsinfoEntity is an abstract base class for all objects against which
30  * metadata needs to be recorded. Every object has a unique identifier,
31  * which is recommended to be in URI format. Named, multi-valued data
32  * parameters can be associated with each entity. When filtering lists
33  * of entities, the parameter values can be used for matching.
34  */
35 
36 struct _OsinfoEntityPrivate
37 {
38     gchar *id;
39 
40     // Key: gchar*
41     // Value: GList of gchar* values
42     GHashTable *params;
43 };
44 
45 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(OsinfoEntity, osinfo_entity, G_TYPE_OBJECT);
46 
47 static void osinfo_entity_finalize(GObject *object);
48 
49 enum {
50     PROP_0,
51 
52     PROP_ID,
53 
54     LAST_PROP
55 };
56 static GParamSpec *properties[LAST_PROP];
57 
58 static void
osinfo_entity_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)59 osinfo_entity_set_property(GObject      *object,
60                             guint         property_id,
61                             const GValue *value,
62                             GParamSpec   *pspec)
63 {
64     OsinfoEntity *entity = OSINFO_ENTITY(object);
65 
66     switch (property_id)
67         {
68         case PROP_ID:
69             g_free(entity->priv->id);
70             entity->priv->id = g_value_dup_string(value);
71             break;
72         default:
73             /* We don't have any other property... */
74             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
75             break;
76         }
77 }
78 
79 static void
osinfo_entity_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)80 osinfo_entity_get_property(GObject    *object,
81                             guint       property_id,
82                             GValue     *value,
83                             GParamSpec *pspec)
84 {
85     OsinfoEntity *entity = OSINFO_ENTITY(object);
86 
87     switch (property_id)
88         {
89         case PROP_ID:
90             g_value_set_string(value, entity->priv->id);
91             break;
92         default:
93             /* We don't have any other property... */
94             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
95             break;
96         }
97 }
98 
99 static void
osinfo_entity_finalize(GObject * object)100 osinfo_entity_finalize(GObject *object)
101 {
102     OsinfoEntity *entity = OSINFO_ENTITY(object);
103 
104     g_free(entity->priv->id);
105     g_hash_table_destroy(entity->priv->params);
106 
107     /* Chain up to the parent class */
108     G_OBJECT_CLASS(osinfo_entity_parent_class)->finalize(object);
109 }
110 
111 /* Init functions */
112 static void
osinfo_entity_class_init(OsinfoEntityClass * klass)113 osinfo_entity_class_init(OsinfoEntityClass *klass)
114 {
115     GObjectClass *g_klass = G_OBJECT_CLASS(klass);
116 
117     g_klass->set_property = osinfo_entity_set_property;
118     g_klass->get_property = osinfo_entity_get_property;
119     g_klass->finalize = osinfo_entity_finalize;
120 
121     /**
122      * OsinfoEntity:id:
123      *
124      * The unique identifier for the entity The format of identifiers
125      * is undefined, but the recommended practice is to use a URI.
126      * This parameter must be set at time of construction as no
127      * default value is provided.
128      */
129     properties[PROP_ID] = g_param_spec_string("id",
130                                               "ID",
131                                               _("Unique identifier"),
132                                               NULL /* default value */,
133                                               G_PARAM_CONSTRUCT |
134                                               G_PARAM_READWRITE |
135                                               G_PARAM_STATIC_STRINGS);
136 
137     g_object_class_install_properties(g_klass, LAST_PROP, properties);
138 }
139 
140 
osinfo_entity_param_values_free(gpointer values)141 static void osinfo_entity_param_values_free(gpointer values)
142 {
143     g_list_free_full(values, g_free);
144 }
145 
146 
147 static void
osinfo_entity_init(OsinfoEntity * entity)148 osinfo_entity_init(OsinfoEntity *entity)
149 {
150     entity->priv = osinfo_entity_get_instance_private(entity);
151     entity->priv->params = g_hash_table_new_full(g_str_hash,
152                                                g_str_equal,
153                                                g_free,
154                                                osinfo_entity_param_values_free);
155 }
156 
157 
158 /**
159  * osinfo_entity_set_param:
160  * @entity: an #OsinfoEntity containing the parameters
161  * @key: the name of the key
162  * @value: the data to associated with that key
163  *
164  * Sets a new parameter against the entity. If the key already
165  * has a value associated with it, the existing value will be
166  * cleared.
167  */
osinfo_entity_set_param(OsinfoEntity * entity,const gchar * key,const gchar * value)168 void osinfo_entity_set_param(OsinfoEntity *entity, const gchar *key, const gchar *value)
169 {
170     GList *values = NULL;
171 
172     g_return_if_fail(OSINFO_IS_ENTITY(entity));
173     g_return_if_fail(key != NULL);
174     g_return_if_fail(value != NULL);
175 
176     values = g_list_append(values, g_strdup(value));
177     g_hash_table_replace(entity->priv->params, g_strdup(key), values);
178 }
179 
180 
181 /**
182  * osinfo_entity_set_param_boolean:
183  * @entity: an #OsinfoEntity containing the parameters
184  * @key: the name of the key
185  * @value: the boolean value to be associated with that key
186  *
187  * Sets a new parameter against the entity. If the key already
188  * has a value associated with it, the existing value will be
189  * cleared.
190  *
191  * Since: 0.2.0
192  */
osinfo_entity_set_param_boolean(OsinfoEntity * entity,const gchar * key,gboolean value)193 void osinfo_entity_set_param_boolean(OsinfoEntity *entity, const gchar *key, gboolean value)
194 {
195     osinfo_entity_set_param(entity, key, value ? "true" : "false");
196 }
197 
198 /**
199  * osinfo_entity_set_param_int64:
200  * @entity: an #OsinfoEntity containing the parameters
201  * @key: the name of the key
202  * @value: the int64 value to be associated with that key
203  *
204  * Sets a new parameter against the entity. If the key already
205  * has a value associated with it, the existing value will be
206  * cleared.
207  *
208  * Since: 0.2.1
209  */
osinfo_entity_set_param_int64(OsinfoEntity * entity,const gchar * key,gint64 value)210 void osinfo_entity_set_param_int64(OsinfoEntity *entity, const gchar *key, gint64 value)
211 {
212     gchar *str;
213 
214     str = g_strdup_printf("%"G_GINT64_FORMAT, value);
215     osinfo_entity_set_param(entity, key, str);
216     g_free(str);
217 }
218 
219 /**
220  * osinfo_entity_set_param_enum:
221  * @entity: an #OsinfoEntity containing the parameters
222  * @key: the name of the key
223  * @value: the enum value to be associated with that key
224  * @enum_type: the enum type
225  *
226  * Sets a new parameter against the entity. If the key already
227  * has a value associated with it, the existing value will be
228  * cleared.
229  *
230  * Since: 0.2.2
231  */
osinfo_entity_set_param_enum(OsinfoEntity * entity,const gchar * key,gint value,GType enum_type)232 void osinfo_entity_set_param_enum(OsinfoEntity *entity, const gchar *key, gint value, GType enum_type)
233 {
234     GEnumClass *enum_class;
235     GEnumValue *enum_value;
236 
237     g_return_if_fail(G_TYPE_IS_ENUM(enum_type));
238 
239     enum_class = g_type_class_ref(enum_type);
240     enum_value = g_enum_get_value(enum_class, value);
241     g_type_class_unref(enum_class);
242     g_return_if_fail(enum_value != NULL);
243 
244     osinfo_entity_set_param(entity, key, enum_value->value_nick);
245 }
246 
247 /**
248  * osinfo_entity_add_param:
249  * @entity: an #OsinfoEntity containing the parameters
250  * @key: the name of the key
251  * @value: the data to associated with that key
252  *
253  * Adds a new parameter against the entity. A key can have multiple
254  * values associated. Thus repeated calls with the same key will
255  * build up a list of possible values.
256  */
osinfo_entity_add_param(OsinfoEntity * entity,const gchar * key,const gchar * value)257 void osinfo_entity_add_param(OsinfoEntity *entity, const gchar *key, const gchar *value)
258 {
259     GList *values = NULL;
260     gpointer origKey = NULL;
261     gpointer foundValue = NULL;
262     gboolean found;
263 
264     g_return_if_fail(OSINFO_IS_ENTITY(entity));
265     g_return_if_fail(key != NULL);
266     g_return_if_fail(value != NULL);
267 
268     // First check if there exists an existing array of entries for this key
269     // If not, create a ptrarray of strings for this key and insert into map
270     found = g_hash_table_lookup_extended(entity->priv->params, key, &origKey, &foundValue);
271     if (found) {
272         g_hash_table_steal(entity->priv->params, key);
273         g_free(origKey);
274         values = foundValue;
275     }
276 
277     values = g_list_append(values, g_strdup(value));
278     g_hash_table_insert(entity->priv->params, g_strdup(key), values);
279 }
280 
281 
282 /**
283  * osinfo_entity_clear_param:
284  * @entity: an #OsinfoEntity containing the parameters
285  * @key: the name of the key
286  *
287  * Remove all values associated with a key
288  */
osinfo_entity_clear_param(OsinfoEntity * entity,const gchar * key)289 void osinfo_entity_clear_param(OsinfoEntity *entity, const gchar *key)
290 {
291     g_return_if_fail(OSINFO_IS_ENTITY(entity));
292 
293     g_hash_table_remove(entity->priv->params, key);
294 }
295 
296 /**
297  * osinfo_entity_get_id:
298  * @entity: an #OsinfoEntity
299  *
300  * Retrieves the unique key for the entity. The format of identifiers
301  * is undefined, but the recommended practice is to use a URI.
302  *
303  * Returns: (transfer none): the unique key for the entity
304  */
osinfo_entity_get_id(OsinfoEntity * entity)305 const gchar *osinfo_entity_get_id(OsinfoEntity *entity)
306 {
307     g_return_val_if_fail(OSINFO_IS_ENTITY(entity), NULL);
308 
309     return entity->priv->id;
310 }
311 
312 
313 /**
314  * osinfo_entity_get_param_keys:
315  * @entity: an #OsinfoEntity containing the parameters
316  *
317  * Retrieve all the known parameter keys associated with
318  * the entity
319  *
320  * Returns: (transfer container) (element-type utf8): The list of string parameters
321  */
osinfo_entity_get_param_keys(OsinfoEntity * entity)322 GList *osinfo_entity_get_param_keys(OsinfoEntity *entity)
323 {
324     GList *keys;
325 
326     g_return_val_if_fail(OSINFO_IS_ENTITY(entity), NULL);
327 
328     keys = g_hash_table_get_keys(entity->priv->params);
329     keys = g_list_append(keys, (char *)"id");
330 
331     return keys;
332 }
333 
334 
335 /**
336  * osinfo_entity_get_param_value:
337  * @entity: an #OsinfoEntity containing the parameters
338  * @key: the name of the key
339  *
340  * Retrieve the parameter value associated with a named key. If
341  * multiple values are stored against the key, only the first
342  * value is returned. If no value is associated, NULL is returned
343  *
344  * Returns: (transfer none): the value associated with the key, or NULL
345  */
osinfo_entity_get_param_value(OsinfoEntity * entity,const gchar * key)346 const gchar *osinfo_entity_get_param_value(OsinfoEntity *entity, const gchar *key)
347 {
348     GList *values;
349 
350     g_return_val_if_fail(OSINFO_IS_ENTITY(entity), NULL);
351     g_return_val_if_fail(key != NULL, NULL);
352 
353     if (g_str_equal(key, OSINFO_ENTITY_PROP_ID))
354         return entity->priv->id;
355 
356     values = g_hash_table_lookup(entity->priv->params, key);
357 
358     if (values)
359         return values->data;
360     return NULL;
361 }
362 
str_to_bool(const char * str)363 static gboolean str_to_bool(const char *str)
364 {
365     return (g_strcmp0("true", str) == 0 || g_strcmp0("yes", str) == 0);
366 }
367 
368 /**
369  * osinfo_entity_get_param_value_boolean:
370  * @entity: an #OsinfoEntity containing the parameters
371  * @key: the name of the key
372  *
373  * Retrieve the parameter value associated with a named key as a
374  * boolean. If multiple values are stored against the key, only the
375  * first value is returned. If no value is associated, FALSE is returned
376  *
377  * Returns: the value associated with the key as a boolean, or FALSE
378  *
379  * Since: 0.2.0
380  */
osinfo_entity_get_param_value_boolean(OsinfoEntity * entity,const gchar * key)381 gboolean osinfo_entity_get_param_value_boolean(OsinfoEntity *entity, const gchar *key)
382 {
383     const gchar *value = osinfo_entity_get_param_value(entity, key);
384 
385     return value && str_to_bool(value);
386 }
387 
388 /**
389  * osinfo_entity_get_param_value_boolean_with_default:
390  * @entity: an #OsinfoEntity containing the parameters
391  * @key: the name of the key
392  * @default_value: the value to be returned in case there's no value
393  *                 associated with the @key
394  *
395  * Retrieve the parameter value associated with a named key as a
396  * boolean. If multiple values are stored against the key, only the
397  * first value is returned. If no value is associated, @default_value
398  * is returned.
399  *
400  * Returns: the value associated with the key as a boolean, or
401  * @default_value
402  *
403  * Since: 0.2.1
404  */
osinfo_entity_get_param_value_boolean_with_default(OsinfoEntity * entity,const char * key,gboolean default_value)405 gboolean osinfo_entity_get_param_value_boolean_with_default(OsinfoEntity *entity,
406                                                             const char *key,
407                                                             gboolean default_value)
408 {
409     const gchar *value;
410 
411     value = osinfo_entity_get_param_value(entity, key);
412     if (value == NULL)
413         return default_value;
414     else
415         return str_to_bool(value);
416 }
417 
418 /**
419  * osinfo_entity_get_param_value_int64:
420  * @entity: an #OsinfoEntity containing the parameters
421  * @key: the name of the key
422  *
423  * Retrieve the parameter value associated with a named key as an
424  * int64. If multiple values are stored against the key, only the
425  * first value is returned. If no value is associated, -1 is returned.
426  *
427  * Returns: the value associated with the key as an int64, or -1.
428  *
429  * Since: 0.2.1
430  */
osinfo_entity_get_param_value_int64(OsinfoEntity * entity,const gchar * key)431 gint64 osinfo_entity_get_param_value_int64(OsinfoEntity *entity,
432                                            const gchar *key)
433 {
434     return osinfo_entity_get_param_value_int64_with_default(entity, key, -1);
435 }
436 
437 /**
438  * osinfo_entity_get_param_value_int64_with_default:
439  * @entity: an #OsinfoEntity containing the parameters
440  * @key: the name of the key
441  * @default_value: the value to be returned in case there's no value
442  *                 associated with the @key
443  *
444  * Retrieve the parameter value associated with a named key as an
445  * int64. If multiple values are stored against the key, only the
446  * first value is returned. If no value is associated, @default_value
447  * is returned.
448  *
449  * Returns: the value associated with the key as an int64, or
450  * @default_value
451  *
452  * Since: 0.2.1
453  */
osinfo_entity_get_param_value_int64_with_default(OsinfoEntity * entity,const gchar * key,gint64 default_value)454 gint64 osinfo_entity_get_param_value_int64_with_default(OsinfoEntity *entity,
455                                                         const gchar *key,
456                                                         gint64 default_value)
457 {
458     const gchar *str;
459 
460     str = osinfo_entity_get_param_value(entity, key);
461 
462     if (str == NULL)
463         return default_value;
464 
465     return g_ascii_strtoll(str, NULL, 0);
466 }
467 
468 /**
469  * osinfo_entity_get_param_value_enum:
470  * @entity: an #OsinfoEntity containing the parameters
471  * @key: the name of the key
472  * @enum_type: the enum type
473  * @default_value: the default value to be used, in case there's
474  *                 no value associated with the key
475  *
476  * Retrieve the parameter value associated with a named key as an
477  * enum value. If multiple values are stored against the key, only
478  * the first value is returned. If no value is associated, the
479  * @default_value is returned.
480  *
481  * Returns: the enum value associated with the key, or @default_value.
482  *
483  * Since: 0.2.2
484  */
osinfo_entity_get_param_value_enum(OsinfoEntity * entity,const char * key,GType enum_type,gint default_value)485 gint osinfo_entity_get_param_value_enum(OsinfoEntity *entity,
486                                         const char *key,
487                                         GType enum_type,
488                                         gint default_value)
489 {
490     const gchar *nick;
491     GEnumClass *enum_class;
492     GEnumValue *enum_value;
493 
494     g_return_val_if_fail(G_TYPE_IS_ENUM(enum_type), default_value);
495 
496     nick = osinfo_entity_get_param_value(entity, key);
497     if (nick == NULL)
498         return default_value;
499 
500     enum_class = g_type_class_ref(enum_type);
501     enum_value = g_enum_get_value_by_nick(enum_class, nick);
502     g_type_class_unref(enum_class);
503 
504     if (enum_value != NULL)
505         return enum_value->value;
506 
507     g_return_val_if_reached(default_value);
508 }
509 
510 /**
511  * osinfo_entity_get_param_value_list:
512  * @entity: an #OsinfoEntity containing the parameters
513  * @key: the name of the key
514  *
515  * Retrieve all the parameter values associated with a named
516  * key. If no values are associated, NULL is returned
517  *
518  * Returns: (transfer container) (element-type utf8): the values associated with the key
519  */
osinfo_entity_get_param_value_list(OsinfoEntity * entity,const gchar * key)520 GList *osinfo_entity_get_param_value_list(OsinfoEntity *entity, const gchar *key)
521 {
522     GList *values;
523 
524     g_return_val_if_fail(OSINFO_IS_ENTITY(entity), NULL);
525     g_return_val_if_fail(key != NULL, NULL);
526 
527     if (g_str_equal(key, OSINFO_ENTITY_PROP_ID))
528         return g_list_append(NULL, entity->priv->id);
529 
530     values = g_hash_table_lookup(entity->priv->params, key);
531 
532     return g_list_copy(values);
533 }
534