2003-09-01 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Wed, 3 Sep 2003 02:08:25 +0000 (02:08 +0000)
committerHavoc Pennington <hp@redhat.com>
Wed, 3 Sep 2003 02:08:25 +0000 (02:08 +0000)
* glib/dbus-gparser.c: implement

* glib/dbus-gobject.c: start implementing skeletons support

* configure.in: when disabling checks/assert, also define
G_DISABLE_ASSERT and G_DISABLE_CHECKS

12 files changed:
ChangeLog
configure.in
dbus/dbus-marshal.c
dbus/dbus-protocol.h
glib/Makefile.am
glib/dbus-gidl.c
glib/dbus-gidl.h
glib/dbus-glib.h
glib/dbus-gloader-expat.c [new file with mode: 0644]
glib/dbus-gobject.c [new file with mode: 0644]
glib/dbus-gparser.c
glib/dbus-gparser.h

index f57c964..d9557be 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2003-09-01  Havoc Pennington  <hp@pobox.com>
 
+       * glib/dbus-gparser.c: implement
+
+       * glib/dbus-gobject.c: start implementing skeletons support
+
+       * configure.in: when disabling checks/assert, also define
+       G_DISABLE_ASSERT and G_DISABLE_CHECKS
+
+2003-09-01  Havoc Pennington  <hp@pobox.com>
+
        * glib/Makefile.am: rearrange a bunch of files and get "make
        check" framework set up
 
index 3963a5d..1125ef6 100644 (file)
@@ -54,9 +54,11 @@ if test x$enable_verbose_mode = xyes; then
 fi
 if test x$enable_asserts = xno; then
     AC_DEFINE(DBUS_DISABLE_ASSERT,1,[Disable assertion checking])
+    AC_DEFINE(G_DISABLE_ASSERT,1,[Disable GLib assertion macros])
 fi
 if test x$enable_checks = xno; then
     AC_DEFINE(DBUS_DISABLE_CHECKS,1,[Disable public API sanity checking])
+    AC_DEFINE(G_DISABLE_CHECKS,1,[Disable GLib public API sanity checking])
 fi
 
 #### gcc warning flags
@@ -545,7 +547,7 @@ AC_SUBST(DBUS_TEST_CFLAGS)
 AC_SUBST(DBUS_TEST_LIBS)
 
 # Glib detection
-PKG_CHECK_MODULES(DBUS_GLIB, glib-2.0, have_glib=yes, have_glib=no)
+PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0, have_glib=yes, have_glib=no)
 PKG_CHECK_MODULES(DBUS_GLIB_THREADS, glib-2.0 gthread-2.0, have_glib_threads=yes, have_glib_threads=no)
 
 if test x$have_glib = xno ; then
@@ -570,6 +572,11 @@ AC_SUBST(DBUS_GLIB_CFLAGS)
 AC_SUBST(DBUS_GLIB_LIBS)
 AC_SUBST(DBUS_GLIB_THREADS_LIBS)
 
+DBUS_GLIB_TOOL_CFLAGS=$XML_CFLAGS
+DBUS_GLIB_TOOL_LIBS=$XML_LIBS
+AC_SUBST(DBUS_GLIB_TOOL_CFLAGS)
+AC_SUBST(DBUS_GLIB_TOOL_LIBS)
+
 # Qt detection
 have_qt=no
 AC_MSG_CHECKING([for qglobal.h])
index 5297cb6..c542ee8 100644 (file)
@@ -1473,7 +1473,8 @@ _dbus_demarshal_string_array (const DBusString   *str,
 #define VERBOSE_DECOMPOSE 0
 
 /**
- * Demarshals an object path.
+ * Demarshals an object path.  A path of just "/" is
+ * represented as an empty vector of strings.
  * 
  * @param str the string containing the data
  * @param byte_order the byte order
@@ -1556,7 +1557,6 @@ _dbus_demarshal_object_path (const DBusString *str,
       i = j;
     }
   _dbus_assert (i == len);
-  _dbus_assert (retval[0] != NULL);
   
   *path = retval;
   if (path_len)
index e56ab75..473a105 100644 (file)
@@ -87,6 +87,18 @@ extern "C" {
 #define DBUS_PATH_ORG_FREEDESKTOP_DBUS  "/org/freedesktop/DBus"
 #define DBUS_PATH_ORG_FREEDESKTOP_LOCAL "/org/freedesktop/Local"
   
+/* Interfaces, these #define don't do much other than
+ * catch typos at compile time
+ */
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS  "org.freedesktop.DBus"
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE "org.freedesktop.Introspectable"
+  
+/* This is a special interface whose methods can only be invoked
+ * by the local implementation (messages from remote apps aren't
+ * allowed to specify this interface).
+ */
+#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
+  
 /* Service owner flags */
 #define DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT 0x1
 #define DBUS_SERVICE_FLAG_REPLACE_EXISTING     0x2
@@ -101,17 +113,6 @@ extern "C" {
 #define DBUS_ACTIVATION_REPLY_ACTIVATED      0x0
 #define DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE 0x1
   
-/* Interfaces, these #define don't do much other than
- * catch typos at compile time
- */
-#define DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS  "org.freedesktop.DBus"
-
-/* This is a special interface whose methods can only be invoked
- * by the local implementation (messages from remote apps aren't
- * allowed to specify this interface).
- */
-#define DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL "org.freedesktop.Local"
-  
 #ifdef __cplusplus
 }
 #endif
index 3d67db2..f3b43ed 100644 (file)
@@ -1,4 +1,4 @@
-INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) -DDBUS_COMPILATION=1
+INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) -DDBUS_COMPILATION=1
 
 dbusincludedir=$(includedir)/dbus-1.0/dbus
 
@@ -10,6 +10,7 @@ dbusinclude_HEADERS=                          \
 
 libdbus_glib_1_la_SOURCES =                    \
        dbus-gmain.c                            \
+       dbus-gobject.c                          \
        dbus-gproxy.c                           \
        dbus-gtest.c                            \
        dbus-gtest.h                            \
@@ -23,12 +24,13 @@ dbus_glib_tool_SOURCES =                    \
        dbus-gidl.c                             \
        dbus-gidl.h                             \
        dbus-glib-tool.c                        \
+       dbus-gloader-expat.c                    \
        dbus-gparser.c                          \
        dbus-gparser.h                          \
        dbus-gtool-test.h
 
 
