[libyelp/yelp-mallard-document] Support for Mallard Facets extension
[yelp.git] / libyelp / yelp-mallard-document.c
blob1bab124f55033780c84c484f9032c87d8e5b93e8
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Copyright (C) 2009 Shaun McCance <shaunm@gnome.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Shaun McCance <shaunm@gnome.org>
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gtk/gtk.h>
30 #include <libxml/parser.h>
31 #include <libxml/parserInternals.h>
32 #include <libxml/xinclude.h>
34 #include "yelp-error.h"
35 #include "yelp-mallard-document.h"
36 #include "yelp-settings.h"
37 #include "yelp-transform.h"
38 #include "yelp-debug.h"
40 #define STYLESHEET DATADIR"/yelp/xslt/mal2html.xsl"
41 #define MALLARD_NS "http://projectmallard.org/1.0/"
42 #define MALLARD_FACET_NS "http://projectmallard.org/facet/1.0/"
44 typedef enum {
45 MALLARD_STATE_BLANK,
46 MALLARD_STATE_THINKING,
47 MALLARD_STATE_IDLE,
48 MALLARD_STATE_STOP
49 } MallardState;
51 typedef struct {
52 YelpMallardDocument *mallard;
54 gchar *page_id;
55 gchar *filename;
56 xmlDocPtr xmldoc;
57 YelpTransform *transform;
59 guint chunk_ready;
60 guint finished;
61 guint error;
63 xmlNodePtr cur;
64 xmlNodePtr cache;
65 xmlXPathContextPtr xpath;
67 gboolean link_title;
68 gboolean sort_title;
70 gchar *page_title;
71 gchar *page_desc;
72 } MallardPageData;
74 static void yelp_mallard_document_class_init (YelpMallardDocumentClass *klass);
75 static void yelp_mallard_document_init (YelpMallardDocument *mallard);
76 static void yelp_mallard_document_dispose (GObject *object);
77 static void yelp_mallard_document_finalize (GObject *object);
79 static gboolean mallard_request_page (YelpDocument *document,
80 const gchar *page_id,
81 GCancellable *cancellable,
82 YelpDocumentCallback callback,
83 gpointer user_data);
85 static void transform_chunk_ready (YelpTransform *transform,
86 gchar *chunk_id,
87 MallardPageData *page_data);
88 static void transform_finished (YelpTransform *transform,
89 MallardPageData *page_data);
90 static void transform_error (YelpTransform *transform,
91 MallardPageData *page_data);
93 static void mallard_think (YelpMallardDocument *mallard);
94 static void mallard_try_run (YelpMallardDocument *mallard,
95 const gchar *page_id);
97 static void mallard_page_data_cancel (MallardPageData *page_data);
98 static void mallard_page_data_walk (MallardPageData *page_data);
99 static void mallard_page_data_info (MallardPageData *page_data,
100 xmlNodePtr info_node,
101 xmlNodePtr cache_node);
102 static void mallard_page_data_run (MallardPageData *page_data);
103 static void mallard_page_data_free (MallardPageData *page_data);
105 static gboolean xml_node_is_ns_name (xmlNodePtr node,
106 const xmlChar *ns,
107 const xmlChar *name);
110 G_DEFINE_TYPE (YelpMallardDocument, yelp_mallard_document, YELP_TYPE_DOCUMENT);
111 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), YELP_TYPE_MALLARD_DOCUMENT, YelpMallardDocumentPrivate))
113 typedef struct _YelpMallardDocumentPrivate YelpMallardDocumentPrivate;
114 struct _YelpMallardDocumentPrivate {
115 YelpUri *uri;
116 MallardState state;
118 GMutex *mutex;
119 GThread *thread;
120 gboolean thread_running;
121 GSList *pending;
123 xmlDocPtr cache;
124 xmlNsPtr cache_ns;
125 GHashTable *pages_hash;
127 xmlXPathCompExprPtr normalize;
130 /******************************************************************************/
132 static void
133 yelp_mallard_document_class_init (YelpMallardDocumentClass *klass)
135 GObjectClass *object_class = G_OBJECT_CLASS (klass);
136 YelpDocumentClass *document_class = YELP_DOCUMENT_CLASS (klass);
138 object_class->dispose = yelp_mallard_document_dispose;
139 object_class->finalize = yelp_mallard_document_finalize;
141 document_class->request_page = mallard_request_page;
143 g_type_class_add_private (klass, sizeof (YelpMallardDocumentPrivate));
146 static void
147 yelp_mallard_document_init (YelpMallardDocument *mallard)
149 YelpMallardDocumentPrivate *priv = GET_PRIV (mallard);
150 xmlNodePtr cur;
152 priv->mutex = g_mutex_new ();
154 priv->thread_running = FALSE;
156 priv->cache = xmlNewDoc (BAD_CAST "1.0");
157 priv->cache_ns = xmlNewNs (NULL, BAD_CAST MALLARD_NS, BAD_CAST "mal");
158 cur = xmlNewDocNode (priv->cache, priv->cache_ns, BAD_CAST "cache", NULL);
159 xmlDocSetRootElement (priv->cache, cur);
160 priv->cache_ns->next = cur->nsDef;
161 cur->nsDef = priv->cache_ns;
162 priv->pages_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
163 NULL,
164 (GDestroyNotify) mallard_page_data_free);
165 priv->normalize = xmlXPathCompile ("normalize-space(.)");
168 static void
169 yelp_mallard_document_dispose (GObject *object)
171 G_OBJECT_CLASS (yelp_mallard_document_parent_class)->dispose (object);
174 static void
175 yelp_mallard_document_finalize (GObject *object)
177 YelpMallardDocumentPrivate *priv = GET_PRIV (object);
179 g_object_unref (priv->uri);
180 g_mutex_free (priv->mutex);
181 g_hash_table_destroy (priv->pages_hash);
183 if (priv->normalize)
184 xmlXPathFreeCompExpr (priv->normalize);
186 G_OBJECT_CLASS (yelp_mallard_document_parent_class)->finalize (object);
189 /******************************************************************************/
191 YelpDocument *
192 yelp_mallard_document_new (YelpUri *uri)
194 YelpMallardDocument *mallard;
195 YelpMallardDocumentPrivate *priv;
197 g_return_val_if_fail (uri != NULL, NULL);
199 mallard = (YelpMallardDocument *) g_object_new (YELP_TYPE_MALLARD_DOCUMENT, NULL);
200 priv = GET_PRIV (mallard);
201 priv->uri = g_object_ref (uri);
203 yelp_document_set_page_id ((YelpDocument *) mallard, NULL, "index");
204 yelp_document_set_page_id ((YelpDocument *) mallard, "index", "index");
206 return (YelpDocument *) mallard;
210 static gboolean
211 mallard_request_page (YelpDocument *document,
212 const gchar *page_id,
213 GCancellable *cancellable,
214 YelpDocumentCallback callback,
215 gpointer user_data)
217 YelpMallardDocumentPrivate *priv = GET_PRIV (document);
218 gchar *docuri;
219 GError *error;
220 gboolean handled;
222 debug_print (DB_FUNCTION, "entering\n");
223 debug_print (DB_ARG, " page_id=\"%s\"\n", page_id);
225 if (page_id == NULL)
226 page_id = "index";
228 handled =
229 YELP_DOCUMENT_CLASS (yelp_mallard_document_parent_class)->request_page (document,
230 page_id,
231 cancellable,
232 callback,
233 user_data);
234 if (handled) {
235 return TRUE;
238 g_mutex_lock (priv->mutex);
240 if (priv->state == MALLARD_STATE_BLANK) {
241 priv->state = MALLARD_STATE_THINKING;
242 priv->thread_running = TRUE;
243 g_object_ref (document);
244 priv->thread = g_thread_create ((GThreadFunc) mallard_think,
245 document, FALSE, NULL);
248 switch (priv->state) {
249 case MALLARD_STATE_THINKING:
250 priv->pending = g_slist_prepend (priv->pending, (gpointer) g_strdup (page_id));
251 break;
252 case MALLARD_STATE_IDLE:
253 mallard_try_run ((YelpMallardDocument *) document, page_id);
254 break;
255 case MALLARD_STATE_BLANK:
256 case MALLARD_STATE_STOP:
257 docuri = yelp_uri_get_document_uri (priv->uri);
258 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
259 _("The page ‘%s’ was not found in the document ‘%s’."),
260 page_id, docuri);
261 g_free (docuri);
262 yelp_document_signal (document, page_id,
263 YELP_DOCUMENT_SIGNAL_ERROR,
264 error);
265 g_error_free (error);
266 break;
269 g_mutex_unlock (priv->mutex);
271 return FALSE;
274 /******************************************************************************/
276 static void
277 mallard_think (YelpMallardDocument *mallard)
279 YelpMallardDocumentPrivate *priv = GET_PRIV (mallard);
280 GError *error = NULL;
281 YelpDocument *document;
282 gchar **search_path;
283 gboolean editor_mode;
285 gchar **path;
286 gint path_i;
287 GFile *gfile;
288 GFileEnumerator *children;
289 GFileInfo *pageinfo;
291 editor_mode = yelp_settings_get_editor_mode (yelp_settings_get_default ());
293 search_path = yelp_uri_get_search_path (priv->uri);
295 if (!search_path || search_path[0] == NULL ||
296 !g_file_test (search_path[0], G_FILE_TEST_IS_DIR)) {
297 /* This basically only happens when someone passes an actual directory
298 manually, which will have a singleton search path.
300 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
301 _("The directory ‘%s’ does not exist."),
302 search_path[0]);
303 yelp_document_error_pending ((YelpDocument *) document, error);
304 g_error_free (error);
305 goto done;
308 path = yelp_uri_get_search_path (priv->uri);
309 for (path_i = 0; path[path_i] != NULL; path_i++) {
310 gfile = g_file_new_for_path (path[path_i]);
311 children = g_file_enumerate_children (gfile,
312 G_FILE_ATTRIBUTE_STANDARD_NAME,
313 G_FILE_QUERY_INFO_NONE,
314 NULL, NULL);
315 while ((pageinfo = g_file_enumerator_next_file (children, NULL, NULL))) {
316 MallardPageData *page_data;
317 gchar *filename;
318 GFile *pagefile;
319 filename = g_file_info_get_attribute_as_string (pageinfo,
320 G_FILE_ATTRIBUTE_STANDARD_NAME);
321 if (!g_str_has_suffix (filename, ".page") &&
322 !(editor_mode && g_str_has_suffix (filename, ".page.stub"))) {
323 g_free (filename);
324 g_object_unref (pageinfo);
325 continue;
327 page_data = g_new0 (MallardPageData, 1);
328 page_data->mallard = mallard;
329 pagefile = g_file_resolve_relative_path (gfile, filename);
330 page_data->filename = g_file_get_path (pagefile);
331 mallard_page_data_walk (page_data);
332 if (page_data->page_id == NULL) {
333 mallard_page_data_free (page_data);
335 else if (g_hash_table_lookup (priv->pages_hash, page_data->page_id) != NULL) {
336 mallard_page_data_free (page_data);
338 else {
339 g_mutex_lock (priv->mutex);
340 yelp_document_set_root_id ((YelpDocument *) mallard,
341 page_data->page_id, "index");
342 yelp_document_set_page_id ((YelpDocument *) mallard,
343 page_data->page_id, page_data->page_id);
344 g_hash_table_insert (priv->pages_hash, page_data->page_id, page_data);
345 yelp_document_set_page_title ((YelpDocument *) mallard,
346 page_data->page_id,
347 page_data->page_title);
348 yelp_document_set_page_desc ((YelpDocument *) mallard,
349 page_data->page_id,
350 page_data->page_desc);
351 yelp_document_signal ((YelpDocument *) mallard,
352 page_data->page_id,
353 YELP_DOCUMENT_SIGNAL_INFO,
354 NULL);
355 g_mutex_unlock (priv->mutex);
357 g_object_unref (pagefile);
358 g_free (filename);
359 g_object_unref (pageinfo);
362 g_strfreev (path);
364 g_mutex_lock (priv->mutex);
365 priv->state = MALLARD_STATE_IDLE;
366 while (priv->pending) {
367 gchar *page_id = (gchar *) priv->pending->data;
368 mallard_try_run (mallard, page_id);
369 g_free (page_id);
370 priv->pending = g_slist_delete_link (priv->pending, priv->pending);
372 g_mutex_unlock (priv->mutex);
374 done:
375 g_object_unref (children);
376 g_object_unref (gfile);
378 priv->thread_running = FALSE;
379 g_object_unref (mallard);
382 static void
383 mallard_try_run (YelpMallardDocument *mallard,
384 const gchar *page_id)
386 /* We expect to be in a locked mutex when this function is called. */
387 YelpMallardDocumentPrivate *priv = GET_PRIV (mallard);
388 MallardPageData *page_data = NULL;
389 gchar *real_id = NULL;
390 GError *error;
392 debug_print (DB_FUNCTION, "entering\n");
393 debug_print (DB_ARG, " page_id=\"%s\"\n", page_id);
395 if (page_id != NULL)
396 real_id = yelp_document_get_page_id ((YelpDocument *) mallard, page_id);
398 if (real_id != NULL) {
399 page_data = g_hash_table_lookup (priv->pages_hash, real_id);
400 g_free (real_id);
403 if (page_data == NULL) {
404 gchar *docuri = yelp_uri_get_document_uri (priv->uri);
405 error = g_error_new (YELP_ERROR, YELP_ERROR_NOT_FOUND,
406 _("The page ‘%s’ was not found in the document ‘%s’."),
407 page_id, docuri);
408 g_free (docuri);
409 yelp_document_signal ((YelpDocument *) mallard, page_id,
410 YELP_DOCUMENT_SIGNAL_ERROR,
411 error);
412 g_error_free (error);
413 return;
416 mallard_page_data_run (page_data);
419 /******************************************************************************/
420 /** MallardPageData ***********************************************************/
422 static void
423 mallard_page_data_cancel (MallardPageData *page_data)
425 if (page_data->transform) {
426 if (page_data->chunk_ready) {
427 g_signal_handler_disconnect (page_data->transform, page_data->chunk_ready);
428 page_data->chunk_ready = 0;
430 if (page_data->finished) {
431 g_signal_handler_disconnect (page_data->transform, page_data->finished);
432 page_data->finished = 0;
434 if (page_data->error) {
435 g_signal_handler_disconnect (page_data->transform, page_data->error);
436 page_data->error = 0;
438 yelp_transform_cancel (page_data->transform);
439 g_object_unref (page_data->transform);
440 page_data->transform = NULL;
444 static void
445 mallard_page_data_walk (MallardPageData *page_data)
447 YelpMallardDocumentPrivate *priv = GET_PRIV (page_data->mallard);
448 xmlParserCtxtPtr parserCtxt = NULL;
449 xmlChar *id = NULL;
451 if (page_data->cur == NULL) {
452 parserCtxt = xmlNewParserCtxt ();
453 page_data->xmldoc = xmlCtxtReadFile (parserCtxt,
454 (const char *) page_data->filename, NULL,
455 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
456 XML_PARSE_NOENT | XML_PARSE_NONET );
457 if (page_data->xmldoc == NULL)
458 goto done;
459 if (xmlXIncludeProcessFlags (page_data->xmldoc,
460 XML_PARSE_DTDLOAD | XML_PARSE_NOCDATA |
461 XML_PARSE_NOENT | XML_PARSE_NONET )
462 < 0)
463 goto done;
464 page_data->cur = xmlDocGetRootElement (page_data->xmldoc);
465 page_data->cache = xmlDocGetRootElement (priv->cache);
466 page_data->xpath = xmlXPathNewContext (page_data->xmldoc);
467 mallard_page_data_walk (page_data);
468 } else {
469 gboolean ispage;
470 xmlNodePtr child, oldcur, oldcache, info;
472 id = xmlGetProp (page_data->cur, BAD_CAST "id");
473 if (id == NULL)
474 goto done;
476 ispage = xml_node_is_ns_name (page_data->cur, MALLARD_NS, BAD_CAST "page");
477 if (ispage && g_hash_table_lookup (priv->pages_hash, id) != NULL)
478 goto done;
480 page_data->cache = xmlNewChild (page_data->cache,
481 priv->cache_ns,
482 page_data->cur->name,
483 NULL);
485 if (ispage) {
486 xmlChar *style;
487 gchar **styles;
488 gchar *icon = "help-contents";
489 page_data->page_id = g_strdup ((gchar *) id);
490 xmlSetProp (page_data->cache, BAD_CAST "id", id);
491 yelp_document_set_page_id ((YelpDocument *) page_data->mallard,
492 g_strrstr (page_data->filename, G_DIR_SEPARATOR_S),
493 page_data->page_id);
494 style = xmlGetProp (page_data->cur, BAD_CAST "style");
495 if (style) {
496 gint i;
497 styles = g_strsplit (style, " ", -1);
498 for (i = 0; styles[i] != NULL; i++) {
499 if (g_str_equal (styles[i], "task")) {
500 icon = "yelp-page-task";
501 break;
503 else if (g_str_equal (styles[i], "tip")) {
504 icon = "yelp-page-tip";
505 break;
507 else if (g_str_equal (styles[i], "ui")) {
508 icon = "yelp-page-ui";
509 break;
511 else if (g_str_equal (styles[i], "video")) {
512 icon = "yelp-page-video";
513 break;
516 xmlFree (style);
518 yelp_document_set_page_icon ((YelpDocument *) page_data->mallard,
519 page_data->page_id, icon);
520 } else {
521 gchar *newid = g_strdup_printf ("%s#%s", page_data->page_id, id);
522 xmlSetProp (page_data->cache, BAD_CAST "id", BAD_CAST newid);
523 g_free (newid);
526 info = xmlNewChild (page_data->cache,
527 priv->cache_ns,
528 BAD_CAST "info", NULL);
529 page_data->link_title = FALSE;
530 page_data->sort_title = FALSE;
531 for (child = page_data->cur->children; child; child = child->next) {
532 if (child->type != XML_ELEMENT_NODE)
533 continue;
534 if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "info")) {
535 mallard_page_data_info (page_data, child, info);
537 else if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "title")) {
538 xmlNodePtr node;
539 xmlNodePtr title_node = xmlNewChild (page_data->cache,
540 priv->cache_ns,
541 BAD_CAST "title", NULL);
542 for (node = child->children; node; node = node->next) {
543 xmlAddChild (title_node, xmlCopyNode (node, 1));
545 if (!page_data->link_title) {
546 xmlNodePtr title_node = xmlNewChild (info,
547 priv->cache_ns,
548 BAD_CAST "title", NULL);
549 xmlSetProp (title_node, BAD_CAST "type", BAD_CAST "link");
550 for (node = child->children; node; node = node->next) {
551 xmlAddChild (title_node, xmlCopyNode (node, 1));
554 if (!page_data->sort_title) {
555 xmlNodePtr title_node = xmlNewChild (info,
556 priv->cache_ns,
557 BAD_CAST "title", NULL);
558 xmlSetProp (title_node, BAD_CAST "type", BAD_CAST "sort");
559 for (node = child->children; node; node = node->next) {
560 xmlAddChild (title_node, xmlCopyNode (node, 1));
563 if (page_data->page_title == NULL) {
564 YelpMallardDocumentPrivate *priv = GET_PRIV (page_data->mallard);
565 xmlXPathObjectPtr obj;
566 page_data->xpath->node = child;
567 obj = xmlXPathCompiledEval (priv->normalize, page_data->xpath);
568 page_data->page_title = g_strdup (obj->stringval);
569 xmlXPathFreeObject (obj);
572 else if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "section")) {
573 oldcur = page_data->cur;
574 oldcache = page_data->cache;
575 page_data->cur = child;
576 mallard_page_data_walk (page_data);
577 page_data->cur = oldcur;
578 page_data->cache = oldcache;
583 done:
584 if (id)
585 xmlFree (id);
586 if (parserCtxt)
587 xmlFreeParserCtxt (parserCtxt);
590 static void
591 mallard_page_data_info (MallardPageData *page_data,
592 xmlNodePtr info_node,
593 xmlNodePtr cache_node)
595 xmlNodePtr child;
596 gboolean editor_mode = yelp_settings_get_editor_mode (yelp_settings_get_default ());
598 for (child = info_node->children; child; child = child->next) {
599 if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "info")) {
600 mallard_page_data_info (page_data, child, cache_node);
602 else if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "title")) {
603 xmlNodePtr node, title_node;
604 xmlChar *type, *role;
605 title_node = xmlCopyNode (child, 1);
606 xmlAddChild (cache_node, title_node);
608 type = xmlGetProp (child, BAD_CAST "type");
609 role = xmlGetProp (child, BAD_CAST "role");
611 if (xmlStrEqual (type, BAD_CAST "link") && role == NULL)
612 page_data->link_title = TRUE;
613 if (xmlStrEqual (type, BAD_CAST "sort"))
614 page_data->sort_title = TRUE;
615 if (xmlStrEqual (type, BAD_CAST "text")) {
616 YelpMallardDocumentPrivate *priv = GET_PRIV (page_data->mallard);
617 xmlXPathObjectPtr obj;
618 page_data->xpath->node = child;
619 obj = xmlXPathCompiledEval (priv->normalize, page_data->xpath);
620 g_free (page_data->page_title);
621 page_data->page_title = g_strdup (obj->stringval);
622 xmlXPathFreeObject (obj);
625 else if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "desc")) {
626 YelpMallardDocumentPrivate *priv = GET_PRIV (page_data->mallard);
627 xmlXPathObjectPtr obj;
628 page_data->xpath->node = child;
629 obj = xmlXPathCompiledEval (priv->normalize, page_data->xpath);
630 page_data->page_desc = g_strdup (obj->stringval);
631 xmlXPathFreeObject (obj);
633 xmlAddChild (cache_node, xmlCopyNode (child, 1));
635 else if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "link")) {
636 xmlAddChild (cache_node, xmlCopyNode (child, 1));
638 else if (xml_node_is_ns_name (child, MALLARD_NS, BAD_CAST "revision")) {
639 xmlAddChild (cache_node, xmlCopyNode (child, 1));
641 else if (xml_node_is_ns_name (child, MALLARD_FACET_NS, BAD_CAST "tag") ||
642 xml_node_is_ns_name (child, MALLARD_FACET_NS, BAD_CAST "match") ||
643 xml_node_is_ns_name (child, MALLARD_FACET_NS, BAD_CAST "choice") ) {
644 xmlAddChild (cache_node, xmlCopyNode (child, 1));
649 static void
650 mallard_page_data_run (MallardPageData *page_data)
652 YelpSettings *settings = yelp_settings_get_default ();
653 YelpMallardDocumentPrivate *priv = GET_PRIV (page_data->mallard);
654 gint i, ix;
655 gchar **params = NULL;
657 mallard_page_data_cancel (page_data);
658 page_data->transform = yelp_transform_new (STYLESHEET);
660 page_data->chunk_ready =
661 g_signal_connect (page_data->transform, "chunk-ready",
662 (GCallback) transform_chunk_ready,
663 page_data);
664 page_data->finished =
665 g_signal_connect (page_data->transform, "finished",
666 (GCallback) transform_finished,
667 page_data);
668 page_data->error =
669 g_signal_connect (page_data->transform, "error",
670 (GCallback) transform_error,
671 page_data);
673 if (g_str_has_suffix (page_data->filename, ".page.stub")) {
674 gint end;
675 params = yelp_settings_get_all_params (settings, 2, &end);
676 params[end++] = g_strdup ("yelp.stub");
677 params[end++] = g_strdup ("true()");
679 else
680 params = yelp_settings_get_all_params (settings, 0, NULL);
682 yelp_transform_start (page_data->transform,
683 page_data->xmldoc,
684 priv->cache,
685 (const gchar * const *) params);
686 g_strfreev (params);
689 static void
690 mallard_page_data_free (MallardPageData *page_data)
692 mallard_page_data_cancel (page_data);
693 g_free (page_data->page_id);
694 g_free (page_data->filename);
695 if (page_data->xmldoc)
696 xmlFreeDoc (page_data->xmldoc);
697 if (page_data->xpath)
698 xmlXPathFreeContext (page_data->xpath);
699 g_free (page_data->page_title);
700 g_free (page_data->page_desc);
701 g_free (page_data);
704 /******************************************************************************/
705 /** YelpTransform *************************************************************/
707 static void
708 transform_chunk_ready (YelpTransform *transform,
709 gchar *chunk_id,
710 MallardPageData *page_data)
712 YelpMallardDocumentPrivate *priv;
713 gchar *content;
715 debug_print (DB_FUNCTION, "entering\n");
717 g_assert (page_data != NULL && page_data->mallard != NULL &&
718 YELP_IS_MALLARD_DOCUMENT (page_data->mallard));
719 g_assert (transform == page_data->transform);
721 priv = GET_PRIV (page_data->mallard);
723 if (priv->state == MALLARD_STATE_STOP) {
724 mallard_page_data_cancel (page_data);
725 return;
728 content = yelp_transform_take_chunk (transform, chunk_id);
729 yelp_document_give_contents (YELP_DOCUMENT (page_data->mallard),
730 chunk_id,
731 content,
732 "application/xhtml+xml");
734 yelp_document_signal (YELP_DOCUMENT (page_data->mallard),
735 chunk_id,
736 YELP_DOCUMENT_SIGNAL_CONTENTS,
737 NULL);
740 static void
741 transform_finished (YelpTransform *transform,
742 MallardPageData *page_data)
744 YelpMallardDocumentPrivate *priv;
746 g_assert (page_data != NULL && page_data->mallard != NULL &&
747 YELP_IS_MALLARD_DOCUMENT (page_data->mallard));
748 g_assert (transform == page_data->transform);
750 priv = GET_PRIV (page_data->mallard);
752 if (priv->state == MALLARD_STATE_STOP) {
753 mallard_page_data_cancel (page_data);
754 return;
757 mallard_page_data_cancel (page_data);
759 if (page_data->xmldoc)
760 xmlFreeDoc (page_data->xmldoc);
761 page_data->xmldoc = NULL;
764 static void
765 transform_error (YelpTransform *transform,
766 MallardPageData *page_data)
768 YelpMallardDocumentPrivate *priv;
769 GError *error;
771 g_assert (page_data != NULL && page_data->mallard != NULL &&
772 YELP_IS_MALLARD_DOCUMENT (page_data->mallard));
773 g_assert (transform == page_data->transform);
775 priv = GET_PRIV (page_data->mallard);
777 if (priv->state == MALLARD_STATE_STOP) {
778 mallard_page_data_cancel (page_data);
779 return;
782 error = yelp_transform_get_error (transform);
783 yelp_document_error_pending ((YelpDocument *) (page_data->mallard), error);
784 g_error_free (error);
786 mallard_page_data_cancel (page_data);
789 static gboolean
790 xml_node_is_ns_name (xmlNodePtr node,
791 const xmlChar *ns,
792 const xmlChar *name)
794 if (node->ns == NULL)
795 return (ns == NULL);
796 else if (ns != NULL && node->ns->href != NULL)
797 return (xmlStrEqual (ns, node->ns->href) && xmlStrEqual (name, node->name));
798 return FALSE;