[glib/wip/serializable] Add GMarkupEncoder



commit a324690d0679eca8fe67d29f69de85fc455e4941
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Sun May 26 14:39:08 2013 +0100

    Add GMarkupEncoder
    
    An encoder and decoder that uses XML (parseable by GMarkup) as the
    intermediate representation of its data.

 gio/Makefile.am      |    2 +
 gio/gio.h            |    1 +
 gio/giotypes.h       |    1 +
 gio/gmarkupencoder.c |  437 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gio/gmarkupencoder.h |   49 ++++++
 gio/tests/encoder.c  |   85 ++++++----
 6 files changed, 540 insertions(+), 35 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index bbf57f1..6f58683 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 ac4d77a..1f345bf 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 1b4c2b2..f9c35cf 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 0000000..9da2348
--- /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 0000000..2c723bb
--- /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 698f23b..0454011 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 ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]