Coverage Report

Created: 2025-07-18 06:10

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