Coverage Report

Created: 2025-07-12 06:31

/src/tinysparql/src/libtinysparql/tracker-deserializer-resource.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2022, Red Hat Inc.
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, write to the
16
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 * Boston, MA  02110-1301, USA.
18
 *
19
 * Author: Carlos Garnacho <carlosg@gnome.org>
20
 */
21
22
/* Deserialization of a (tree of) TrackerResource into a cursor */
23
24
#include "config.h"
25
26
#include <tracker-common.h>
27
28
#include "tracker-deserializer-resource.h"
29
30
#include "tracker-private.h"
31
32
#include "tracker-uri.h"
33
34
enum {
35
  PROP_0,
36
  PROP_RESOURCE,
37
  PROP_GRAPH,
38
  N_PROPS,
39
};
40
41
static GParamSpec *props[N_PROPS];
42
43
typedef struct {
44
  TrackerResource *resource;
45
  TrackerResourceIterator iter;
46
  const gchar *cur_property;
47
  const GValue *cur_value;
48
  gchar *expanded_subject;
49
  gchar *expanded_property;
50
  gchar *value_as_string;
51
} ResourceStack;
52
53
struct _TrackerDeserializerResource {
54
  TrackerDeserializerRdf parent;
55
  TrackerResource *resource;
56
  GArray *iterators;
57
  GHashTable *visited;
58
  gchar *graph;
59
  gchar *expanded_graph;
60
};
61
62
G_DEFINE_TYPE (TrackerDeserializerResource, tracker_deserializer_resource,
63
         TRACKER_TYPE_DESERIALIZER_RDF)
64
65
static void
66
tracker_deserializer_resource_set_property (GObject      *object,
67
                                            guint         prop_id,
68
                                            const GValue *value,
69
                                            GParamSpec   *pspec)
70
0
{
71
0
  TrackerDeserializerResource *deserializer =
72
0
    TRACKER_DESERIALIZER_RESOURCE (object);
73
74
0
  switch (prop_id) {
75
0
  case PROP_RESOURCE:
76
0
    g_clear_object (&deserializer->resource);
77
0
    deserializer->resource = g_value_dup_object (value);
78
0
    break;
79
0
  case PROP_GRAPH:
80
0
    g_clear_object (&deserializer->graph);
81
0
    deserializer->graph = g_value_dup_string (value);
82
0
    break;
83
0
  default:
84
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
85
0
  }
86
0
}
87
88
static void
89
tracker_deserializer_resource_finalize (GObject *object)
90
0
{
91
0
  TrackerDeserializerResource *deserializer =
92
0
    TRACKER_DESERIALIZER_RESOURCE (object);
93
94
0
  g_clear_object (&deserializer->resource);
95
0
  g_hash_table_unref (deserializer->visited);
96
0
  g_array_unref (deserializer->iterators);
97
0
  g_clear_pointer (&deserializer->graph, g_free);
98
0
  g_clear_pointer (&deserializer->expanded_graph, g_free);
99
100
0
  G_OBJECT_CLASS (tracker_deserializer_resource_parent_class)->finalize (object);
101
0
}
102
103
static gchar *
104
expand_uri (TrackerDeserializerResource *deserializer,
105
            const gchar                 *uri)
106
0
{
107
0
  TrackerNamespaceManager *namespaces;
108
109
0
  if (strncmp (uri, "_:", 2) == 0)
110
0
    return g_strdup (uri);
111
112
0
  namespaces = tracker_deserializer_get_namespaces (TRACKER_DESERIALIZER (deserializer));
113
114
0
  return tracker_namespace_manager_expand_uri (namespaces, uri);
115
0
}
116
117
static void
118
push_stack (TrackerDeserializerResource *deserializer,
119
            TrackerResource             *resource)