-dbus_glib_tool_LDADD= libdbus-glib-1.la $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la
+dbus_glib_tool_LDADD= $(DBUS_GLIB_TOOL_LIBS) libdbus-glib-1.la $(top_builddir)/dbus/libdbus-1.la
 
 if DBUS_BUILD_TESTS
 
index d64e95a..12468ab 100644 (file)
 
 #include "dbus-gidl.h"
 
+struct NodeInfo
+{
+  int refcount;
+  char *name;
+  GSList *interfaces;
+};
+
 struct InterfaceInfo
 {
   int refcount;
@@ -55,6 +62,20 @@ struct ArgInfo
 };
 
 static void
+free_interface_list (GSList **interfaces_p)
+{
+  GSList *tmp;
+  tmp = *interfaces_p;
+  while (tmp != NULL)
+    {
+      interface_info_unref (tmp->data);
+      tmp = tmp->next;
+    }
+  g_slist_free (*interfaces_p);
+  *interfaces_p = NULL;
+}
+
+static void
 free_method_list (GSList **methods_p)
 {
   GSList *tmp;
@@ -82,6 +103,59 @@ free_signal_list (GSList **signals_p)
   *signals_p = NULL;
 }
 
+NodeInfo*
+node_info_new (const char *name)
+{
+  NodeInfo *info;
+
+  /* name can be NULL */
+  
+  info = g_new0 (NodeInfo, 1);
+  info->refcount = 1;
+  info->name = g_strdup (name);
+
+  return info;
+}
+
+void
+node_info_ref (NodeInfo *info)
+{
+  info->refcount += 1;
+}
+
+void
+node_info_unref (NodeInfo *info)
+{
+  info->refcount -= 1;
+  if (info->refcount == 0)
+    {
+      free_interface_list (&info->interfaces);
+      g_free (info->name);
+      g_free (info);
+    }
+}
+
+const char*
+node_info_get_name (NodeInfo *info)
+{
+  return info->name;
+}
+
+GSList*
+node_info_get_interfaces (NodeInfo *info)
+{
+  return info->interfaces;
+}
+
+void
+node_info_add_interface (NodeInfo *info,
+                         InterfaceInfo    *interface)
+{
+  interface_info_ref (interface);
+  info->interfaces = g_slist_append (info->interfaces, interface);
+}
+
+
 InterfaceInfo*
 interface_info_new (const char *name)
 {
@@ -90,7 +164,7 @@ interface_info_new (const char *name)
   info = g_new0 (InterfaceInfo, 1);
   info->refcount = 1;
   info->name = g_strdup (name);
-  
+
   return info;
 }
 
@@ -113,6 +187,12 @@ interface_info_unref (InterfaceInfo *info)
     }
 }
 
+const char*
+interface_info_get_name (InterfaceInfo *info)
+{
+  return info->name;
+}
+
 GSList*
 interface_info_get_methods (InterfaceInfo *info)
 {
@@ -163,7 +243,7 @@ method_info_new (const char *name)
   info = g_new0 (MethodInfo, 1);
   info->refcount = 1;
   info->name = g_strdup (name);
-  
+
   return info;
 }
 
@@ -213,7 +293,7 @@ signal_info_new (const char *name)
   info = g_new0 (SignalInfo, 1);
   info->refcount = 1;
   info->name = g_strdup (name);
-  
+
   return info;
 }
 
@@ -264,10 +344,12 @@ arg_info_new (const char  *name,
 
   info = g_new0 (ArgInfo, 1);
   info->refcount = 1;
+
+  /* name can be NULL */
   info->name = g_strdup (name);
   info->direction = direction;
   info->type = type;
-  
+
   return info;
 }
 
index 68649cf..6e4e207 100644 (file)
@@ -29,6 +29,7 @@
 
 G_BEGIN_DECLS
 
+typedef struct NodeInfo      NodeInfo;
 typedef struct InterfaceInfo InterfaceInfo;
 typedef struct MethodInfo    MethodInfo;
 typedef struct SignalInfo    SignalInfo;
@@ -40,9 +41,18 @@ typedef enum
   ARG_OUT
 } ArgDirection;
 
+NodeInfo*      node_info_new              (const char    *name);
+void           node_info_ref              (NodeInfo      *info);
+void           node_info_unref            (NodeInfo      *info);
+const char*    node_info_get_name         (NodeInfo      *info);
+GSList*        node_info_get_interfaces   (NodeInfo      *info);
+void           node_info_add_interface    (NodeInfo      *info,
+                                           InterfaceInfo *interface);
+
 InterfaceInfo* interface_info_new         (const char    *name);
 void           interface_info_ref         (InterfaceInfo *info);
 void           interface_info_unref       (InterfaceInfo *info);
