Coverage Report

Created: 2026-04-12 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tinysparql/src/libtinysparql/tracker-serializer-xml.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2020, 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
/* Serialization of cursors to the XML format defined at:
23
 * https://www.w3.org/TR/2013/REC-rdf-sparql-XMLres-20130321/
24
 */
25
26
#include "config.h"
27
28
#include "tracker-serializer-xml.h"
29
30
#include <libxml/xmlwriter.h>
31
32
/* Make required type casts a bit more descriptive. */
33
0
#define XML(x) ((const xmlChar *) x)
34
35
struct _TrackerSerializerXml
36
{
37
  TrackerSerializer parent_instance;
38
  xmlBufferPtr buffer;
39
  xmlTextWriterPtr writer;
40
  GPtrArray *vars;
41
  gssize current_pos;
42
43
  guint stream_closed : 1;
44
  guint cursor_started : 1;
45
  guint cursor_finished : 1;
46
  guint head_printed : 1;
47
};
48
49
0
G_DEFINE_TYPE (TrackerSerializerXml, tracker_serializer_xml,
50
0
               TRACKER_TYPE_SERIALIZER)
51
0
52
0
static void
53
0
tracker_serializer_xml_finalize (GObject *object)
54
0
{
55
0
  TrackerSerializerXml *serializer_xml = TRACKER_SERIALIZER_XML (object);
56
57
0
  g_input_stream_close (G_INPUT_STREAM (object), NULL, NULL);
58
59
0
  g_clear_pointer (&serializer_xml->vars, g_ptr_array_unref);
60
61
0
  G_OBJECT_CLASS (tracker_serializer_xml_parent_class)->finalize (object);
62
0
}
63
64
static gboolean
65
serialize_up_to_position (TrackerSerializerXml  *serializer_xml,
66
                          gsize                  pos,
67
                          GCancellable          *cancellable,
68
                          GError               **error)
69
0
{
70
0
  TrackerSparqlCursor *cursor;
71
0
  GError *inner_error = NULL;
72
0
  gint i;
73
74
0
  if (!serializer_xml->buffer)
75
0
    serializer_xml->buffer = xmlBufferCreate ();
76
0
  if (!serializer_xml->writer)
77
0
    serializer_xml->writer = xmlNewTextWriterMemory (serializer_xml->buffer, 0);
78
0
  if (!serializer_xml->vars)
79
0
    serializer_xml->vars = g_ptr_array_new_with_free_func (g_free);
80
81
0
  cursor = tracker_serializer_get_cursor (TRACKER_SERIALIZER (serializer_xml));
82
83
0
  if (!serializer_xml->head_printed) {
84
0
    xmlTextWriterStartDocument (serializer_xml->writer, "1.0", "UTF-8", NULL);
85
86
0
    if (xmlTextWriterStartElement (serializer_xml->writer, XML ("sparql")) < 0)
87
0
      goto error;
88
89
0
    if (xmlTextWriterStartElement (serializer_xml->writer, XML ("head")) < 0)
90
0
      goto error;
91
92
0
    for (i = 0; i < tracker_sparql_cursor_get_n_columns (cursor); i++) {
93
0
      const gchar *var;
94
95
0
      var = tracker_sparql_cursor_get_variable_name (cursor, i);
96
97
0
      if (xmlTextWriterStartElement (serializer_xml->writer, XML ("variable")) < 0)
98
0
        goto error;
99
100
0
      if (var && *var) {
101
0
        g_ptr_array_add (serializer_xml->vars,
102
0
                         g_strdup (var));
103
0
      } else {
104
0
        g_ptr_array_add (serializer_xml->vars,
105
0
                         g_strdup_printf ("var%d", i + 1));
106
0
      }
107
108
0
      if (xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
109
0
                                             XML ("name"),
110
0
                                             "%s",
111
0
                                             (char *) g_ptr_array_index (serializer_xml->vars, i)) < 0)
112
0
        goto error;
113
114
0
      xmlTextWriterEndElement (serializer_xml->writer);
115
0
    }
116
117
0
    xmlTextWriterEndElement (serializer_xml->writer);
118
119
0
    if (xmlTextWriterStartElement (serializer_xml->writer, XML ("results")) < 0)
120
0
      goto error;
121
122
0
    serializer_xml->head_printed = TRUE;
123
0
  }
