[yelp/yelp-3-0] [yelp-docbook-document] Adding DocBook support into libyelp



commit 2311a1568af07235722302f9a1936a8b05699cf4
Author: Shaun McCance <shaunm gnome org>
Date:   Sun Nov 15 16:36:27 2009 -0600

    [yelp-docbook-document] Adding DocBook support into libyelp

 libyelp/Makefile.am             |    2 +
 libyelp/yelp-docbook-document.c |  803 +++++++++++++++++++++++++++++++++++++++
 libyelp/yelp-docbook-document.h |   51 +++
 libyelp/yelp-document.c         |  213 +----------
 libyelp/yelp-transform.c        |   52 ++-
 src/yelp-docbook.c              |  788 --------------------------------------
 src/yelp-docbook.h              |   53 ---
 stylesheets/db2html.xsl.in      |    3 +-
 8 files changed, 897 insertions(+), 1068 deletions(-)
---
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index 6ab86f1..ed79d0c 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -3,6 +3,7 @@ lib_LTLIBRARIES = libyelp.la
 libyelp_la_SOURCES =				\
 	yelp-debug.c				\
 	yelp-error.c				\
+	yelp-docbook-document.c			\
 	yelp-document.c				\
 	yelp-location-entry.c			\
 	yelp-mallard-document.c			\
@@ -22,6 +23,7 @@ libyelp_la_LIBADD =				\
 	$(YELP_LIBS)
 
 libyelp_headers =				\
+	yelp-docbook-document.h			\
 	yelp-document.h				\
 	yelp-location-entry.h			\
 	yelp-mallard-document.h			\