+const char*    interface_info_get_name    (InterfaceInfo *info);
 GSList*        interface_info_get_methods (InterfaceInfo *info);
 GSList*        interface_info_get_signals (InterfaceInfo *info);
 void           interface_info_add_method  (InterfaceInfo *info,
index 7ca2241..3a87dec 100644 (file)
@@ -24,7 +24,7 @@
 #define DBUS_GLIB_H
 
 #include <dbus/dbus.h>
-#include <glib.h>
+#include <glib-object.h>
 
 G_BEGIN_DECLS
 
@@ -36,6 +36,30 @@ void dbus_connection_setup_with_g_main (DBusConnection *connection,
 void dbus_server_setup_with_g_main     (DBusServer     *server,
                                        GMainContext   *context);
 
+typedef struct DBusGObjectInfo DBusGObjectInfo;
+typedef struct DBusGMethodInfo DBusGMethodInfo;
+
+struct DBusGMethodInfo
+{
+  GCallback                 function;
+  DBusHandleMessageFunction marshaller;
+  int                       data_offset;
+};
+
+struct DBusGObjectInfo
+{
+  const DBusGMethodInfo *infos;
+  const unsigned char *data;    
+  void *dbus_internal_padding1;
+  void *dbus_internal_padding2;
+};
+
+void dbus_gobject_class_install_info  (GObjectClass          *object_class,
+                                       const DBusGObjectInfo *info);
+void dbus_connection_register_gobject (DBusConnection        *connection,
+                                       const char            *at_path,
+                                       GObject               *object);
+
 #undef DBUS_INSIDE_DBUS_GLIB_H
 
 G_END_DECLS
diff --git a/glib/dbus-gloader-expat.c b/glib/dbus-gloader-expat.c
new file mode 100644 (file)
index 0000000..050d353
--- /dev/null
@@ -0,0 +1,254 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gloader-expat.c  expat XML loader
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "dbus-gparser.h"
+#include <expat.h>
+
+static void*
+expat_g_malloc (size_t sz)
+{
+  return g_malloc (sz);
+}
+
+static void*
+expat_g_realloc (void *mem, size_t sz)
+{
+  return g_realloc (mem, sz);
+}
+
+static XML_Memory_Handling_Suite memsuite =
+{
+  expat_g_malloc,
+  expat_g_realloc,
+  g_free
+};
+
+typedef struct
+{
+  Parser *parser;
+  const char *filename;
+  GString *content;
+  GError **error;
+  gboolean failed;
+} ExpatParseContext;
+
+static dbus_bool_t
+process_content (ExpatParseContext *context)
+{
+  if (context->failed)
+    return FALSE;
+
+  if (context->content->len > 0)
+    {
+      if (!parser_content (context->parser,
+                           context->content->str,
+                           context->content->len,
+                           context->error))
+        {
+          context->failed = TRUE;
+          return FALSE;
+        }
+      g_string_set_size (context->content, 0);
+    }
+
+  return TRUE;
+}
+
+static void
+expat_StartElementHandler (void            *userData,
+                           const XML_Char  *name,
+                           const XML_Char **atts)
+{
+  ExpatParseContext *context = userData;
+  int i;
+  char **names;
+  char **values;
+
+  /* Expat seems to suck and can't abort the parse if we
+   * throw an error. Expat 2.0 is supposed to fix this.
+   */
+  if (context->failed)
+    return;
+
+  if (!process_content (context))
+    return;
+
+  /* "atts" is key, value, key, value, NULL */
+  for (i = 0; atts[i] != NULL; ++i)
+    ; /* nothing */
+
+  g_assert (i % 2 == 0);
+  names = g_new0 (char *, i / 2 + 1);
+  values = g_new0 (char *, i / 2 + 1);
+
+  i = 0;
+  while (atts[i] != NULL)
+    {
+      g_assert (i % 2 == 0);
+      names [i / 2] = (char*) atts[i];
+      values[i / 2] = (char*) atts[i+1];
+
+      i += 2;
+    }
+
+  if (!parser_start_element (context->parser,
+                             name,
+                             (const char **) names,
+                             (const char **) values,
+                             context->error))
+    {
+      g_free (names);
+      g_free (values);
+      context->failed = TRUE;
+      return;
+    }
+
+  g_free (names);
+  g_free (values);
+}
+
+static void
+expat_EndElementHandler (void           *userData,
+                         const XML_Char *name)
+{
+  ExpatParseContext *context = userData;
+
+  if (!process_content (context))
+    return;
+
+  if (!parser_end_element (context->parser,
+                           name,
+                           context->error))
+    {
+      context->failed = TRUE;
+      return;
+    }
+}
+
+/* s is not 0 terminated. */
+static void
+expat_CharacterDataHandler (void           *userData,
+                            const XML_Char *s,
+                            int             len)
+{
+  ExpatParseContext *context = userData;
+
+  if (context->failed)
+    return;
+
+  g_string_append_len (context->content,
+                       s, len);
+}
+
+Parser*
+description_load_from_file (const char       *filename,
+                            GError          **error)
+{
+  char *contents;
+  gsize len;
+  Parser *parser;
+  
+  contents = NULL;
+  if (!g_file_get_contents (filename, &contents, &len, error))
+    return NULL;
+
+  parser = description_load_from_string (contents, len, error);
+  g_free (contents);
+
+  return parser;
+}
+
+Parser*
+description_load_from_string (const char  *str,
+                              int          len,
+                              GError     **error)
+{
+  XML_Parser expat;
+  ExpatParseContext context;
+  
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  expat = NULL;
+  context.parser = NULL;
+  context.error = error;
+  context.failed = FALSE;
+  
+  expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL);
+  if (expat == NULL)
+    g_error ("No memory to create XML parser\n");
+
+  context.parser = parser_new ();
+  context.content = g_string_new (NULL);
+  
+  XML_SetUserData (expat, &context);
+  XML_SetElementHandler (expat,
+                         expat_StartElementHandler,
+                         expat_EndElementHandler);
+  XML_SetCharacterDataHandler (expat,
+                               expat_CharacterDataHandler);
+  
+  if (!XML_Parse (expat, str, len, TRUE))
+    {
+      if (context.error != NULL &&
+          *context.error == NULL)
+        {
+            enum XML_Error e;
+
+            e = XML_GetErrorCode (expat);
+            if (e == XML_ERROR_NO_MEMORY)
+              g_error ("Not enough memory to parse XML document");
+            else
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_PARSE,
+                           "Error in D-BUS description XML, line %d, column %d: %s\n",
+                           XML_GetCurrentLineNumber (expat),
+                           XML_GetCurrentColumnNumber (expat),
+                           XML_ErrorString (e));
+        }
+      
+        goto failed;
+    }
+  
+  if (context.failed)
+    goto failed;
+
+  if (!parser_finished (context.parser, error))
+    goto failed;
+
+  XML_ParserFree (expat);
+  g_string_free (context.content, TRUE);
+
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);  
+  return context.parser;
+
+ failed:
+  g_return_val_if_fail (error == NULL || *error != NULL, NULL);
+
+  g_string_free (context.content, TRUE);
+  if (expat)
+    XML_ParserFree (expat);
+  if (context.parser)
+    parser_unref (context.parser);
+  return NULL;
+}
diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c
new file mode 100644 (file)
index 0000000..aa53265
--- /dev/null
@@ -0,0 +1,780 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gobject.c Exporting a GObject remotely
+ *
+ * Copyright (C) 2003 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <config.h>
+#include "dbus-glib.h"
+#include "dbus-gtest.h"
+#include <string.h>
+
+/**
+ * @addtogroup DBusGLibInternals
+ * @{
+ */
+
+static GStaticMutex info_hash_mutex = G_STATIC_MUTEX_INIT;
+static GHashTable *info_hash = NULL;
+
+static char*
+javacaps_to_uscore (const char *caps)
+{
+  const char *p;
+  GString *str;
+
+  str = g_string_new (NULL);
+  p = caps;
+  while (*p)
+    {
+      if (g_ascii_isupper (*p))
+        {
+          if (str->len > 0 &&
+              (str->len < 2 || str->str[str->len-2] != '_'))
+            g_string_append_c (str, '_');
+          g_string_append_c (str, g_ascii_tolower (*p));
+        }
+      else
+        {
+          g_string_append_c (str, *p);
+        }
+      ++p;
+    }
+
+  return g_string_free (str, FALSE);
+}
+
+static char*
+uscore_to_javacaps (const char *uscore)
+{
+  const char *p;
+  GString *str;
+  gboolean last_was_uscore;
+
+  last_was_uscore = TRUE;
+  
+  str = g_string_new (NULL);
+  p = uscore;
+  while (*p)
+    {
+      if (*p == '-' || *p == '_')
+        {
+          last_was_uscore = TRUE;
+        }
+      else
+        {
+          if (last_was_uscore)
+            {
+              g_string_append_c (str, g_ascii_toupper (*p));
+              last_was_uscore = FALSE;
+            }
+          else
+            g_string_append_c (str, *p);
+        }
+      ++p;
+    }
+
+  return g_string_free (str, FALSE);
+}
+
+static void
+gobject_unregister_function (DBusConnection  *connection,
+                             void            *user_data)
+{
+  GObject *object;
+
+  object = G_OBJECT (user_data);
+
+
+}
+
+static int
+gtype_to_dbus_type (GType type)
+{
+  switch (type)
+    {
+    case G_TYPE_CHAR:
+    case G_TYPE_UCHAR:
+      return DBUS_TYPE_BYTE;
+      
+    case G_TYPE_BOOLEAN:
+      return DBUS_TYPE_BOOLEAN;
+
+      /* long gets cut to 32 bits so the remote API is consistent
+       * on all architectures
+       */
+      
+    case G_TYPE_LONG:
+    case G_TYPE_INT:
+      return DBUS_TYPE_INT32;
+    case G_TYPE_ULONG:
+    case G_TYPE_UINT:
+      return DBUS_TYPE_UINT32;
+
+    case G_TYPE_INT64:
+      return DBUS_TYPE_INT64;
+
+    case G_TYPE_UINT64:
+      return DBUS_TYPE_UINT64;
+      
+    case G_TYPE_FLOAT:
+    case G_TYPE_DOUBLE:
+      return DBUS_TYPE_DOUBLE;
+
+    case G_TYPE_STRING:
+      return DBUS_TYPE_STRING;
+
+    default:
+      return DBUS_TYPE_INVALID;
+    }
+}
+
+static const char *
+dbus_type_to_string (int type)
+{
+  switch (type)
+    {
+    case DBUS_TYPE_INVALID:
+      return "invalid";
+    case DBUS_TYPE_NIL:
+      return "nil";
+    case DBUS_TYPE_BOOLEAN:
+      return "boolean";
+    case DBUS_TYPE_INT32:
+      return "int32";
+    case DBUS_TYPE_UINT32:
+      return "uint32";
+    case DBUS_TYPE_DOUBLE:
+      return "double";
+    case DBUS_TYPE_STRING:
+      return "string";
+    case DBUS_TYPE_NAMED:
+      return "named";
+    case DBUS_TYPE_ARRAY:
+      return "array";
+    case DBUS_TYPE_DICT:
+      return "dict";
+    default:
+      return "unknown";
+    }
+}
+
+static DBusHandlerResult
+handle_introspect (DBusConnection *connection,
+                   DBusMessage    *message,
+                   GObject        *object)
+{
+  GString *xml;
+  GParamSpec **specs;
+  unsigned int n_specs;
+  unsigned int i;
+  GType last_type;
+  DBusMessage *ret;
+  
+  xml = g_string_new (NULL);
+
+  g_string_append (xml, "<node>\n");
+
+  last_type = G_TYPE_INVALID;
+
+  specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object),
+                                          &n_specs);
+
+  i = 0;
+  while (i < n_specs)
+    {
+      GParamSpec *spec = specs[i];
+      gboolean can_set;
+      gboolean can_get;
+      char *s;
+      int dbus_type;
+      
+      dbus_type = gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
+      if (dbus_type == DBUS_TYPE_INVALID)
+        goto next;
+      
+      if (spec->owner_type != last_type)
+       {
+          if (last_type != G_TYPE_INVALID)
+            g_string_append (xml, "  </interface>\n");
+
+
+          /* FIXME what should the namespace on the interface be in
+           * general?  should people be able to set it for their
+           * objects?
+           */
+          
+          g_string_append (xml, "  <interface name=\"org.gtk.objects.");
+          g_string_append (xml, g_type_name (spec->owner_type));
+          g_string_append (xml, "\">\n");
+
+          last_type = spec->owner_type;
+       }
+
+      can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
+                    (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
+
+      can_get = (spec->flags & G_PARAM_READABLE) != 0;
+
+      s = uscore_to_javacaps (spec->name);
+      
+      if (can_set)
+        {
+          g_string_append (xml, "    <method name=\"set");
+          g_string_append (xml, s);
+          g_string_append (xml, "\">\n");
+          
+          g_string_append (xml, "      <arg type=\"");
+          g_string_append (xml, dbus_type_to_string (dbus_type));
+          g_string_append (xml, "\"/>\n");
+        }
+
+      if (can_get)
+        {
+          g_string_append (xml, "    <method name=\"get");
+          g_string_append (xml, s);
+          g_string_append (xml, "\">\n");
+          
+          g_string_append (xml, "      <arg type=\"");
+          g_string_append (xml, dbus_type_to_string (dbus_type));
+          g_string_append (xml, "\" direction=\"out\"/>\n");
+        }
+
+      g_free (s);
+
+    next:
+      ++i;
+    }
+
+  if (last_type != G_TYPE_INVALID)
+    g_string_append (xml, "  </interface>\n");
+
+  g_free (specs);
+
+  /* Close the XML, and send it to the requesting app */
+
+  g_string_append (xml, "</node>\n");
+
+  ret = dbus_message_new_method_return (message);
+  if (ret == NULL)
+    g_error ("out of memory");
+
+  dbus_message_append_args (message,
+                            DBUS_TYPE_STRING, xml->str,
+                            DBUS_TYPE_INVALID);
+
+  dbus_connection_send (connection, message, NULL);
+  dbus_message_unref (message);
+
+  g_string_free (xml, TRUE);
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusMessage*
+set_object_property (DBusConnection *connection,
+                     DBusMessage    *message,
+                     GObject        *object,
+                     GParamSpec     *pspec)
+{
+  GValue value;
+  DBusMessageIter iter;
+  int type;
+  gboolean can_set;
+  DBusMessage *ret;
+
+  dbus_message_iter_init (message, &iter);
+  type = dbus_message_get_type (message);
+
+  can_set = TRUE;
+  switch (type)
+    {
+    case DBUS_TYPE_BYTE:
+      {
+        unsigned char b;
+
+        b = dbus_message_iter_get_byte (&iter);
+
+        g_value_init (&value, G_TYPE_UCHAR);
+
+        g_value_set_uchar (&value, b);
+      }
+      break;
+    case DBUS_TYPE_BOOLEAN:
+      {
+        gboolean b;
+
+        b = dbus_message_iter_get_boolean (&iter);
+
+        g_value_init (&value, G_TYPE_BOOLEAN);
+
+        g_value_set_boolean (&value, b);
+      }
+      break;
+    case DBUS_TYPE_INT32:
+      {
+        gint32 i;
+
+        i = dbus_message_iter_get_int32 (&iter);
+
+        g_value_init (&value, G_TYPE_INT);
+
+        g_value_set_int (&value, i);
+      }
+      break;
+    case DBUS_TYPE_UINT32:
+      {
+        guint32 i;
+
+        i = dbus_message_iter_get_uint32 (&iter);
+
+        g_value_init (&value, G_TYPE_UINT);
+
+        g_value_set_uint (&value, i);
+      }
+      break;
+    case DBUS_TYPE_INT64:
+      {
+        gint64 i;
+
+        i = dbus_message_iter_get_int64 (&iter);
+
+        g_value_init (&value, G_TYPE_INT64);
+
+        g_value_set_int64 (&value, i);
+      }
+      break;
+    case DBUS_TYPE_UINT64:
+      {
+        guint64 i;
+
+        i = dbus_message_iter_get_uint64 (&iter);
+
+        g_value_init (&value, G_TYPE_UINT64);
+
+        g_value_set_uint64 (&value, i);
+      }
+      break;
+    case DBUS_TYPE_DOUBLE:
+      {
+        double d;
+
+        d = dbus_message_iter_get_double (&iter);
+
+        g_value_init (&value, G_TYPE_DOUBLE);
+
+        g_value_set_double (&value, d);
+      }
+      break;
+    case DBUS_TYPE_STRING:
+      {
+        char *s;
+
+        /* FIXME use a const string accessor */
+
+        s = dbus_message_iter_get_string (&iter);
+
+        g_value_init (&value, G_TYPE_STRING);
+
+        g_value_set_string (&value, s);
+
+        g_free (s);
+      }
+      break;
+
+      /* FIXME array and other types, especially byte array
+       * converted to G_TYPE_STRING
+       */
+
+    default:
+      can_set = FALSE;
+      break;
+    }
+
+  /* The g_object_set_property() will transform some types, e.g. it
+   * will let you use a uchar to set an int property etc. Note that
+   * any error in value range or value conversion will just
+   * g_warning(). These GObject skels are not for secure applications.
+   */
+
+  if (can_set)
+    {
+      g_object_set_property (object,
+                             pspec->name,
+                             &value);
+
+      ret = dbus_message_new_method_return (message);
+      if (ret == NULL)
+        g_error ("out of memory");
+
+      g_value_unset (&value);
+    }
+  else
+    {
+      ret = dbus_message_new_error (message,
+                                    DBUS_ERROR_INVALID_ARGS,
+                                    "Argument's D-BUS type can't be converted to a GType");
+      if (ret == NULL)
+        g_error ("out of memory");
+    }
+
+  return ret;
+}
+
+static DBusMessage*
+get_object_property (DBusConnection *connection,
+                     DBusMessage    *message,
+                     GObject        *object,
+                     GParamSpec     *pspec)
+{
+  GType value_type;
+  gboolean can_get;
+  DBusMessage *ret;
+  GValue value;
+  DBusMessageIter iter;
+
+  value_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+
+  ret = dbus_message_new_method_return (message);
+  if (ret == NULL)
+    g_error ("out of memory");
+
+  can_get = TRUE;
+  g_value_init (&value, value_type);
+  g_object_get_property (object, pspec->name, &value);
+
+  value_type = G_VALUE_TYPE (&value);
+
+  dbus_message_append_iter_init (message, &iter);
+  
+  switch (value_type)
+    {
+    case G_TYPE_CHAR:
+      dbus_message_iter_append_byte (&iter,
+                                     g_value_get_char (&value));
+      break;
+    case G_TYPE_UCHAR:
+      dbus_message_iter_append_byte (&iter,
+                                     g_value_get_uchar (&value));
+      break;
+    case G_TYPE_BOOLEAN:
+      dbus_message_iter_append_boolean (&iter,
+                                        g_value_get_boolean (&value));
+      break;
+    case G_TYPE_INT:
+      dbus_message_iter_append_int32 (&iter,
+                                      g_value_get_int (&value));
+      break;
+    case G_TYPE_UINT:
+      dbus_message_iter_append_uint32 (&iter,
+                                       g_value_get_uint (&value));
+      break;
+      /* long gets cut to 32 bits so the remote API is consistent
+       * on all architectures
+       */
+    case G_TYPE_LONG:
+      dbus_message_iter_append_int32 (&iter,
+                                      g_value_get_long (&value));
+      break;
+    case G_TYPE_ULONG:
+      dbus_message_iter_append_uint32 (&iter,
+                                       g_value_get_ulong (&value));
+      break;
+    case G_TYPE_INT64:
+      dbus_message_iter_append_int64 (&iter,
+                                      g_value_get_int64 (&value));
+      break;
+    case G_TYPE_UINT64:
+      dbus_message_iter_append_uint64 (&iter,
+                                       g_value_get_uint64 (&value));
+      break;
+    case G_TYPE_FLOAT:
+      dbus_message_iter_append_double (&iter,
+                                       g_value_get_float (&value));
+      break;
+    case G_TYPE_DOUBLE:
+      dbus_message_iter_append_double (&iter,
+                                       g_value_get_double (&value));
+      break;
+    case G_TYPE_STRING:
+      /* FIXME, the GValue string may not be valid UTF-8 */
+      dbus_message_iter_append_string (&iter,
+                                       g_value_get_string (&value));
+      break;
+    default:
+      can_get = FALSE;
+      break;
+    }
+
+  g_value_unset (&value);
+
+  if (!can_get)
+    {
+      dbus_message_unref (ret);
+      ret = dbus_message_new_error (message,
+                                    DBUS_ERROR_UNKNOWN_METHOD,
+                                    "Can't convert GType of object property to a D-BUS type");
+    }
+
+  return ret;
+}
+
+static DBusHandlerResult
+gobject_message_function (DBusConnection  *connection,
+                          DBusMessage     *message,
+                          void            *user_data)
+{
+  const DBusGObjectInfo *info;
+  GParamSpec *pspec;
+  GObject *object;
+  const char *member;
+  gboolean setter;
+  gboolean getter;
+  char *s;
+
+  object = G_OBJECT (user_data);
+
+  if (dbus_message_is_method_call (message,
+                                   DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
+                                   "Introspect"))
+    return handle_introspect (connection, message, object);
+
+  member = dbus_message_get_member (message);
+
+  /* Try the metainfo, which lets us invoke methods */
+
+  g_static_mutex_lock (&info_hash_mutex);
+  /* FIXME this needs to walk up the inheritance tree, not
+   * just look at the most-derived class
+   */
+  info = g_hash_table_lookup (info_hash,
+                              G_OBJECT_GET_CLASS (object));
+  g_static_mutex_unlock (&info_hash_mutex);
+
+  if (info != NULL)
+    {
+
+
+
+    }
+
+  /* If no metainfo, we can still do properties and signals
+   * via standard GLib introspection
+   */
+  setter = (member[0] == 's' && member[1] == 'e' && member[2] == 't');
+  getter = (member[0] == 'g' && member[1] == 'e' && member[2] == 't');
+
+  if (!(setter || getter))
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+  s = javacaps_to_uscore (&member[3]);
+
+  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
+                                        s);
+
+  g_free (s);
+
+  if (pspec != NULL)
+    {
+      DBusMessage *ret;
+
+      if (setter)
+        {
+          ret = set_object_property (connection, message,
+                                     object, pspec);
+        }
+      else if (getter)
+        {
+          ret = get_object_property (connection, message,
+                                     object, pspec);
+        }
+      else
+        {
+          g_assert_not_reached ();
+          ret = NULL;
+        }
+
+      g_assert (ret != NULL);
+
+      dbus_connection_send (connection, ret, NULL);
+      dbus_message_unref (ret);
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable gobject_dbus_vtable = {
+  gobject_unregister_function,
+  gobject_message_function,
+  NULL
+};
+
+/** @} */ /* end of internals */
+
+/**
+ * @addtogroup DBusGLib
+ * @{
+ */
+
+/**
+ * Install introspection information about the given object class
+ * sufficient to allow methods on the object to be invoked by name.
+ * The introspection information is normally generated by
+ * dbus-glib-tool, then this function is called in the
+ * class_init() for the object class.
+ *
+ * Once introspection information has been installed, instances of the
+ * object registered with dbus_connection_register_gobject() can have
+ * their methods invoked remotely.
+ *
+ * @param object_class class struct of the object
+ * @param info introspection data generated by dbus-glib-tool
+ */
+void
+dbus_gobject_class_install_info (GObjectClass          *object_class,
+                                 const DBusGObjectInfo *info)
+{
+  g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
+
+  g_static_mutex_lock (&info_hash_mutex);
+
+  if (info_hash == NULL)
+    {
+      info_hash = g_hash_table_new (NULL, NULL); /* direct hash */
+    }
+
+  g_hash_table_replace (info_hash, object_class, (void*) info);
+
+  g_static_mutex_unlock (&info_hash_mutex);
+}
+
+static char**
+split_path (const char *path)
+{
+  int len;
+  char **split;
+  int n_components;
+  int i, j, comp;
+
+  len = strlen (path);
+
+  n_components = 0;
+  i = 0;
+  while (i < len)
+    {
+      if (path[i] == '/')
+        n_components += 1;
+      ++i;
+    }
+
+  split = g_new0 (char*, n_components + 1);
+
+  comp = 0;
+  i = 0;
+  while (i < len)
+    {
+      if (path[i] == '/')
+        ++i;
+      j = i;
+
+      while (j < len && path[j] != '/')
+        ++j;
+
+      /* Now [i, j) is the path component */
+      g_assert (i < j);
+      g_assert (path[i] != '/');
+      g_assert (j == len || path[j] == '/');
+
+      split[comp] = g_strndup (&path[i], j - i + 1);
+
+      split[comp][j-i] = '\0';
+
+      ++comp;
+      i = j;
+    }
+  g_assert (i == len);
+
+  return split;
+}
+
+/**
+ * Registers a GObject at the given path. Properties, methods, and signals
+ * of the object can then be accessed remotely. Methods are only available
+ * if method introspection data has been added to the object's class
+ * with g_object_class_install_info().
+ *
+ * The registration will be cancelled if either the DBusConnection or
+ * the GObject gets finalized.
+ *
+ * @param connection the D-BUS connection
+ * @param at_path the path where the object will live (the object's name)
+ * @param object the object
+ */
+void
+dbus_connection_register_gobject (DBusConnection        *connection,
+                                  const char            *at_path,
+                                  GObject               *object)
+{
+  char **split;
+
+  g_return_if_fail (connection != NULL);
+  g_return_if_fail (at_path != NULL);
+  g_return_if_fail (G_IS_OBJECT (object));
+
+  split = split_path (at_path);
+
+  if (!dbus_connection_register_object_path (connection,
+                                             (const char**) split,
+                                             &gobject_dbus_vtable,
+                                             object))
+    g_error ("Failed to register GObject with DBusConnection");
+
+  g_strfreev (split);
+
+  /* FIXME set up memory management (so we break the
+   * registration if object or connection vanishes)
+   */
+}
+
+/** @} */ /* end of public API */
+
+#ifdef DBUS_BUILD_TESTS
+
+/**
+ * @ingroup DBusGLibInternals
+ * Unit test for GLib GObject integration ("skeletons")
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_gobject_test (const char *test_data_dir)
+{
+  static struct { const char *javacaps; const char *uscore; } name_pairs[] = {
+    { "setFoo", "set_foo" },
+    { "foo", "foo" },
+    { "getFooBar", "get_foo_bar" },
+    { "Hello", "hello" },
+    { "frobateUIHandler", "frobate_ui_handler" }
+  };
+
+  return TRUE;
+}
+
+#endif /* DBUS_BUILD_TESTS */
index 6fea4ab..c2b54d3 100644 (file)
  */
 #include "dbus-gparser.h"
 #include "dbus-gidl.h"
+#include <string.h>
+
+#include <libintl.h>
+#define _(x) gettext ((x))
+#define N_(x) x
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+typedef struct
+{
+  const char  *name;
+  const char **retloc;
+} LocateAttr;
+
+static gboolean
+locate_attributes (const char  *element_name,
+                   const char **attribute_names,
+                   const char **attribute_values,
+                   GError     **error,
+                   const char  *first_attribute_name,
+                   const char **first_attribute_retloc,
+                   ...)
+{
+  va_list args;
+  const char *name;
+  const char **retloc;
+  int n_attrs;
+#define MAX_ATTRS 24
+  LocateAttr attrs[MAX_ATTRS];
+  gboolean retval;
+  int i;
+
+  g_return_val_if_fail (first_attribute_name != NULL, FALSE);
+  g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
+
+  retval = TRUE;
+
+  n_attrs = 1;
+  attrs[0].name = first_attribute_name;
+  attrs[0].retloc = first_attribute_retloc;
+  *first_attribute_retloc = NULL;
+  
+  va_start (args, first_attribute_retloc);
+
+  name = va_arg (args, const char*);
+  retloc = va_arg (args, const char**);
+
+  while (name != NULL)
+    {
+      g_return_val_if_fail (retloc != NULL, FALSE);
+
+      g_assert (n_attrs < MAX_ATTRS);
+      
+      attrs[n_attrs].name = name;
+      attrs[n_attrs].retloc = retloc;
+      n_attrs += 1;
+      *retloc = NULL;      
+
+      name = va_arg (args, const char*);
+      retloc = va_arg (args, const char**);
+    }
+
+  va_end (args);
+
+  if (!retval)
+    return retval;
+
+  i = 0;
+  while (attribute_names[i])
+    {
+      int j;
+      gboolean found;
+
+      found = FALSE;
+      j = 0;
+      while (j < n_attrs)
+        {
+          if (strcmp (attrs[j].name, attribute_names[i]) == 0)
+            {
+              retloc = attrs[j].retloc;
+
+              if (*retloc != NULL)
+                {
+                  g_set_error (error,
+                               G_MARKUP_ERROR,
+                               G_MARKUP_ERROR_PARSE,
+                               _("Attribute \"%s\" repeated twice on the same <%s> element"),
+                               attrs[j].name, element_name);
+                  retval = FALSE;
+                  goto out;
+                }
+
+              *retloc = attribute_values[i];
+              found = TRUE;
+            }
+
+          ++j;
+        }
+
+      if (!found)
+        {
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_PARSE,
+                       _("Attribute \"%s\" is invalid on <%s> element in this context"),
+                       attribute_names[i], element_name);
+          retval = FALSE;
+          goto out;
+        }
+
+      ++i;
+    }
+
+ out:
+  return retval;
+}
+
+static gboolean
+check_no_attributes (const char  *element_name,
+                     const char **attribute_names,
+                     const char **attribute_values,
+                     GError     **error)
+{
+  if (attribute_names[0] != NULL)
+    {
+      g_set_error (error,
+                   G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Attribute \"%s\" is invalid on <%s> element in this context"),
+                   attribute_names[0], element_name);
+      return FALSE;
+    }
+
+  return TRUE;
+}
 
 struct Parser
 {
   int refcount;
 
+  NodeInfo *result; /* Filled in when we pop the last node */
+  GSList *node_stack;
+  InterfaceInfo *interface;
+  MethodInfo *method;
+  SignalInfo *signal;
+  ArgInfo *arg;
 };
 
 Parser*
