summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@gnome.org>2013-05-26 14:39:08 +0100
committerEmmanuele Bassi <ebassi@gnome.org>2013-05-26 14:39:08 +0100
commita324690d0679eca8fe67d29f69de85fc455e4941 (patch)
tree74e5d2b720d5b856aac981ad844623ce75b6257c
parenta2ac1d0fd100532b2d646c24c409dde00816b414 (diff)
downloadglib-wip/serializable.tar.gz
Add GMarkupEncoderwip/serializable
An encoder and decoder that uses XML (parseable by GMarkup) as the intermediate representation of its data.
-rw-r--r--gio/Makefile.am2
-rw-r--r--gio/gio.h1
-rw-r--r--gio/giotypes.h1
-rw-r--r--gio/gmarkupencoder.c437
-rw-r--r--gio/gmarkupencoder.h49
-rw-r--r--gio/tests/encoder.c85
6 files changed, 540 insertions, 35 deletions
diff --git a/gio/Makefile.am b/gio/Makefile.am
index bbf57f113..6f58683ce 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -389,6 +389,7 @@ libgio_2_0_la_SOURCES = \
giostream.c \
gkeyfileencoder.c \
gloadableicon.c \
+ gmarkupencoder.c \
gmount.c \
gmemoryinputstream.c \
gmemoryoutputstream.c \
@@ -565,6 +566,7 @@ gio_headers = \
giostream.h \
gkeyfileencoder.h \
gloadableicon.h \
+ gmarkupencoder.h \
gmount.h \
gmemoryinputstream.h \
gmemoryoutputstream.h \
diff --git a/gio/gio.h b/gio/gio.h
index ac4d77a56..1f345bfd1 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -90,6 +90,7 @@
#include <gio/giostream.h>
#include <gio/gkeyfileencoder.h>
#include <gio/gloadableicon.h>
+#include <gio/gmarkupencoder.h>
#include <gio/gmemoryinputstream.h>
#include <gio/gmemoryoutputstream.h>
#include <gio/gmount.h>
diff --git a/gio/giotypes.h b/gio/giotypes.h
index 1b4c2b2fc..f9c35cfd0 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -50,6 +50,7 @@ typedef struct _GEncoder GEncoder;
typedef struct _GSerializable GSerializable;
typedef struct _GBinaryEncoder GBinaryEncoder;
typedef struct _GKeyfileEncoder GKeyfileEncoder;
+typedef struct _GMarkupEncoder GMarkupEncoder;
typedef struct _GSimpleActionGroup GSimpleActionGroup;
typedef struct _GRemoteActionGroup GRemoteActionGroup;
diff --git a/gio/gmarkupencoder.c b/gio/gmarkupencoder.c
new file mode 100644
index 000000000..9da234887
--- /dev/null
+++ b/gio/gmarkupencoder.c
@@ -0,0 +1,437 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2013 Emmanuele Bassi <ebassi@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gmarkupencoder
+ * @Title: GMarkupEncoder
+ * @Short_Description: Encodes and decodes data as markup
+ *
+ * #GMarkupEncoder is a class that allows encoding and decoding data
+ * as an XML subset that can be parsed by #GMarkupParser.
+ *
+ * Note that you can only use #GMarkupEncoder to decode the output
+ * of data encoded by a #GMarkupEncoder.
+ */
+
+#include "config.h"
+
+#include "gmarkupencoder.h"
+#include "gencoder.h"
+#include "gioerror.h"
+#include "glibintl.h"
+
+#include <string.h>
+
+struct _GMarkupEncoder
+{
+ GEncoder parent_instance;
+
+ /* parser state */
+ char *cur_key;
+ char *cur_value;
+ char *cur_value_type;
+
+ guint in_entries : 1;
+ guint in_entry : 1;
+ guint in_key : 1;
+ guint in_value : 1;
+};
+
+struct _GMarkupEncoderClass
+{
+ GEncoderClass parent_class;
+};
+
+G_DEFINE_TYPE (GMarkupEncoder, g_markup_encoder, G_TYPE_ENCODER)
+
+static void
+g_markup_encoder_start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ GMarkupEncoder *self = user_data;
+
+ if (strcmp (element_name, "entries") == 0)
+ {
+ if (self->in_entries)
+ {
+ g_set_error_literal (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'entries' tag cannot be nested");
+ return;
+ }
+
+ g_assert (!self->in_entry);
+ g_assert (!self->in_key);
+ g_assert (!self->in_value);
+ self->in_entries = TRUE;
+
+ return;
+ }
+
+ if (strcmp (element_name, "entry") == 0)
+ {
+ if (!self->in_entries)
+ {
+ g_set_error_literal (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'entry' tag can only be used inside an 'entries' tag");
+ return;
+ }
+
+ g_assert (!self->in_entry);
+ g_assert (!self->in_key);
+ g_assert (!self->in_value);
+ self->in_entry = TRUE;
+
+ g_free (self->cur_key);
+ self->cur_key = NULL;
+ g_free (self->cur_value_type);
+ self->cur_value_type = NULL;
+ g_free (self->cur_value);
+ self->cur_value = NULL;
+ return;
+ }
+
+ if (strcmp (element_name, "key") == 0)
+ {
+ if (!self->in_entry)
+ {
+ g_set_error_literal (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'key' tag can only be used inside an 'entry' tag");
+ return;
+ }
+
+ g_assert (!self->in_value);
+ self->in_key = TRUE;
+ return;
+ }
+
+ if (strcmp (element_name, "value") == 0)
+ {
+ gboolean res;
+
+ if (!self->in_entry)
+ {
+ g_set_error_literal (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "The 'value' tag can only be used inside an 'entry' tag");
+ return;
+ }
+
+ res = g_markup_collect_attributes (element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ G_MARKUP_COLLECT_STRDUP, "type", &self->cur_value_type,
+ G_MARKUP_COLLECT_INVALID);
+ if (!res)
+ return;
+
+ g_assert (!self->in_key);
+ self->in_value = TRUE;
+ return;
+ }
+}
+
+static void
+g_markup_encoder_add_current_entry (GMarkupEncoder *self,
+ GError **error)
+{
+ GError *internal_error;
+ GVariant *variant;
+
+ if (self->cur_key == NULL)
+ {
+ if (self->cur_value_type != NULL)
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "No key defined for entry of type '%s'",
+ self->cur_value_type);
+ }
+ else
+ {
+ g_set_error_literal (error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "No key defined for entry");
+ }
+
+ return;
+ }
+
+ if (self->cur_value_type == NULL)
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "No value type defined for key '%s'",
+ self->cur_key);
+ return;
+ }
+
+ if (self->cur_value == NULL)
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "No value defined for key '%s' of type '%s'",
+ self->cur_key,
+ self->cur_value_type);
+ return;
+ }
+
+ internal_error = NULL;
+ variant = g_variant_parse (G_VARIANT_TYPE (self->cur_value_type),
+ self->cur_value,
+ NULL,
+ NULL,
+ &internal_error);
+ if (internal_error != NULL)
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Unable to parse the entry value: %s",
+ internal_error->message);
+ g_error_free (internal_error);
+ return;
+ }
+
+ g_encoder_add_key (G_ENCODER (self), self->cur_key, variant);
+ g_variant_unref (variant);
+}
+
+static void
+g_markup_encoder_end_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ GMarkupEncoder *self = user_data;
+
+ if (strcmp (element_name, "entries") == 0)
+ {
+ self->in_entries = FALSE;
+ return;
+ }
+
+ if (strcmp (element_name, "entry") == 0)
+ {
+ g_assert (self->in_entries);
+ self->in_entry = FALSE;
+
+ g_markup_encoder_add_current_entry (self, error);
+
+ g_free (self->cur_key);
+ self->cur_key = NULL;
+ g_free (self->cur_value_type);
+ self->cur_value_type = NULL;
+ g_free (self->cur_value);
+ self->cur_value = NULL;
+ return;
+ }
+
+ if (strcmp (element_name, "key") == 0)
+ {
+ g_assert (self->in_entries);
+ g_assert (self->in_entry);
+ self->in_key = FALSE;
+ return;
+ }
+
+ if (strcmp (element_name, "value") == 0)
+ {
+ g_assert (self->in_entries);
+ g_assert (self->in_entry);
+ self->in_value = FALSE;
+ return;
+ }
+
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ "Unknown element '%s' in markup",
+ element_name);
+}
+
+static void
+g_markup_encoder_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ GMarkupEncoder *self = user_data;
+
+ if (self->in_key)
+ {
+ g_free (self->cur_key);
+ self->cur_key = g_strndup (text, text_len);
+ return;
+ }
+
+ if (self->in_value)
+ {
+ g_free (self->cur_value);
+ self->cur_value = g_strndup (text, text_len);
+ return;
+ }
+}
+
+static const GMarkupParser markup_parser = {
+ /* .start_element = */ g_markup_encoder_start_element,
+ /* .end_element = */ g_markup_encoder_end_element,
+ /* .text = */ g_markup_encoder_text,
+ /* .passthrough = */ NULL,
+ /* .error = */ NULL,
+};
+
+static void
+clear_parser_state (gpointer data)
+{
+ GMarkupEncoder *self = data;
+
+ g_free (self->cur_key);
+ self->cur_key = NULL;
+ g_free (self->cur_value_type);
+ self->cur_value_type = NULL;
+ g_free (self->cur_value);
+ self->cur_value = NULL;
+
+ self->in_entries = FALSE;
+ self->in_entry = FALSE;
+ self->in_key = FALSE;
+ self->in_value = FALSE;
+}
+
+static gboolean
+g_markup_encoder_read_from_bytes (GEncoder *encoder,
+ GBytes *buffer,
+ GError **error)
+{
+ GMarkupParseContext *context;
+ GError *internal_error = NULL;
+
+ clear_parser_state (encoder);
+
+ context = g_markup_parse_context_new (&markup_parser, 0, encoder, clear_parser_state);
+
+ g_markup_parse_context_parse (context,
+ g_bytes_get_data (buffer, NULL),
+ g_bytes_get_size (buffer),
+ &internal_error);
+
+ g_markup_parse_context_free (context);
+
+ if (internal_error)
+ {
+ g_set_error (error, G_IO_ERROR,
+ G_IO_ERROR_INVALID_DATA,
+ "Unable to read markup data: %s",
+ internal_error->message);
+ g_error_free (internal_error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static GBytes *
+g_markup_encoder_write_to_bytes (GEncoder *encoder,
+ GError **error)
+{
+ GVariant *data = g_encoder_close (encoder);
+ GVariant *entry;
+ GVariantIter iter;
+ GString *buffer;
+ gsize len;
+
+ buffer = g_string_sized_new (1024);
+
+ g_string_append (buffer, "<?xml version=\"1.0\"?>\n");
+ g_string_append (buffer, "<entries version=\"1.0\">\n");
+
+ g_variant_iter_init (&iter, data);
+ while ((entry = g_variant_iter_next_value (&iter)) != NULL)
+ {
+ GVariant *key = g_variant_get_child_value (entry, 0);
+ GVariant *tmp = g_variant_get_child_value (entry, 1);
+ GVariant *value;
+ char *value_str;
+
+ g_string_append (buffer, " <entry>\n");
+
+ value = g_variant_get_variant (tmp);
+ value_str = g_variant_print (value, FALSE);
+
+ g_string_append_printf (buffer,
+ " <key>%s</key>\n",
+ g_variant_get_string (key, NULL));
+
+ g_string_append_printf (buffer,
+ " <value type=\"%s\">%s</value>\n",
+ (const char *) g_variant_get_type (value),
+ value_str);
+
+ g_free (value_str);
+ g_variant_unref (value);
+ g_variant_unref (tmp);
+ g_variant_unref (key);
+
+ g_string_append (buffer, " </entry>\n");
+ }
+
+ g_string_append (buffer, "</entries>");
+ len = buffer->len;
+
+ return g_bytes_new_take (g_string_free (buffer, FALSE), len);
+}
+
+static void
+g_markup_encoder_class_init (GMarkupEncoderClass *klass)
+{
+ GEncoderClass *encoder_class = G_ENCODER_CLASS (klass);
+
+ encoder_class->read_from_bytes = g_markup_encoder_read_from_bytes;
+ encoder_class->write_to_bytes = g_markup_encoder_write_to_bytes;
+}
+
+static void
+g_markup_encoder_init (GMarkupEncoder *self)
+{
+}
+
+/**
+ * g_markup_encoder_new:
+ *
+ * Creates a new #GMarkupEncoder instance.
+ *
+ * Returns: (transfer full): the newly created #GMarkupEncoder instance.
+ * Use g_object_unref() when done.
+ *
+ * Since: 2.38
+ */
+GEncoder *
+g_markup_encoder_new (void)
+{
+ return g_object_new (G_TYPE_MARKUP_ENCODER, NULL);
+}
diff --git a/gio/gmarkupencoder.h b/gio/gmarkupencoder.h
new file mode 100644
index 000000000..2c723bb97
--- /dev/null
+++ b/gio/gmarkupencoder.h
@@ -0,0 +1,49 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2013 Emmanuele Bassi <ebassi@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_MARKUP_ENCODER_H__
+#define __G_MARKUP_ENCODER_H__
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MARKUP_ENCODER (g_markup_encoder_get_type ())
+#define G_MARKUP_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_MARKUP_ENCODER, GMarkupEncoder))
+#define G_IS_MARKUP_ENCODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_MARKUP_ENCODER))
+#define G_MARKUP_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_MARKUP_ENCODER, GMarkupEncoderClass))
+#define G_IS_MARKUP_ENCODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_MARKUP_ENCODER))
+#define G_MARKUP_ENCODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_MARKUP_ENCODER, GMarkupEncoderClass))
+
+typedef struct _GMarkupEncoderClass GMarkupEncoderClass;
+
+GLIB_AVAILABLE_IN_2_38
+GType g_markup_encoder_get_type (void) G_GNUC_CONST;
+
+GLIB_AVAILABLE_IN_2_38
+GEncoder * g_markup_encoder_new (void);
+
+G_END_DECLS
+
+#endif /* __G_MARKUP_ENCODER_H__ */
diff --git a/gio/tests/encoder.c b/gio/tests/encoder.c
index 698f23bb7..04540115f 100644
--- a/gio/tests/encoder.c
+++ b/gio/tests/encoder.c
@@ -1,34 +1,35 @@
#include <gio/gio.h>
-static void
-encoder_binary (void)
+static GBytes *
+encode_data (GEncoder *encoder)
{
- GEncoder *encoder = g_binary_encoder_new ();
GError *error = NULL;
GBytes *buffer;
- gboolean bool_value;
- char *str_value;
-
- g_object_add_weak_pointer (G_OBJECT (encoder), (gpointer *) &encoder);
g_encoder_add_key_bool (encoder, "BoolValue", TRUE);
g_encoder_add_key_string (encoder, "StringValue", "Hello");
+ g_encoder_add_key_double (encoder, "DoubleValue", 3.14159);
buffer = g_encoder_write_to_bytes (encoder, &error);
g_assert_no_error (error);
g_assert (buffer != NULL);
- g_object_unref (encoder);
- g_assert (encoder == NULL);
-
if (g_test_verbose ())
g_print ("*** buffer (len: %d) = ***\n%s\n",
(int) g_bytes_get_size (buffer),
(const char *) g_bytes_get_data (buffer, NULL));
- encoder = g_binary_encoder_new ();
+ return buffer;
+}
- g_object_add_weak_pointer (G_OBJECT (encoder), (gpointer *) &encoder);
+static void
+decode_data (GEncoder *encoder,
+ GBytes *buffer)
+{
+ GError *error = NULL;
+ gboolean bool_value;
+ char *str_value;
+ double dbl_value;
g_encoder_read_from_bytes (encoder, buffer, &error);
g_assert_no_error (error);
@@ -40,45 +41,58 @@ encoder_binary (void)
g_assert_cmpstr (str_value, ==, "Hello");
g_free (str_value);
- g_bytes_unref (buffer);
+ g_encoder_get_key_double (encoder, "DoubleValue", &dbl_value);
+ g_assert_cmpfloat ((float) dbl_value, ==, 3.14159f);
+}
+
+static void
+encoder_binary (void)
+{
+ GEncoder *encoder, *decoder;
+ GBytes *buffer;
+ encoder = g_binary_encoder_new ();
+ buffer = encode_data (encoder);
g_object_unref (encoder);
- g_assert (encoder == NULL);
+
+ decoder = g_binary_encoder_new ();
+ decode_data (decoder, buffer);
+ g_object_unref (decoder);
+ g_bytes_unref (buffer);
}
static void
encoder_keyfile (void)
{
- GEncoder *encoder = g_keyfile_encoder_new ();
- GError *error = NULL;
+ GEncoder *encoder, *decoder;
GBytes *buffer;
- gboolean res;
+ encoder = g_keyfile_encoder_new ();
g_keyfile_encoder_set_section_name (G_KEYFILE_ENCODER (encoder), "Test");
- g_encoder_add_key_bool (encoder, "BoolValue", TRUE);
-
- buffer = g_encoder_write_to_bytes (encoder, &error);
- g_assert_no_error (error);
- g_assert (buffer != NULL);
-
+ buffer = encode_data (encoder);
g_object_unref (encoder);
- if (g_test_verbose ())
- g_print ("*** buffer (len: %d) = ***\n%s",
- (int) g_bytes_get_size (buffer),
- (const char *) g_bytes_get_data (buffer, NULL));
-
- encoder = g_keyfile_encoder_new ();
- g_keyfile_encoder_set_section_name (G_KEYFILE_ENCODER (encoder), "Test");
+ decoder = g_keyfile_encoder_new ();
+ g_keyfile_encoder_set_section_name (G_KEYFILE_ENCODER (decoder), "Test");
+ decode_data (decoder, buffer);
+ g_object_unref (decoder);
+ g_bytes_unref (buffer);
+}
- g_encoder_read_from_bytes (encoder, buffer, &error);
- g_assert_no_error (error);
+static void
+encoder_markup (void)
+{
+ GEncoder *encoder, *decoder;
+ GBytes *buffer;
- g_encoder_get_key_bool (encoder, "BoolValue", &res);
- g_assert (res);
+ encoder = g_markup_encoder_new ();
+ buffer = encode_data (encoder);
+ g_object_unref (encoder);
+ decoder = g_markup_encoder_new ();
+ decode_data (decoder, buffer);
+ g_object_unref (decoder);
g_bytes_unref (buffer);
- g_object_unref (encoder);
}
int
@@ -88,6 +102,7 @@ main (int argc, char *argv[])
g_test_add_func ("/encoder/binary", encoder_binary);
g_test_add_func ("/encoder/key-file", encoder_keyfile);
+ g_test_add_func ("/encoder/markup", encoder_markup);
return g_test_run ();
}