diff --git a/libyelp/yelp-docbook-document.c b/libyelp/yelp-docbook-document.c
new file mode 100644
index 0000000..028646e
--- /dev/null
+++ b/libyelp/yelp-docbook-document.c
@@ -0,0 +1,803 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xinclude.h>
+
+#include "yelp-docbook-document.h"
+#include "yelp-error.h"
+#include "yelp-settings.h"
+#include "yelp-transform.h"
+#include "yelp-debug.h"
+
+#define STYLESHEET DATADIR"/yelp/xslt/db2html.xsl"
+
+typedef enum {
+    DOCBOOK_STATE_BLANK,   /* Brand new, run transform as needed */
+    DOCBOOK_STATE_PARSING, /* Parsing/transforming document, please wait */
+    DOCBOOK_STATE_PARSED,  /* All done, if we ain't got it, it ain't here */
+    DOCBOOK_STATE_STOP     /* Stop everything now, object to be disposed */
+} DocbookState;
+
+enum {
+    DOCBOOK_COLUMN_ID,
+    DOCBOOK_COLUMN_TITLE
+};
+
+static void           yelp_docbook_document_class_init      (YelpDocbookDocumentClass *klass);
+static void           yelp_docbook_document_init            (YelpDocbookDocument      *docbook);
+static void           yelp_docbook_document_dispose         (GObject                  *object);
+static void           yelp_docbook_document_finalize        (GObject                  *object);
+
+static gboolean       docbook_request_page      (YelpDocument         *document,
+                                                 const gchar          *page_id,
+                                                 GCancellable         *cancellable,
+                                                 YelpDocumentCallback  callback,
+                                                 gpointer              user_data);
+
+static void           docbook_process           (YelpDocbookDocument  *docbook);
+static void           docbook_disconnect        (YelpDocbookDocument  *docbook);
+
+static void           docbook_walk              (YelpDocbookDocument  *docbook);
+static gboolean       docbook_walk_chunkQ       (YelpDocbookDocument  *docbook);
+static gboolean       docbook_walk_divisionQ    (YelpDocbookDocument  *docbook);
+static gchar *        docbook_walk_get_title    (YelpDocbookDocument  *docbook);
+
+static void           transform_chunk_ready     (YelpTransform        *transform,
+                                                 gchar                *chunk_id,
+                                                 YelpDocbookDocument  *docbook);
+static void           transform_finished        (YelpTransform        *transform,
+                                                 YelpDocbookDocument  *docbook);
+static void           transform_error           (YelpTransform        *transform,
+                                                 YelpDocbookDocument  *docbook);
+static void           transform_finalized       (YelpDocbookDocument  *docbook,
+                                                 gpointer              transform);
+
+/* FIXME */
+#if 0
+/* static gpointer       docbook_get_sections    (YelpDocument        *document); */
+#endif
+
+G_DEFINE_TYPE (YelpDocbookDocument, yelp_docbook_document, YELP_TYPE_DOCUMENT);
+#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocumentPrivate))
+
+typedef struct _YelpDocbookDocumentPrivate  YelpDocbookDocumentPrivate;
+struct _YelpDocbookDocumentPrivate {
+    YelpUri       *uri;
+
+    DocbookState   state;
+
+    GMutex        *mutex;
+    GThread       *thread;
+
+    gboolean       process_running;
+    gboolean       transform_running;
+
+    YelpTransform *transform;
+    guint          chunk_ready;
+    guint          finished;
+    guint          error;
+
+    /* FIXME: all */
+    GtkTreeModel  *sections;
+    GtkTreeIter   *sections_iter; /* On the stack, do not free */
+
+    xmlDocPtr     xmldoc;
+    xmlNodePtr    xmlcur;
+    gint          max_depth;
+    gint          cur_depth;
+    gchar        *cur_page_id;
+    gchar        *cur_prev_id;
+};
+
+/******************************************************************************/
+
+static void
+yelp_docbook_document_class_init (YelpDocbookDocumentClass *klass)
+{
+    GObjectClass      *object_class   = G_OBJECT_CLASS (klass);
+    YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
+
+    object_class->dispose = yelp_docbook_document_dispose;
+    object_class->finalize = yelp_docbook_document_finalize;
+
+    document_class->request_page = docbook_request_page;
+    /*document_class->get_sections = docbook_get_sections;*/
+
+    g_type_class_add_private (klass, sizeof (YelpDocbookDocumentPrivate));
+}
+
+static void
+yelp_docbook_document_init (YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+
+    priv->sections = NULL;
+
+    priv->state = DOCBOOK_STATE_BLANK;
+
+    priv->mutex = g_mutex_new ();
+}
+
+static void
+yelp_docbook_document_dispose (GObject *object)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
+
+    g_object_unref (priv->uri);
+
+    g_object_unref (priv->sections);
+
+    G_OBJECT_CLASS (yelp_docbook_document_parent_class)->dispose (object);
+}
+
+static void
+yelp_docbook_document_finalize (GObject *object)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (object);
+
+    if (priv->xmldoc)
+        xmlFreeDoc (priv->xmldoc);
+
+    g_free (priv->cur_page_id);
+    g_free (priv->cur_prev_id);
+
+    g_mutex_free (priv->mutex);
+
+    G_OBJECT_CLASS (yelp_docbook_document_parent_class)->finalize (object);
+}
+
+/******************************************************************************/
+
+YelpDocument *
+yelp_docbook_document_new (YelpUri *uri)
+{
+    YelpDocbookDocument *docbook;
+    YelpDocbookDocumentPrivate *priv;
+
+    g_return_val_if_fail (uri != NULL, NULL);
+
+    docbook = (YelpDocbookDocument *) g_object_new (YELP_TYPE_DOCBOOK_DOCUMENT, NULL);
+    priv = GET_PRIV (docbook);
+
+    priv->uri = g_object_ref (uri);
+
+    yelp_document_set_page_id (YELP_DOCUMENT (docbook), "//about", "//about");
+
+    priv->sections =
+        GTK_TREE_MODEL (gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING));
+
+    return (YelpDocument *) docbook;
+}
+
+/******************************************************************************/
+
+/** YelpDocument **************************************************************/
+
+/* static gpointer */
+/* docbook_get_sections (YelpDocument *document) */
+/* { */
+/*     YelpDocbook *db = (YelpDocbook *) document; */
+    
+/*     return (gpointer) (db->priv->sections); */
+/* } */
+
+static gboolean
+docbook_request_page (YelpDocument         *document,
+                      const gchar          *page_id,
+                      GCancellable         *cancellable,
+                      YelpDocumentCallback  callback,
+                      gpointer              user_data)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (document);
+    gchar *docuri;
+    GError *error;
+    gboolean handled;
+
+    debug_print (DB_FUNCTION, "entering\n");
+    debug_print (DB_ARG, "    page_id=\"%s\"\n", page_id);
+
+    if (page_id == NULL)
+        page_id = "//index";
+
+    handled =
+        YELP_DOCUMENT_CLASS (yelp_docbook_document_parent_class)->request_page (document,
+                                                                                page_id,
+                                                                                cancellable,
+                                                                                callback,
+                                                                                user_data);
+    if (handled) {
+        return;
+    }
+
+    g_mutex_lock (priv->mutex);
+
+    switch (priv->state) {
+    case DOCBOOK_STATE_BLANK:
+        priv->state = DOCBOOK_STATE_PARSING;
+        priv->process_running = TRUE;
+        g_object_ref (document);
+        priv->thread = g_thread_create ((GThreadFunc) docbook_process,
+                                        document, FALSE, NULL);
+        break;
+    case DOCBOOK_STATE_PARSING:
+        break;
+    case DOCBOOK_STATE_PARSED:
+    case DOCBOOK_STATE_STOP:
+        docuri = yelp_uri_get_document_uri (priv->uri);
+        error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+                             _("The page â??%sâ?? was not found in the document â??%sâ??."),
+                             page_id, docuri);
+        g_free (docuri);
+        yelp_document_signal (document, page_id,
+                              YELP_DOCUMENT_SIGNAL_ERROR,
+                              error);
+        g_error_free (error);
+        break;
+    }
+
+    g_mutex_unlock (priv->mutex);
+}
+
+/******************************************************************************/
+
+static void
+docbook_process (YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    YelpDocument *document = YELP_DOCUMENT (docbook);
+    GFile *file = NULL;
+    gchar *filepath = NULL;
+    xmlDocPtr xmldoc = NULL;
+    xmlChar *id = NULL;
+    xmlParserCtxtPtr parserCtxt = NULL;
+    GError *error;
+    gint  params_i = 0;
+    gchar **params = NULL;
+
+    debug_print (DB_FUNCTION, "entering\n");
+
+    file = yelp_uri_get_file (priv->uri);
+    if (file == NULL) {
+        error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+                             _("The file does not exist."));
+        yelp_document_error_pending (document, error);
+        g_error_free (error);
+        goto done;
+    }
+
+    filepath = g_file_get_path (file);
+    g_object_unref (file);
+    if (!g_file_test (filepath, G_FILE_TEST_IS_REGULAR)) {
+        error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+                             _("The file â??%sâ?? does not exist."),
+                             filepath);
+        yelp_document_error_pending (document, error);
+        g_error_free (error);
+        goto done;
+    }
+
+    parserCtxt = xmlNewParserCtxt ();
+    xmldoc = xmlCtxtReadFile (parserCtxt,
+                              filepath, NULL,
+                              XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+                              XML_PARSE_NOENT   | XML_PARSE_NONET   );
+
+    if (xmldoc == NULL) {
+        error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING,
+                             _("The file â??%sâ?? could not be parsed because it is"
+                               " not a well-formed XML document."),
+                             filepath);
+        yelp_document_error_pending (document, error);
+        g_error_free (error);
+        goto done;
+    }
+
+    if (xmlXIncludeProcessFlags (xmldoc,
+                                 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
+                                 XML_PARSE_NOENT   | XML_PARSE_NONET   )
+        < 0) {
+        error = g_error_new (YELP_ERROR, YELP_ERROR_PROCESSING, 
+                             _("The file â??%sâ?? could not be parsed because"
+                               " one or more of its included files is not"
+                               " a well-formed XML document."),
+                             filepath);
+        yelp_document_error_pending (document, error);
+        g_error_free (error);
+        goto done;
+    }
+
+    g_mutex_lock (priv->mutex);
+    if (!xmlStrcmp (xmlDocGetRootElement (xmldoc)->name, BAD_CAST "book"))
+        priv->max_depth = 2;
+    else
+        priv->max_depth = 1;
+
+    priv->xmldoc = xmldoc;
+    priv->xmlcur = xmlDocGetRootElement (xmldoc);
+
+    id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+    if (id) {
+        yelp_document_set_page_id (document, NULL, (gchar *) id);
+        yelp_document_set_page_id (document, "//index", (gchar *) id);
+        yelp_document_set_prev_id (document, (gchar *) id, "//about");
+        yelp_document_set_next_id (document, "//about", (gchar *) id);
+    }
+    else {
+        yelp_document_set_page_id (document, NULL, "//index");
+        yelp_document_set_prev_id (document, "//index", "//about");
+        yelp_document_set_next_id (document, "//about", "//index");
+        /* add the id attribute to the root element with value "index"
+         * so when we try to load the document later, it doesn't fail */
+        xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST "//index");
+    }
+    g_mutex_unlock (priv->mutex);
+
+    g_mutex_lock (priv->mutex);
+    if (priv->state == DOCBOOK_STATE_STOP) {
+        g_mutex_unlock (priv->mutex);
+        goto done;
+    }
+    g_mutex_unlock (priv->mutex);
+
+    docbook_walk (docbook);
+
+    g_mutex_lock (priv->mutex);
+    if (priv->state == DOCBOOK_STATE_STOP) {
+        g_mutex_unlock (priv->mutex);
+        goto done;
+    }
+
+    priv->transform = yelp_transform_new (STYLESHEET);
+    priv->chunk_ready =
+        g_signal_connect (priv->transform, "chunk-ready",
+                          (GCallback) transform_chunk_ready,
+                          docbook);
+    priv->finished =
+        g_signal_connect (priv->transform, "finished",
+                          (GCallback) transform_finished,
+                          docbook);
+    priv->error =
+        g_signal_connect (priv->transform, "error",
+                          (GCallback) transform_error,
+                          docbook);
+
+    params = yelp_settings_get_all_params (yelp_settings_get_default (), 2, &params_i);
+    params[params_i++] = g_strdup ("db.chunk.max_depth");
+    params[params_i++] = g_strdup_printf ("%i", priv->max_depth);
+    params[params_i] = NULL;
+
+    priv->transform_running = TRUE;
+    yelp_transform_start (priv->transform,
+                          priv->xmldoc,
+                          NULL,
+			  (const gchar * const *) params);
+    g_strfreev (params);
+    g_mutex_unlock (priv->mutex);
+
+ done:
+    g_free (filepath);
+    if (id)
+        xmlFree (id);
+    if (parserCtxt)
+        xmlFreeParserCtxt (parserCtxt);
+
+    priv->process_running = FALSE;
+    g_object_unref (docbook);
+}
+
+static void
+docbook_disconnect (YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    if (priv->chunk_ready) {
+        g_signal_handler_disconnect (priv->transform, priv->chunk_ready);
+        priv->chunk_ready = 0;
+    }
+    if (priv->finished) {
+        g_signal_handler_disconnect (priv->transform, priv->finished);
+        priv->finished = 0;
+    }
+    if (priv->error) {
+        g_signal_handler_disconnect (priv->transform, priv->error);
+        priv->error = 0;
+    }
+    yelp_transform_cancel (priv->transform);
+    g_object_unref (priv->transform);
+    priv->transform = NULL;
+    priv->transform_running = FALSE;
+}
+
+/******************************************************************************/
+
+static void
+docbook_walk (YelpDocbookDocument *docbook)
+{
+    static       gint autoid = 0;
+    gchar        autoidstr[20];
+    xmlChar     *id = NULL;
+    xmlChar     *title = NULL;
+    gchar       *old_page_id = NULL;
+    xmlNodePtr   cur, old_cur;
+    GtkTreeIter  iter;
+    GtkTreeIter *old_iter = NULL;
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    YelpDocument *document = YELP_DOCUMENT (docbook);
+
+    debug_print (DB_FUNCTION, "entering\n");
+    debug_print (DB_DEBUG, "  priv->xmlcur->name: %s\n", priv->xmlcur->name);
+
+    /* Check for the db.chunk.max_depth PI and set max chunk depth */
+    if (priv->cur_depth == 0)
+        for (cur = priv->xmlcur; cur; cur = cur->prev)
+            if (cur->type == XML_PI_NODE)
+                if (!xmlStrcmp (cur->name, (const xmlChar *) "db.chunk.max_depth")) {
+                    gint max = atoi ((gchar *) cur->content);
+                    if (max)
+                        priv->max_depth = max;
+                    break;
+                }
+
+    id = xmlGetProp (priv->xmlcur, BAD_CAST "id");
+
+    if (docbook_walk_divisionQ (docbook) && !id) {
+        /* If id attribute is not present, autogenerate a
+         * unique value, and insert it into the in-memory tree */
+        g_snprintf (autoidstr, 20, "//autoid-%d", ++autoid);
+        xmlNewProp (priv->xmlcur, BAD_CAST "id", BAD_CAST autoidstr);
+        id = xmlGetProp (priv->xmlcur, BAD_CAST "id"); 
+    }
+
+    if (docbook_walk_chunkQ (docbook)) {
+        title = BAD_CAST docbook_walk_get_title (docbook);
+
+        debug_print (DB_DEBUG, "  id: \"%s\"\n", id);
+        debug_print (DB_DEBUG, "  title: \"%s\"\n", title);
+
+        yelp_document_set_title (document, (gchar *) id, (gchar *) title);
+
+        gdk_threads_enter ();
+        gtk_tree_store_append (GTK_TREE_STORE (priv->sections),
+                               &iter,
+                               priv->sections_iter);
+        gtk_tree_store_set (GTK_TREE_STORE (priv->sections),
+                            &iter,
+                            DOCBOOK_COLUMN_ID, id,
+                            DOCBOOK_COLUMN_TITLE, title,
+                            -1);
+        gdk_threads_leave ();
+
+        if (priv->cur_prev_id) {
+            yelp_document_set_prev_id (document, (gchar *) id, priv->cur_prev_id);
+            yelp_document_set_next_id (document, priv->cur_prev_id, (gchar *) id);
+            g_free (priv->cur_prev_id);
+        }
+        priv->cur_prev_id = g_strdup ((gchar *) id);
+
+        if (priv->cur_page_id)
+            yelp_document_set_up_id (document, (gchar *) id, priv->cur_page_id);
+        old_page_id = priv->cur_page_id;
+        priv->cur_page_id = g_strdup ((gchar *) id);
+
+        old_iter = priv->sections_iter;
+        if (priv->xmlcur->parent->type != XML_DOCUMENT_NODE)
+            priv->sections_iter = &iter;
+    }
+
+    old_cur = priv->xmlcur;
+    priv->cur_depth++;
+    if (id)
+        yelp_document_set_page_id (document, (gchar *) id, priv->cur_page_id);
+
+    for (cur = priv->xmlcur->children; cur; cur = cur->next) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            priv->xmlcur = cur;
+            docbook_walk (docbook);
+        }
+    }
+    priv->cur_depth--;
+    priv->xmlcur = old_cur;
+
+    if (docbook_walk_chunkQ (docbook)) {
+        priv->sections_iter = old_iter;
+        g_free (priv->cur_page_id);
+        priv->cur_page_id = old_page_id;
+    }
+
+    if (priv->cur_depth == 0) {
+        g_free (priv->cur_prev_id);
+        priv->cur_prev_id = NULL;
+
+        g_free (priv->cur_page_id);
+        priv->cur_page_id = NULL;
+    }
+
+    if (id != NULL)
+        xmlFree (id);
+    if (title != NULL)
+        xmlFree (title);
+}
+
+static gboolean
+docbook_walk_chunkQ (YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    if (priv->cur_depth <= priv->max_depth)
+        return docbook_walk_divisionQ (docbook);
+    else
+        return FALSE;
+}
+
+static gboolean
+docbook_walk_divisionQ (YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    xmlNodePtr node = priv->xmlcur;
+    return (!xmlStrcmp (node->name, (const xmlChar *) "appendix")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "article")      ||
+            !xmlStrcmp (node->name, (const xmlChar *) "book")         ||
+            !xmlStrcmp (node->name, (const xmlChar *) "bibliography") ||
+            !xmlStrcmp (node->name, (const xmlChar *) "chapter")      ||
+            !xmlStrcmp (node->name, (const xmlChar *) "colophon")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "glossary")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "index")        ||
+            !xmlStrcmp (node->name, (const xmlChar *) "part")         ||
+            !xmlStrcmp (node->name, (const xmlChar *) "preface")      ||
+            !xmlStrcmp (node->name, (const xmlChar *) "reference")    ||
+            !xmlStrcmp (node->name, (const xmlChar *) "refentry")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "refsect1")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "refsect2")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "refsect3")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "refsection")   ||
+            !xmlStrcmp (node->name, (const xmlChar *) "sect1")        ||
+            !xmlStrcmp (node->name, (const xmlChar *) "sect2")        ||
+            !xmlStrcmp (node->name, (const xmlChar *) "sect3")        ||
+            !xmlStrcmp (node->name, (const xmlChar *) "sect4")        ||
+            !xmlStrcmp (node->name, (const xmlChar *) "sect5")        ||
+            !xmlStrcmp (node->name, (const xmlChar *) "section")      ||
+            !xmlStrcmp (node->name, (const xmlChar *) "set")          ||
+            !xmlStrcmp (node->name, (const xmlChar *) "setindex")     ||
+            !xmlStrcmp (node->name, (const xmlChar *) "simplesect")   );
+}
+
+static gchar *
+docbook_walk_get_title (YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    gchar *infoname = NULL;
+    xmlNodePtr child = NULL;
+    xmlNodePtr title = NULL;
+    xmlNodePtr title_tmp = NULL;
+
+    if (!xmlStrcmp (priv->xmlcur->name, BAD_CAST "refentry")) {
+        /* The title for a refentry element can come from the following:
+         *   refmeta/refentrytitle
+         *   refentryinfo/title[abbrev]
+         *   refnamediv/refname
+         * We take the first one we find.
+         */
+        for (child = priv->xmlcur->children; child; child = child->next) {
+            if (!xmlStrcmp (child->name, BAD_CAST "refmeta")) {
+                for (title = child->children; title; title = title->next) {
+                    if (!xmlStrcmp (title->name, BAD_CAST "refentrytitle"))
+                        break;
+                }
+                if (title)
+                    goto done;
+            }
+            else if (!xmlStrcmp (child->name, BAD_CAST "refentryinfo")) {
+                for (title = child->children; title; title = title->next) {
+                    if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
+                        break;
+                    else if (!xmlStrcmp (title->name, BAD_CAST "title"))
+                        title_tmp = title;
+                }
+                if (title)
+                    goto done;
+                else if (title_tmp) {
+                    title = title_tmp;
+                    goto done;
+                }
+            }
+            else if (!xmlStrcmp (child->name, BAD_CAST "refnamediv")) {
+                for (title = child->children; title; title = title->next) {
+                    if (!xmlStrcmp (title->name, BAD_CAST "refname"))
+                        break;
+                    else if (!xmlStrcmp (title->name, BAD_CAST "refpurpose")) {
+                        title = NULL;
+                        break;
+                    }
+                }
+                if (title)
+                    goto done;
+            }
+            else if (!xmlStrncmp (child->name, BAD_CAST "refsect", 7))
+                break;
+        }
+    }
+    else {
+        /* The title for other elements appears in the following:
+         *   title[abbrev]
+         *   *info/title[abbrev]
+         *   blockinfo/title[abbrev]
+         *   objectinfo/title[abbrev]
+         * We take them in that order.
+         */
+        xmlNodePtr infos[3] = {NULL, NULL, NULL};
+        int i;
+
+        infoname = g_strdup_printf ("%sinfo", priv->xmlcur->name);
+
+        for (child = priv->xmlcur->children; child; child = child->next) {
+            if (!xmlStrcmp (child->name, BAD_CAST "titleabbrev")) {
+                title = child;
+                goto done;
+            }
+            else if (!xmlStrcmp (child->name, BAD_CAST "title"))
+                title_tmp = child;
+            else if (!xmlStrcmp (child->name, BAD_CAST infoname))
+                infos[0] = child;
+            else if (!xmlStrcmp (child->name, BAD_CAST "blockinfo"))
+                infos[1] = child;
+            else if (!xmlStrcmp (child->name, BAD_CAST "objectinfo"))
+                infos[2] = child;
+        }
+
+        if (title_tmp) {
+            title = title_tmp;
+            goto done;
+        }
+
+        for (i = 0; i < 3; i++) {
+            child = infos[i];
+            if (child) {
+                for (title = child->children; title; title = title->next) {
+                    if (!xmlStrcmp (title->name, BAD_CAST "titleabbrev"))
+                        goto done;
+                    else if (!xmlStrcmp (title->name, BAD_CAST "title"))
+                        title_tmp = title;
+                }
+                if (title_tmp) {
+                    title = title_tmp;
+                    goto done;
+                }
+            }
+        }
+    }
+
+ done:
+    g_free (infoname);
+
+    if (title)
+        return (gchar *) xmlNodeGetContent (title);
+    else
+        return g_strdup (_("Unknown"));
+}
+
+/******************************************************************************/
+
+static void
+transform_chunk_ready (YelpTransform       *transform,
+                       gchar               *chunk_id,
+                       YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    gchar *content;
+
+    debug_print (DB_FUNCTION, "entering\n");
+    g_assert (transform == priv->transform);
+
+    if (priv->state == DOCBOOK_STATE_STOP) {
+        docbook_disconnect (docbook);
+        return;
+    }
+
+    content = yelp_transform_take_chunk (transform, chunk_id);
+    yelp_document_give_contents (YELP_DOCUMENT (docbook),
+                                 chunk_id,
+                                 content,
+                                 "application/xhtml+xml");
+
+    yelp_document_signal (YELP_DOCUMENT (docbook),
+                          chunk_id,
+                          YELP_DOCUMENT_SIGNAL_CONTENTS,
+                          NULL);
+}
+
+static void
+transform_finished (YelpTransform       *transform,
+                    YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    gchar *docuri;
+    GError *error;
+
+    debug_print (DB_FUNCTION, "entering\n");
+    g_assert (transform == priv->transform);
+
+    if (priv->state == DOCBOOK_STATE_STOP) {
+        docbook_disconnect (docbook);
+        return;
+    }
+
+    docbook_disconnect (docbook);
+
+    /* We want to free priv->xmldoc, but we can't free it before transform
+       is finalized.   Otherwise, we could crash when YelpTransform frees
+       its libxslt resources.
+     */
+    g_object_weak_ref (transform,
+                       transform_finalized,
+                       docbook);
+
+    docuri = yelp_uri_get_document_uri (priv->uri);
+    error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
+                         _("The requested page was not found in the document â??%sâ??."),
+                         docuri);
+    g_free (docuri);
+    yelp_document_error_pending ((YelpDocument *) docbook, error);
+    g_error_free (error);
+}
+
+static void
+transform_error (YelpTransform       *transform,
+                 YelpDocbookDocument *docbook)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+    GError *error;
+
+    debug_print (DB_FUNCTION, "entering\n");
+    g_assert (transform == priv->transform);
+
+    if (priv->state == DOCBOOK_STATE_STOP) {
+        docbook_disconnect (docbook);
+        return;
+    }
+
+    error = yelp_transform_get_error (transform);
+    yelp_document_error_pending ((YelpDocument *) docbook, error);
+    g_error_free (error);
+
+    docbook_disconnect (docbook);
+}
+
+static void
+transform_finalized (YelpDocbookDocument *docbook,
+                     gpointer             transform)
+{
+    YelpDocbookDocumentPrivate *priv = GET_PRIV (docbook);
+ 
+   debug_print (DB_FUNCTION, "entering\n");
+
+    if (priv->xmldoc)
+	xmlFreeDoc (priv->xmldoc);
+    priv->xmldoc = NULL;
+
+}
diff --git a/libyelp/yelp-docbook-document.h b/libyelp/yelp-docbook-document.h
new file mode 100644
index 0000000..3e0f184
--- /dev/null
+++ b/libyelp/yelp-docbook-document.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2003-2009 Shaun McCance  <shaunm gnome org>
+ *
+ * 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.
+ *
+ * Author: Shaun McCance  <shaunm gnome org>
+ */
+
+#ifndef __YELP_DOCBOOK_DOCUMENT_H__
+#define __YELP_DOCBOOK_DOCUMENT_H__
+
+#include <glib-object.h>
+
+#include "yelp-document.h"
+
+#define YELP_TYPE_DOCBOOK_DOCUMENT         (yelp_docbook_document_get_type ())
+#define YELP_DOCBOOK_DOCUMENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocument))
+#define YELP_DOCBOOK_DOCUMENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocumentClass))
+#define YELP_IS_DOCBOOK_DOCUMENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), YELP_TYPE_DOCBOOK_DOCUMENT))
+#define YELP_IS_DOCBOOK_DOCUMENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), YELP_TYPE_DOCBOOK_DOCUMENT))
+#define YELP_DOCBOOK_DOCUMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), YELP_TYPE_DOCBOOK_DOCUMENT, YelpDocbookDocumentClass))
+
+typedef struct _YelpDocbookDocument      YelpDocbookDocument;
+typedef struct _YelpDocbookDocumentClass YelpDocbookDocumentClass;
+
+struct _YelpDocbookDocument {
+    YelpDocument      parent;
+};
+
+struct _YelpDocbookDocumentClass {
+    YelpDocumentClass parent_class;
+};
+
+GType           yelp_docbook_document_get_type     (void);
+YelpDocument *  yelp_docbook_document_new          (YelpUri  *uri);
+
+#endif /* __YELP_DOCBOOK_DOCUMENT_H__ */
diff --git a/libyelp/yelp-document.c b/libyelp/yelp-document.c
index 53ba5a2..899aae6 100644
--- a/libyelp/yelp-document.c
+++ b/libyelp/yelp-document.c
@@ -30,6 +30,7 @@
 #include "yelp-debug.h"
 #include "yelp-document.h"
 #include "yelp-error.h"