124
125
0
  while (!serializer_xml->cursor_finished &&
126
0
         (gsize) xmlBufferLength (serializer_xml->buffer) < pos) {
127
0
    if (!tracker_sparql_cursor_next (cursor, cancellable, &inner_error)) {
128
0
      if (inner_error) {
129
0
        g_propagate_error (error, inner_error);
130
0
        return FALSE;
131
0
      } else {
132
0
        xmlTextWriterEndElement (serializer_xml->writer);
133
0
        xmlTextWriterEndElement (serializer_xml->writer);
134
0
        xmlTextWriterEndDocument (serializer_xml->writer);
135
0
        serializer_xml->cursor_finished = TRUE;
136
0
        break;
137
0
      }
138
0
    } else {
139
0
      serializer_xml->cursor_started = TRUE;
140
0
    }
141
142
0
    if (xmlTextWriterStartElement (serializer_xml->writer, XML ("result")) < 0)
143
0
      goto error;
144
145
0
    for (i = 0; i < tracker_sparql_cursor_get_n_columns (cursor); i++) {
146
0
      const gchar *var, *str, *type = NULL, *datatype = NULL, *langtag = NULL;
147
148
0
      switch (tracker_sparql_cursor_get_value_type (cursor, i)) {
149
0
      case TRACKER_SPARQL_VALUE_TYPE_URI:
150
0
        type = "uri";
151
0
        break;
152
0
      case TRACKER_SPARQL_VALUE_TYPE_STRING:
153
0
        type = "literal";
154
0
        datatype = TRACKER_PREFIX_XSD "string";
155
0
        break;
156
0
      case TRACKER_SPARQL_VALUE_TYPE_INTEGER:
157
0
        type = "literal";
158
0
        datatype = TRACKER_PREFIX_XSD "integer";
159
0
        break;
160
0
      case TRACKER_SPARQL_VALUE_TYPE_BOOLEAN:
161
0
        type = "literal";
162
0
        datatype = TRACKER_PREFIX_XSD "boolean";
163
0
        break;
164
0
      case TRACKER_SPARQL_VALUE_TYPE_DOUBLE:
165
0
        type = "literal";
166
0
        datatype = TRACKER_PREFIX_XSD "double";
167
0
        break;
168
0
      case TRACKER_SPARQL_VALUE_TYPE_DATETIME:
169
0
        type = "literal";
170
0
        datatype = TRACKER_PREFIX_XSD "dateTime";
171
0
        break;
172
0
      case TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE:
173
0
        type = "bnode";
174
0
        break;
175
0
      case TRACKER_SPARQL_VALUE_TYPE_UNBOUND:
176
0
                                continue;
177
0
      }
178
179
0
      var = g_ptr_array_index (serializer_xml->vars, i);
180
181
0
      if (xmlTextWriterStartElement (serializer_xml->writer, XML ("binding")) < 0)
182
0
        goto error;
183
184
0
      if (xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
185
0
                                             XML ("name"),
186
0
                                             "%s",
187
0
                                             var) < 0)
188
0
        goto error;
189
190
0
      if (xmlTextWriterStartElement (serializer_xml->writer, XML (type)) < 0)
191
0
        goto error;
192
193
0
      str = tracker_sparql_cursor_get_langstring (cursor, i, &langtag, NULL);
194
195
0
      if (langtag) {
196
0
        datatype = TRACKER_PREFIX_RDF "langString";
197
198
0
        if (xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
199
0
                                               XML ("xml:lang"),
200
0
                                               "%s",
201
0
                                               langtag) < 0)
202
0
          goto error;
203
0
      }
204
205
0
      if (datatype) {
206
0
        if (xmlTextWriterWriteFormatAttribute (serializer_xml->writer,
207
0
                                               XML ("datatype"),
208
0
                                               "%s",
209
0
                                               datatype) < 0)
210
0
          goto error;
211
0
      }
212
213
0
      if (str) {
214
0
        if (xmlTextWriterWriteRaw (serializer_xml->writer, XML (str)) < 0)
215
0
          goto error;
216
0
      }
217
218
0
      xmlTextWriterEndElement (serializer_xml->writer);
219
0
      xmlTextWriterEndElement (serializer_xml->writer);
220
0
    }
221
222
0
    xmlTextWriterEndElement (serializer_xml->writer);
223
0
  }
224
225
0
  return TRUE;
226
227
0
 error:
228
0
  g_set_error_literal (error,
229
0
                       TRACKER_SPARQL_ERROR,
230
0
                       TRACKER_SPARQL_ERROR_INTERNAL,
231
0
                       "Error writing XML cursor content");
232
0
  return FALSE;
233
0
}
234
235
static gssize
236
tracker_serializer_xml_read (GInputStream  *istream,
237
                             gpointer       buffer,
238
                             gsize          count,
239
                             GCancellable  *cancellable,
240
                             GError       **error)
241
0
{
242
0
  TrackerSerializerXml *serializer_xml = TRACKER_SERIALIZER_XML (istream);
243
0
  gsize bytes_unflushed, bytes_copied;
244
0
  const xmlChar *xml_buf;
245
246
0
  if (serializer_xml->stream_closed ||
247
0
      (serializer_xml->cursor_finished &&
248
0
       serializer_xml->current_pos == xmlBufferLength (serializer_xml->buffer)))
249
0
    return 0;
250
251
0
  if (!serialize_up_to_position (serializer_xml,
252
0
                                 serializer_xml->current_pos + count,
253
0
                                 cancellable,
254
0
                                 error))
255
0
    return -1;
256
257
0
  bytes_unflushed =
258
0
    xmlBufferLength (serializer_xml->buffer) - serializer_xml->current_pos;
259
0
  bytes_copied = MIN (count, bytes_unflushed);
260
261
0
  xml_buf = xmlBufferContent (serializer_xml->buffer);
262
263
0
  memcpy (buffer,
264
0
          &xml_buf[serializer_xml->current_pos],
265
0
          bytes_copied);
266
0
  serializer_xml->current_pos += bytes_copied;
267
268
0
  return bytes_copied;
269
0
}
270
271
static gboolean
272
tracker_serializer_xml_close (GInputStream  *istream,
273
                              GCancellable  *cancellable,
274
                              GError       **error)
275
0
{
276
0
  TrackerSerializerXml *serializer_xml = TRACKER_SERIALIZER_XML (istream);
277
278
0
  serializer_xml->stream_closed = TRUE;
279
0
  g_clear_pointer (&serializer_xml->buffer, xmlBufferFree);
280
0
  g_clear_pointer (&serializer_xml->writer, xmlFreeTextWriter);
281
282
0
  return TRUE;
283
0
}
284
285
static void
286
tracker_serializer_xml_class_init (TrackerSerializerXmlClass *klass)
287
0
{
288
0
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
289
0
  GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);
290
291
0
  object_class->finalize = tracker_serializer_xml_finalize;
292
293
0
  istream_class->read_fn = tracker_serializer_xml_read;
294
0
  istream_class->close_fn = tracker_serializer_xml_close;
295
0
}
296
297
static void
298
tracker_serializer_xml_init (TrackerSerializerXml *serializer)
299
0
{
300
0
}