120
0
{
121
0
  ResourceStack item = { 0, };
122
123
0
  item.resource = resource;
124
0
  item.expanded_subject =
125
0
    expand_uri (deserializer, tracker_resource_get_identifier (resource));
126
0
  tracker_resource_iterator_init (&item.iter, item.resource);
127
0
  g_array_append_val (deserializer->iterators, item);
128
0
  g_hash_table_add (deserializer->visited, item.resource);
129
0
}
130
131
static ResourceStack *
132
peek_stack (TrackerDeserializerResource *deserializer)
133
0
{
134
0
  if (deserializer->iterators->len == 0)
135
0
    return NULL;
136
137
0
  return &g_array_index (deserializer->iterators, ResourceStack,
138
0
                         deserializer->iterators->len - 1);
139
0
}
140
141
static void
142
pop_stack (TrackerDeserializerResource *deserializer)
143
0
{
144
0
  if (deserializer->iterators->len == 0)
145
0
    return;
146
147
0
  g_array_set_size (deserializer->iterators,
148
0
                    deserializer->iterators->len - 1);
149
0
}
150
151
static void
152
tracker_deserializer_resource_constructed (GObject *object)
153
0
{
154
0
  TrackerDeserializerResource *deserializer =
155
0
    TRACKER_DESERIALIZER_RESOURCE (object);
156
157
0
  G_OBJECT_CLASS (tracker_deserializer_resource_parent_class)->constructed (object);
158
159
0
  if (deserializer->graph)
160
0
    deserializer->expanded_graph = expand_uri (deserializer, deserializer->graph);
161
162
0
  push_stack (deserializer, deserializer->resource);
163
0
}
164
165
static TrackerSparqlValueType
166
value_type_from_gtype (const GValue *value)
167
0
{
168
0
  if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
169
0
    TrackerResource *resource;
170
171
0
    resource = g_value_get_object (value);
172
173
0
    if (strncmp (tracker_resource_get_identifier (resource),
174
0
                 "_:", 2) == 0)
175
0
      return TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
176
0
    else
177
0
      return TRACKER_SPARQL_VALUE_TYPE_URI;
178
0
  } else if (G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
179
0
    const gchar *uri = g_value_get_string (value);
180
0
    if (g_str_has_prefix (uri, "_:"))
181
0
      return TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
182
0
    else
183
0
      return TRACKER_SPARQL_VALUE_TYPE_URI;
184
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_STRING)) {
185
0
    return TRACKER_SPARQL_VALUE_TYPE_STRING;
186
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_BOOLEAN)) {
187
0
    return TRACKER_SPARQL_VALUE_TYPE_BOOLEAN;
188
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_INT) ||
189
0
             G_VALUE_HOLDS (value, G_TYPE_UINT) ||
190
0
             G_VALUE_HOLDS (value, G_TYPE_INT64)) {
191
0
    return TRACKER_SPARQL_VALUE_TYPE_INTEGER;
192
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_DOUBLE)) {
193
0
    return TRACKER_SPARQL_VALUE_TYPE_DOUBLE;
194
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_DATE_TIME)) {
195
0
    return TRACKER_SPARQL_VALUE_TYPE_DATETIME;
196
0
  }
197
198
0
  return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
199
0
}
200
201
static TrackerSparqlValueType
202
tracker_deserializer_resource_get_value_type (TrackerSparqlCursor *cursor,
203
                                              gint                 col)
204
0
{
205
0
  TrackerDeserializerResource *deserializer =
206
0
    TRACKER_DESERIALIZER_RESOURCE (cursor);
207
0
  ResourceStack *item;
208
209
0
  item = peek_stack (deserializer);
210
0
  if (!item)
211
0
    return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
212
213
0
  switch (col) {
214
0
  case TRACKER_RDF_COL_SUBJECT:
215
0
    if (strncmp (tracker_resource_get_identifier (item->resource),
216
0
                 "_:", 2) == 0)
217
0
      return TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE;
218
0
    else
219
0
      return TRACKER_SPARQL_VALUE_TYPE_URI;
220
0
    break;
221
0
  case TRACKER_RDF_COL_PREDICATE:
222
0
    return TRACKER_SPARQL_VALUE_TYPE_URI;
223
0
    break;
224
0
  case TRACKER_RDF_COL_OBJECT:
225
0
    return value_type_from_gtype (item->cur_value);
226
0
    break;
227
0
  case TRACKER_RDF_COL_GRAPH:
228
0
    return deserializer->graph ?
229
0
      TRACKER_SPARQL_VALUE_TYPE_URI : TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
230
0
    break;
231
0
  default:
232
0
    return TRACKER_SPARQL_VALUE_TYPE_UNBOUND;
233
0
    break;
234
0
  }
235
0
}
236
237
static const gchar *
238
tracker_deserializer_resource_get_string (TrackerSparqlCursor  *cursor,
239
                                          gint                  col,
240
                                          const gchar         **langtag,
241
                                          glong                *length)