+#include "yelp-docbook-document.h"
 #include "yelp-mallard-document.h"
 #include "yelp-simple-document.h"
 
@@ -71,9 +72,6 @@ struct _YelpDocumentPriv {
     Hash   *prev_ids;      /* Mapping of page IDs to "previous page" IDs */
     Hash   *next_ids;      /* Mapping of page IDs to "next page" IDs */
     Hash   *up_ids;        /* Mapping of page IDs to "up page" IDs */
-
-    GMutex       *str_mutex;
-    GHashTable   *str_refs;
 };
 
 G_DEFINE_TYPE (YelpDocument, yelp_document, G_TYPE_OBJECT);
@@ -124,12 +122,6 @@ static void           request_free              (Request              *request);
 static const gchar *  str_ref                   (const gchar          *str);
 static void           str_unref                 (const gchar          *str);
 
-#if 0
-static gboolean       request_idle_error        (Request             *request);
-static gboolean       request_idle_final        (YelpDocument        *document);
-
-#endif
-
 GStaticMutex str_mutex = G_STATIC_MUTEX_INIT;
 GHashTable  *str_refs  = NULL;
 
@@ -166,7 +158,7 @@ yelp_document_get_for_uri (YelpUri *uri)
 	document = yelp_simple_document_new (uri);
 	break;
     case YELP_URI_DOCUMENT_TYPE_DOCBOOK:
