/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 | } |