242
0
{
243
0
  TrackerDeserializerResource *deserializer =
244
0
    TRACKER_DESERIALIZER_RESOURCE (cursor);
245
0
  const gchar *str = NULL;
246
0
  ResourceStack *item;
247
248
0
  if (length)
249
0
    *length = 0;
250
0
  if (langtag)
251
0
    *langtag = NULL;
252
253
0
  item = peek_stack (deserializer);
254
0
  if (!item)
255
0
    return NULL;
256
257
0
  switch (col) {
258
0
  case TRACKER_RDF_COL_SUBJECT:
259
0
    str = item->expanded_subject;
260
0
    break;
261
0
  case TRACKER_RDF_COL_PREDICATE:
262
0
    str = item->expanded_property;
263
0
    break;
264
0
  case TRACKER_RDF_COL_OBJECT:
265
0
    str = item->value_as_string;
266
0
    break;
267
0
  case TRACKER_RDF_COL_GRAPH:
268
0
    str = deserializer->expanded_graph;
269
0
    break;
270
0
  default:
271
0
    break;
272
0
  }
273
274
0
  if (!str)
275
0
    return NULL;
276
277
0
  if (length)
278
0
    *length = strlen (str);
279
280
0
  return str;
281
0
}
282
283
static gchar *
284
convert_gvalue_to_string (TrackerDeserializerResource *deserializer,
285
                          const GValue                *value)
286
0
{
287
0
  if (G_VALUE_HOLDS (value, TRACKER_TYPE_RESOURCE)) {
288
0
    TrackerResource *resource;
289
290
0
    resource = g_value_get_object (value);
291
0
    return expand_uri (deserializer,
292
0
                       tracker_resource_get_identifier (resource));
293
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_STRING) ||
294
0
             G_VALUE_HOLDS (value, TRACKER_TYPE_URI)) {
295
0
    return expand_uri (deserializer, g_value_get_string (value));
296
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_BOOLEAN)) {
297
0
    gboolean val;
298
299
0
    val = g_value_get_boolean (value);
300
0
    return g_strdup (val ? "true" : "false");
301
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_INT)) {
302
0
    gint val;
303
304
0
    val = g_value_get_int (value);
305
0
    return g_strdup_printf ("%d", val);
306
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_UINT)) {
307
0
    guint val;
308
309
0
    val = g_value_get_uint (value);
310
0
    return g_strdup_printf ("%u", val);
311
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_INT64)) {
312
0
    gint64 val;
313
314
0
    val = g_value_get_int64 (value);
315
0
    return g_strdup_printf ("%" G_GINT64_FORMAT, val);
316
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_DOUBLE)) {
317
0
    gchar *buf;
318
0
    gdouble val;
319
320
0
    buf = g_new0 (gchar, G_ASCII_DTOSTR_BUF_SIZE);
321
0
    val = g_value_get_double (value);
322
0
    return g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, val);
323
0
  } else if (G_VALUE_HOLDS (value, G_TYPE_DATE_TIME)) {
324
0
    GDateTime *val;
325
326
0
    val = g_value_get_boxed (value);
327
0
    return tracker_date_format_iso8601 (val);
328
0
  }
329
330
0
  return NULL;
331
0
}
332
333
static gboolean
334
tracker_deserializer_resource_next (TrackerSparqlCursor  *cursor,
335
                                    GCancellable         *cancellable,
336
                                    GError              **error)