-	/* FIXME */
+	document = yelp_docbook_document_new (uri);
 	break;
     case YELP_URI_DOCUMENT_TYPE_MALLARD:
 	document = yelp_mallard_document_new (uri);
@@ -853,6 +845,7 @@ static gboolean
 request_idle_contents (Request *request)
 {
     YelpDocument *document;
+    YelpDocumentPriv *priv;
     YelpDocumentCallback callback = NULL;
     gpointer user_data = user_data;
 
@@ -864,9 +857,12 @@ request_idle_contents (Request *request)
     }
 
     document = g_object_ref (request->document);
+    priv = GET_PRIV (document);
 
     g_mutex_lock (document->priv->mutex);
 
+    priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
+
     callback = request->callback;
     user_data = request->user_data;
     request->idle_funcs--;
@@ -1024,200 +1020,3 @@ str_unref (const gchar *str)
 
     g_static_mutex_unlock (&str_mutex);
 }
-#if 0
-
-void
-yelp_document_add_page (YelpDocument *document, gchar *page_id, const gchar *contents)
-{
-    GSList *reqs, *cur;
-    Request *request;
-    YelpDocumentPriv *priv;
-
-    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
-
-    debug_print (DB_FUNCTION, "entering\n");
-    debug_print (DB_ARG, "  page_id = \"%s\"\n", page_id);
-    priv = document->priv;
-
-    g_mutex_lock (priv->mutex);
-
-    hashh_replace (priv->contents,
-                   g_strdup (page_id),
-                   str_ref ((gchar *) contents));
-
-    reqs = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
-    for (cur = reqs; cur != NULL; cur = cur->next) {
-	if (cur->data) {
-	    request = (Request *) cur->data;
-	    request->idle_funcs++;
-	    g_idle_add ((GSourceFunc) request_idle_page, request);
-	}
-    }
-
-    g_mutex_unlock (priv->mutex);
-}
-
-gboolean
-yelp_document_has_page (YelpDocument *document, gchar *page_id)
-{
-    gchar *content;
-    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
-    content = g_hash_table_lookup (document->priv->contents, page_id);
-    return !(content == NULL);
-}
-
-void
-yelp_document_error_request (YelpDocument *document, gint req_id, YelpError *error)
-{
-    Request *request;
-    YelpDocumentPriv *priv;
-
-    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
-
-    debug_print (DB_FUNCTION, "entering\n");
-    priv = document->priv;
-
-    g_mutex_lock (priv->mutex);
-
-    request = g_hash_table_lookup (priv->reqs_by_req_id,
-				   GINT_TO_POINTER (req_id));
-    if (request) {
-	request->error = error;
-	request->idle_funcs++;
-	g_idle_add ((GSourceFunc) request_idle_error, request);
-    } else {
-	yelp_error_free (error);
-    }
-
-    g_mutex_unlock (priv->mutex);
-}
-
-void
-yelp_document_error_page (YelpDocument *document, gchar *page_id, YelpError *error)
-{
-    GSList *requests;
-    Request *request = NULL;
-    YelpDocumentPriv *priv;
-
-    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
-
-    debug_print (DB_FUNCTION, "entering\n");
-    priv = document->priv;
-    g_mutex_lock (priv->mutex);
-
-    requests = g_hash_table_lookup (priv->reqs_by_page_id, page_id);
-    while (requests) {
-	request = (Request *) requests->data;
-	if (request && request->error == NULL) {
-	    request->error = yelp_error_copy (error);
-	    request->idle_funcs++;
-	    g_idle_add ((GSourceFunc) request_idle_error, request);
-	}
-	requests = requests->next;
-    }
-
-    yelp_error_free (error);
-
-    g_mutex_unlock (priv->mutex);
-}
-
-void
-yelp_document_final_pending (YelpDocument *document, YelpError *error)
-{
-    YelpDocumentPriv *priv;
-
-    g_assert (document != NULL && YELP_IS_DOCUMENT (document));
-
-    debug_print (DB_FUNCTION, "entering\n");
-    priv = document->priv;
-
-    g_mutex_lock (priv->mutex);
-    if (priv->reqs_pending) {
-	priv->final_error = error;
-	g_idle_add ((GSourceFunc) request_idle_final, document);
-    } else {
-	yelp_error_free (error);
-    }
-
-    g_mutex_unlock (priv->mutex);
-}
-
-
-/******************************************************************************/
-
-
-
-static gboolean
-request_idle_final (YelpDocument *document)
-{
-    YelpDocumentPriv *priv;
-    YelpDocumentFunc  func = NULL;
-    YelpError *error = NULL;
-    gint req_id = 0;
-    gpointer user_data = user_data;
-    Request *request = NULL;
-    GSList *cur = NULL;
-
-    debug_print (DB_FUNCTION, "entering\n");
-
-    priv = document->priv;
-
-    g_mutex_lock (priv->mutex);
-
-    if (priv->reqs_pending == NULL) {
-	/*
-	  Time to bail as we shouldn't be here anyway.
-	*/
-	g_mutex_unlock (priv->mutex);
-	return FALSE;
-    }
-    
-    for (cur = priv->reqs_pending; cur; cur = cur->next) {
-	request = cur->data;
-	if (request->idle_funcs != 0) {
-	    /* 
-	       While there are outstanding requests, we should wait for them
-	       to complete before signalling the error
-	    */
-	    request->idle_funcs++;
-	    g_mutex_unlock (priv->mutex);
-	    return TRUE;
-	}
-    }
-
-    for (cur = priv->reqs_pending; cur; cur = cur->next) {
-	request = cur->data;
-	
-	if (cur->next)
-	    request->error = yelp_error_copy (priv->final_error);
-	else
-	    request->error = error;
-	
-	if (request->error) {
-	    func = request->func;
-	    req_id = request->req_id;
-	    user_data = request->user_data;
-	    error = request->error;
-	    request->error = NULL;
-	    
-	    priv->reqs_pending = g_slist_remove (priv->reqs_pending, request);
-	}
-	
-	
-	if (func)
-	    func (document,
-		  YELP_DOCUMENT_SIGNAL_ERROR,
-		  req_id,
-		  error,
-		  user_data);
-    }
-    g_mutex_unlock (priv->mutex);
-    
-    g_object_unref (document);
-    return FALSE;
-}
-
-/******************************************************************************/
-
-
-#endif
diff --git a/libyelp/yelp-transform.c b/libyelp/yelp-transform.c
index 1d02338..eecfe6a 100644
--- a/libyelp/yelp-transform.c
+++ b/libyelp/yelp-transform.c
@@ -145,11 +145,11 @@ yelp_transform_class_init (YelpTransformClass *klass)
     object_class->set_property = yelp_transform_set_property;
 
     signals[CHUNK_READY] = g_signal_new ("chunk-ready",
-                                   G_TYPE_FROM_CLASS (klass),
-                                   G_SIGNAL_RUN_LAST,
-                                   0, NULL, NULL,
-                                   g_cclosure_marshal_VOID__STRING,
-                                   G_TYPE_NONE, 1, G_TYPE_STRING);
+                                         G_TYPE_FROM_CLASS (klass),
+                                         G_SIGNAL_RUN_LAST,
+                                         0, NULL, NULL,
+                                         g_cclosure_marshal_VOID__STRING,
+                                         G_TYPE_NONE, 1, G_TYPE_STRING);
     signals[FINISHED] = g_signal_new ("finished",
                                       G_TYPE_FROM_CLASS (klass),
                                       G_SIGNAL_RUN_LAST,
@@ -181,6 +181,8 @@ yelp_transform_dispose (GObject *object)
 {
     YelpTransformPrivate *priv = GET_PRIV (object);
 
+    debug_print (DB_FUNCTION, "entering\n");
+
     if (priv->queue) {
         gchar *chunk_id;
         while ((chunk_id = (gchar *) g_async_queue_try_pop (priv->queue)))
@@ -189,6 +191,32 @@ yelp_transform_dispose (GObject *object)
         priv->queue = NULL;
     }
 
+    /* We do not free input or aux.  They belong to the caller, which
+       must ensure they exist for the lifetime of the transform.  We
+       have to set priv->aux_xslt->doc (which is priv->aux) to NULL
+       before xsltFreeTransformContext.  Otherwise it will be freed,
+       which we don't want.
+     */
+    if (priv->aux_xslt)
+        priv->aux_xslt->doc = NULL;
+
+    /* We free these in dispose to make absolutely certain that they're
+       freed by the time any weak notify callbacks are called.  These
+       may be used elsewhere to free resources like the input document.
+     */
+    if (priv->context) {
+        xsltFreeTransformContext (priv->context);
+        priv->context = NULL;
+    }
+    if (priv->stylesheet) {
+        xsltFreeStylesheet (priv->stylesheet);
+        priv->stylesheet = NULL;
+    }
+    if (priv->output) {
+        xmlFreeDoc (priv->output);
+        priv->output = NULL;
+    }
+
     G_OBJECT_CLASS (yelp_transform_parent_class)->dispose (object);
 }
 
@@ -202,20 +230,6 @@ yelp_transform_finalize (GObject *object)
 
     debug_print (DB_FUNCTION, "entering\n");
 
-    if (priv->output)
-        xmlFreeDoc (priv->output);
-    if (priv->stylesheet)
-        xsltFreeStylesheet (priv->stylesheet);
-    /* We do not free input or aux.  They belong to the caller, which
-       must ensure they exist for the lifetime of the transform.  We
-       have to set priv->aux_xslt->doc (which is priv->aux) to NULL
-       before xsltFreeTransformContext.  Otherwise it will be freed,
-       which we don't want.
-     */
-    if (priv->aux_xslt)
-        priv->aux_xslt->doc = NULL;
-    if (priv->context)
-        xsltFreeTransformContext (priv->context);
     if (priv->error)
         g_error_free (priv->error);
 
diff --git a/stylesheets/db2html.xsl.in b/stylesheets/db2html.xsl.in
index cfd681d..67111ed 100644
--- a/stylesheets/db2html.xsl.in
+++ b/stylesheets/db2html.xsl.in
@@ -14,9 +14,10 @@
 
 <xsl:param name="db.chunk.chunk_top" select="true()"/>
 <xsl:param name="db.chunk.extension" select="''"/>
-<xsl:param name="db.chunk.info_basename"  select="'__yelp_info'"/>
+<xsl:param name="db.chunk.info_basename"  select="'//about'"/>
 
 <xsl:param name="db2html.navbar.top" select="false()"/>
+<xsl:param name="db2html.sidenav" select="false()"/>
 
 <!-- == db.number == -->
 <!--



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