2 * transform.c: Implemetation of the XSL Transformation 1.0 engine
3 * transform part, i.e. applying a Stylesheet to a document
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
8 * Michael Kay "XSLT Programmer's Reference" pp 637-643
9 * Writing Multiple Output Files
11 * XSLT-1.1 Working Draft
12 * http://www.w3.org/TR/xslt11#multiple-output
14 * See Copyright for the status of this software.
23 #include <libxml/xmlmemory.h>
24 #include <libxml/parser.h>
25 #include <libxml/tree.h>
26 #include <libxml/valid.h>
27 #include <libxml/hash.h>
28 #include <libxml/encoding.h>
29 #include <libxml/xmlerror.h>
30 #include <libxml/xpath.h>
31 #include <libxml/parserInternals.h>
32 #include <libxml/xpathInternals.h>
33 #include <libxml/HTMLtree.h>
34 #include <libxml/uri.h>
36 #include "xsltInternals.h"
37 #include "xsltutils.h"
39 #include "transform.h"
40 #include "variables.h"
41 #include "numbersInternals.h"
42 #include "namespaces.h"
43 #include "attributes.h"
44 #include "templates.h"
47 #include "documents.h"
48 #include "extensions.h"
52 #ifdef WITH_XSLT_DEBUG
53 #define WITH_XSLT_DEBUG_PROCESS
56 int xsltMaxDepth = 250;
63 # define FALSE (0 == 1)
64 # define TRUE (!FALSE)
67 #define IS_BLANK_NODE(n) \
68 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
71 * Generic function for accessing stacks in the transform Context
74 #define PUSH_AND_POP(scope, type, name) \
75 scope int name##Push(xsltTransformContextPtr ctxt, type value) { \
76 if (ctxt->name##Nr >= ctxt->name##Max) { \
77 ctxt->name##Max *= 2; \
78 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
79 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
80 if (ctxt->name##Tab == NULL) { \
81 xmlGenericError(xmlGenericErrorContext, \
82 "realloc failed !\n"); \
86 ctxt->name##Tab[ctxt->name##Nr] = value; \
88 return(ctxt->name##Nr++); \
90 scope type name##Pop(xsltTransformContextPtr ctxt) { \
92 if (ctxt->name##Nr <= 0) return(0); \
94 if (ctxt->name##Nr > 0) \
95 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
98 ret = ctxt->name##Tab[ctxt->name##Nr]; \
99 ctxt->name##Tab[ctxt->name##Nr] = 0; \
104 * Those macros actually generate the functions
106 PUSH_AND_POP(static, xsltTemplatePtr, templ)
107 PUSH_AND_POP(static, xsltStackElemPtr, vars)
109 /************************************************************************
111 * XInclude default settings *
113 ************************************************************************/
115 static int xsltDoXIncludeDefault = 0;
118 * xsltSetXIncludeDefault:
119 * @xinclude: whether to do XInclude processing
121 * Set whether XInclude should be processed on document being loaded by default
124 xsltSetXIncludeDefault(int xinclude) {
125 xsltDoXIncludeDefault = (xinclude != 0);
129 * xsltGetXIncludeDefault:
131 * return the default state for XInclude processing
133 * Returns 0 if there is no processing 1 otherwise
136 xsltGetXIncludeDefault(void) {
137 return(xsltDoXIncludeDefault);
140 /************************************************************************
142 * handling of transformation contexts *
144 ************************************************************************/
147 * xsltNewTransformContext:
148 * @style: a parsed XSLT stylesheet
149 * @doc: the input document
151 * Create a new XSLT TransformContext
153 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
155 static xsltTransformContextPtr
156 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
157 xsltTransformContextPtr cur;
158 xsltDocumentPtr docu;
160 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
162 xsltGenericError(xsltGenericErrorContext,
163 "xsltNewTransformContext : malloc failed\n");
166 memset(cur, 0, sizeof(xsltTransformContext));
169 * initialize the template stack
171 cur->templTab = (xsltTemplatePtr *)
172 xmlMalloc(10 * sizeof(xsltTemplatePtr));
173 if (cur->templTab == NULL) {
174 xmlGenericError(xmlGenericErrorContext,
175 "xsltNewTransformContext: out of memory\n");
184 * initialize the variables stack
186 cur->varsTab = (xsltStackElemPtr *)
187 xmlMalloc(10 * sizeof(xsltStackElemPtr));
188 if (cur->varsTab == NULL) {
189 xmlGenericError(xmlGenericErrorContext,
190 "xsltNewTransformContext: out of memory\n");
191 xmlFree(cur->templTab);
201 cur->xpathCtxt = xmlXPathNewContext(doc);
202 if (cur->xpathCtxt == NULL) {
203 xsltGenericError(xsltGenericErrorContext,
204 "xsltNewTransformContext : xmlXPathNewContext failed\n");
205 xmlFree(cur->templTab);
206 xmlFree(cur->varsTab);
210 cur->xpathCtxt->proximityPosition = 0;
211 cur->xpathCtxt->contextSize = 0;
212 XSLT_REGISTER_VARIABLE_LOOKUP(cur);
213 cur->xpathCtxt->nsHash = style->nsHash;
214 docu = xsltNewDocument(cur, doc);
216 xsltGenericError(xsltGenericErrorContext,
217 "xsltNewTransformContext : xsltNewDocument failed\n");
218 xmlFree(cur->templTab);
219 xmlFree(cur->varsTab);
224 cur->document = docu;
226 cur->xinclude = xsltDoXIncludeDefault;
227 cur->outputFile = NULL;
232 * xsltFreeTransformContext:
233 * @ctxt: an XSLT parser context
235 * Free up the memory allocated by @ctxt
238 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
241 if (ctxt->xpathCtxt != NULL) {
242 ctxt->xpathCtxt->nsHash = NULL;
243 xmlXPathFreeContext(ctxt->xpathCtxt);
245 if (ctxt->templTab != NULL)
246 xmlFree(ctxt->templTab);
247 if (ctxt->varsTab != NULL)
248 xmlFree(ctxt->varsTab);
249 xsltFreeDocuments(ctxt);
250 xsltFreeCtxtExts(ctxt);
251 xsltFreeGlobalVariables(ctxt);
252 memset(ctxt, -1, sizeof(xsltTransformContext));
256 /************************************************************************
258 * Copy of Nodes in an XSLT fashion *
260 ************************************************************************/
262 xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
267 * @ctxt: a XSLT process context
268 * @target: the element where the attribute will be grafted
269 * @attr: the attribute
271 * Do a copy of an attribute
273 * Returns: a new xmlAttrPtr, or NULL in case of error.
276 xsltCopyProp(xsltTransformContextPtr ctxt, xmlNodePtr target,
278 xmlAttrPtr ret = NULL;
285 if (attr->ns != NULL) {
286 ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target);
290 val = xmlNodeListGetString(attr->doc, attr->children, 1);
291 ret = xmlSetNsProp(target, ns, attr->name, val);
299 * @ctxt: a XSLT process context
300 * @target: the element where the attributes will be grafted
301 * @cur: the first attribute
303 * Do a copy of an attribute list.
305 * Returns: a new xmlAttrPtr, or NULL in case of error.
308 xsltCopyPropList(xsltTransformContextPtr ctxt, xmlNodePtr target,
310 xmlAttrPtr ret = NULL;
311 xmlAttrPtr p = NULL,q;
314 while (cur != NULL) {
315 if (cur->ns != NULL) {
316 ns = xsltGetNamespace(ctxt, cur->parent, cur->ns, target);
320 q = xmlCopyProp(target, cur);
338 * @ctxt: a XSLT process context
339 * @node: the element node in the source tree.
340 * @insert: the parent in the result tree.
342 * Make a copy of the element node @node
343 * and insert it as last child of @insert
345 * Returns a pointer to the new node, or NULL in case of error
348 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
352 copy = xmlCopyNode(node, 0);
354 copy->doc = ctxt->output;
355 xmlAddChild(insert, copy);
356 if (node->type == XML_ELEMENT_NODE) {
358 * Add namespaces as they are needed
360 if (node->nsDef != NULL)
361 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
363 if (node->ns != NULL) {
364 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
367 xsltGenericError(xsltGenericErrorContext,
368 "xsltCopyNode: copy %s failed\n", node->name);
375 * @ctxt: a XSLT process context
376 * @list: the list of element node in the source tree.
377 * @insert: the parent in the result tree.
379 * Make a copy of the full list of tree @list
380 * and insert them as last children of @insert
382 * Returns a pointer to the new list, or NULL in case of error
385 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list,
387 xmlNodePtr copy, ret = NULL;
389 while (list != NULL) {
390 copy = xsltCopyTree(ctxt, list, insert);
403 * @ctxt: a XSLT process context
404 * @node: the element node in the source tree.
405 * @insert: the parent in the result tree.
407 * Make a copy of the full tree under the element node @node
408 * and insert it as last child of @insert
410 * Returns a pointer to the new tree, or NULL in case of error
413 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
419 switch (node->type) {
420 case XML_ELEMENT_NODE:
422 case XML_CDATA_SECTION_NODE:
423 case XML_ENTITY_REF_NODE:
424 case XML_ENTITY_NODE:
426 case XML_COMMENT_NODE:
427 case XML_DOCUMENT_NODE:
428 case XML_HTML_DOCUMENT_NODE:
429 #ifdef LIBXML_DOCB_ENABLED
430 case XML_DOCB_DOCUMENT_NODE:
433 case XML_ATTRIBUTE_NODE:
435 xsltCopyProp(ctxt, insert, (xmlAttrPtr) node));
436 case XML_NAMESPACE_DECL:
438 xsltCopyNamespaceList(ctxt, insert, (xmlNsPtr) node));
440 case XML_DOCUMENT_TYPE_NODE:
441 case XML_DOCUMENT_FRAG_NODE:
442 case XML_NOTATION_NODE:
444 case XML_ELEMENT_DECL:
445 case XML_ATTRIBUTE_DECL:
446 case XML_ENTITY_DECL:
447 case XML_XINCLUDE_START:
448 case XML_XINCLUDE_END:
451 copy = xmlCopyNode(node, 0);
452 copy->doc = ctxt->output;
454 xmlAddChild(insert, copy);
457 * Add namespaces as they are needed
459 if (node->nsDef != NULL)
460 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
461 if (node->ns != NULL) {
462 copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
464 if (node->properties != NULL)
465 copy->properties = xsltCopyPropList(ctxt, copy,
467 if (node->children != NULL)
468 xsltCopyTreeList(ctxt, node->children, copy);
470 xsltGenericError(xsltGenericErrorContext,
471 "xsltCopyTree: copy %s failed\n", node->name);
476 /************************************************************************
478 * Default processing *
480 ************************************************************************/
482 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
484 * xsltDefaultProcessOneNode:
485 * @ctxt: a XSLT process context
486 * @node: the node in the source tree.
488 * Process the source node with the default built-in template rule:
489 * <xsl:template match="*|/">
490 * <xsl:apply-templates/>
495 * <xsl:template match="text()|@*">
496 * <xsl:value-of select="."/>
499 * Note also that namespaces declarations are copied directly:
501 * the built-in template rule is the only template rule that is applied
502 * for namespace nodes.
505 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
508 xmlNodePtr delete = NULL, cur;
509 int strip_spaces = -1;
510 int nbchild = 0, oldSize;
511 int childno = 0, oldPos;
512 xsltTemplatePtr template;
518 switch (node->type) {
519 case XML_DOCUMENT_NODE:
520 case XML_HTML_DOCUMENT_NODE:
521 case XML_ELEMENT_NODE:
523 case XML_CDATA_SECTION_NODE:
524 #ifdef WITH_XSLT_DEBUG_PROCESS
525 xsltGenericDebug(xsltGenericDebugContext,
526 "xsltDefaultProcessOneNode: copy CDATA %s\n",
529 copy = xmlNewDocText(ctxt->output, node->content);
531 xmlAddChild(ctxt->insert, copy);
533 xsltGenericError(xsltGenericErrorContext,
534 "xsltDefaultProcessOneNode: cdata copy failed\n");
538 #ifdef WITH_XSLT_DEBUG_PROCESS
539 if (node->content == NULL)
540 xsltGenericDebug(xsltGenericDebugContext,
541 "xsltDefaultProcessOneNode: copy empty text\n");
543 xsltGenericDebug(xsltGenericDebugContext,
544 "xsltDefaultProcessOneNode: copy text %s\n",
547 copy = xmlCopyNode(node, 0);
549 xmlAddChild(ctxt->insert, copy);
551 xsltGenericError(xsltGenericErrorContext,
552 "xsltDefaultProcessOneNode: text copy failed\n");
555 case XML_ATTRIBUTE_NODE:
556 cur = node->children;
557 while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
560 xsltGenericError(xsltGenericErrorContext,
561 "xsltDefaultProcessOneNode: no text for attribute\n");
563 #ifdef WITH_XSLT_DEBUG_PROCESS
564 if (cur->content == NULL)
565 xsltGenericDebug(xsltGenericDebugContext,
566 "xsltDefaultProcessOneNode: copy empty text\n");
568 xsltGenericDebug(xsltGenericDebugContext,
569 "xsltDefaultProcessOneNode: copy text %s\n",
572 copy = xmlCopyNode(cur, 0);
574 xmlAddChild(ctxt->insert, copy);
576 xsltGenericError(xsltGenericErrorContext,
577 "xsltDefaultProcessOneNode: text copy failed\n");
585 * Handling of Elements: first pass, cleanup and counting
587 cur = node->children;
588 while (cur != NULL) {
591 if ((IS_BLANK_NODE(cur)) &&
592 (cur->parent != NULL) &&
593 (ctxt->style->stripSpaces != NULL)) {
594 if (strip_spaces == -1)
596 xsltFindElemSpaceHandling(ctxt, cur->parent);
597 if (strip_spaces == 1) {
602 /* no break on purpose */
603 case XML_CDATA_SECTION_NODE:
604 case XML_DOCUMENT_NODE:
605 case XML_HTML_DOCUMENT_NODE:
606 case XML_ELEMENT_NODE:
608 case XML_COMMENT_NODE:
612 #ifdef WITH_XSLT_DEBUG_PROCESS
613 xsltGenericDebug(xsltGenericDebugContext,
614 "xsltDefaultProcessOneNode: skipping node type %d\n",
620 if (delete != NULL) {
621 #ifdef WITH_XSLT_DEBUG_PROCESS
622 xsltGenericDebug(xsltGenericDebugContext,
623 "xsltDefaultProcessOneNode: removing ignorable blank node\n");
625 xmlUnlinkNode(delete);
631 * Handling of Elements: second pass, actual processing
633 attrs = node->properties;
634 while (attrs != NULL) {
635 template = xsltGetTemplate(ctxt, (xmlNodePtr) attrs, NULL);
639 oldNode = ctxt->node;
641 templPush(ctxt, template);
642 xsltApplyOneTemplate(ctxt, node, template->content, 1);
644 ctxt->node = oldNode;
648 oldSize = ctxt->xpathCtxt->contextSize;
649 oldPos = ctxt->xpathCtxt->proximityPosition;
650 cur = node->children;
651 while (cur != NULL) {
654 case XML_DOCUMENT_NODE:
655 case XML_HTML_DOCUMENT_NODE:
656 case XML_ELEMENT_NODE:
657 ctxt->xpathCtxt->contextSize = nbchild;
658 ctxt->xpathCtxt->proximityPosition = childno;
659 varsPush( ctxt, NULL );
660 xsltProcessOneNode(ctxt, cur);
661 xsltFreeStackElemList( varsPop(ctxt) );
663 case XML_CDATA_SECTION_NODE:
664 template = xsltGetTemplate(ctxt, node, NULL);
668 #ifdef WITH_XSLT_DEBUG_PROCESS
669 xsltGenericDebug(xsltGenericDebugContext,
670 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
673 oldNode = ctxt->node;
675 templPush(ctxt, template);
676 xsltApplyOneTemplate(ctxt, node, template->content, 1);
678 ctxt->node = oldNode;
679 } else /* if (ctxt->mode == NULL) */ {
680 #ifdef WITH_XSLT_DEBUG_PROCESS
681 xsltGenericDebug(xsltGenericDebugContext,
682 "xsltDefaultProcessOneNode: copy CDATA %s\n",
685 copy = xmlNewDocText(ctxt->output, node->content);
687 xmlAddChild(ctxt->insert, copy);
689 xsltGenericError(xsltGenericErrorContext,
690 "xsltDefaultProcessOneNode: cdata copy failed\n");
695 template = xsltGetTemplate(ctxt, cur, NULL);
699 #ifdef WITH_XSLT_DEBUG_PROCESS
700 xsltGenericDebug(xsltGenericDebugContext,
701 "xsltDefaultProcessOneNode: applying template for text %s\n",
704 oldNode = ctxt->node;
706 ctxt->xpathCtxt->contextSize = nbchild;
707 ctxt->xpathCtxt->proximityPosition = childno;
708 templPush(ctxt, template);
709 xsltApplyOneTemplate(ctxt, cur, template->content, 1);
711 ctxt->node = oldNode;
712 } else /* if (ctxt->mode == NULL) */ {
713 #ifdef WITH_XSLT_DEBUG_PROCESS
714 if (cur->content == NULL)
715 xsltGenericDebug(xsltGenericDebugContext,
716 "xsltDefaultProcessOneNode: copy empty text\n");
718 xsltGenericDebug(xsltGenericDebugContext,
719 "xsltDefaultProcessOneNode: copy text %s\n",
722 copy = xmlCopyNode(cur, 0);
724 xmlAddChild(ctxt->insert, copy);
726 xsltGenericError(xsltGenericErrorContext,
727 "xsltDefaultProcessOneNode: text copy failed\n");
732 case XML_COMMENT_NODE:
733 template = xsltGetTemplate(ctxt, cur, NULL);
737 #ifdef WITH_XSLT_DEBUG_PROCESS
738 if (cur->type == XML_PI_NODE)
739 xsltGenericDebug(xsltGenericDebugContext,
740 "xsltDefaultProcessOneNode: template found for PI %s\n",
742 else if (cur->type == XML_COMMENT_NODE)
743 xsltGenericDebug(xsltGenericDebugContext,
744 "xsltDefaultProcessOneNode: template found for comment\n");
746 oldNode = ctxt->node;
748 ctxt->xpathCtxt->contextSize = nbchild;
749 ctxt->xpathCtxt->proximityPosition = childno;
750 templPush(ctxt, template);
751 xsltApplyOneTemplate(ctxt, cur, template->content, 1);
753 ctxt->node = oldNode;
761 ctxt->xpathCtxt->contextSize = oldSize;
762 ctxt->xpathCtxt->proximityPosition = oldPos;
766 * xsltProcessOneNode:
767 * @ctxt: a XSLT process context
768 * @node: the node in the source tree.
770 * Process the source node.
773 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
774 xsltTemplatePtr template;
778 * Cleanup children empty nodes if asked for
780 if ((node->children != NULL) &&
781 (xsltFindElemSpaceHandling(ctxt, node))) {
782 xmlNodePtr delete = NULL, cur = node->children;
784 while (cur != NULL) {
785 if (IS_BLANK_NODE(cur))
789 if (delete != NULL) {
790 #ifdef WITH_XSLT_DEBUG_PROCESS
791 xsltGenericDebug(xsltGenericDebugContext,
792 "xsltProcessOneNode: removing ignorable blank node\n");
794 xmlUnlinkNode(delete);
801 template = xsltGetTemplate(ctxt, node, NULL);
803 * If no template is found, apply the default rule.
805 if (template == NULL) {
806 #ifdef WITH_XSLT_DEBUG_PROCESS
807 if (node->type == XML_DOCUMENT_NODE)
808 xsltGenericDebug(xsltGenericDebugContext,
809 "xsltProcessOneNode: no template found for /\n");
810 else if (node->type == XML_CDATA_SECTION_NODE)
811 xsltGenericDebug(xsltGenericDebugContext,
812 "xsltProcessOneNode: no template found for CDATA\n");
813 else if (node->type == XML_ATTRIBUTE_NODE)
814 xsltGenericDebug(xsltGenericDebugContext,
815 "xsltProcessOneNode: no template found for attribute %s\n",
816 ((xmlAttrPtr) node)->name);
818 xsltGenericDebug(xsltGenericDebugContext,
819 "xsltProcessOneNode: no template found for %s\n", node->name);
821 oldNode = ctxt->node;
823 xsltDefaultProcessOneNode(ctxt, node);
824 ctxt->node = oldNode;
828 if (node->type == XML_ATTRIBUTE_NODE) {
829 #ifdef WITH_XSLT_DEBUG_PROCESS
830 xsltGenericDebug(xsltGenericDebugContext,
831 "xsltProcessOneNode: applying template '%s' for attribute %s\n",
832 template->match, node->name);
834 templPush(ctxt, template);
835 xsltApplyOneTemplate(ctxt, node, template->content, 1);
838 #ifdef WITH_XSLT_DEBUG_PROCESS
839 if (node->type == XML_DOCUMENT_NODE)
840 xsltGenericDebug(xsltGenericDebugContext,
841 "xsltProcessOneNode: applying template '%s' for /\n",
844 xsltGenericDebug(xsltGenericDebugContext,
845 "xsltProcessOneNode: applying template '%s' for %s\n",
846 template->match, node->name);
848 oldNode = ctxt->node;
850 templPush(ctxt, template);
851 xsltApplyOneTemplate(ctxt, node, template->content, 1);
853 ctxt->node = oldNode;
858 * xsltApplyOneTemplate:
859 * @ctxt: a XSLT process context
860 * @node: the node in the source tree.
861 * @list: the template replacement nodelist
862 * @real: is this a real template processing
864 * Process the apply-templates node on the source node
867 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
868 xmlNodePtr list, int real) {
869 xmlNodePtr cur = NULL, insert, copy = NULL;
870 xmlNodePtr oldInsert;
871 xmlNodePtr oldCurrent = NULL;
872 xmlNodePtr oldInst = NULL;
879 if (ctxt->templNr >= xsltMaxDepth) {
880 xsltGenericError(xsltGenericErrorContext,
881 "xsltApplyOneTemplate: loop found ???\n");
882 xsltGenericError(xsltGenericErrorContext,
883 "try increasing xsltMaxDepth (--maxdepth)\n");
884 xsltDebug(ctxt, node, list, NULL);
891 oldInsert = insert = ctxt->insert;
892 oldInst = ctxt->inst;
894 oldCurrent = ctxt->node;
899 * Insert all non-XSLT nodes found in the template
902 while (cur != NULL) {
905 * test, we must have a valid insertion point
907 if (insert == NULL) {
908 #ifdef WITH_XSLT_DEBUG_PROCESS
909 xsltGenericDebug(xsltGenericDebugContext,
910 "xsltApplyOneTemplate: insert == NULL !\n");
913 ctxt->node = oldCurrent;
914 ctxt->inst = oldInst;
918 if (IS_XSLT_ELEM(cur)) {
920 * This is an XSLT node
922 xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->_private;
924 if (IS_XSLT_NAME(cur, "message")) {
925 xsltMessage(ctxt, node, cur);
927 xsltGenericError(xsltGenericDebugContext,
928 "xsltApplyOneTemplate: %s was not compiled\n",
934 if (info->func != NULL) {
935 ctxt->insert = insert;
936 info->func(ctxt, node, cur, info);
937 ctxt->insert = oldInsert;
941 if (IS_XSLT_NAME(cur, "variable")) {
942 xsltParseStylesheetVariable(ctxt, cur);
943 } else if (IS_XSLT_NAME(cur, "param")) {
944 xsltParseStylesheetParam(ctxt, cur);
945 } else if (IS_XSLT_NAME(cur, "message")) {
946 xsltMessage(ctxt, node, cur);
948 xsltGenericError(xsltGenericDebugContext,
949 "xsltApplyOneTemplate: problem with xsl:%s\n",
954 } else if ((cur->type == XML_TEXT_NODE) ||
955 (cur->type == XML_CDATA_SECTION_NODE)) {
957 * This text comes from the stylesheet
958 * For stylesheets, the set of whitespace-preserving
959 * element names consists of just xsl:text.
961 #ifdef WITH_XSLT_DEBUG_PROCESS
962 if (cur->type == XML_CDATA_SECTION_NODE)
963 xsltGenericDebug(xsltGenericDebugContext,
964 "xsltApplyOneTemplate: copy CDATA text %s\n",
966 else if (cur->name == xmlStringTextNoenc)
967 xsltGenericDebug(xsltGenericDebugContext,
968 "xsltApplyOneTemplate: copy unescaped text %s\n",
971 xsltGenericDebug(xsltGenericDebugContext,
972 "xsltApplyOneTemplate: copy text %s\n", cur->content);
974 copy = xmlNewText(cur->content);
976 if ((cur->name == xmlStringTextNoenc) ||
977 (cur->type == XML_CDATA_SECTION_NODE))
978 copy->name = xmlStringTextNoenc;
979 xmlAddChild(insert, copy);
981 xsltGenericError(xsltGenericErrorContext,
982 "xsltApplyOneTemplate: text copy failed\n");
984 } else if ((cur->type == XML_ELEMENT_NODE) &&
985 (cur->ns != NULL) && (cur->_private != NULL)) {
986 xsltTransformFunction function;
988 * Flagged as an extension element
990 function = (xsltTransformFunction)
991 xmlHashLookup2(ctxt->extElements, cur->name, cur->ns->href);
992 if (function == NULL) {
993 xsltGenericError(xsltGenericErrorContext,
994 "xsltApplyOneTemplate: failed to find extension %s\n",
997 #ifdef WITH_XSLT_DEBUG_PROCESS
998 xsltGenericDebug(xsltGenericDebugContext,
999 "xsltApplyOneTemplate: extension construct %s\n", cur->name);
1002 ctxt->insert = insert;
1003 function(ctxt, node, cur, cur->_private);
1004 ctxt->insert = oldInsert;
1007 } else if (cur->type == XML_ELEMENT_NODE) {
1008 #ifdef WITH_XSLT_DEBUG_PROCESS
1009 xsltGenericDebug(xsltGenericDebugContext,
1010 "xsltApplyOneTemplate: copy node %s\n", cur->name);
1012 copy = xsltCopyNode(ctxt, cur, insert);
1014 * all the attributes are directly inherited
1016 if (cur->properties != NULL) {
1017 attrs = xsltAttrListTemplateProcess(ctxt, copy,
1023 * Skip to next node, in document order.
1025 if (cur->children != NULL) {
1026 if (cur->children->type != XML_ENTITY_DECL) {
1027 cur = cur->children;
1034 if (cur->next != NULL) {
1041 insert = insert->parent;
1044 if (cur == list->parent) {
1048 if (cur->next != NULL) {
1052 } while (cur != NULL);
1055 ctxt->node = oldCurrent;
1056 ctxt->inst = oldInst;
1060 /************************************************************************
1062 * XSLT-1.1 extensions *
1064 ************************************************************************/
1068 * @ctxt: an XSLT processing context
1069 * @node: The current node
1070 * @inst: the instruction in the stylesheet
1071 * @comp: precomputed informations
1073 * Process an XSLT-1.1 document element
1076 xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
1077 xmlNodePtr inst, xsltStylePreCompPtr comp)
1079 xsltStylesheetPtr style = NULL;
1081 xmlChar *filename = NULL, *prop, *elements;
1082 xmlChar *element, *end;
1083 xmlDocPtr res = NULL;
1084 xmlDocPtr oldOutput;
1085 xmlNodePtr oldInsert;
1086 const char *oldOutputFile;
1087 xsltOutputType oldType;
1088 xmlChar *URL = NULL;
1089 const xmlChar *method;
1090 const xmlChar *doctypePublic;
1091 const xmlChar *doctypeSystem;
1092 const xmlChar *version;
1094 if ((ctxt == NULL) || (node == NULL) || (inst == NULL)
1098 if (comp->filename == NULL) {
1100 if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
1101 #ifdef WITH_XSLT_DEBUG_EXTRA
1102 xsltGenericDebug(xsltGenericDebugContext,
1103 "Found saxon:output extension\n");
1105 URL = xsltEvalAttrValueTemplate(ctxt, inst,
1106 (const xmlChar *) "file",
1107 XSLT_SAXON_NAMESPACE);
1108 } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
1109 #ifdef WITH_XSLT_DEBUG_EXTRA
1110 xsltGenericDebug(xsltGenericDebugContext,
1111 "Found xalan:write extension\n");
1113 URL = xsltEvalAttrValueTemplate(ctxt, inst,
1116 XSLT_XALAN_NAMESPACE);
1117 } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
1118 URL = xsltEvalAttrValueTemplate(ctxt, inst,
1119 (const xmlChar *) "href",
1122 #ifdef WITH_XSLT_DEBUG_EXTRA
1123 xsltGenericDebug(xsltGenericDebugContext,
1124 "Found xslt11:document construct\n");
1126 URL = xsltEvalAttrValueTemplate(ctxt, inst,
1132 #ifdef WITH_XSLT_DEBUG_EXTRA
1133 xsltGenericDebug(xsltGenericDebugContext,
1134 "Found xt:document extension\n");
1141 URL = xmlStrdup(comp->filename);
1145 xsltGenericError(xsltGenericErrorContext,
1146 "xsltDocumentElem: href/URI-Reference not found\n");
1149 filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
1150 if (filename == NULL) {
1151 xsltGenericError(xsltGenericErrorContext,
1152 "xsltDocumentElem: URL computation failed for %s\n",
1158 oldOutputFile = ctxt->outputFile;
1159 oldOutput = ctxt->output;
1160 oldInsert = ctxt->insert;
1161 oldType = ctxt->type;
1162 ctxt->outputFile = (const char *) filename;
1164 style = xsltNewStylesheet();
1165 if (style == NULL) {
1166 xsltGenericError(xsltGenericErrorContext,
1167 "xsltDocumentElem: out of memory\n");
1172 * Version described in 1.1 draft allows full parametrization
1176 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1177 (const xmlChar *) "version",
1180 if (style->version != NULL)
1181 xmlFree(style->version);
1182 style->version = prop;
1184 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1185 (const xmlChar *) "encoding",
1188 if (style->encoding != NULL)
1189 xmlFree(style->encoding);
1190 style->encoding = prop;
1192 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1193 (const xmlChar *) "method",
1198 if (style->method != NULL)
1199 xmlFree(style->method);
1200 style->method = NULL;
1201 if (style->methodURI != NULL)
1202 xmlFree(style->methodURI);
1203 style->methodURI = NULL;
1205 URI = xsltGetQNameURI(inst, &prop);
1208 } else if (URI == NULL) {
1209 if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
1210 (xmlStrEqual(prop, (const xmlChar *) "html")) ||
1211 (xmlStrEqual(prop, (const xmlChar *) "text"))) {
1212 style->method = prop;
1214 xsltGenericError(xsltGenericErrorContext,
1215 "invalid value for method: %s\n", prop);
1219 style->method = prop;
1220 style->methodURI = xmlStrdup(URI);
1223 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1225 "doctype-system", XSLT_NAMESPACE);
1227 if (style->doctypeSystem != NULL)
1228 xmlFree(style->doctypeSystem);
1229 style->doctypeSystem = prop;
1231 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1233 "doctype-public", XSLT_NAMESPACE);
1235 if (style->doctypePublic != NULL)
1236 xmlFree(style->doctypePublic);
1237 style->doctypePublic = prop;
1239 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1240 (const xmlChar *) "standalone",
1243 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1244 style->standalone = 1;
1245 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1246 style->standalone = 0;
1248 xsltGenericError(xsltGenericErrorContext,
1249 "invalid value for standalone: %s\n",
1256 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1257 (const xmlChar *) "indent",
1260 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1262 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1265 xsltGenericError(xsltGenericErrorContext,
1266 "invalid value for indent: %s\n", prop);
1272 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1274 "omit-xml-declaration",
1277 if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
1278 style->omitXmlDeclaration = 1;
1279 } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
1280 style->omitXmlDeclaration = 0;
1282 xsltGenericError(xsltGenericErrorContext,
1283 "invalid value for omit-xml-declaration: %s\n",
1290 elements = xsltEvalAttrValueTemplate(ctxt, inst,
1292 "cdata-section-elements",
1294 if (elements != NULL) {
1295 if (style->stripSpaces == NULL)
1296 style->stripSpaces = xmlHashCreate(10);
1297 if (style->stripSpaces == NULL)
1301 while (*element != 0) {
1302 while (IS_BLANK(*element))
1307 while ((*end != 0) && (!IS_BLANK(*end)))
1309 element = xmlStrndup(element, end - element);
1311 #ifdef WITH_XSLT_DEBUG_PARSING
1312 xsltGenericDebug(xsltGenericDebugContext,
1313 "add cdata section output element %s\n",
1316 xmlHashAddEntry(style->stripSpaces, element,
1317 (xmlChar *) "cdata");
1325 xsltParseStylesheetOutput(style, inst);
1329 * Create a new document tree and process the element template
1331 XSLT_GET_IMPORT_PTR(method, style, method)
1332 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
1333 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
1334 XSLT_GET_IMPORT_PTR(version, style, version)
1336 if ((method != NULL) &&
1337 (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
1338 if (xmlStrEqual(method, (const xmlChar *) "html")) {
1339 ctxt->type = XSLT_OUTPUT_HTML;
1340 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
1341 res = htmlNewDoc(doctypeSystem, doctypePublic);
1343 if (version == NULL)
1344 version = (const xmlChar *) "4.0";
1345 #ifdef XSLT_GENERATE_HTML_DOCTYPE
1346 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
1348 res = htmlNewDoc(doctypeSystem, doctypePublic);
1352 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
1353 xsltGenericError(xsltGenericErrorContext,
1354 "xsltApplyStylesheet: unsupported method xhtml, using html\n",
1356 ctxt->type = XSLT_OUTPUT_HTML;
1357 res = htmlNewDoc(doctypeSystem, doctypePublic);
1360 } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
1361 ctxt->type = XSLT_OUTPUT_TEXT;
1362 res = xmlNewDoc(style->version);
1366 xsltGenericError(xsltGenericErrorContext,
1367 "xsltApplyStylesheet: unsupported method %s\n",
1372 ctxt->type = XSLT_OUTPUT_XML;
1373 res = xmlNewDoc(style->version);
1377 res->charset = XML_CHAR_ENCODING_UTF8;
1378 if (style->encoding != NULL)
1379 res->encoding = xmlStrdup(style->encoding);
1381 ctxt->insert = (xmlNodePtr) res;
1382 varsPush(ctxt, NULL);
1383 xsltApplyOneTemplate(ctxt, node, inst->children, 0);
1384 xsltFreeStackElemList(varsPop(ctxt));
1389 ret = xsltSaveResultToFilename((const char *) filename,
1392 xsltGenericError(xsltGenericErrorContext,
1393 "xsltDocumentElem: unable to save to %s\n",
1395 #ifdef WITH_XSLT_DEBUG_EXTRA
1397 xsltGenericDebug(xsltGenericDebugContext,
1398 "Wrote %d bytes to %s\n", ret,, filename);
1403 ctxt->output = oldOutput;
1404 ctxt->insert = oldInsert;
1405 ctxt->type = oldType;
1406 ctxt->outputFile = oldOutputFile;
1409 if (filename != NULL)
1412 xsltFreeStylesheet(style);
1417 /************************************************************************
1419 * Most of the XSLT-1.0 transformations *
1421 ************************************************************************/
1423 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
1427 * @ctxt: a XSLT process context
1428 * @node: the node in the source tree.
1429 * @inst: the xslt sort node
1430 * @comp: precomputed informations
1432 * function attached to xsl:sort nodes, but this should not be
1436 xsltSort(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
1437 xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst ATTRIBUTE_UNUSED,
1438 xsltStylePreCompPtr comp) {
1440 xsltGenericError(xsltGenericErrorContext,
1441 "xsl:sort : compilation failed\n");
1444 xsltGenericError(xsltGenericErrorContext,
1445 "xsl:sort : improper use this should not be reached\n");
1450 * @ctxt: a XSLT process context
1451 * @node: the node in the source tree.
1452 * @inst: the xslt copy node
1453 * @comp: precomputed informations
1455 * Process the xslt copy node on the source node
1458 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
1459 xmlNodePtr inst, xsltStylePreCompPtr comp) {
1460 xmlNodePtr copy, oldInsert;
1462 oldInsert = ctxt->insert;
1463 if (ctxt->insert != NULL) {
1464 switch (node->type) {
1466 case XML_CDATA_SECTION_NODE:
1468 * This text comes from the stylesheet
1469 * For stylesheets, the set of whitespace-preserving
1470 * element names consists of just xsl:text.
1472 #ifdef WITH_XSLT_DEBUG_PROCESS
1473 if (node->type == XML_CDATA_SECTION_NODE)
1474 xsltGenericDebug(xsltGenericDebugContext,
1475 "xsl:copy: CDATA text %s\n", node->content);
1477 xsltGenericDebug(xsltGenericDebugContext,
1478 "xsl:copy: text %s\n", node->content);
1480 copy = xmlNewText(node->content);
1482 if ((node->name == xmlStringTextNoenc) ||
1483 (node->type == XML_CDATA_SECTION_NODE))
1484 copy->name = xmlStringTextNoenc;
1485 xmlAddChild(ctxt->insert, copy);
1487 xsltGenericError(xsltGenericErrorContext,
1488 "xsl:copy: text copy failed\n");
1491 case XML_DOCUMENT_NODE:
1492 case XML_HTML_DOCUMENT_NODE:
1494 case XML_ELEMENT_NODE:
1495 #ifdef WITH_XSLT_DEBUG_PROCESS
1496 xsltGenericDebug(xsltGenericDebugContext,
1497 "xsl:copy: node %s\n", node->name);
1499 copy = xsltCopyNode(ctxt, node, ctxt->insert);
1500 ctxt->insert = copy;
1501 if (comp->use != NULL) {
1502 xsltApplyAttributeSet(ctxt, node, inst, comp->use);
1505 case XML_ATTRIBUTE_NODE: {
1506 #ifdef WITH_XSLT_DEBUG_PROCESS
1507 xsltGenericDebug(xsltGenericDebugContext,
1508 "xsl:copy: attribute %s\n", node->name);
1510 if (ctxt->insert->type == XML_ELEMENT_NODE) {
1511 xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
1512 if (attr->ns != NULL) {
1513 if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
1514 (xmlStrncasecmp(attr->ns->prefix,
1515 (xmlChar *)"xml", 3))) {
1516 ret = xmlCopyProp(ctxt->insert, attr);
1517 ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
1521 ret = xmlCopyProp(ctxt->insert, attr);
1523 cur = ctxt->insert->properties;
1525 while (cur->next != NULL)
1530 ctxt->insert->properties = ret;
1535 #ifdef WITH_XSLT_DEBUG_PROCESS
1536 xsltGenericDebug(xsltGenericDebugContext,
1537 "xsl:copy: PI %s\n", node->name);
1539 copy = xmlNewPI(node->name, node->content);
1540 xmlAddChild(ctxt->insert, copy);
1542 case XML_COMMENT_NODE:
1543 #ifdef WITH_XSLT_DEBUG_PROCESS
1544 xsltGenericDebug(xsltGenericDebugContext,
1545 "xsl:copy: comment\n");
1547 copy = xmlNewComment(node->content);
1548 xmlAddChild(ctxt->insert, copy);
1556 switch (node->type) {
1557 case XML_DOCUMENT_NODE:
1558 case XML_HTML_DOCUMENT_NODE:
1559 case XML_ELEMENT_NODE:
1560 varsPush(ctxt, NULL);
1561 xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
1562 xsltFreeStackElemList(varsPop(ctxt));
1567 ctxt->insert = oldInsert;
1572 * @ctxt: a XSLT process context
1573 * @node: the node in the source tree.
1574 * @inst: the xslt text node
1575 * @comp: precomputed informations
1577 * Process the xslt text node on the source node
1580 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
1581 xmlNodePtr inst, xsltStylePreCompPtr comp) {
1582 if ((inst->children != NULL) && (comp != NULL)) {
1583 xmlNodePtr text = inst->children;
1586 while (text != NULL) {
1587 if (((text->type != XML_TEXT_NODE) &&
1588 (text->type != XML_CDATA_SECTION_NODE)) ||
1589 (text->next != NULL)) {
1590 xsltGenericError(xsltGenericErrorContext,
1591 "xsl:text content problem\n");
1594 copy = xmlNewDocText(ctxt->output, text->content);
1595 if ((comp->noescape) || (text->type != XML_CDATA_SECTION_NODE)) {
1596 #ifdef WITH_XSLT_DEBUG_PARSING
1597 xsltGenericDebug(xsltGenericDebugContext,
1598 "Disable escaping: %s\n", text->content);
1600 copy->name = xmlStringTextNoenc;
1602 xmlAddChild(ctxt->insert, copy);
1610 * @ctxt: a XSLT process context
1611 * @node: the node in the source tree.
1612 * @inst: the xslt element node
1613 * @comp: precomputed informations
1615 * Process the xslt element node on the source node
1618 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
1619 xmlNodePtr inst, xsltStylePreCompPtr comp) {
1620 xmlChar *prop = NULL, *attributes = NULL;
1621 xmlChar *ncname = NULL, *name, *namespace;
1622 xmlChar *prefix = NULL;
1623 xmlChar *value = NULL;
1624 xmlNsPtr ns = NULL, oldns = NULL;
1626 xmlNodePtr oldInsert;
1629 if (ctxt->insert == NULL)
1631 if (!comp->has_name) {
1638 oldInsert = ctxt->insert;
1640 if (comp->name == NULL) {
1641 prop = xsltEvalAttrValueTemplate(ctxt, inst,
1642 (const xmlChar *)"name", XSLT_NAMESPACE);
1644 xsltGenericError(xsltGenericErrorContext,
1645 "xsl:element : name is missing\n");
1653 ncname = xmlSplitQName2(name, &prefix);
1654 if (ncname == NULL) {
1660 if ((comp->ns == NULL) && (comp->has_ns)) {
1661 namespace = xsltEvalAttrValueTemplate(ctxt, inst,
1662 (const xmlChar *)"namespace", XSLT_NAMESPACE);
1663 if (namespace != NULL) {
1664 ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
1668 } else if (comp->ns != NULL) {
1669 ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
1672 if ((ns == NULL) && (prefix != NULL)) {
1673 if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
1674 #ifdef WITH_XSLT_DEBUG_PARSING
1675 xsltGenericDebug(xsltGenericDebugContext,
1676 "xsl:element : xml prefix forbidden\n");
1680 oldns = xmlSearchNs(inst->doc, inst, prefix);
1681 if (oldns == NULL) {
1682 xsltGenericError(xsltGenericErrorContext,
1683 "xsl:element : no namespace bound to prefix %s\n", prefix);
1685 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
1689 copy = xmlNewDocNode(ctxt->output, ns, name, NULL);
1691 xsltGenericError(xsltGenericErrorContext,
1692 "xsl:element : creation of %s failed\n", name);
1695 if ((ns == NULL) && (oldns != NULL)) {
1696 /* very specific case xsltGetNamespace failed */
1697 ns = xmlNewNs(copy, oldns->href, oldns->prefix);
1700 xmlAddChild(ctxt->insert, copy);
1701 ctxt->insert = copy;
1702 if (comp->has_use) {
1703 if (comp->use != NULL) {
1704 xsltApplyAttributeSet(ctxt, node, inst, comp->use);
1706 attributes = xsltEvalAttrValueTemplate(ctxt, inst,
1707 (const xmlChar *)"use-attribute-sets", XSLT_NAMESPACE);
1708 if (attributes != NULL) {
1709 xsltApplyAttributeSet(ctxt, node, inst, attributes);
1710 xmlFree(attributes);
1715 varsPush(ctxt, NULL);
1716 xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
1717 xsltFreeStackElemList(varsPop(ctxt));
1719 ctxt->insert = oldInsert;
1734 * @ctxt: a XSLT process context
1735 * @node: the node in the source tree.
1736 * @inst: the xslt attribute node
1737 * @comp: precomputed informations
1739 * Process the xslt attribute node on the source node
1742 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
1743 xmlNodePtr inst, xsltStylePreCompPtr comp) {
1744 xmlChar *prop = NULL;
1745 xmlChar *ncname = NULL, *name, *namespace;
1746 xmlChar *prefix = NULL;
1747 xmlChar *value = NULL;
1752 if (ctxt->insert == NULL)
1755 xsltGenericError(xsltGenericErrorContext,
1756 "xsl:attribute : compilation failed\n");
1760 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
1762 if (!comp->has_name) {
1765 if (ctxt->insert->children != NULL) {
1766 xsltGenericError(xsltGenericErrorContext,
1767 "xsl:attribute : node has already children\n");
1770 if (comp->name == NULL) {
1771 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name",
1774 xsltGenericError(xsltGenericErrorContext,
1775 "xsl:attribute : name is missing\n");
1783 ncname = xmlSplitQName2(name, &prefix);
1784 if (ncname == NULL) {
1789 if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
1790 #ifdef WITH_XSLT_DEBUG_PARSING
1791 xsltGenericDebug(xsltGenericDebugContext,
1792 "xsl:attribute : xml prefix forbidden\n");
1796 if ((comp->ns == NULL) && (comp->has_ns)) {
1797 namespace = xsltEvalAttrValueTemplate(ctxt, inst,
1798 (const xmlChar *)"namespace", XSLT_NAMESPACE);
1799 if (namespace != NULL) {
1800 ns = xsltGetSpecialNamespace(ctxt, inst, namespace, prefix,
1804 if (prefix != NULL) {
1805 ns = xmlSearchNs(inst->doc, inst, prefix);
1807 xsltGenericError(xsltGenericErrorContext,
1808 "xsl:attribute : no namespace bound to prefix %s\n", prefix);
1810 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
1814 } else if (comp->ns != NULL) {
1815 ns = xsltGetSpecialNamespace(ctxt, inst, comp->ns, prefix,
1819 value = xsltEvalTemplateString(ctxt, node, inst);
1820 if (value == NULL) {
1822 attr = xmlSetNsProp(ctxt->insert, ns, name,
1823 (const xmlChar *)"");
1825 attr = xmlSetProp(ctxt->insert, name, (const xmlChar *)"");
1828 attr = xmlSetNsProp(ctxt->insert, ns, name, value);
1830 attr = xmlSetProp(ctxt->insert, name, value);
1848 * @ctxt: a XSLT process context
1849 * @node: the node in the source tree.
1850 * @inst: the xslt comment node
1851 * @comp: precomputed informations
1853 * Process the xslt comment node on the source node
1856 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
1857 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
1858 xmlChar *value = NULL;
1861 value = xsltEvalTemplateString(ctxt, node, inst);
1862 /* TODO: use or generate the compiled form */
1863 /* TODO: check that there is no -- sequence and doesn't end up with - */
1864 #ifdef WITH_XSLT_DEBUG_PROCESS
1866 xsltGenericDebug(xsltGenericDebugContext,
1867 "xsl:comment: empty\n");
1869 xsltGenericDebug(xsltGenericDebugContext,
1870 "xsl:comment: content %s\n", value);
1873 comment = xmlNewComment(value);
1874 xmlAddChild(ctxt->insert, comment);
1881 * xsltProcessingInstruction:
1882 * @ctxt: a XSLT process context
1883 * @node: the node in the source tree.
1884 * @inst: the xslt processing-instruction node
1885 * @comp: precomputed informations
1887 * Process the xslt processing-instruction node on the source node
1890 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
1891 xmlNodePtr inst, xsltStylePreCompPtr comp) {
1892 xmlChar *ncname = NULL, *name;
1893 xmlChar *value = NULL;
1897 if (ctxt->insert == NULL)
1899 if (comp->has_name == 0)
1901 if (comp->name == NULL) {
1902 ncname = xsltEvalAttrValueTemplate(ctxt, inst,
1903 (const xmlChar *)"name", XSLT_NAMESPACE);
1904 if (ncname == NULL) {
1905 xsltGenericError(xsltGenericErrorContext,
1906 "xsl:processing-instruction : name is missing\n");
1913 /* TODO: check that it's both an an NCName and a PITarget. */
1916 value = xsltEvalTemplateString(ctxt, node, inst);
1917 /* TODO: check that there is no ?> sequence */
1918 #ifdef WITH_XSLT_DEBUG_PROCESS
1920 xsltGenericDebug(xsltGenericDebugContext,
1921 "xsl:processing-instruction: %s empty\n", ncname);
1923 xsltGenericDebug(xsltGenericDebugContext,
1924 "xsl:processing-instruction: %s content %s\n", ncname, value);
1927 pi = xmlNewPI(name, value);
1928 xmlAddChild(ctxt->insert, pi);
1939 * @ctxt: a XSLT process context
1940 * @node: the node in the source tree.
1941 * @inst: the xslt copy-of node
1942 * @comp: precomputed informations
1944 * Process the xslt copy-of node on the source node
1947 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
1948 xmlNodePtr inst, xsltStylePreCompPtr comp) {
1949 xmlXPathObjectPtr res = NULL;
1950 xmlNodePtr copy = NULL;
1951 xmlNodeSetPtr list = NULL;
1953 int oldProximityPosition, oldContextSize;
1955 xmlNsPtr *oldNamespaces;
1957 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1959 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
1960 xsltGenericError(xsltGenericErrorContext,
1961 "xsl:copy-of : compilation failed\n");
1965 #ifdef WITH_XSLT_DEBUG_PROCESS
1966 xsltGenericDebug(xsltGenericDebugContext,
1967 "xsltCopyOf: select %s\n", comp->select);
1970 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1971 oldContextSize = ctxt->xpathCtxt->contextSize;
1972 oldNsNr = ctxt->xpathCtxt->nsNr;
1973 oldNamespaces = ctxt->xpathCtxt->namespaces;
1974 ctxt->xpathCtxt->node = node;
1975 ctxt->xpathCtxt->namespaces = comp->nsList;
1976 ctxt->xpathCtxt->nsNr = comp->nsNr;
1977 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
1978 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1979 ctxt->xpathCtxt->contextSize = oldContextSize;
1980 ctxt->xpathCtxt->nsNr = oldNsNr;
1981 ctxt->xpathCtxt->namespaces = oldNamespaces;
1983 if (res->type == XPATH_NODESET) {
1984 #ifdef WITH_XSLT_DEBUG_PROCESS
1985 xsltGenericDebug(xsltGenericDebugContext,
1986 "xsltCopyOf: result is a node set\n");
1988 list = res->nodesetval;
1990 /* sort the list in document order */
1991 xsltDocumentSortFunction(list);
1992 /* append everything in this order under ctxt->insert */
1993 for (i = 0;i < list->nodeNr;i++) {
1994 if (list->nodeTab[i] == NULL)
1996 if ((list->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1997 (list->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) {
1998 xsltCopyTreeList(ctxt, list->nodeTab[i]->children,
2000 } else if (list->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
2001 xsltCopyProp(ctxt, ctxt->insert,
2002 (xmlAttrPtr) list->nodeTab[i]);
2004 xsltCopyTree(ctxt, list->nodeTab[i], ctxt->insert);
2008 } else if (res->type == XPATH_XSLT_TREE) {
2009 #ifdef WITH_XSLT_DEBUG_PROCESS
2010 xsltGenericDebug(xsltGenericDebugContext,
2011 "xsltCopyOf: result is a result tree fragment\n");
2013 list = res->nodesetval;
2014 if ((list != NULL) && (list->nodeTab != NULL) &&
2015 (list->nodeTab[0] != NULL)) {
2016 xsltCopyTreeList(ctxt, list->nodeTab[0]->children,
2020 /* convert to a string */
2021 res = xmlXPathConvertString(res);
2022 if ((res != NULL) && (res->type == XPATH_STRING)) {
2023 /* append content as text node */
2024 copy = xmlNewText(res->stringval);
2026 xmlAddChild(ctxt->insert, copy);
2030 xsltGenericError(xsltGenericErrorContext,
2031 "xsltCopyOf: text copy failed\n");
2033 #ifdef WITH_XSLT_DEBUG_PROCESS
2035 xsltGenericDebug(xsltGenericDebugContext,
2036 "xsltCopyOf: result %s\n", res->stringval);
2042 xmlXPathFreeObject(res);
2047 * @ctxt: a XSLT process context
2048 * @node: the node in the source tree.
2049 * @inst: the xslt value-of node
2050 * @comp: precomputed informations
2052 * Process the xslt value-of node on the source node
2055 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
2056 xmlNodePtr inst, xsltStylePreCompPtr comp) {
2057 xmlXPathObjectPtr res = NULL;
2058 xmlNodePtr copy = NULL;
2059 int oldProximityPosition, oldContextSize;
2061 xmlNsPtr *oldNamespaces;
2063 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2065 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
2066 xsltGenericError(xsltGenericErrorContext,
2067 "xsl:value-of : compilation failed\n");
2071 #ifdef WITH_XSLT_DEBUG_PROCESS
2072 xsltGenericDebug(xsltGenericDebugContext,
2073 "xsltValueOf: select %s\n", comp->select);
2076 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2077 oldContextSize = ctxt->xpathCtxt->contextSize;
2078 oldNsNr = ctxt->xpathCtxt->nsNr;
2079 oldNamespaces = ctxt->xpathCtxt->namespaces;
2080 ctxt->xpathCtxt->node = node;
2081 ctxt->xpathCtxt->namespaces = comp->nsList;
2082 ctxt->xpathCtxt->nsNr = comp->nsNr;
2083 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2084 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2085 ctxt->xpathCtxt->contextSize = oldContextSize;
2086 ctxt->xpathCtxt->nsNr = oldNsNr;
2087 ctxt->xpathCtxt->namespaces = oldNamespaces;
2089 if (res->type != XPATH_STRING)
2090 res = xmlXPathConvertString(res);
2091 if (res->type == XPATH_STRING) {
2092 copy = xmlNewText(res->stringval);
2095 copy->name = xmlStringTextNoenc;
2096 xmlAddChild(ctxt->insert, copy);
2101 xsltGenericError(xsltGenericErrorContext,
2102 "xsltValueOf: text copy failed\n");
2104 #ifdef WITH_XSLT_DEBUG_PROCESS
2106 xsltGenericDebug(xsltGenericDebugContext,
2107 "xsltValueOf: result %s\n", res->stringval);
2110 xmlXPathFreeObject(res);
2115 * @ctxt: a XSLT process context
2116 * @node: the node in the source tree.
2117 * @inst: the xslt number node
2118 * @comp: precomputed informations
2120 * Process the xslt number node on the source node
2123 xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
2124 xmlNodePtr inst, xsltStylePreCompPtr comp)
2127 xsltGenericError(xsltGenericErrorContext,
2128 "xsl:number : compilation failed\n");
2132 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
2135 comp->numdata.doc = inst->doc;
2136 comp->numdata.node = inst;
2138 xsltNumberFormat(ctxt, &comp->numdata, node);
2143 * @ctxt: a XSLT process context
2144 * @node: the node in the source tree.
2145 * @inst: the xslt apply-imports node
2146 * @comp: precomputed informations
2148 * Process the xslt apply-imports node on the source node
2151 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr node,
2152 xmlNodePtr inst ATTRIBUTE_UNUSED, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
2153 xsltTemplatePtr template;
2155 if ((ctxt->templ == NULL) || (ctxt->templ->style == NULL)) {
2156 xsltGenericError(xsltGenericErrorContext,
2157 "xsl:apply-imports : internal error no current template\n");
2160 template = xsltGetTemplate(ctxt, node, ctxt->templ->style);
2161 if (template != NULL) {
2162 templPush(ctxt, template);
2163 varsPush(ctxt, NULL);
2164 xsltApplyOneTemplate(ctxt, node, template->content, 1);
2165 xsltFreeStackElemList(varsPop(ctxt));
2172 * @ctxt: a XSLT process context
2173 * @node: the node in the source tree.
2174 * @inst: the xslt call-template node
2175 * @comp: precomputed informations
2177 * Process the xslt call-template node on the source node
2180 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2181 xmlNodePtr inst, xsltStylePreCompPtr comp) {
2182 xmlNodePtr cur = NULL;
2183 xsltStackElemPtr params = NULL, param;
2186 if (ctxt->insert == NULL)
2189 xsltGenericError(xsltGenericErrorContext,
2190 "xsl:call-template : compilation failed\n");
2195 * The template must have been precomputed
2197 if (comp->templ == NULL) {
2198 comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
2199 if (comp->templ == NULL) {
2200 xsltGenericError(xsltGenericErrorContext,
2201 "xsl:call-template : template %s not found\n", comp->name);
2207 * Create a new frame but block access to variables
2209 templPush(ctxt, comp->templ);
2210 cur = inst->children;
2211 while (cur != NULL) {
2212 if (ctxt->state == XSLT_STATE_STOPPED) break;
2213 if (IS_XSLT_ELEM(cur)) {
2214 if (IS_XSLT_NAME(cur, "with-param")) {
2215 param = xsltParseStylesheetCallerParam(ctxt, cur);
2216 if (param != NULL) {
2217 param->next = params;
2221 xsltGenericError(xsltGenericDebugContext,
2222 "xsl:call-template: misplaced xsl:%s\n", cur->name);
2225 xsltGenericError(xsltGenericDebugContext,
2226 "xsl:call-template: misplaced %s element\n", cur->name);
2230 varsPush(ctxt, params);
2231 xsltApplyOneTemplate(ctxt, node, comp->templ->content, 1);
2232 xsltFreeStackElemList(varsPop(ctxt));
2237 * xsltApplyTemplates:
2238 * @ctxt: a XSLT process context
2239 * @node: the node in the source tree.
2240 * @inst: the apply-templates node
2241 * @comp: precomputed informations
2243 * Process the apply-templates node on the source node
2246 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
2247 xmlNodePtr inst, xsltStylePreCompPtr comp) {
2248 xmlNodePtr cur, delete = NULL, oldNode;
2249 xmlXPathObjectPtr res = NULL;
2250 xmlNodeSetPtr list = NULL, oldList;
2251 int i, oldProximityPosition, oldContextSize;
2252 const xmlChar *oldMode, *oldModeURI;
2253 xsltStackElemPtr params = NULL, param, tmp, p;
2255 xmlNodePtr sorts[XSLT_MAX_SORT];
2256 xmlDocPtr oldXDocPtr;
2257 xsltDocumentPtr oldCDocPtr;
2259 xmlNsPtr *oldNamespaces;
2262 xsltGenericError(xsltGenericErrorContext,
2263 "xsl:apply-templates : compilation failed\n");
2266 if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
2269 #ifdef WITH_XSLT_DEBUG_PROCESS
2270 if ((node != NULL) && (node->name != NULL))
2271 xsltGenericDebug(xsltGenericDebugContext,
2272 "xsltApplyTemplates: node: %s\n", node->name);
2278 oldNode = ctxt->node;
2279 oldMode = ctxt->mode;
2280 oldModeURI = ctxt->modeURI;
2281 ctxt->mode = comp->mode;
2282 ctxt->modeURI = comp->modeURI;
2285 * The xpath context size and proximity position, as
2286 * well as the xpath and context documents, may be changed
2287 * so we save their initial state and will restore on exit
2289 oldXDocPtr = ctxt->xpathCtxt->doc;
2290 oldCDocPtr = ctxt->document;
2291 oldContextSize = ctxt->xpathCtxt->contextSize;
2292 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2293 oldNsNr = ctxt->xpathCtxt->nsNr;
2294 oldNamespaces = ctxt->xpathCtxt->namespaces;
2295 oldList = ctxt->nodeList;
2297 if (comp->select != NULL) {
2298 if (comp->comp == NULL) {
2299 xsltGenericError(xsltGenericErrorContext,
2300 "xsl:apply-templates : compilation failed\n");
2303 #ifdef WITH_XSLT_DEBUG_PROCESS
2304 xsltGenericDebug(xsltGenericDebugContext,
2305 "xsltApplyTemplates: select %s\n", comp->select);
2308 ctxt->xpathCtxt->node = node;
2309 ctxt->xpathCtxt->namespaces = comp->nsList;
2310 ctxt->xpathCtxt->nsNr = comp->nsNr;
2311 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2312 ctxt->xpathCtxt->contextSize = oldContextSize;
2313 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2315 if (res->type == XPATH_NODESET) {
2316 list = res->nodesetval;
2317 res->nodesetval = NULL;
2323 #ifdef WITH_XSLT_DEBUG_PROCESS
2324 xsltGenericDebug(xsltGenericDebugContext,
2325 "xsltApplyTemplates: select didn't evaluate to a node list\n");
2331 * Build an XPath nodelist with the children
2333 list = xmlXPathNodeSetCreate(NULL);
2334 cur = node->children;
2335 while (cur != NULL) {
2336 switch (cur->type) {
2338 if ((IS_BLANK_NODE(cur)) &&
2339 (cur->parent != NULL) &&
2340 (ctxt->style->stripSpaces != NULL)) {
2343 val = (const xmlChar *)
2344 xmlHashLookup(ctxt->style->stripSpaces,
2346 if ((val != NULL) &&
2347 (xmlStrEqual(val, (xmlChar *) "strip"))) {
2352 /* no break on purpose */
2353 case XML_DOCUMENT_NODE:
2354 case XML_HTML_DOCUMENT_NODE:
2355 case XML_ELEMENT_NODE:
2356 case XML_CDATA_SECTION_NODE:
2358 case XML_COMMENT_NODE:
2359 xmlXPathNodeSetAdd(list, cur);
2362 #ifdef WITH_XSLT_DEBUG_PROCESS
2363 xsltGenericDebug(xsltGenericDebugContext,
2364 "xsltApplyTemplates: skipping cur type %d\n",
2370 if (delete != NULL) {
2371 #ifdef WITH_XSLT_DEBUG_PROCESS
2372 xsltGenericDebug(xsltGenericDebugContext,
2373 "xsltApplyTemplates: removing ignorable blank cur\n");
2375 xmlUnlinkNode(delete);
2376 xmlFreeNode(delete);
2382 #ifdef WITH_XSLT_DEBUG_PROCESS
2384 xsltGenericDebug(xsltGenericDebugContext,
2385 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
2388 ctxt->nodeList = list;
2389 ctxt->xpathCtxt->contextSize = list->nodeNr;
2392 * handle (or skip) the xsl:sort and xsl:with-param
2394 cur = inst->children;
2396 if (ctxt->state == XSLT_STATE_STOPPED) break;
2397 if (IS_XSLT_ELEM(cur)) {
2398 if (IS_XSLT_NAME(cur, "with-param")) {
2399 param = xsltParseStylesheetCallerParam(ctxt, cur);
2400 if (param != NULL) {
2401 param->next = params;
2404 } else if (IS_XSLT_NAME(cur, "sort")) {
2405 if (nbsorts >= XSLT_MAX_SORT) {
2406 xsltGenericError(xsltGenericDebugContext,
2407 "xsl:call-template: %s too many sort\n", node->name);
2409 sorts[nbsorts++] = cur;
2412 xsltGenericError(xsltGenericDebugContext,
2413 "xsl:call-template: misplaced xsl:%s\n", cur->name);
2416 xsltGenericError(xsltGenericDebugContext,
2417 "xsl:call-template: misplaced %s element\n", cur->name);
2423 xsltDoSortFunction(ctxt, sorts, nbsorts);
2426 for (i = 0;i < list->nodeNr;i++) {
2427 ctxt->node = list->nodeTab[i];
2428 ctxt->xpathCtxt->proximityPosition = i + 1;
2429 /* For a 'select' nodeset, need to check if document has changed */
2430 if ( (list->nodeTab[i]->doc!=NULL) &&
2431 (list->nodeTab[i]->doc->doc!=NULL) &&
2432 (list->nodeTab[i]->doc->doc)!=ctxt->xpathCtxt->doc) {
2433 /* The nodeset is from another document, so must change */
2434 ctxt->xpathCtxt->doc=list->nodeTab[i]->doc->doc;
2435 if ((ctxt->document =
2436 xsltFindDocument(ctxt,list->nodeTab[i]->doc->doc))==NULL) {
2437 xsltGenericError(xsltGenericErrorContext,
2438 "xsl:apply-templates : can't find doc\n");
2441 ctxt->xpathCtxt->node = list->nodeTab[i];
2442 #ifdef WITH_XSLT_DEBUG_PROCESS
2443 xsltGenericDebug(xsltGenericDebugContext,
2444 "xsltApplyTemplates: Changing document - context doc %s, xpathdoc %s\n",
2445 ctxt->document->doc->URL, ctxt->xpathCtxt->doc->URL);
2448 varsPush(ctxt, params);
2449 xsltProcessOneNode(ctxt, list->nodeTab[i]);
2450 tmp = varsPop(ctxt);
2452 * Free other parameter and variables which may have been
2453 * added to the set defined in the caller.
2455 if (params == NULL) {
2456 xsltFreeStackElemList(tmp);
2457 } else if (tmp != params) {
2459 while ((p != NULL) && (p->next != params))
2462 xsltFreeStackElemList(tmp);
2465 xsltFreeStackElemList(tmp);
2469 xsltFreeStackElemList(params); /* free the parameter list */
2472 ctxt->nodeList = oldList;
2473 ctxt->xpathCtxt->contextSize = oldContextSize;
2474 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2475 ctxt->xpathCtxt->doc = oldXDocPtr;
2476 ctxt->document = oldCDocPtr;
2477 ctxt->xpathCtxt->nsNr = oldNsNr;
2478 ctxt->xpathCtxt->namespaces = oldNamespaces;
2480 ctxt->node = oldNode;
2481 ctxt->mode = oldMode;
2482 ctxt->modeURI = oldModeURI;
2484 xmlXPathFreeObject(res);
2486 xmlXPathFreeNodeSet(list);
2492 * @ctxt: a XSLT process context
2493 * @node: the node in the source tree.
2494 * @inst: the xslt choose node
2495 * @comp: precomputed informations
2497 * Process the xslt choose node on the source node
2500 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node,
2501 xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
2502 xmlChar *prop = NULL;
2503 xmlXPathObjectPtr res = NULL;
2504 xmlNodePtr replacement, when;
2506 int oldProximityPosition, oldContextSize;
2508 xmlNsPtr *oldNamespaces;
2510 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2516 replacement = inst->children;
2517 if (replacement == NULL) {
2518 xsltGenericError(xsltGenericErrorContext,
2519 "xsl:choose: empty content not allowed\n");
2522 if ((!IS_XSLT_ELEM(replacement)) ||
2523 (!IS_XSLT_NAME(replacement, "when"))) {
2524 xsltGenericError(xsltGenericErrorContext,
2525 "xsl:choose: xsl:when expected first\n");
2528 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "when"))) {
2529 xsltStylePreCompPtr wcomp = replacement->_private;
2531 if ((wcomp == NULL) || (wcomp->test == NULL) || (wcomp->comp == NULL)) {
2532 xsltGenericError(xsltGenericErrorContext,
2533 "xsl:when: compilation failed !\n");
2537 #ifdef WITH_XSLT_DEBUG_PROCESS
2538 xsltGenericDebug(xsltGenericDebugContext,
2539 "xsl:when: test %s\n", wcomp->test);
2542 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2543 oldContextSize = ctxt->xpathCtxt->contextSize;
2544 oldNsNr = ctxt->xpathCtxt->nsNr;
2545 oldNamespaces = ctxt->xpathCtxt->namespaces;
2546 ctxt->xpathCtxt->node = node;
2547 ctxt->xpathCtxt->namespaces = comp->nsList;
2548 ctxt->xpathCtxt->nsNr = comp->nsNr;
2549 res = xmlXPathCompiledEval(wcomp->comp, ctxt->xpathCtxt);
2550 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2551 ctxt->xpathCtxt->contextSize = oldContextSize;
2552 ctxt->xpathCtxt->nsNr = oldNsNr;
2553 ctxt->xpathCtxt->namespaces = oldNamespaces;
2555 if (res->type != XPATH_BOOLEAN)
2556 res = xmlXPathConvertBoolean(res);
2557 if (res->type == XPATH_BOOLEAN)
2558 doit = res->boolval;
2560 #ifdef WITH_XSLT_DEBUG_PROCESS
2561 xsltGenericDebug(xsltGenericDebugContext,
2562 "xsl:when: test didn't evaluate to a boolean\n");
2568 #ifdef WITH_XSLT_DEBUG_PROCESS
2569 xsltGenericDebug(xsltGenericDebugContext,
2570 "xsl:when: test evaluate to %d\n", doit);
2573 varsPush(ctxt, NULL);
2574 xsltApplyOneTemplate(ctxt, ctxt->node, when->children, 0);
2575 xsltFreeStackElemList(varsPop(ctxt));
2582 xmlXPathFreeObject(res);
2584 replacement = replacement->next;
2586 if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "otherwise"))) {
2587 varsPush(ctxt, NULL);
2588 xsltApplyOneTemplate(ctxt, ctxt->node, replacement->children, 0);
2589 xsltFreeStackElemList(varsPop(ctxt));
2590 replacement = replacement->next;
2592 if (replacement != NULL) {
2593 xsltGenericError(xsltGenericErrorContext,
2594 "xsl:choose: unexpected content %s\n", replacement->name);
2603 xmlXPathFreeObject(res);
2608 * @ctxt: a XSLT process context
2609 * @node: the node in the source tree.
2610 * @inst: the xslt if node
2611 * @comp: precomputed informations
2613 * Process the xslt if node on the source node
2616 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
2617 xmlNodePtr inst, xsltStylePreCompPtr comp) {
2618 xmlXPathObjectPtr res = NULL;
2620 int oldContextSize, oldProximityPosition;
2622 xmlNsPtr *oldNamespaces;
2624 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2626 if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
2627 xsltGenericError(xsltGenericErrorContext,
2628 "xsl:if : compilation failed\n");
2632 #ifdef WITH_XSLT_DEBUG_PROCESS
2633 xsltGenericDebug(xsltGenericDebugContext,
2634 "xsltIf: test %s\n", comp->test);
2637 oldContextSize = ctxt->xpathCtxt->contextSize;
2638 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2639 oldNsNr = ctxt->xpathCtxt->nsNr;
2640 oldNamespaces = ctxt->xpathCtxt->namespaces;
2641 ctxt->xpathCtxt->node = node;
2642 ctxt->xpathCtxt->namespaces = comp->nsList;
2643 ctxt->xpathCtxt->nsNr = comp->nsNr;
2644 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2645 ctxt->xpathCtxt->contextSize = oldContextSize;
2646 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2647 ctxt->xpathCtxt->nsNr = oldNsNr;
2648 ctxt->xpathCtxt->namespaces = oldNamespaces;
2650 if (res->type != XPATH_BOOLEAN)
2651 res = xmlXPathConvertBoolean(res);
2652 if (res->type == XPATH_BOOLEAN)
2653 doit = res->boolval;
2655 #ifdef WITH_XSLT_DEBUG_PROCESS
2656 xsltGenericDebug(xsltGenericDebugContext,
2657 "xsltIf: test didn't evaluate to a boolean\n");
2663 #ifdef WITH_XSLT_DEBUG_PROCESS
2664 xsltGenericDebug(xsltGenericDebugContext,
2665 "xsltIf: test evaluate to %d\n", doit);
2668 varsPush(ctxt, NULL);
2669 xsltApplyOneTemplate(ctxt, node, inst->children, 0);
2670 xsltFreeStackElemList(varsPop(ctxt));
2675 xmlXPathFreeObject(res);
2680 * @ctxt: a XSLT process context
2681 * @node: the node in the source tree.
2682 * @inst: the xslt for-each node
2683 * @comp: precomputed informations
2685 * Process the xslt for-each node on the source node
2688 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
2689 xmlNodePtr inst, xsltStylePreCompPtr comp) {
2690 xmlXPathObjectPtr res = NULL;
2691 xmlNodePtr replacement;
2692 xmlNodeSetPtr list = NULL, oldList;
2693 int i, oldProximityPosition, oldContextSize;
2694 xmlNodePtr oldNode = ctxt->node;
2696 xmlNodePtr sorts[XSLT_MAX_SORT];
2697 xmlDocPtr oldXDocPtr;
2698 xsltDocumentPtr oldCDocPtr;
2700 xmlNsPtr *oldNamespaces;
2702 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2704 if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
2705 xsltGenericError(xsltGenericErrorContext,
2706 "xsl:for-each : compilation failed\n");
2710 #ifdef WITH_XSLT_DEBUG_PROCESS
2711 xsltGenericDebug(xsltGenericDebugContext,
2712 "xsltForEach: select %s\n", comp->select);
2715 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2716 oldContextSize = ctxt->xpathCtxt->contextSize;
2717 oldNsNr = ctxt->xpathCtxt->nsNr;
2718 oldNamespaces = ctxt->xpathCtxt->namespaces;
2719 ctxt->xpathCtxt->node = node;
2720 ctxt->xpathCtxt->namespaces = comp->nsList;
2721 ctxt->xpathCtxt->nsNr = comp->nsNr;
2722 oldCDocPtr = ctxt->document;
2723 oldXDocPtr = ctxt->xpathCtxt->doc;
2724 res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
2725 ctxt->xpathCtxt->contextSize = oldContextSize;
2726 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2727 ctxt->xpathCtxt->nsNr = oldNsNr;
2728 ctxt->xpathCtxt->namespaces = oldNamespaces;
2730 if (res->type == XPATH_NODESET)
2731 list = res->nodesetval;
2734 #ifdef WITH_XSLT_DEBUG_PROCESS
2735 xsltGenericDebug(xsltGenericDebugContext,
2736 "xsltForEach: select didn't evaluate to a node list\n");
2741 #ifdef WITH_XSLT_DEBUG_PROCESS
2742 xsltGenericDebug(xsltGenericDebugContext,
2743 "xsltForEach: select evaluates to %d nodes\n", list->nodeNr);
2746 oldList = ctxt->nodeList;
2747 ctxt->nodeList = list;
2748 oldContextSize = ctxt->xpathCtxt->contextSize;
2749 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2750 ctxt->xpathCtxt->contextSize = list->nodeNr;
2753 * handle and skip the xsl:sort
2755 replacement = inst->children;
2756 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
2757 if (nbsorts >= XSLT_MAX_SORT) {
2758 xsltGenericError(xsltGenericDebugContext,
2759 "xsl:for-each: too many sort\n");
2761 sorts[nbsorts++] = replacement;
2763 replacement = replacement->next;
2767 xsltDoSortFunction(ctxt, sorts, nbsorts);
2771 for (i = 0;i < list->nodeNr;i++) {
2772 ctxt->node = list->nodeTab[i];
2773 ctxt->xpathCtxt->proximityPosition = i + 1;
2774 /* For a 'select' nodeset, need to check if document has changed */
2775 if ( (list->nodeTab[i]->doc!=NULL) &&
2776 (list->nodeTab[i]->doc->doc!=NULL) &&
2777 (list->nodeTab[i]->doc->doc)!=ctxt->xpathCtxt->doc) {
2778 /* The nodeset is from another document, so must change */
2779 ctxt->xpathCtxt->doc=list->nodeTab[i]->doc->doc;
2780 if ((ctxt->document =
2781 xsltFindDocument(ctxt,list->nodeTab[i]->doc->doc))==NULL) {
2782 xsltGenericError(xsltGenericErrorContext,
2783 "xsl:apply-templates : can't find doc\n");
2786 ctxt->xpathCtxt->node = list->nodeTab[i];
2787 #ifdef WITH_XSLT_DEBUG_PROCESS
2788 xsltGenericDebug(xsltGenericDebugContext,
2789 "xsltApplyTemplates: Changing document - context doc %s, xpathdoc %s\n",
2790 ctxt->document->doc->URL, ctxt->xpathCtxt->doc->URL);
2793 /* ctxt->insert = oldInsert; */
2794 varsPush(ctxt, NULL);
2795 xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement, 0);
2796 xsltFreeStackElemList(varsPop(ctxt));
2798 ctxt->document = oldCDocPtr;
2799 ctxt->nodeList = oldList;
2800 ctxt->node = oldNode;
2801 ctxt->xpathCtxt->doc = oldXDocPtr;
2802 ctxt->xpathCtxt->contextSize = oldContextSize;
2803 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2804 ctxt->xpathCtxt->nsNr = oldNsNr;
2805 ctxt->xpathCtxt->namespaces = oldNamespaces;
2809 xmlXPathFreeObject(res);
2812 /************************************************************************
2814 * Generic interface *
2816 ************************************************************************/
2818 #ifdef XSLT_GENERATE_HTML_DOCTYPE
2819 typedef struct xsltHTMLVersion {
2820 const char *version;
2825 static xsltHTMLVersion xsltHTMLVersions[] = {
2826 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
2827 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
2828 { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
2829 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
2830 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
2831 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
2832 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
2833 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
2834 { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
2835 "http://www.w3.org/TR/html4/strict.dtd"},
2836 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
2837 "http://www.w3.org/TR/html4/loose.dtd"},
2838 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
2839 "http://www.w3.org/TR/html4/frameset.dtd"},
2840 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
2841 "http://www.w3.org/TR/html4/loose.dtd"},
2842 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
2847 * @version: the version string
2848 * @public: used to return the public ID
2849 * @system: used to return the system ID
2851 * Returns -1 if not found, 0 otherwise and the system and public
2852 * Identifier for this given verion of HTML
2855 xsltGetHTMLIDs(const xmlChar *version, const xmlChar **public,
2856 const xmlChar **system) {
2858 if (version == NULL)
2860 for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
2862 if (!xmlStrcasecmp(version,
2863 (const xmlChar *) xsltHTMLVersions[i].version)) {
2865 *public = (const xmlChar *) xsltHTMLVersions[i].public;
2867 *system = (const xmlChar *) xsltHTMLVersions[i].system;
2876 * xsltApplyStylesheetInternal:
2877 * @style: a parsed XSLT stylesheet
2878 * @doc: a parsed XML document
2879 * @params: a NULL terminated arry of parameters names/values tuples
2880 * @output: the targetted output
2882 * Apply the stylesheet to the document
2883 * NOTE: This may lead to a non-wellformed output XML wise !
2885 * Returns the result document or NULL in case of error
2888 xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
2889 const char **params, const char *output) {
2890 xmlDocPtr res = NULL;
2891 xsltTransformContextPtr ctxt = NULL;
2893 const xmlChar *method;
2894 const xmlChar *doctypePublic;
2895 const xmlChar *doctypeSystem;
2896 const xmlChar *version;
2897 xsltStackElemPtr variables;
2898 xsltStackElemPtr vptr;
2901 if ((style == NULL) || (doc == NULL))
2903 ctxt = xsltNewTransformContext(style, doc);
2906 xsltRegisterExtras(ctxt);
2908 ctxt->outputFile = output;
2910 ctxt->outputFile = NULL;
2912 XSLT_GET_IMPORT_PTR(method, style, method)
2913 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
2914 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
2915 XSLT_GET_IMPORT_PTR(version, style, version)
2917 if ((method != NULL) &&
2918 (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
2919 if (xmlStrEqual(method, (const xmlChar *) "html")) {
2920 ctxt->type = XSLT_OUTPUT_HTML;
2921 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
2922 res = htmlNewDoc(doctypeSystem, doctypePublic);
2924 if (version == NULL)
2925 version = (const xmlChar *) "4.0";
2926 #ifdef XSLT_GENERATE_HTML_DOCTYPE
2927 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
2929 res = htmlNewDoc(doctypeSystem, doctypePublic);
2933 } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
2934 xsltGenericError(xsltGenericErrorContext,
2935 "xsltApplyStylesheet: unsupported method xhtml, using html\n",
2937 ctxt->type = XSLT_OUTPUT_HTML;
2938 res = htmlNewDoc(doctypeSystem, doctypePublic);
2941 } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
2942 ctxt->type = XSLT_OUTPUT_TEXT;
2943 res = xmlNewDoc(style->version);
2947 xsltGenericError(xsltGenericErrorContext,
2948 "xsltApplyStylesheet: unsupported method %s\n",
2953 ctxt->type = XSLT_OUTPUT_XML;
2954 res = xmlNewDoc(style->version);
2958 res->charset = XML_CHAR_ENCODING_UTF8;
2959 if (style->encoding != NULL)
2960 res->encoding = xmlStrdup(style->encoding);
2961 variables = style->variables;
2964 * Start the evaluation, evaluate the params, the stylesheets globals
2965 * and start by processing the top node.
2968 ctxt->insert = (xmlNodePtr) res;
2969 ctxt->globalVars = xmlHashCreate(20);
2971 xsltEvalUserParams(ctxt, params);
2972 xsltEvalGlobalVariables(ctxt);
2973 ctxt->node = (xmlNodePtr) doc;
2974 varsPush(ctxt, NULL);
2975 xsltProcessOneNode(ctxt, ctxt->node);
2976 xsltFreeStackElemList(varsPop(ctxt));
2978 xsltCleanupTemplates(style); /* TODO: <- style should be read only */
2981 * Now cleanup our variables so stylesheet can be re-used
2983 * TODO: this is not needed anymore global variables are copied
2984 * and not evaluated directly anymore, keep this as a check
2986 if (style->variables != variables) {
2987 vptr = style->variables;
2988 while (vptr->next!=variables)
2991 xsltFreeStackElemList(style->variables);
2992 style->variables = variables;
2994 vptr = style->variables;
2996 if (vptr->computed) {
2997 if (vptr->value != NULL) {
2998 xmlXPathFreeObject(vptr->value);
3008 * Do some post processing work depending on the generated output
3010 root = xmlDocGetRootElement(res);
3013 * Apply the default selection of the method
3015 if ((method == NULL) &&
3016 (root->ns == NULL) &&
3017 (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
3019 tmp = res->children;
3020 while ((tmp != NULL) && (tmp != root)) {
3021 if (tmp->type == XML_ELEMENT_NODE)
3023 if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
3027 ctxt->type = XSLT_OUTPUT_HTML;
3028 res->type = XML_HTML_DOCUMENT_NODE;
3029 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3030 res->intSubset = xmlCreateIntSubset(res, root->name,
3031 doctypePublic, doctypeSystem);
3032 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3034 if (version == NULL)
3035 version = (const xmlChar *) "4.0";
3036 xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
3037 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3038 res->intSubset = xmlCreateIntSubset(res, root->name,
3039 doctypePublic, doctypeSystem);
3045 if (ctxt->type == XSLT_OUTPUT_XML) {
3046 XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
3047 XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
3048 if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
3049 res->intSubset = xmlCreateIntSubset(res, root->name,
3050 doctypePublic, doctypeSystem);
3053 xmlXPathFreeNodeSet(ctxt->nodeList);
3054 xsltFreeTransformContext(ctxt);
3061 xsltFreeTransformContext(ctxt);
3066 * xsltApplyStylesheet:
3067 * @style: a parsed XSLT stylesheet
3068 * @doc: a parsed XML document
3069 * @params: a NULL terminated arry of parameters names/values tuples
3071 * Apply the stylesheet to the document
3072 * NOTE: This may lead to a non-wellformed output XML wise !
3074 * Returns the result document or NULL in case of error
3077 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
3078 const char **params) {
3079 return(xsltApplyStylesheetInternal(style, doc, params, NULL));
3083 * xsltRunStylesheet:
3084 * @style: a parsed XSLT stylesheet
3085 * @doc: a parsed XML document
3086 * @params: a NULL terminated arry of parameters names/values tuples
3087 * @output: the URL/filename ot the generated resource if available
3088 * @SAX: a SAX handler for progressive callback output (not implemented yet)
3089 * @IObuf: an output buffer for progressive output (not implemented yet)
3091 * Apply the stylesheet to the document and generate the output according
3092 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
3094 * NOTE: This may lead to a non-wellformed output XML wise !
3095 * NOTE: This may also result in multiple files being generated
3096 * NOTE: using IObuf, the result encoding used will be the one used for
3097 * creating the output buffer, use the following macro to read it
3098 * from the stylesheet
3099 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
3100 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
3101 * since the interface uses only UTF8
3103 * Returns the number of by written to the main resource or -1 in case of
3107 xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc, const char **params,
3108 const char *output, xmlSAXHandlerPtr SAX,
3109 xmlOutputBufferPtr IObuf)
3114 if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
3116 if ((SAX != NULL) && (IObuf != NULL))
3119 /* unsupported yet */
3121 TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
3125 tmp = xsltApplyStylesheetInternal(style, doc, params, output);
3127 xsltGenericError(xsltGenericErrorContext,
3128 "xsltRunStylesheet : run failed\n");
3131 if (IObuf != NULL) {
3132 /* TODO: incomplete, IObuf output not progressive */
3133 ret = xsltSaveResultTo(IObuf, tmp, style);
3135 ret = xsltSaveResultToFilename(output, tmp, style, 0);