337
0
{
338
0
  TrackerDeserializerResource *deserializer =
339
0
    TRACKER_DESERIALIZER_RESOURCE (cursor);
340
0
  ResourceStack *item;
341
342
0
 retry:
343
0
  item = peek_stack (deserializer);
344
0
  if (!item)
345
0
    return FALSE;
346
347
0
  g_clear_pointer (&item->expanded_property, g_free);
348
0
  g_clear_pointer (&item->value_as_string, g_free);
349
350
0
  if (tracker_resource_iterator_next (&item->iter,
351
0
                                      &item->cur_property,
352
0
                                      &item->cur_value)) {
353
0
    item->expanded_property =
354
0
      expand_uri (deserializer, item->cur_property);
355
0
    item->value_as_string =
356
0
      convert_gvalue_to_string (deserializer, item->cur_value);
357
358
0
    if (G_VALUE_HOLDS (item->cur_value, TRACKER_TYPE_RESOURCE)) {
359
0
      TrackerResource *child;
360
361
      /* Since the value extracted is a new resource, iterate
362
       * through it first before handling this property/value
363
       * on the current resource.
364
       */
365
0
      child = g_value_get_object (item->cur_value);
366
0
      if (!g_hash_table_contains (deserializer->visited, child)) {
367
0
        push_stack (deserializer, child);
368
0
        goto retry;
369
0
      }
370
0
    }
371
372
0
    return TRUE;
373
0
  } else {
374
0
    pop_stack (deserializer);
375
0
    item = peek_stack (deserializer);
376
    /* We already fetched a property/value before pushing */
377
0
    return item != NULL;
378
0
  }
379
0
}
380
381
static void
382
tracker_deserializer_resource_class_init (TrackerDeserializerResourceClass *klass)
383
0
{
384
0
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
385
0
  TrackerSparqlCursorClass *cursor_class =
386
0
    TRACKER_SPARQL_CURSOR_CLASS (klass);
387
388
0
  object_class->set_property = tracker_deserializer_resource_set_property;
389
0
  object_class->finalize = tracker_deserializer_resource_finalize;
390
0
  object_class->constructed = tracker_deserializer_resource_constructed;
391
392
0
  cursor_class->get_value_type = tracker_deserializer_resource_get_value_type;
393
0
  cursor_class->get_string = tracker_deserializer_resource_get_string;
394
0
  cursor_class->next = tracker_deserializer_resource_next;
395
396
0
  props[PROP_RESOURCE] =
397
0
    g_param_spec_object ("resource",
398
0
                         "Resource",
399
0
                         "Resource",
400
0
                         TRACKER_TYPE_RESOURCE,
401
0
                         G_PARAM_CONSTRUCT_ONLY |
402
0
                         G_PARAM_STATIC_STRINGS |
403
0
                         G_PARAM_WRITABLE);
404
0
  props[PROP_GRAPH] =
405
0
    g_param_spec_string ("graph",
406
0
                         "Graph",
407
0
                         "Graph",
408
0
                         NULL,
409
0
                         G_PARAM_CONSTRUCT_ONLY |
410
0
                         G_PARAM_STATIC_STRINGS |
411
0
                         G_PARAM_WRITABLE);
412
413
0
  g_object_class_install_properties (object_class, N_PROPS, props);
414
0
}
415
416
static void
417
clear_stack (gpointer user_data)
418
0
{
419
0
  ResourceStack *item = user_data;
420
421
0
  g_clear_pointer (&item->expanded_subject, g_free);
422
0
  g_clear_pointer (&item->expanded_property, g_free);
423
0
  g_clear_pointer (&item->value_as_string, g_free);
424
0
}
425
426
static void
427
tracker_deserializer_resource_init (TrackerDeserializerResource *deserializer)
428
0
{
429
0
  deserializer->iterators = g_array_new (FALSE, FALSE, sizeof (ResourceStack));
430
0
  g_array_set_clear_func (deserializer->iterators, clear_stack);
431
0
  deserializer->visited = g_hash_table_new (NULL, NULL);
432
0
}
433
434
TrackerSparqlCursor *
435
tracker_deserializer_resource_new (TrackerResource         *resource,
436
                                   TrackerNamespaceManager *namespaces,
437
                                   const gchar             *graph)
438
0
{
439
0
  return g_object_new (TRACKER_TYPE_DESERIALIZER_RESOURCE,
440
0
                       "resource", resource,
441
0
                       "namespace-manager", namespaces,
442
0
                       "has-graph", graph != NULL,
443
0
                       "graph", graph,
444
0
                       NULL);
445
0
}