@@ -53,7 +194,8 @@ parser_unref (Parser *parser)
   parser->refcount -= 1;
   if (parser->refcount == 0)
     {
-      
+      if (parser->result)
+        node_info_unref (parser->result);
 
       g_free (parser);
     }
@@ -64,11 +206,12 @@ parser_check_doctype (Parser      *parser,
                       const char  *doctype,
                       GError     **error)
 {
-  g_return_val_if_fail (error == NULL || *error == NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
   
   if (strcmp (doctype, "dbus_description") != 0)
     {
       g_set_error (error,
+                   G_MARKUP_ERROR,
                    G_MARKUP_ERROR_PARSE,
                    "D-BUS description file has the wrong document type %s, use dbus_description",
                    doctype);
@@ -78,6 +221,324 @@ parser_check_doctype (Parser      *parser,
     return TRUE;
 }
 
+static gboolean
+parse_node (Parser      *parser,
+            const char  *element_name,
+            const char **attribute_names,
+            const char **attribute_values,
+            GError     **error)
+{
+  const char *name;
+  NodeInfo *node;
+  
+  if (parser->interface ||
+      parser->method ||
+      parser->signal ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put a <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  /* Only the root node can have no name */
+  if (parser->node_stack != NULL && name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  node = node_info_new (name);
+  parser->node_stack = g_slist_prepend (parser->node_stack,
+                                        node);
+  
+  return TRUE;
+}
+
+static gboolean
+parse_interface (Parser      *parser,
+                 const char  *element_name,
+                 const char **attribute_names,
+                 const char **attribute_values,
+                 GError     **error)
+{
+  const char *name;
+  InterfaceInfo *iface;
+  NodeInfo *top;
+  
+  if (parser->interface ||
+      parser->method ||
+      parser->signal ||
+      parser->arg ||
+      (parser->node_stack == NULL))
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put a <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  top = parser->node_stack->data;
+  
+  iface = interface_info_new (name);
+  node_info_add_interface (top, iface);
+  interface_info_unref (iface);
+
+  parser->interface = iface;
+  
+  return TRUE;
+}
+
+static gboolean
+parse_method (Parser      *parser,
+              const char  *element_name,
+              const char **attribute_names,
+              const char **attribute_values,
+              GError     **error)
+{
+  const char *name;
+  MethodInfo *method;
+  NodeInfo *top;
+  
+  if (parser->interface == NULL ||
+      parser->node_stack == NULL ||
+      parser->method ||
+      parser->signal ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put a <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  top = parser->node_stack->data;
+  
+  method = method_info_new (name);
+  interface_info_add_method (parser->interface, method);
+  method_info_unref (method);
+
+  parser->method = method;
+  
+  return TRUE;
+}
+
+static gboolean
+parse_signal (Parser      *parser,
+              const char  *element_name,
+              const char **attribute_names,
+              const char **attribute_values,
+              GError     **error)
+{
+  const char *name;
+  SignalInfo *signal;
+  NodeInfo *top;
+  
+  if (parser->interface == NULL ||
+      parser->node_stack == NULL ||
+      parser->signal ||
+      parser->signal ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put a <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          NULL))
+    return FALSE;
+
+  if (name == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "name", element_name);
+      return FALSE;
+    }
+
+  top = parser->node_stack->data;
+  
+  signal = signal_info_new (name);
+  interface_info_add_signal (parser->interface, signal);
+  signal_info_unref (signal);
+
+  parser->signal = signal;
+  
+  return TRUE;
+}
+
+static int
+basic_type_from_string (const char *str)
+{
+  if (strcmp (str, "string") == 0)
+    return DBUS_TYPE_STRING;
+  else if (strcmp (str, "int32") == 0)
+    return DBUS_TYPE_INT32;
+  else if (strcmp (str, "uint32") == 0)
+    return DBUS_TYPE_UINT32;
+  else if (strcmp (str, "int64") == 0)
+    return DBUS_TYPE_INT64;
+  else if (strcmp (str, "uint64") == 0)
+    return DBUS_TYPE_UINT64;
+  else if (strcmp (str, "double") == 0)
+    return DBUS_TYPE_DOUBLE;
+  else if (strcmp (str, "byte") == 0)
+    return DBUS_TYPE_BYTE;
+  else if (strcmp (str, "boolean") == 0)
+    return DBUS_TYPE_BOOLEAN;
+  else if (strcmp (str, "byte") == 0)
+    return DBUS_TYPE_BYTE;
+  else if (strcmp (str, "object") == 0)
+    return DBUS_TYPE_OBJECT_PATH;
+  else
+    return DBUS_TYPE_INVALID;
+}
+
+static int
+type_from_string (const char *str)
+{
+  return basic_type_from_string (str);
+}
+
+static gboolean
+parse_arg (Parser      *parser,
+           const char  *element_name,
+           const char **attribute_names,
+           const char **attribute_values,
+           GError     **error)
+{
+  const char *name;
+  const char *type;
+  const char *direction;
+  ArgDirection dir;
+  int t;
+  ArgInfo *arg;
+  
+  if (!(parser->method || parser->signal) ||
+      parser->node_stack == NULL ||
+      parser->arg)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Can't put a <%s> element here"),
+                   element_name);
+      return FALSE;      
+    }
+
+  name = NULL;
+  if (!locate_attributes (element_name, attribute_names,
+                          attribute_values, error,
+                          "name", &name,
+                          "type", &type,
+                          "direction", &direction,
+                          NULL))
+    return FALSE;
+
+  /* name can be null for args */
+  
+  if (type == NULL)
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute required on <%s> element "),
+                   "type", element_name);
+      return FALSE;
+    }
+
+  if (direction == NULL)
+    {
+      /* methods default to in, signal to out */
+      if (parser->method)
+        direction = "in";
+      else if (parser->signal)
+        direction = "out";
+      else
+        g_assert_not_reached ();
+    }
+
+  if (strcmp (direction, "in") == 0)
+    dir = ARG_IN;
+  else if (strcmp (direction, "out") == 0)
+    dir = ARG_OUT;
+  else
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("\"%s\" attribute on <%s> has value \"in\" or \"out\""),
+                   "direction", element_name);
+      return FALSE;
+    }
+
+  t = type_from_string (type);
+  
+  arg = arg_info_new (name, dir, t);
+  if (parser->method)
+    method_info_add_arg (parser->method, arg);
+  else if (parser->signal)
+    signal_info_add_arg (parser->signal, arg);
+  else
+    g_assert_not_reached ();
+
+  arg_info_unref (arg);
+
+  parser->arg = arg;
+  
+  return TRUE;
+}
+
 gboolean
 parser_start_element (Parser      *parser,
                       const char  *element_name,
@@ -85,8 +546,46 @@ parser_start_element (Parser      *parser,
                       const char **attribute_values,
                       GError     **error)
 {
-  g_return_val_if_fail (error == NULL || *error == NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
+  if (ELEMENT_IS ("node"))
+    {
+      if (!parse_node (parser, element_name, attribute_names,
+                       attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("interface"))
+    {
+      if (!parse_interface (parser, element_name, attribute_names,
+                            attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("method"))
+    {
+      if (!parse_method (parser, element_name, attribute_names,
+                         attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("signal"))
+    {
+      if (!parse_signal (parser, element_name, attribute_names,
+                         attribute_values, error))
+        return FALSE;
+    }
+  else if (ELEMENT_IS ("arg"))
+    {
+      if (!parse_arg (parser, element_name, attribute_names,
+                      attribute_values, error))
+        return FALSE;
+    }
+  else
+    {
+      g_set_error (error, G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_PARSE,
+                   _("Element <%s> not recognized"),
+                   element_name);
+    }
+  
   return TRUE;
 }
 
@@ -95,8 +594,40 @@ parser_end_element (Parser      *parser,
                     const char  *element_name,
                     GError     **error)
 {
-  g_return_val_if_fail (error == NULL || *error == NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (ELEMENT_IS ("interface"))
+    {
+      parser->interface = NULL;
+    }
+  else if (ELEMENT_IS ("method"))
+    {
+      parser->method = NULL;
+    }
+  else if (ELEMENT_IS ("signal"))
+    {
+      parser->signal = NULL;
+    }
+  else if (ELEMENT_IS ("arg"))
+    {
+      parser->arg = NULL;
+    }
+  else if (ELEMENT_IS ("node"))
+    {
+      NodeInfo *top;
 
+      g_assert (parser->node_stack != NULL);
+      top = parser->node_stack->data;
+
+      parser->node_stack = g_slist_remove (parser->node_stack,
+                                           top);
+
+      if (parser->node_stack == NULL)
+        parser->result = top; /* We are done, store the result */      
+    }
+  else
+    g_assert_not_reached (); /* should have had an error on start_element */
+  
   return TRUE;
 }
 
@@ -106,7 +637,7 @@ parser_content (Parser      *parser,
                 int          len,
                 GError     **error)
 {
-  g_return_val_if_fail (error == NULL || *error == NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   return TRUE;
 }
@@ -115,7 +646,13 @@ gboolean
 parser_finished (Parser      *parser,
                  GError     **error)
 {
-  g_return_val_if_fail (error == NULL || *error == NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   return TRUE;
 }
+
+NodeInfo*
+parser_get_nodes (Parser *parser)
+{
+  return parser->result;
+}
index 01339fb..3e87165 100644 (file)
 
 #include <dbus/dbus.h>
 #include <glib.h>
+#include "dbus-gidl.h"
 
 G_BEGIN_DECLS
 
-
 typedef struct Parser Parser;
 
 Parser*  parser_new           (void);
@@ -52,7 +52,13 @@ gboolean parser_content       (Parser      *parser,
 gboolean parser_finished      (Parser      *parser,
                                GError     **error);
 
+Parser* description_load_from_file   (const char  *filename,
+                                      GError     **error);
+Parser* description_load_from_string (const char  *str,
+                                      int          len,
+                                      GError     **error);
 
+NodeInfo* parser_get_nodes (Parser *parser);
 
 G_END_DECLS