2 * transform.c: Implementation 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.
25 #include <libxml/xmlmemory.h>
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28 #include <libxml/valid.h>
29 #include <libxml/hash.h>
30 #include <libxml/encoding.h>
31 #include <libxml/xmlerror.h>
32 #include <libxml/xpath.h>
33 #include <libxml/parserInternals.h>
34 #include <libxml/xpathInternals.h>
35 #include <libxml/HTMLtree.h>
36 #include <libxml/debugXML.h>
37 #include <libxml/uri.h>
39 #include "xsltInternals.h"
40 #include "xsltutils.h"
42 #include "transform.h"
43 #include "variables.h"
44 #include "numbersInternals.h"
45 #include "namespaces.h"
46 #include "attributes.h"
47 #include "templates.h"
50 #include "documents.h"
51 #include "extensions.h"
56 #ifdef WITH_XSLT_DEBUG
57 #define WITH_XSLT_DEBUG_EXTRA
58 #define WITH_XSLT_DEBUG_PROCESS
61 #define XSLT_GENERATE_HTML_DOCTYPE
62 #ifdef XSLT_GENERATE_HTML_DOCTYPE
63 static int xsltGetHTMLIDs(const xmlChar
*version
, const xmlChar
**publicID
,
64 const xmlChar
**systemID
);
67 int xsltMaxDepth
= 3000;
68 int xsltMaxVars
= 15000;
75 # define FALSE (0 == 1)
76 # define TRUE (!FALSE)
79 #define IS_BLANK_NODE(n) \
80 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
84 * Forward declarations
88 xsltCopyNamespaceListInternal(xmlNodePtr node
, xmlNsPtr cur
);
91 xsltCopyTreeInternal(xsltTransformContextPtr ctxt
,
94 xmlNodePtr insert
, int isLRE
, int topElemVisited
);
97 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt
,
98 xmlNodePtr contextNode
, xmlNodePtr list
,
99 xsltTemplatePtr templ
);
102 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt
,
103 xmlNodePtr contextNode
,
105 xsltTemplatePtr templ
,
106 xsltStackElemPtr withParams
);
110 * @ctxt: the transformation context
111 * @value: the template to push on the stack
113 * Push a template on the stack
115 * Returns the new index in the stack or 0 in case of error
118 templPush(xsltTransformContextPtr ctxt
, xsltTemplatePtr value
)
120 if (ctxt
->templMax
== 0) {
123 (xsltTemplatePtr
*) xmlMalloc(ctxt
->templMax
*
124 sizeof(ctxt
->templTab
[0]));
125 if (ctxt
->templTab
== NULL
) {
126 xmlGenericError(xmlGenericErrorContext
, "malloc failed !\n");
130 else if (ctxt
->templNr
>= ctxt
->templMax
) {
133 (xsltTemplatePtr
*) xmlRealloc(ctxt
->templTab
,
135 sizeof(ctxt
->templTab
[0]));
136 if (ctxt
->templTab
== NULL
) {
137 xmlGenericError(xmlGenericErrorContext
, "realloc failed !\n");
141 ctxt
->templTab
[ctxt
->templNr
] = value
;
143 return (ctxt
->templNr
++);
147 * @ctxt: the transformation context
149 * Pop a template value from the stack
151 * Returns the stored template value
153 static xsltTemplatePtr
154 templPop(xsltTransformContextPtr ctxt
)
158 if (ctxt
->templNr
<= 0)
161 if (ctxt
->templNr
> 0)
162 ctxt
->templ
= ctxt
->templTab
[ctxt
->templNr
- 1];
164 ctxt
->templ
= (xsltTemplatePtr
) 0;
165 ret
= ctxt
->templTab
[ctxt
->templNr
];
166 ctxt
->templTab
[ctxt
->templNr
] = 0;
171 * xsltLocalVariablePop:
172 * @ctxt: the transformation context
173 * @limitNr: number of variables which should remain
174 * @level: the depth in the xsl:template's tree
176 * Pops all variable values at the given @depth from the stack.
178 * Returns the stored variable value
180 * This is an internal routine and should not be called by users!
183 xsltLocalVariablePop(xsltTransformContextPtr ctxt
, int limitNr
, int level
)
185 xsltStackElemPtr variable
;
187 if (ctxt
->varsNr
<= 0)
191 if (ctxt
->varsNr
<= limitNr
)
193 variable
= ctxt
->varsTab
[ctxt
->varsNr
- 1];
194 if (variable
->level
<= level
)
196 if (variable
->level
>= 0)
197 xsltFreeStackElemList(variable
);
199 } while (ctxt
->varsNr
!= 0);
200 if (ctxt
->varsNr
> 0)
201 ctxt
->vars
= ctxt
->varsTab
[ctxt
->varsNr
- 1];
207 * xsltTemplateParamsCleanup:
209 * Removes xsl:param and xsl:with-param items from the
210 * variable-stack. Only xsl:with-param items are not freed.
213 xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt
)
215 xsltStackElemPtr param
;
217 for (; ctxt
->varsNr
> ctxt
->varsBase
; ctxt
->varsNr
--) {
218 param
= ctxt
->varsTab
[ctxt
->varsNr
-1];
220 * Free xsl:param items.
221 * xsl:with-param items will have a level of -1 or -2.
223 if (param
->level
>= 0) {
224 xsltFreeStackElemList(param
);
227 if (ctxt
->varsNr
> 0)
228 ctxt
->vars
= ctxt
->varsTab
[ctxt
->varsNr
- 1];
235 * @ctxt: the transformation context
236 * @value: the profiling value to push on the stack
238 * Push a profiling value on the stack
240 * Returns the new index in the stack or 0 in case of error
243 profPush(xsltTransformContextPtr ctxt
, long value
)
245 if (ctxt
->profMax
== 0) {
248 (long *) xmlMalloc(ctxt
->profMax
* sizeof(ctxt
->profTab
[0]));
249 if (ctxt
->profTab
== NULL
) {
250 xmlGenericError(xmlGenericErrorContext
, "malloc failed !\n");
254 else if (ctxt
->profNr
>= ctxt
->profMax
) {
257 (long *) xmlRealloc(ctxt
->profTab
,
258 ctxt
->profMax
* sizeof(ctxt
->profTab
[0]));
259 if (ctxt
->profTab
== NULL
) {
260 xmlGenericError(xmlGenericErrorContext
, "realloc failed !\n");
264 ctxt
->profTab
[ctxt
->profNr
] = value
;
266 return (ctxt
->profNr
++);
270 * @ctxt: the transformation context
272 * Pop a profiling value from the stack
274 * Returns the stored profiling value
277 profPop(xsltTransformContextPtr ctxt
)
281 if (ctxt
->profNr
<= 0)
284 if (ctxt
->profNr
> 0)
285 ctxt
->prof
= ctxt
->profTab
[ctxt
->profNr
- 1];
287 ctxt
->prof
= (long) 0;
288 ret
= ctxt
->profTab
[ctxt
->profNr
];
289 ctxt
->profTab
[ctxt
->profNr
] = 0;
294 profCallgraphAdd(xsltTemplatePtr templ
, xsltTemplatePtr parent
)
298 if (templ
->templMax
== 0) {
300 templ
->templCalledTab
=
301 (xsltTemplatePtr
*) xmlMalloc(templ
->templMax
*
302 sizeof(templ
->templCalledTab
[0]));
303 templ
->templCountTab
=
304 (int *) xmlMalloc(templ
->templMax
*
305 sizeof(templ
->templCountTab
[0]));
306 if (templ
->templCalledTab
== NULL
|| templ
->templCountTab
== NULL
) {
307 xmlGenericError(xmlGenericErrorContext
, "malloc failed !\n");
311 else if (templ
->templNr
>= templ
->templMax
) {
312 templ
->templMax
*= 2;
313 templ
->templCalledTab
=
314 (xsltTemplatePtr
*) xmlRealloc(templ
->templCalledTab
,
316 sizeof(templ
->templCalledTab
[0]));
317 templ
->templCountTab
=
318 (int *) xmlRealloc(templ
->templCountTab
,
320 sizeof(templ
->templCountTab
[0]));
321 if (templ
->templCalledTab
== NULL
|| templ
->templCountTab
== NULL
) {
322 xmlGenericError(xmlGenericErrorContext
, "realloc failed !\n");
327 for (i
= 0; i
< templ
->templNr
; i
++) {
328 if (templ
->templCalledTab
[i
] == parent
) {
329 templ
->templCountTab
[i
]++;
333 if (i
== templ
->templNr
) {
334 /* not found, add new one */
335 templ
->templCalledTab
[templ
->templNr
] = parent
;
336 templ
->templCountTab
[templ
->templNr
] = 1;
341 /************************************************************************
343 * XInclude default settings *
345 ************************************************************************/
347 static int xsltDoXIncludeDefault
= 0;
350 * xsltSetXIncludeDefault:
351 * @xinclude: whether to do XInclude processing
353 * Set whether XInclude should be processed on document being loaded by default
356 xsltSetXIncludeDefault(int xinclude
) {
357 xsltDoXIncludeDefault
= (xinclude
!= 0);
361 * xsltGetXIncludeDefault:
363 * Provides the default state for XInclude processing
365 * Returns 0 if there is no processing 1 otherwise
368 xsltGetXIncludeDefault(void) {
369 return(xsltDoXIncludeDefault
);
372 unsigned long xsltDefaultTrace
= (unsigned long) XSLT_TRACE_ALL
;
375 * xsltDebugSetDefaultTrace:
376 * @val: tracing level mask
378 * Set the default debug tracing level mask
380 void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val
) {
381 xsltDefaultTrace
= val
;
385 * xsltDebugGetDefaultTrace:
387 * Get the current default debug tracing level mask
389 * Returns the current default debug tracing level mask
391 xsltDebugTraceCodes
xsltDebugGetDefaultTrace() {
392 return xsltDefaultTrace
;
395 /************************************************************************
397 * Handling of Transformation Contexts *
399 ************************************************************************/
401 static xsltTransformCachePtr
402 xsltTransformCacheCreate(void)
404 xsltTransformCachePtr ret
;
406 ret
= (xsltTransformCachePtr
) xmlMalloc(sizeof(xsltTransformCache
));
408 xsltTransformError(NULL
, NULL
, NULL
,
409 "xsltTransformCacheCreate : malloc failed\n");
412 memset(ret
, 0, sizeof(xsltTransformCache
));
417 xsltTransformCacheFree(xsltTransformCachePtr cache
)
422 * Free tree fragments.
425 xmlDocPtr tmp
, cur
= cache
->RVT
;
428 cur
= (xmlDocPtr
) cur
->next
;
429 if (tmp
->_private
!= NULL
) {
431 * Tree the document info.
433 xsltFreeDocumentKeys((xsltDocumentPtr
) tmp
->_private
);
434 xmlFree(tmp
->_private
);
442 if (cache
->stackItems
) {
443 xsltStackElemPtr tmp
, cur
= cache
->stackItems
;
448 * REVISIT TODO: Should be call a destruction-function
458 * xsltNewTransformContext:
459 * @style: a parsed XSLT stylesheet
460 * @doc: the input document
462 * Create a new XSLT TransformContext
464 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
466 xsltTransformContextPtr
467 xsltNewTransformContext(xsltStylesheetPtr style
, xmlDocPtr doc
) {
468 xsltTransformContextPtr cur
;
469 xsltDocumentPtr docu
;
474 cur
= (xsltTransformContextPtr
) xmlMalloc(sizeof(xsltTransformContext
));
476 xsltTransformError(NULL
, NULL
, (xmlNodePtr
)doc
,
477 "xsltNewTransformContext : malloc failed\n");
480 memset(cur
, 0, sizeof(xsltTransformContext
));
482 cur
->cache
= xsltTransformCacheCreate();
483 if (cur
->cache
== NULL
)
486 * setup of the dictionary must be done early as some of the
487 * processing later like key handling may need it.
489 cur
->dict
= xmlDictCreateSub(style
->dict
);
490 cur
->internalized
= ((style
->internalized
) && (cur
->dict
!= NULL
));
491 #ifdef WITH_XSLT_DEBUG
492 xsltGenericDebug(xsltGenericDebugContext
,
493 "Creating sub-dictionary from stylesheet for transformation\n");
497 * initialize the template stack
499 cur
->templTab
= (xsltTemplatePtr
*)
500 xmlMalloc(10 * sizeof(xsltTemplatePtr
));
501 if (cur
->templTab
== NULL
) {
502 xsltTransformError(NULL
, NULL
, (xmlNodePtr
) doc
,
503 "xsltNewTransformContext: out of memory\n");
509 cur
->maxTemplateDepth
= xsltMaxDepth
;
512 * initialize the variables stack
514 cur
->varsTab
= (xsltStackElemPtr
*)
515 xmlMalloc(10 * sizeof(xsltStackElemPtr
));
516 if (cur
->varsTab
== NULL
) {
517 xmlGenericError(xmlGenericErrorContext
,
518 "xsltNewTransformContext: out of memory\n");
525 cur
->maxTemplateVars
= xsltMaxVars
;
528 * the profiling stack is not initialized by default
537 cur
->xpathCtxt
= xmlXPathNewContext(doc
);
538 if (cur
->xpathCtxt
== NULL
) {
539 xsltTransformError(NULL
, NULL
, (xmlNodePtr
) doc
,
540 "xsltNewTransformContext : xmlXPathNewContext failed\n");
544 * Create an XPath cache.
546 if (xmlXPathContextSetCache(cur
->xpathCtxt
, 1, -1, 0) == -1)
549 * Initialize the extras array
551 if (style
->extrasNr
!= 0) {
552 cur
->extrasMax
= style
->extrasNr
+ 20;
553 cur
->extras
= (xsltRuntimeExtraPtr
)
554 xmlMalloc(cur
->extrasMax
* sizeof(xsltRuntimeExtra
));
555 if (cur
->extras
== NULL
) {
556 xmlGenericError(xmlGenericErrorContext
,
557 "xsltNewTransformContext: out of memory\n");
560 cur
->extrasNr
= style
->extrasNr
;
561 for (i
= 0;i
< cur
->extrasMax
;i
++) {
562 cur
->extras
[i
].info
= NULL
;
563 cur
->extras
[i
].deallocate
= NULL
;
564 cur
->extras
[i
].val
.ptr
= NULL
;
572 XSLT_REGISTER_VARIABLE_LOOKUP(cur
);
573 XSLT_REGISTER_FUNCTION_LOOKUP(cur
);
574 cur
->xpathCtxt
->nsHash
= style
->nsHash
;
576 * Initialize the registered external modules
578 xsltInitCtxtExts(cur
);
580 * Setup document element ordering for later efficiencies
583 if (xslDebugStatus
== XSLT_DEBUG_NONE
)
584 xmlXPathOrderDocElems(doc
);
586 * Must set parserOptions before calling xsltNewDocument
589 cur
->parserOptions
= XSLT_PARSE_OPTIONS
;
590 docu
= xsltNewDocument(cur
, doc
);
592 xsltTransformError(cur
, NULL
, (xmlNodePtr
)doc
,
593 "xsltNewTransformContext : xsltNewDocument failed\n");
597 cur
->document
= docu
;
599 cur
->outputFile
= NULL
;
600 cur
->sec
= xsltGetDefaultSecurityPrefs();
601 cur
->debugStatus
= xslDebugStatus
;
602 cur
->traceCode
= (unsigned long*) &xsltDefaultTrace
;
603 cur
->xinclude
= xsltGetXIncludeDefault();
604 cur
->keyInitLevel
= 0;
610 xsltFreeTransformContext(cur
);
615 * xsltFreeTransformContext:
616 * @ctxt: an XSLT parser context
618 * Free up the memory allocated by @ctxt
621 xsltFreeTransformContext(xsltTransformContextPtr ctxt
) {
626 * Shutdown the extension modules associated to the stylesheet
629 xsltShutdownCtxtExts(ctxt
);
631 if (ctxt
->xpathCtxt
!= NULL
) {
632 ctxt
->xpathCtxt
->nsHash
= NULL
;
633 xmlXPathFreeContext(ctxt
->xpathCtxt
);
635 if (ctxt
->templTab
!= NULL
)
636 xmlFree(ctxt
->templTab
);
637 if (ctxt
->varsTab
!= NULL
)
638 xmlFree(ctxt
->varsTab
);
639 if (ctxt
->profTab
!= NULL
)
640 xmlFree(ctxt
->profTab
);
641 if ((ctxt
->extrasNr
> 0) && (ctxt
->extras
!= NULL
)) {
644 for (i
= 0;i
< ctxt
->extrasNr
;i
++) {
645 if ((ctxt
->extras
[i
].deallocate
!= NULL
) &&
646 (ctxt
->extras
[i
].info
!= NULL
))
647 ctxt
->extras
[i
].deallocate(ctxt
->extras
[i
].info
);
649 xmlFree(ctxt
->extras
);
651 xsltFreeGlobalVariables(ctxt
);
652 xsltFreeDocuments(ctxt
);
653 xsltFreeCtxtExts(ctxt
);
655 xsltTransformCacheFree(ctxt
->cache
);
656 xmlDictFree(ctxt
->dict
);
657 #ifdef WITH_XSLT_DEBUG
658 xsltGenericDebug(xsltGenericDebugContext
,
659 "freeing transformation dictionary\n");
661 memset(ctxt
, -1, sizeof(xsltTransformContext
));
665 /************************************************************************
667 * Copy of Nodes in an XSLT fashion *
669 ************************************************************************/
671 xmlNodePtr
xsltCopyTree(xsltTransformContextPtr ctxt
,
672 xmlNodePtr node
, xmlNodePtr insert
, int literal
);
676 * @parent: the parent node
677 * @cur: the child node
679 * Wrapper version of xmlAddChild with a more consistent behaviour on
680 * error. One expect the use to be child = xsltAddChild(parent, child);
681 * and the routine will take care of not leaking on errors or node merge
683 * Returns the child is successfully attached or NULL if merged or freed
686 xsltAddChild(xmlNodePtr parent
, xmlNodePtr cur
) {
689 if ((cur
== NULL
) || (parent
== NULL
))
691 if (parent
== NULL
) {
695 ret
= xmlAddChild(parent
, cur
);
702 * @ctxt: a XSLT process context
703 * @target: the text node where the text will be attached
704 * @string: the text string
705 * @len: the string length in byte
707 * Extend the current text node with the new string, it handles coalescing
709 * Returns: the text node
712 xsltAddTextString(xsltTransformContextPtr ctxt
, xmlNodePtr target
,
713 const xmlChar
*string
, int len
) {
717 if ((len
<= 0) || (string
== NULL
) || (target
== NULL
))
720 if (ctxt
->lasttext
== target
->content
) {
722 if (ctxt
->lasttuse
+ len
>= ctxt
->lasttsize
) {
726 size
= ctxt
->lasttsize
+ len
+ 100;
728 newbuf
= (xmlChar
*) xmlRealloc(target
->content
,size
);
729 if (newbuf
== NULL
) {
730 xsltTransformError(ctxt
, NULL
, target
,
731 "xsltCopyText: text allocation failed\n");
734 ctxt
->lasttsize
= size
;
735 ctxt
->lasttext
= newbuf
;
736 target
->content
= newbuf
;
738 memcpy(&(target
->content
[ctxt
->lasttuse
]), string
, len
);
739 ctxt
->lasttuse
+= len
;
740 target
->content
[ctxt
->lasttuse
] = 0;
742 xmlNodeAddContent(target
, string
);
743 ctxt
->lasttext
= target
->content
;
744 len
= xmlStrlen(target
->content
);
745 ctxt
->lasttsize
= len
;
746 ctxt
->lasttuse
= len
;
752 * xsltCopyTextString:
753 * @ctxt: a XSLT process context
754 * @target: the element where the text will be attached
755 * @string: the text string
756 * @noescape: should disable-escaping be activated for this text node.
758 * Adds @string to a newly created or an existent text node child of
761 * Returns: the text node, where the text content of @cur is copied to.
762 * NULL in case of API or internal errors.
765 xsltCopyTextString(xsltTransformContextPtr ctxt
, xmlNodePtr target
,
766 const xmlChar
*string
, int noescape
)
774 #ifdef WITH_XSLT_DEBUG_PROCESS
775 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_TEXT
,xsltGenericDebug(xsltGenericDebugContext
,
776 "xsltCopyTextString: copy text %s\n",
781 * Play safe and reset the merging mechanism for every new
784 if ((target
== NULL
) || (target
->children
== NULL
)) {
785 ctxt
->lasttext
= NULL
;
788 /* handle coalescing of text nodes here */
789 len
= xmlStrlen(string
);
790 if ((ctxt
->type
== XSLT_OUTPUT_XML
) &&
791 (ctxt
->style
->cdataSection
!= NULL
) &&
793 (target
->type
== XML_ELEMENT_NODE
) &&
794 (((target
->ns
== NULL
) &&
795 (xmlHashLookup2(ctxt
->style
->cdataSection
,
796 target
->name
, NULL
) != NULL
)) ||
797 ((target
->ns
!= NULL
) &&
798 (xmlHashLookup2(ctxt
->style
->cdataSection
,
799 target
->name
, target
->ns
->href
) != NULL
))))
802 * Process "cdata-section-elements".
804 if ((target
->last
!= NULL
) &&
805 (target
->last
->type
== XML_CDATA_SECTION_NODE
))
807 return(xsltAddTextString(ctxt
, target
->last
, string
, len
));
809 copy
= xmlNewCDataBlock(ctxt
->output
, string
, len
);
810 } else if (noescape
) {
812 * Process "disable-output-escaping".
814 if ((target
!= NULL
) && (target
->last
!= NULL
) &&
815 (target
->last
->type
== XML_TEXT_NODE
) &&
816 (target
->last
->name
== xmlStringTextNoenc
))
818 return(xsltAddTextString(ctxt
, target
->last
, string
, len
));
820 copy
= xmlNewTextLen(string
, len
);
822 copy
->name
= xmlStringTextNoenc
;
825 * Default processing.
827 if ((target
!= NULL
) && (target
->last
!= NULL
) &&
828 (target
->last
->type
== XML_TEXT_NODE
) &&
829 (target
->last
->name
== xmlStringText
)) {
830 return(xsltAddTextString(ctxt
, target
->last
, string
, len
));
832 copy
= xmlNewTextLen(string
, len
);
836 copy
= xsltAddChild(target
, copy
);
837 ctxt
->lasttext
= copy
->content
;
838 ctxt
->lasttsize
= len
;
839 ctxt
->lasttuse
= len
;
841 xsltTransformError(ctxt
, NULL
, target
,
842 "xsltCopyTextString: text copy failed\n");
843 ctxt
->lasttext
= NULL
;
850 * @ctxt: a XSLT process context
851 * @target: the element where the text will be attached
852 * @cur: the text or CDATA node
853 * @interned: the string is in the target doc dictionary
855 * Copy the text content of @cur and append it to @target's children.
857 * Returns: the text node, where the text content of @cur is copied to.
858 * NULL in case of API or internal errors.
861 xsltCopyText(xsltTransformContextPtr ctxt
, xmlNodePtr target
,
862 xmlNodePtr cur
, int interned
)
866 if ((cur
->type
!= XML_TEXT_NODE
) &&
867 (cur
->type
!= XML_CDATA_SECTION_NODE
))
869 if (cur
->content
== NULL
)
872 #ifdef WITH_XSLT_DEBUG_PROCESS
873 if (cur
->type
== XML_CDATA_SECTION_NODE
) {
874 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_TEXT
,xsltGenericDebug(xsltGenericDebugContext
,
875 "xsltCopyText: copy CDATA text %s\n",
877 } else if (cur
->name
== xmlStringTextNoenc
) {
878 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_TEXT
,xsltGenericDebug(xsltGenericDebugContext
,
879 "xsltCopyText: copy unescaped text %s\n",
882 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_TEXT
,xsltGenericDebug(xsltGenericDebugContext
,
883 "xsltCopyText: copy text %s\n",
889 * Play save and reset the merging mechanism for every new
892 if ((target
== NULL
) || (target
->children
== NULL
)) {
893 ctxt
->lasttext
= NULL
;
896 if ((ctxt
->style
->cdataSection
!= NULL
) &&
897 (ctxt
->type
== XSLT_OUTPUT_XML
) &&
899 (target
->type
== XML_ELEMENT_NODE
) &&
900 (((target
->ns
== NULL
) &&
901 (xmlHashLookup2(ctxt
->style
->cdataSection
,
902 target
->name
, NULL
) != NULL
)) ||
903 ((target
->ns
!= NULL
) &&
904 (xmlHashLookup2(ctxt
->style
->cdataSection
,
905 target
->name
, target
->ns
->href
) != NULL
))))
908 * Process "cdata-section-elements".
911 * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
914 * TODO: Since this doesn't merge adjacent CDATA-section nodes,
915 * we'll get: <![CDATA[x]]><!CDATA[y]]>.
916 * TODO: Reported in #321505.
918 if ((target
->last
!= NULL
) &&
919 (target
->last
->type
== XML_CDATA_SECTION_NODE
))
922 * Append to existing CDATA-section node.
924 copy
= xsltAddTextString(ctxt
, target
->last
, cur
->content
,
925 xmlStrlen(cur
->content
));
930 len
= xmlStrlen(cur
->content
);
931 copy
= xmlNewCDataBlock(ctxt
->output
, cur
->content
, len
);
934 ctxt
->lasttext
= copy
->content
;
935 ctxt
->lasttsize
= len
;
936 ctxt
->lasttuse
= len
;
938 } else if ((target
!= NULL
) &&
939 (target
->last
!= NULL
) &&
940 /* both escaped or both non-escaped text-nodes */
941 (((target
->last
->type
== XML_TEXT_NODE
) &&
942 (target
->last
->name
== cur
->name
)) ||
943 /* non-escaped text nodes and CDATA-section nodes */
944 (((target
->last
->type
== XML_CDATA_SECTION_NODE
) &&
945 (cur
->name
== xmlStringTextNoenc
)))))
948 * we are appending to an existing text node
950 copy
= xsltAddTextString(ctxt
, target
->last
, cur
->content
,
951 xmlStrlen(cur
->content
));
953 } else if ((interned
) && (target
!= NULL
) &&
954 (target
->doc
!= NULL
) &&
955 (target
->doc
->dict
== ctxt
->dict
))
958 * TODO: DO we want to use this also for "text" output?
960 copy
= xmlNewTextLen(NULL
, 0);
963 if (cur
->name
== xmlStringTextNoenc
)
964 copy
->name
= xmlStringTextNoenc
;
967 * Must confirm that content is in dict (bug 302821)
968 * TODO: This check should be not needed for text coming
969 * from the stylesheets
971 if (xmlDictOwns(ctxt
->dict
, cur
->content
))
972 copy
->content
= cur
->content
;
974 if ((copy
->content
= xmlStrdup(cur
->content
)) == NULL
)
979 * normal processing. keep counters to extend the text node
980 * in xsltAddTextString if needed.
984 len
= xmlStrlen(cur
->content
);
985 copy
= xmlNewTextLen(cur
->content
, len
);
988 if (cur
->name
== xmlStringTextNoenc
)
989 copy
->name
= xmlStringTextNoenc
;
990 ctxt
->lasttext
= copy
->content
;
991 ctxt
->lasttsize
= len
;
992 ctxt
->lasttuse
= len
;
995 if (target
!= NULL
) {
996 copy
->doc
= target
->doc
;
998 * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
999 * to ensure that the optimized text-merging mechanism
1000 * won't interfere with normal node-merging in any case.
1002 copy
= xsltAddChild(target
, copy
);
1005 xsltTransformError(ctxt
, NULL
, target
,
1006 "xsltCopyText: text copy failed\n");
1010 if ((copy
== NULL
) || (copy
->content
== NULL
)) {
1011 xsltTransformError(ctxt
, NULL
, target
,
1012 "Internal error in xsltCopyText(): "
1013 "Failed to copy the string.\n");
1014 ctxt
->state
= XSLT_STATE_STOPPED
;
1020 * xsltShallowCopyAttr:
1021 * @ctxt: a XSLT process context
1022 * @invocNode: responsible node in the stylesheet; used for error reports
1023 * @target: the element where the attribute will be grafted
1024 * @attr: the attribute to be copied
1026 * Do a copy of an attribute.
1028 * - xsltCopyTreeInternal()
1032 * Returns: a new xmlAttrPtr, or NULL in case of error.
1035 xsltShallowCopyAttr(xsltTransformContextPtr ctxt
, xmlNodePtr invocNode
,
1036 xmlNodePtr target
, xmlAttrPtr attr
)
1044 if (target
->type
!= XML_ELEMENT_NODE
) {
1045 xsltTransformError(ctxt
, NULL
, invocNode
,
1046 "Cannot add an attribute node to a non-element node.\n");
1050 if (target
->children
!= NULL
) {
1051 xsltTransformError(ctxt
, NULL
, invocNode
,
1052 "Attribute nodes must be added before "
1053 "any child nodes to an element.\n");
1057 value
= xmlNodeListGetString(attr
->doc
, attr
->children
, 1);
1058 if (attr
->ns
!= NULL
) {
1061 ns
= xsltGetSpecialNamespace(ctxt
, invocNode
,
1062 attr
->ns
->href
, attr
->ns
->prefix
, target
);
1064 xsltTransformError(ctxt
, NULL
, invocNode
,
1065 "Namespace fixup error: Failed to acquire an in-scope "
1066 "namespace binding of the copied attribute '{%s}%s'.\n",
1067 attr
->ns
->href
, attr
->name
);
1069 * TODO: Should we just stop here?
1073 * Note that xmlSetNsProp() will take care of duplicates
1074 * and assigns the new namespace even to a duplicate.
1076 copy
= xmlSetNsProp(target
, ns
, attr
->name
, value
);
1078 copy
= xmlSetNsProp(target
, NULL
, attr
->name
, value
);
1088 * NOTE: This was optimized according to bug #342695.
1089 * TODO: Can this further be optimized, if source and target
1090 * share the same dict and attr->children is just 1 text node
1091 * which is in the dict? How probable is such a case?
1094 * TODO: Do we need to create an empty text node if the value
1095 * is the empty string?
1097 value
= xmlNodeListGetString(attr
->doc
, attr
->children
, 1);
1098 if (value
!= NULL
) {
1099 txtNode
= xmlNewDocText(target
->doc
, NULL
);
1100 if (txtNode
== NULL
)
1102 if ((target
->doc
!= NULL
) &&
1103 (target
->doc
->dict
!= NULL
))
1106 (xmlChar
*) xmlDictLookup(target
->doc
->dict
,
1107 BAD_CAST value
, -1);
1110 txtNode
->content
= value
;
1111 copy
->children
= txtNode
;
1119 * xsltCopyAttrListNoOverwrite:
1120 * @ctxt: a XSLT process context
1121 * @invocNode: responsible node in the stylesheet; used for error reports
1122 * @target: the element where the new attributes will be grafted
1123 * @attr: the first attribute in the list to be copied
1125 * Copies a list of attribute nodes, starting with @attr, over to the
1126 * @target element node.
1129 * - xsltCopyTreeInternal()
1131 * Returns 0 on success and -1 on errors and internal errors.
1134 xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt
,
1135 xmlNodePtr invocNode
,
1136 xmlNodePtr target
, xmlAttrPtr attr
)
1139 xmlNsPtr origNs
= NULL
, copyNs
= NULL
;
1143 * Don't use xmlCopyProp() here, since it will try to
1144 * reconciliate namespaces.
1146 while (attr
!= NULL
) {
1148 * Find a namespace node in the tree of @target.
1149 * Avoid searching for the same ns.
1151 if (attr
->ns
!= origNs
) {
1153 if (attr
->ns
!= NULL
) {
1154 copyNs
= xsltGetSpecialNamespace(ctxt
, invocNode
,
1155 attr
->ns
->href
, attr
->ns
->prefix
, target
);
1162 * If attribute has a value, we need to copy it (watching out
1163 * for possible entities)
1165 if ((attr
->children
) && (attr
->children
->type
== XML_TEXT_NODE
) &&
1166 (attr
->children
->next
== NULL
)) {
1167 copy
= xmlNewNsProp(target
, copyNs
, attr
->name
,
1168 attr
->children
->content
);
1169 } else if (attr
->children
!= NULL
) {
1170 value
= xmlNodeListGetString(attr
->doc
, attr
->children
, 1);
1171 copy
= xmlNewNsProp(target
, copyNs
, attr
->name
, BAD_CAST value
);
1174 copy
= xmlNewNsProp(target
, copyNs
, attr
->name
, NULL
);
1186 * xsltShallowCopyElem:
1187 * @ctxt: the XSLT process context
1188 * @node: the element node in the source tree
1189 * or the Literal Result Element
1190 * @insert: the parent in the result tree
1191 * @isLRE: if @node is a Literal Result Element
1193 * Make a copy of the element node @node
1194 * and insert it as last child of @insert.
1196 * URGENT TODO: The problem with this one (for the non-refactored code)
1197 * is that it is used for both, Literal Result Elements *and*
1198 * copying input nodes.
1200 * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
1203 * xsltApplySequenceConstructor()
1204 * (for Literal Result Elements - which is a problem)
1205 * xsltCopy() (for shallow-copying elements via xsl:copy)
1207 * Returns a pointer to the new node, or NULL in case of error
1210 xsltShallowCopyElem(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1211 xmlNodePtr insert
, int isLRE
)
1215 if ((node
->type
== XML_DTD_NODE
) || (insert
== NULL
))
1217 if ((node
->type
== XML_TEXT_NODE
) ||
1218 (node
->type
== XML_CDATA_SECTION_NODE
))
1219 return(xsltCopyText(ctxt
, insert
, node
, 0));
1221 copy
= xmlDocCopyNode(node
, insert
->doc
, 0);
1223 copy
->doc
= ctxt
->output
;
1224 copy
= xsltAddChild(insert
, copy
);
1226 if (node
->type
== XML_ELEMENT_NODE
) {
1228 * Add namespaces as they are needed
1230 if (node
->nsDef
!= NULL
) {
1232 * TODO: Remove the LRE case in the refactored code
1236 xsltCopyNamespaceList(ctxt
, copy
, node
->nsDef
);
1238 xsltCopyNamespaceListInternal(copy
, node
->nsDef
);
1242 * URGENT TODO: The problem with this is that it does not
1243 * copy over all namespace nodes in scope.
1244 * The damn thing about this is, that we would need to
1245 * use the xmlGetNsList(), for every single node; this is
1246 * also done in xsltCopyTreeInternal(), but only for the top node.
1248 if (node
->ns
!= NULL
) {
1251 * REVISIT TODO: Since the non-refactored code still does
1252 * ns-aliasing, we need to call xsltGetNamespace() here.
1253 * Remove this when ready.
1255 copy
->ns
= xsltGetNamespace(ctxt
, node
, node
->ns
, copy
);
1257 copy
->ns
= xsltGetSpecialNamespace(ctxt
,
1258 node
, node
->ns
->href
, node
->ns
->prefix
, copy
);
1261 } else if ((insert
->type
== XML_ELEMENT_NODE
) &&
1262 (insert
->ns
!= NULL
))
1265 * "Undeclare" the default namespace.
1267 xsltGetSpecialNamespace(ctxt
, node
, NULL
, NULL
, copy
);
1271 xsltTransformError(ctxt
, NULL
, node
,
1272 "xsltShallowCopyElem: copy %s failed\n", node
->name
);
1279 * @ctxt: a XSLT process context
1280 * @invocNode: responsible node in the stylesheet; used for error reports
1281 * @list: the list of element nodes in the source tree.
1282 * @insert: the parent in the result tree.
1283 * @isLRE: is this a literal result element list
1284 * @topElemVisited: indicates if a top-most element was already processed
1286 * Make a copy of the full list of tree @list
1287 * and insert it as last children of @insert
1289 * NOTE: Not to be used for Literal Result Elements.
1294 * Returns a pointer to the new list, or NULL in case of error
1297 xsltCopyTreeList(xsltTransformContextPtr ctxt
, xmlNodePtr invocNode
,
1299 xmlNodePtr insert
, int isLRE
, int topElemVisited
)
1301 xmlNodePtr copy
, ret
= NULL
;
1303 while (list
!= NULL
) {
1304 copy
= xsltCopyTreeInternal(ctxt
, invocNode
,
1305 list
, insert
, isLRE
, topElemVisited
);
1317 * xsltCopyNamespaceListInternal:
1318 * @node: the target node
1319 * @cur: the first namespace
1321 * Do a copy of a namespace list. If @node is non-NULL the
1322 * new namespaces are added automatically.
1324 * xsltCopyTreeInternal()
1326 * QUESTION: What is the exact difference between this function
1327 * and xsltCopyNamespaceList() in "namespaces.c"?
1328 * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
1330 * Returns: a new xmlNsPtr, or NULL in case of error.
1333 xsltCopyNamespaceListInternal(xmlNodePtr elem
, xmlNsPtr ns
) {
1334 xmlNsPtr ret
= NULL
;
1335 xmlNsPtr p
= NULL
, q
, luNs
;
1340 * One can add namespaces only on element nodes
1342 if ((elem
!= NULL
) && (elem
->type
!= XML_ELEMENT_NODE
))
1346 if (ns
->type
!= XML_NAMESPACE_DECL
)
1349 * Avoid duplicating namespace declarations on the tree.
1352 if ((elem
->ns
!= NULL
) &&
1353 xmlStrEqual(elem
->ns
->prefix
, ns
->prefix
) &&
1354 xmlStrEqual(elem
->ns
->href
, ns
->href
))
1359 luNs
= xmlSearchNs(elem
->doc
, elem
, ns
->prefix
);
1360 if ((luNs
!= NULL
) && (xmlStrEqual(luNs
->href
, ns
->href
)))
1366 q
= xmlNewNs(elem
, ns
->href
, ns
->prefix
);
1369 } else if (q
!= NULL
) {
1374 } while (ns
!= NULL
);
1379 * xsltShallowCopyNsNode:
1380 * @ctxt: the XSLT transformation context
1381 * @invocNode: responsible node in the stylesheet; used for error reports
1382 * @insert: the target element node in the result tree
1383 * @ns: the namespace node
1385 * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
1387 * Returns a new/existing ns-node, or NULL.
1390 xsltShallowCopyNsNode(xsltTransformContextPtr ctxt
,
1391 xmlNodePtr invocNode
,
1396 * TODO: Contrary to header comments, this is declared as int.
1397 * be modified to return a node pointer, or NULL if any error
1401 if ((insert
== NULL
) || (insert
->type
!= XML_ELEMENT_NODE
))
1404 if (insert
->children
!= NULL
) {
1405 xsltTransformError(ctxt
, NULL
, invocNode
,
1406 "Namespace nodes must be added before "
1407 "any child nodes are added to an element.\n");
1411 * BIG NOTE: Xalan-J simply overwrites any ns-decls with
1412 * an equal prefix. We definitively won't do that.
1414 * MSXML 4.0 and the .NET ignores ns-decls for which an
1415 * equal prefix is already in use.
1417 * Saxon raises an error like:
1418 * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
1419 * nodes with the same name".
1421 * NOTE: We'll currently follow MSXML here.
1422 * REVISIT TODO: Check if it's better to follow Saxon here.
1424 if (ns
->prefix
== NULL
) {
1426 * If we are adding ns-nodes to an element using e.g.
1427 * <xsl:copy-of select="/foo/namespace::*">, then we need
1428 * to ensure that we don't incorrectly declare a default
1429 * namespace on an element in no namespace, which otherwise
1430 * would move the element incorrectly into a namespace, if
1431 * the node tree is serialized.
1433 if (insert
->ns
== NULL
)
1435 } else if ((ns
->prefix
[0] == 'x') &&
1436 xmlStrEqual(ns
->prefix
, BAD_CAST
"xml"))
1439 * The XML namespace is built in.
1444 if (insert
->nsDef
!= NULL
) {
1445 tmpns
= insert
->nsDef
;
1447 if ((tmpns
->prefix
== NULL
) == (ns
->prefix
== NULL
)) {
1448 if ((tmpns
->prefix
== ns
->prefix
) ||
1449 xmlStrEqual(tmpns
->prefix
, ns
->prefix
))
1454 if (xmlStrEqual(tmpns
->href
, ns
->href
))
1459 tmpns
= tmpns
->next
;
1460 } while (tmpns
!= NULL
);
1462 tmpns
= xmlSearchNs(insert
->doc
, insert
, ns
->prefix
);
1463 if ((tmpns
!= NULL
) && xmlStrEqual(tmpns
->href
, ns
->href
))
1466 * Declare a new namespace.
1467 * TODO: The problem (wrt efficiency) with this xmlNewNs() is
1468 * that it will again search the already declared namespaces
1469 * for a duplicate :-/
1471 return(xmlNewNs(insert
, ns
->href
, ns
->prefix
));
1475 * TODO: We could as well raise an error here (like Saxon does),
1476 * or at least generate a warning.
1482 * xsltCopyTreeInternal:
1483 * @ctxt: the XSLT transformation context
1484 * @invocNode: responsible node in the stylesheet; used for error reports
1485 * @node: the element node in the source tree
1486 * @insert: the parent in the result tree
1487 * @isLRE: indicates if @node is a Literal Result Element
1488 * @topElemVisited: indicates if a top-most element was already processed
1490 * Make a copy of the full tree under the element node @node
1491 * and insert it as last child of @insert
1493 * NOTE: Not to be used for Literal Result Elements.
1498 * Returns a pointer to the new tree, or NULL in case of error
1501 xsltCopyTreeInternal(xsltTransformContextPtr ctxt
,
1502 xmlNodePtr invocNode
,
1504 xmlNodePtr insert
, int isLRE
, int topElemVisited
)
1510 switch (node
->type
) {
1511 case XML_ELEMENT_NODE
:
1512 case XML_ENTITY_REF_NODE
:
1513 case XML_ENTITY_NODE
:
1515 case XML_COMMENT_NODE
:
1516 case XML_DOCUMENT_NODE
:
1517 case XML_HTML_DOCUMENT_NODE
:
1518 #ifdef LIBXML_DOCB_ENABLED
1519 case XML_DOCB_DOCUMENT_NODE
:
1522 case XML_TEXT_NODE
: {
1523 int noenc
= (node
->name
== xmlStringTextNoenc
);
1524 return(xsltCopyTextString(ctxt
, insert
, node
->content
, noenc
));
1526 case XML_CDATA_SECTION_NODE
:
1527 return(xsltCopyTextString(ctxt
, insert
, node
->content
, 0));
1528 case XML_ATTRIBUTE_NODE
:
1530 xsltShallowCopyAttr(ctxt
, invocNode
, insert
, (xmlAttrPtr
) node
));
1531 case XML_NAMESPACE_DECL
:
1532 return((xmlNodePtr
) xsltShallowCopyNsNode(ctxt
, invocNode
,
1533 insert
, (xmlNsPtr
) node
));
1535 case XML_DOCUMENT_TYPE_NODE
:
1536 case XML_DOCUMENT_FRAG_NODE
:
1537 case XML_NOTATION_NODE
:
1539 case XML_ELEMENT_DECL
:
1540 case XML_ATTRIBUTE_DECL
:
1541 case XML_ENTITY_DECL
:
1542 case XML_XINCLUDE_START
:
1543 case XML_XINCLUDE_END
:
1546 if (XSLT_IS_RES_TREE_FRAG(node
)) {
1547 if (node
->children
!= NULL
)
1548 copy
= xsltCopyTreeList(ctxt
, invocNode
,
1549 node
->children
, insert
, 0, 0);
1554 copy
= xmlDocCopyNode(node
, insert
->doc
, 0);
1556 copy
->doc
= ctxt
->output
;
1557 copy
= xsltAddChild(insert
, copy
);
1559 * The node may have been coalesced into another text node.
1561 if (insert
->last
!= copy
)
1562 return(insert
->last
);
1565 if (node
->type
== XML_ELEMENT_NODE
) {
1567 * Copy in-scope namespace nodes.
1569 * REVISIT: Since we try to reuse existing in-scope ns-decls by
1570 * using xmlSearchNsByHref(), this will eventually change
1571 * the prefix of an original ns-binding; thus it might
1572 * break QNames in element/attribute content.
1573 * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
1574 * context, plus a ns-lookup function, which writes directly
1575 * to a given list, then we wouldn't need to create/free the
1576 * nsList every time.
1578 if ((topElemVisited
== 0) &&
1579 (node
->parent
!= NULL
) &&
1580 (node
->parent
->type
!= XML_DOCUMENT_NODE
) &&
1581 (node
->parent
->type
!= XML_HTML_DOCUMENT_NODE
))
1583 xmlNsPtr
*nsList
, *curns
, ns
;
1586 * If this is a top-most element in a tree to be
1587 * copied, then we need to ensure that all in-scope
1588 * namespaces are copied over. For nodes deeper in the
1589 * tree, it is sufficient to reconcile only the ns-decls
1590 * (node->nsDef entries).
1593 nsList
= xmlGetNsList(node
->doc
, node
);
1594 if (nsList
!= NULL
) {
1598 * Search by prefix first in order to break as less
1599 * QNames in element/attribute content as possible.
1601 ns
= xmlSearchNs(insert
->doc
, insert
,
1605 (! xmlStrEqual(ns
->href
, (*curns
)->href
)))
1609 * Search by namespace name.
1610 * REVISIT TODO: Currently disabled.
1613 ns
= xmlSearchNsByHref(insert
->doc
,
1614 insert
, (*curns
)->href
);
1619 * Declare a new namespace on the copied element.
1621 ns
= xmlNewNs(copy
, (*curns
)->href
,
1623 /* TODO: Handle errors */
1625 if (node
->ns
== *curns
) {
1627 * If this was the original's namespace then set
1628 * the generated counterpart on the copy.
1633 } while (*curns
!= NULL
);
1636 } else if (node
->nsDef
!= NULL
) {
1638 * Copy over all namespace declaration attributes.
1640 if (node
->nsDef
!= NULL
) {
1642 xsltCopyNamespaceList(ctxt
, copy
, node
->nsDef
);
1644 xsltCopyNamespaceListInternal(copy
, node
->nsDef
);
1648 * Set the namespace.
1650 if (node
->ns
!= NULL
) {
1651 if (copy
->ns
== NULL
) {
1653 * This will map copy->ns to one of the newly created
1654 * in-scope ns-decls, OR create a new ns-decl on @copy.
1656 copy
->ns
= xsltGetSpecialNamespace(ctxt
, invocNode
,
1657 node
->ns
->href
, node
->ns
->prefix
, copy
);
1659 } else if ((insert
->type
== XML_ELEMENT_NODE
) &&
1660 (insert
->ns
!= NULL
))
1663 * "Undeclare" the default namespace on @copy with xmlns="".
1665 xsltGetSpecialNamespace(ctxt
, invocNode
, NULL
, NULL
, copy
);
1668 * Copy attribute nodes.
1670 if (node
->properties
!= NULL
) {
1671 xsltCopyAttrListNoOverwrite(ctxt
, invocNode
,
1672 copy
, node
->properties
);
1674 if (topElemVisited
== 0)
1680 if (node
->children
!= NULL
) {
1681 xsltCopyTreeList(ctxt
, invocNode
,
1682 node
->children
, copy
, isLRE
, topElemVisited
);
1685 xsltTransformError(ctxt
, NULL
, invocNode
,
1686 "xsltCopyTreeInternal: Copying of '%s' failed.\n", node
->name
);
1693 * @ctxt: the XSLT transformation context
1694 * @node: the element node in the source tree
1695 * @insert: the parent in the result tree
1696 * @literal: indicates if @node is a Literal Result Element
1698 * Make a copy of the full tree under the element node @node
1699 * and insert it as last child of @insert
1700 * For literal result element, some of the namespaces may not be copied
1701 * over according to section 7.1.
1702 * TODO: Why is this a public function?
1704 * Returns a pointer to the new tree, or NULL in case of error
1707 xsltCopyTree(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1708 xmlNodePtr insert
, int literal
)
1710 return(xsltCopyTreeInternal(ctxt
, node
, node
, insert
, literal
, 0));
1714 /************************************************************************
1716 * Error/fallback processing *
1718 ************************************************************************/
1721 * xsltApplyFallbacks:
1722 * @ctxt: a XSLT process context
1723 * @node: the node in the source tree.
1724 * @inst: the node generating the error
1726 * Process possible xsl:fallback nodes present under @inst
1728 * Returns the number of xsl:fallback element found and processed
1731 xsltApplyFallbacks(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1737 if ((ctxt
== NULL
) || (node
== NULL
) || (inst
== NULL
) ||
1738 (inst
->children
== NULL
))
1741 child
= inst
->children
;
1742 while (child
!= NULL
) {
1743 if ((IS_XSLT_ELEM(child
)) &&
1744 (xmlStrEqual(child
->name
, BAD_CAST
"fallback"))) {
1745 #ifdef WITH_XSLT_DEBUG_PARSING
1746 xsltGenericDebug(xsltGenericDebugContext
,
1747 "applying xsl:fallback\n");
1750 xsltApplySequenceConstructor(ctxt
, node
, child
->children
,
1753 child
= child
->next
;
1758 /************************************************************************
1760 * Default processing *
1762 ************************************************************************/
1765 * xsltDefaultProcessOneNode:
1766 * @ctxt: a XSLT process context
1767 * @node: the node in the source tree.
1768 * @params: extra parameters passed to the template if any
1770 * Process the source node with the default built-in template rule:
1771 * <xsl:template match="*|/">
1772 * <xsl:apply-templates/>
1777 * <xsl:template match="text()|@*">
1778 * <xsl:value-of select="."/>
1781 * Note also that namespace declarations are copied directly:
1783 * the built-in template rule is the only template rule that is applied
1784 * for namespace nodes.
1787 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1788 xsltStackElemPtr params
) {
1790 xmlNodePtr
delete = NULL
, cur
;
1791 int nbchild
= 0, oldSize
;
1792 int childno
= 0, oldPos
;
1793 xsltTemplatePtr
template;
1797 * Handling of leaves
1799 switch (node
->type
) {
1800 case XML_DOCUMENT_NODE
:
1801 case XML_HTML_DOCUMENT_NODE
:
1802 case XML_ELEMENT_NODE
:
1804 case XML_CDATA_SECTION_NODE
:
1805 #ifdef WITH_XSLT_DEBUG_PROCESS
1806 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1807 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1810 copy
= xsltCopyText(ctxt
, ctxt
->insert
, node
, 0);
1812 xsltTransformError(ctxt
, NULL
, node
,
1813 "xsltDefaultProcessOneNode: cdata copy failed\n");
1817 #ifdef WITH_XSLT_DEBUG_PROCESS
1818 if (node
->content
== NULL
) {
1819 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1820 "xsltDefaultProcessOneNode: copy empty text\n"));
1823 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1824 "xsltDefaultProcessOneNode: copy text %s\n",
1828 copy
= xsltCopyText(ctxt
, ctxt
->insert
, node
, 0);
1830 xsltTransformError(ctxt
, NULL
, node
,
1831 "xsltDefaultProcessOneNode: text copy failed\n");
1834 case XML_ATTRIBUTE_NODE
:
1835 cur
= node
->children
;
1836 while ((cur
!= NULL
) && (cur
->type
!= XML_TEXT_NODE
))
1839 xsltTransformError(ctxt
, NULL
, node
,
1840 "xsltDefaultProcessOneNode: no text for attribute\n");
1842 #ifdef WITH_XSLT_DEBUG_PROCESS
1843 if (cur
->content
== NULL
) {
1844 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1845 "xsltDefaultProcessOneNode: copy empty text\n"));
1847 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1848 "xsltDefaultProcessOneNode: copy text %s\n",
1852 copy
= xsltCopyText(ctxt
, ctxt
->insert
, cur
, 0);
1854 xsltTransformError(ctxt
, NULL
, node
,
1855 "xsltDefaultProcessOneNode: text copy failed\n");
1863 * Handling of Elements: first pass, cleanup and counting
1865 cur
= node
->children
;
1866 while (cur
!= NULL
) {
1867 switch (cur
->type
) {
1869 case XML_CDATA_SECTION_NODE
:
1870 case XML_DOCUMENT_NODE
:
1871 case XML_HTML_DOCUMENT_NODE
:
1872 case XML_ELEMENT_NODE
:
1874 case XML_COMMENT_NODE
:
1878 /* Unlink the DTD, it's still reachable using doc->intSubset */
1879 if (cur
->next
!= NULL
)
1880 cur
->next
->prev
= cur
->prev
;
1881 if (cur
->prev
!= NULL
)
1882 cur
->prev
->next
= cur
->next
;
1885 #ifdef WITH_XSLT_DEBUG_PROCESS
1886 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1887 "xsltDefaultProcessOneNode: skipping node type %d\n",
1893 if (delete != NULL
) {
1894 #ifdef WITH_XSLT_DEBUG_PROCESS
1895 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1896 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
1898 xmlUnlinkNode(delete);
1899 xmlFreeNode(delete);
1903 if (delete != NULL
) {
1904 #ifdef WITH_XSLT_DEBUG_PROCESS
1905 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1906 "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
1908 xmlUnlinkNode(delete);
1909 xmlFreeNode(delete);
1914 * Handling of Elements: second pass, actual processing
1916 oldSize
= ctxt
->xpathCtxt
->contextSize
;
1917 oldPos
= ctxt
->xpathCtxt
->proximityPosition
;
1918 cur
= node
->children
;
1919 while (cur
!= NULL
) {
1921 switch (cur
->type
) {
1922 case XML_DOCUMENT_NODE
:
1923 case XML_HTML_DOCUMENT_NODE
:
1924 case XML_ELEMENT_NODE
:
1925 ctxt
->xpathCtxt
->contextSize
= nbchild
;
1926 ctxt
->xpathCtxt
->proximityPosition
= childno
;
1927 xsltProcessOneNode(ctxt
, cur
, params
);
1929 case XML_CDATA_SECTION_NODE
:
1930 template = xsltGetTemplate(ctxt
, cur
, NULL
);
1932 #ifdef WITH_XSLT_DEBUG_PROCESS
1933 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1934 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
1938 * Instantiate the xsl:template.
1940 xsltApplyXSLTTemplate(ctxt
, cur
, template->content
,
1942 } else /* if (ctxt->mode == NULL) */ {
1943 #ifdef WITH_XSLT_DEBUG_PROCESS
1944 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1945 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1948 copy
= xsltCopyText(ctxt
, ctxt
->insert
, cur
, 0);
1950 xsltTransformError(ctxt
, NULL
, cur
,
1951 "xsltDefaultProcessOneNode: cdata copy failed\n");
1956 template = xsltGetTemplate(ctxt
, cur
, NULL
);
1958 #ifdef WITH_XSLT_DEBUG_PROCESS
1959 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1960 "xsltDefaultProcessOneNode: applying template for text %s\n",
1963 ctxt
->xpathCtxt
->contextSize
= nbchild
;
1964 ctxt
->xpathCtxt
->proximityPosition
= childno
;
1966 * Instantiate the xsl:template.
1968 xsltApplyXSLTTemplate(ctxt
, cur
, template->content
,
1970 } else /* if (ctxt->mode == NULL) */ {
1971 #ifdef WITH_XSLT_DEBUG_PROCESS
1972 if (cur
->content
== NULL
) {
1973 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1974 "xsltDefaultProcessOneNode: copy empty text\n"));
1976 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1977 "xsltDefaultProcessOneNode: copy text %s\n",
1981 copy
= xsltCopyText(ctxt
, ctxt
->insert
, cur
, 0);
1983 xsltTransformError(ctxt
, NULL
, cur
,
1984 "xsltDefaultProcessOneNode: text copy failed\n");
1989 case XML_COMMENT_NODE
:
1990 template = xsltGetTemplate(ctxt
, cur
, NULL
);
1992 #ifdef WITH_XSLT_DEBUG_PROCESS
1993 if (cur
->type
== XML_PI_NODE
) {
1994 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1995 "xsltDefaultProcessOneNode: template found for PI %s\n",
1997 } else if (cur
->type
== XML_COMMENT_NODE
) {
1998 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
1999 "xsltDefaultProcessOneNode: template found for comment\n"));
2002 ctxt
->xpathCtxt
->contextSize
= nbchild
;
2003 ctxt
->xpathCtxt
->proximityPosition
= childno
;
2005 * Instantiate the xsl:template.
2007 xsltApplyXSLTTemplate(ctxt
, cur
, template->content
,
2016 ctxt
->xpathCtxt
->contextSize
= oldSize
;
2017 ctxt
->xpathCtxt
->proximityPosition
= oldPos
;
2021 * xsltProcessOneNode:
2022 * @ctxt: a XSLT process context
2023 * @contextNode: the "current node" in the source tree
2024 * @withParams: extra parameters (e.g. xsl:with-param) passed to the
2027 * Process the source node.
2030 xsltProcessOneNode(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
,
2031 xsltStackElemPtr withParams
)
2033 xsltTemplatePtr templ
;
2036 templ
= xsltGetTemplate(ctxt
, contextNode
, NULL
);
2038 * If no template is found, apply the default rule.
2040 if (templ
== NULL
) {
2041 #ifdef WITH_XSLT_DEBUG_PROCESS
2042 if (contextNode
->type
== XML_DOCUMENT_NODE
) {
2043 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2044 "xsltProcessOneNode: no template found for /\n"));
2045 } else if (contextNode
->type
== XML_CDATA_SECTION_NODE
) {
2046 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2047 "xsltProcessOneNode: no template found for CDATA\n"));
2048 } else if (contextNode
->type
== XML_ATTRIBUTE_NODE
) {
2049 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2050 "xsltProcessOneNode: no template found for attribute %s\n",
2051 ((xmlAttrPtr
) contextNode
)->name
));
2053 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2054 "xsltProcessOneNode: no template found for %s\n", contextNode
->name
));
2057 oldNode
= ctxt
->node
;
2058 ctxt
->node
= contextNode
;
2059 xsltDefaultProcessOneNode(ctxt
, contextNode
, withParams
);
2060 ctxt
->node
= oldNode
;
2064 if (contextNode
->type
== XML_ATTRIBUTE_NODE
) {
2065 xsltTemplatePtr oldCurTempRule
= ctxt
->currentTemplateRule
;
2067 * Set the "current template rule".
2069 ctxt
->currentTemplateRule
= templ
;
2071 #ifdef WITH_XSLT_DEBUG_PROCESS
2072 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2073 "xsltProcessOneNode: applying template '%s' for attribute %s\n",
2074 templ
->match
, contextNode
->name
));
2076 xsltApplyXSLTTemplate(ctxt
, contextNode
, templ
->content
, templ
, withParams
);
2078 ctxt
->currentTemplateRule
= oldCurTempRule
;
2080 xsltTemplatePtr oldCurTempRule
= ctxt
->currentTemplateRule
;
2082 * Set the "current template rule".
2084 ctxt
->currentTemplateRule
= templ
;
2086 #ifdef WITH_XSLT_DEBUG_PROCESS
2087 if (contextNode
->type
== XML_DOCUMENT_NODE
) {
2088 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2089 "xsltProcessOneNode: applying template '%s' for /\n",
2092 XSLT_TRACE(ctxt
,XSLT_TRACE_PROCESS_NODE
,xsltGenericDebug(xsltGenericDebugContext
,
2093 "xsltProcessOneNode: applying template '%s' for %s\n",
2094 templ
->match
, contextNode
->name
));
2097 xsltApplyXSLTTemplate(ctxt
, contextNode
, templ
->content
, templ
, withParams
);
2099 ctxt
->currentTemplateRule
= oldCurTempRule
;
2104 xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt
,
2105 xmlNodePtr contextNode
,
2107 xsltTemplatePtr templ
,
2110 xmlNodePtr debugedNode
= NULL
;
2112 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) {
2114 *addCallResult
= xslAddCall(templ
, templ
->elem
);
2116 *addCallResult
= xslAddCall(NULL
, list
);
2118 switch (ctxt
->debugStatus
) {
2119 case XSLT_DEBUG_RUN_RESTART
:
2120 case XSLT_DEBUG_QUIT
:
2126 xslHandleDebugger(templ
->elem
, contextNode
, templ
, ctxt
);
2127 debugedNode
= templ
->elem
;
2129 xslHandleDebugger(list
, contextNode
, templ
, ctxt
);
2131 } else if (ctxt
->inst
) {
2132 xslHandleDebugger(ctxt
->inst
, contextNode
, templ
, ctxt
);
2133 debugedNode
= ctxt
->inst
;
2136 return(debugedNode
);
2140 * xsltLocalVariablePush:
2141 * @ctxt: the transformation context
2142 * @variable: variable to be pushed to the variable stack
2143 * @level: new value for variable's level
2145 * Places the variable onto the local variable stack
2147 * Returns: 0 for success, -1 for any error
2149 * This is an internal routine and should not be called by users!
2152 xsltLocalVariablePush(xsltTransformContextPtr ctxt
,
2153 xsltStackElemPtr variable
,
2156 if (ctxt
->varsMax
== 0) {
2159 (xsltStackElemPtr
*) xmlMalloc(ctxt
->varsMax
*
2160 sizeof(ctxt
->varsTab
[0]));
2161 if (ctxt
->varsTab
== NULL
) {
2162 xmlGenericError(xmlGenericErrorContext
, "malloc failed !\n");
2166 if (ctxt
->varsNr
>= ctxt
->varsMax
) {
2169 (xsltStackElemPtr
*) xmlRealloc(ctxt
->varsTab
,
2171 sizeof(ctxt
->varsTab
[0]));
2172 if (ctxt
->varsTab
== NULL
) {
2173 xmlGenericError(xmlGenericErrorContext
, "realloc failed !\n");
2177 ctxt
->varsTab
[ctxt
->varsNr
++] = variable
;
2178 ctxt
->vars
= variable
;
2179 variable
->level
= level
;
2184 * xsltReleaseLocalRVTs:
2186 * Fragments which are results of extension instructions
2187 * are preserved; all other fragments are freed/cached.
2190 xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt
, xmlDocPtr base
)
2192 xmlDocPtr cur
= ctxt
->localRVT
, tmp
;
2194 while ((cur
!= NULL
) && (cur
!= base
)) {
2195 if (cur
->psvi
== (void *) ((long) 1)) {
2196 cur
= (xmlDocPtr
) cur
->next
;
2199 cur
= (xmlDocPtr
) cur
->next
;
2201 if (tmp
== ctxt
->localRVT
)
2202 ctxt
->localRVT
= cur
;
2205 * We need ctxt->localRVTBase for extension instructions
2206 * which return values (like EXSLT's function).
2208 if (tmp
== ctxt
->localRVTBase
)
2209 ctxt
->localRVTBase
= cur
;
2212 tmp
->prev
->next
= (xmlNodePtr
) cur
;
2214 cur
->prev
= tmp
->prev
;
2215 xsltReleaseRVT(ctxt
, tmp
);
2221 * xsltApplySequenceConstructor:
2222 * @ctxt: a XSLT process context
2223 * @contextNode: the "current node" in the source tree
2224 * @list: the nodes of a sequence constructor;
2225 * (plus leading xsl:param elements)
2226 * @templ: the compiled xsl:template (optional)
2228 * Processes a sequence constructor.
2230 * NOTE: ctxt->currentTemplateRule was introduced to reflect the
2231 * semantics of "current template rule". I.e. the field ctxt->templ
2232 * is not intended to reflect this, thus always pushed onto the
2236 xsltApplySequenceConstructor(xsltTransformContextPtr ctxt
,
2237 xmlNodePtr contextNode
, xmlNodePtr list
,
2238 xsltTemplatePtr templ
)
2240 xmlNodePtr oldInsert
, oldInst
, oldCurInst
, oldContextNode
;
2241 xmlNodePtr cur
, insert
, copy
= NULL
;
2242 int level
= 0, oldVarsNr
;
2243 xmlDocPtr oldLocalFragmentTop
, oldLocalFragmentBase
;
2245 #ifdef XSLT_REFACTORED
2246 xsltStylePreCompPtr info
;
2249 #ifdef WITH_DEBUGGER
2250 int addCallResult
= 0;
2251 xmlNodePtr debuggedNode
= NULL
;
2257 #ifdef WITH_DEBUGGER
2258 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) {
2260 xsltDebuggerStartSequenceConstructor(ctxt
, contextNode
,
2261 list
, templ
, &addCallResult
);
2262 if (debuggedNode
== NULL
)
2271 oldLocalFragmentTop
= ctxt
->localRVT
;
2272 oldInsert
= insert
= ctxt
->insert
;
2273 oldInst
= oldCurInst
= ctxt
->inst
;
2274 oldContextNode
= ctxt
->node
;
2276 * Save current number of variables on the stack; new vars are popped when
2279 oldVarsNr
= ctxt
->varsNr
;
2281 * Process the sequence constructor.
2284 while (cur
!= NULL
) {
2287 #ifdef WITH_DEBUGGER
2288 switch (ctxt
->debugStatus
) {
2289 case XSLT_DEBUG_RUN_RESTART
:
2290 case XSLT_DEBUG_QUIT
:
2296 * Test; we must have a valid insertion point.
2298 if (insert
== NULL
) {
2300 #ifdef WITH_XSLT_DEBUG_PROCESS
2301 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2302 "xsltApplySequenceConstructor: insert == NULL !\n"));
2307 #ifdef WITH_DEBUGGER
2308 if ((ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) && (debuggedNode
!= cur
))
2309 xslHandleDebugger(cur
, contextNode
, templ
, ctxt
);
2312 #ifdef XSLT_REFACTORED
2313 if (cur
->type
== XML_ELEMENT_NODE
) {
2314 info
= (xsltStylePreCompPtr
) cur
->psvi
;
2316 * We expect a compiled representation on:
2317 * 1) XSLT instructions of this XSLT version (1.0)
2318 * (with a few exceptions)
2319 * 2) Literal result elements
2320 * 3) Extension instructions
2321 * 4) XSLT instructions of future XSLT versions
2322 * (forwards-compatible mode).
2326 * Handle the rare cases where we don't expect a compiled
2327 * representation on an XSLT element.
2329 if (IS_XSLT_ELEM_FAST(cur
) && IS_XSLT_NAME(cur
, "message")) {
2330 xsltMessage(ctxt
, contextNode
, cur
);
2334 * Something really went wrong:
2336 xsltTransformError(ctxt
, NULL
, cur
,
2337 "Internal error in xsltApplySequenceConstructor(): "
2338 "The element '%s' in the stylesheet has no compiled "
2339 "representation.\n",
2344 if (info
->type
== XSLT_FUNC_LITERAL_RESULT_ELEMENT
) {
2345 xsltStyleItemLRElementInfoPtr lrInfo
=
2346 (xsltStyleItemLRElementInfoPtr
) info
;
2348 * Literal result elements
2349 * --------------------------------------------------------
2351 #ifdef WITH_XSLT_DEBUG_PROCESS
2352 XSLT_TRACE(ctxt
, XSLT_TRACE_APPLY_TEMPLATE
,
2353 xsltGenericDebug(xsltGenericDebugContext
,
2354 "xsltApplySequenceConstructor: copy literal result "
2355 "element '%s'\n", cur
->name
));
2358 * Copy the raw element-node.
2359 * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
2363 copy
= xmlDocCopyNode(cur
, insert
->doc
, 0);
2365 xsltTransformError(ctxt
, NULL
, cur
,
2366 "Internal error in xsltApplySequenceConstructor(): "
2367 "Failed to copy literal result element '%s'.\n",
2372 * Add the element-node to the result tree.
2374 copy
->doc
= ctxt
->output
;
2375 copy
= xsltAddChild(insert
, copy
);
2377 * Create effective namespaces declarations.
2378 * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
2380 if (lrInfo
->effectiveNs
!= NULL
) {
2381 xsltEffectiveNsPtr effNs
= lrInfo
->effectiveNs
;
2382 xmlNsPtr ns
, lastns
= NULL
;
2384 while (effNs
!= NULL
) {
2386 * Avoid generating redundant namespace
2387 * declarations; thus lookup if there is already
2388 * such a ns-decl in the result.
2390 ns
= xmlSearchNs(copy
->doc
, copy
, effNs
->prefix
);
2392 (xmlStrEqual(ns
->href
, effNs
->nsName
)))
2394 effNs
= effNs
->next
;
2397 ns
= xmlNewNs(copy
, effNs
->nsName
, effNs
->prefix
);
2399 xsltTransformError(ctxt
, NULL
, cur
,
2400 "Internal error in "
2401 "xsltApplySequenceConstructor(): "
2402 "Failed to copy a namespace "
2413 effNs
= effNs
->next
;
2418 * NOTE that we don't need to apply ns-alising: this was
2419 * already done at compile-time.
2421 if (cur
->ns
!= NULL
) {
2423 * If there's no such ns-decl in the result tree,
2424 * then xsltGetSpecialNamespace() will
2425 * create a ns-decl on the copied node.
2427 copy
->ns
= xsltGetSpecialNamespace(ctxt
, cur
,
2428 cur
->ns
->href
, cur
->ns
->prefix
, copy
);
2431 * Undeclare the default namespace if needed.
2432 * This can be skipped, if the result element has
2433 * no ns-decls, in which case the result element
2434 * obviously does not declare a default namespace;
2435 * AND there's either no parent, or the parent
2436 * element is in no namespace; this means there's no
2437 * default namespace is scope to care about.
2439 * REVISIT: This might result in massive
2440 * generation of ns-decls if nodes in a default
2441 * namespaces are mixed with nodes in no namespace.
2445 ((insert
!= NULL
) &&
2446 (insert
->type
== XML_ELEMENT_NODE
) &&
2447 (insert
->ns
!= NULL
)))
2449 xsltGetSpecialNamespace(ctxt
, cur
,
2455 * SPEC XSLT 2.0 "Each attribute of the literal result
2456 * element, other than an attribute in the XSLT namespace,
2457 * is processed to produce an attribute for the element in
2459 * NOTE: See bug #341325.
2461 if (cur
->properties
!= NULL
) {
2462 xsltAttrListTemplateProcess(ctxt
, copy
, cur
->properties
);
2464 } else if (IS_XSLT_ELEM_FAST(cur
)) {
2467 * --------------------------------------------------------
2469 if (info
->type
== XSLT_FUNC_UNKOWN_FORWARDS_COMPAT
) {
2471 * We hit an unknown XSLT element.
2472 * Try to apply one of the fallback cases.
2474 ctxt
->insert
= insert
;
2475 if (!xsltApplyFallbacks(ctxt
, contextNode
, cur
)) {
2476 xsltTransformError(ctxt
, NULL
, cur
,
2477 "The is no fallback behaviour defined for "
2478 "the unknown XSLT element '%s'.\n",
2481 ctxt
->insert
= oldInsert
;
2482 } else if (info
->func
!= NULL
) {
2484 * Execute the XSLT instruction.
2486 ctxt
->insert
= insert
;
2488 info
->func(ctxt
, contextNode
, cur
,
2489 (xsltElemPreCompPtr
) info
);
2492 * Cleanup temporary tree fragments.
2494 if (oldLocalFragmentTop
!= ctxt
->localRVT
)
2495 xsltReleaseLocalRVTs(ctxt
, oldLocalFragmentTop
);
2497 ctxt
->insert
= oldInsert
;
2498 } else if (info
->type
== XSLT_FUNC_VARIABLE
) {
2499 xsltStackElemPtr tmpvar
= ctxt
->vars
;
2501 xsltParseStylesheetVariable(ctxt
, cur
);
2503 if (tmpvar
!= ctxt
->vars
) {
2505 * TODO: Using a @tmpvar is an annoying workaround, but
2506 * the current mechanisms do not provide any other way
2507 * of knowing if the var was really pushed onto the
2510 ctxt
->vars
->level
= level
;
2512 } else if (info
->type
== XSLT_FUNC_MESSAGE
) {
2514 * TODO: Won't be hit, since we don't compile xsl:message.
2516 xsltMessage(ctxt
, contextNode
, cur
);
2518 xsltTransformError(ctxt
, NULL
, cur
,
2519 "Unexpected XSLT element '%s'.\n", cur
->name
);
2524 xsltTransformFunction func
;
2526 * Extension intructions (elements)
2527 * --------------------------------------------------------
2529 if (cur
->psvi
== xsltExtMarker
) {
2531 * The xsltExtMarker was set during the compilation
2532 * of extension instructions if there was no registered
2533 * handler for this specific extension function at
2535 * Libxslt will now lookup if a handler is
2536 * registered in the context of this transformation.
2538 func
= (xsltTransformFunction
)
2539 xsltExtElementLookup(ctxt
, cur
->name
, cur
->ns
->href
);
2541 func
= ((xsltElemPreCompPtr
) cur
->psvi
)->func
;
2545 * No handler available.
2546 * Try to execute fallback behaviour via xsl:fallback.
2548 #ifdef WITH_XSLT_DEBUG_PROCESS
2549 XSLT_TRACE(ctxt
, XSLT_TRACE_APPLY_TEMPLATE
,
2550 xsltGenericDebug(xsltGenericDebugContext
,
2551 "xsltApplySequenceConstructor: unknown extension %s\n",
2554 ctxt
->insert
= insert
;
2555 if (!xsltApplyFallbacks(ctxt
, contextNode
, cur
)) {
2556 xsltTransformError(ctxt
, NULL
, cur
,
2557 "Unknown extension instruction '{%s}%s'.\n",
2558 cur
->ns
->href
, cur
->name
);
2560 ctxt
->insert
= oldInsert
;
2563 * Execute the handler-callback.
2565 #ifdef WITH_XSLT_DEBUG_PROCESS
2566 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2567 "xsltApplySequenceConstructor: extension construct %s\n",
2570 ctxt
->insert
= insert
;
2572 * We need the fragment base for extension instructions
2573 * which return values (like EXSLT's function).
2575 oldLocalFragmentBase
= ctxt
->localRVTBase
;
2576 ctxt
->localRVTBase
= NULL
;
2578 func(ctxt
, contextNode
, cur
, cur
->psvi
);
2580 ctxt
->localRVTBase
= oldLocalFragmentBase
;
2582 * Cleanup temporary tree fragments.
2584 if (oldLocalFragmentTop
!= ctxt
->localRVT
)
2585 xsltReleaseLocalRVTs(ctxt
, oldLocalFragmentTop
);
2587 ctxt
->insert
= oldInsert
;
2592 } else if (XSLT_IS_TEXT_NODE(cur
)) {
2595 * ------------------------------------------------------------
2597 #ifdef WITH_XSLT_DEBUG_PROCESS
2598 if (cur
->name
== xmlStringTextNoenc
) {
2599 XSLT_TRACE(ctxt
, XSLT_TRACE_APPLY_TEMPLATE
,
2600 xsltGenericDebug(xsltGenericDebugContext
,
2601 "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
2604 XSLT_TRACE(ctxt
, XSLT_TRACE_APPLY_TEMPLATE
,
2605 xsltGenericDebug(xsltGenericDebugContext
,
2606 "xsltApplySequenceConstructor: copy text '%s'\n",
2610 if (xsltCopyText(ctxt
, insert
, cur
, ctxt
->internalized
) == NULL
)
2614 #else /* XSLT_REFACTORED */
2616 if (IS_XSLT_ELEM(cur
)) {
2618 * This is an XSLT node
2620 xsltStylePreCompPtr info
= (xsltStylePreCompPtr
) cur
->psvi
;
2623 if (IS_XSLT_NAME(cur
, "message")) {
2624 xsltMessage(ctxt
, contextNode
, cur
);
2627 * That's an error try to apply one of the fallback cases
2629 ctxt
->insert
= insert
;
2630 if (!xsltApplyFallbacks(ctxt
, contextNode
, cur
)) {
2631 xsltGenericError(xsltGenericErrorContext
,
2632 "xsltApplySequenceConstructor: %s was not compiled\n",
2635 ctxt
->insert
= oldInsert
;
2640 if (info
->func
!= NULL
) {
2641 oldCurInst
= ctxt
->inst
;
2643 ctxt
->insert
= insert
;
2644 oldLocalFragmentBase
= ctxt
->localRVTBase
;
2645 ctxt
->localRVTBase
= NULL
;
2647 info
->func(ctxt
, contextNode
, cur
, (xsltElemPreCompPtr
) info
);
2649 ctxt
->localRVTBase
= oldLocalFragmentBase
;
2651 * Cleanup temporary tree fragments.
2653 if (oldLocalFragmentTop
!= ctxt
->localRVT
)
2654 xsltReleaseLocalRVTs(ctxt
, oldLocalFragmentTop
);
2656 ctxt
->insert
= oldInsert
;
2657 ctxt
->inst
= oldCurInst
;
2661 if (IS_XSLT_NAME(cur
, "variable")) {
2662 xsltStackElemPtr tmpvar
= ctxt
->vars
;
2664 oldCurInst
= ctxt
->inst
;
2667 xsltParseStylesheetVariable(ctxt
, cur
);
2669 ctxt
->inst
= oldCurInst
;
2671 if (tmpvar
!= ctxt
->vars
) {
2673 * TODO: Using a @tmpvar is an annoying workaround, but
2674 * the current mechanisms do not provide any other way
2675 * of knowing if the var was really pushed onto the
2678 ctxt
->vars
->level
= level
;
2680 } else if (IS_XSLT_NAME(cur
, "message")) {
2681 xsltMessage(ctxt
, contextNode
, cur
);
2683 xsltTransformError(ctxt
, NULL
, cur
,
2684 "Unexpected XSLT element '%s'.\n", cur
->name
);
2687 } else if ((cur
->type
== XML_TEXT_NODE
) ||
2688 (cur
->type
== XML_CDATA_SECTION_NODE
)) {
2691 * This text comes from the stylesheet
2692 * For stylesheets, the set of whitespace-preserving
2693 * element names consists of just xsl:text.
2695 #ifdef WITH_XSLT_DEBUG_PROCESS
2696 if (cur
->type
== XML_CDATA_SECTION_NODE
) {
2697 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2698 "xsltApplySequenceConstructor: copy CDATA text %s\n",
2700 } else if (cur
->name
== xmlStringTextNoenc
) {
2701 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2702 "xsltApplySequenceConstructor: copy unescaped text %s\n",
2705 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2706 "xsltApplySequenceConstructor: copy text %s\n",
2710 if (xsltCopyText(ctxt
, insert
, cur
, ctxt
->internalized
) == NULL
)
2712 } else if ((cur
->type
== XML_ELEMENT_NODE
) &&
2713 (cur
->ns
!= NULL
) && (cur
->psvi
!= NULL
)) {
2714 xsltTransformFunction function
;
2716 oldCurInst
= ctxt
->inst
;
2719 * Flagged as an extension element
2721 if (cur
->psvi
== xsltExtMarker
)
2722 function
= (xsltTransformFunction
)
2723 xsltExtElementLookup(ctxt
, cur
->name
, cur
->ns
->href
);
2725 function
= ((xsltElemPreCompPtr
) cur
->psvi
)->func
;
2727 if (function
== NULL
) {
2731 #ifdef WITH_XSLT_DEBUG_PROCESS
2732 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2733 "xsltApplySequenceConstructor: unknown extension %s\n",
2737 * Search if there are fallbacks
2739 child
= cur
->children
;
2740 while (child
!= NULL
) {
2741 if ((IS_XSLT_ELEM(child
)) &&
2742 (IS_XSLT_NAME(child
, "fallback")))
2745 xsltApplySequenceConstructor(ctxt
, contextNode
,
2746 child
->children
, NULL
);
2748 child
= child
->next
;
2752 xsltTransformError(ctxt
, NULL
, cur
,
2753 "xsltApplySequenceConstructor: failed to find extension %s\n",
2757 #ifdef WITH_XSLT_DEBUG_PROCESS
2758 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2759 "xsltApplySequenceConstructor: extension construct %s\n",
2763 ctxt
->insert
= insert
;
2765 * We need the fragment base for extension instructions
2766 * which return values (like EXSLT's function).
2768 oldLocalFragmentBase
= ctxt
->localRVTBase
;
2769 ctxt
->localRVTBase
= NULL
;
2771 function(ctxt
, contextNode
, cur
, cur
->psvi
);
2773 * Cleanup temporary tree fragments.
2775 if (oldLocalFragmentTop
!= ctxt
->localRVT
)
2776 xsltReleaseLocalRVTs(ctxt
, oldLocalFragmentTop
);
2778 ctxt
->localRVTBase
= oldLocalFragmentBase
;
2779 ctxt
->insert
= oldInsert
;
2782 ctxt
->inst
= oldCurInst
;
2784 } else if (cur
->type
== XML_ELEMENT_NODE
) {
2785 #ifdef WITH_XSLT_DEBUG_PROCESS
2786 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
2787 "xsltApplySequenceConstructor: copy node %s\n",
2790 oldCurInst
= ctxt
->inst
;
2793 if ((copy
= xsltShallowCopyElem(ctxt
, cur
, insert
, 1)) == NULL
)
2796 * Add extra namespaces inherited from the current template
2797 * if we are in the first level children and this is a
2800 if ((templ
!= NULL
) && (oldInsert
== insert
) &&
2801 (ctxt
->templ
!= NULL
) && (ctxt
->templ
->inheritedNs
!= NULL
)) {
2805 for (i
= 0; i
< ctxt
->templ
->inheritedNsNr
; i
++) {
2806 const xmlChar
*URI
= NULL
;
2807 xsltStylesheetPtr style
;
2808 ns
= ctxt
->templ
->inheritedNs
[i
];
2810 /* Note that the XSLT namespace was already excluded
2811 * in xsltGetInheritedNsList().
2814 if (xmlStrEqual(ns
->href
, XSLT_NAMESPACE
))
2817 style
= ctxt
->style
;
2818 while (style
!= NULL
) {
2819 if (style
->nsAliases
!= NULL
)
2820 URI
= (const xmlChar
*)
2821 xmlHashLookup(style
->nsAliases
, ns
->href
);
2825 style
= xsltNextImport(style
);
2827 if (URI
== UNDEFINED_DEFAULT_NS
)
2832 * TODO: The following will still be buggy for the
2833 * non-refactored code.
2835 ret
= xmlSearchNs(copy
->doc
, copy
, ns
->prefix
);
2836 if ((ret
== NULL
) || (!xmlStrEqual(ret
->href
, URI
)))
2838 xmlNewNs(copy
, URI
, ns
->prefix
);
2841 if (copy
->ns
!= NULL
) {
2843 * Fix the node namespace if needed
2845 copy
->ns
= xsltGetNamespace(ctxt
, cur
, copy
->ns
, copy
);
2849 * all the attributes are directly inherited
2851 if (cur
->properties
!= NULL
) {
2852 xsltAttrListTemplateProcess(ctxt
, copy
, cur
->properties
);
2854 ctxt
->inst
= oldCurInst
;
2856 #endif /* else of XSLT_REFACTORED */
2859 * Descend into content in document order.
2861 if (cur
->children
!= NULL
) {
2862 if (cur
->children
->type
!= XML_ENTITY_DECL
) {
2863 cur
= cur
->children
;
2873 * If xslt:message was just processed, we might have hit a
2874 * terminate='yes'; if so, then break the loop and clean up.
2875 * TODO: Do we need to check this also before trying to descend
2878 if (ctxt
->state
== XSLT_STATE_STOPPED
)
2880 if (cur
->next
!= NULL
) {
2889 * Pop variables/params (xsl:variable and xsl:param).
2891 if ((ctxt
->varsNr
> oldVarsNr
) && (ctxt
->vars
->level
> level
)) {
2892 xsltLocalVariablePop(ctxt
, oldVarsNr
, level
);
2895 insert
= insert
->parent
;
2898 if (cur
== list
->parent
) {
2902 if (cur
->next
!= NULL
) {
2906 } while (cur
!= NULL
);
2911 * In case of errors: pop remaining variables.
2913 if (ctxt
->varsNr
> oldVarsNr
)
2914 xsltLocalVariablePop(ctxt
, oldVarsNr
, -1);
2916 ctxt
->node
= oldContextNode
;
2917 ctxt
->inst
= oldInst
;
2918 ctxt
->insert
= oldInsert
;
2920 #ifdef WITH_DEBUGGER
2921 if ((ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) && (addCallResult
)) {
2928 * xsltApplyXSLTTemplate:
2929 * @ctxt: a XSLT transformation context
2930 * @contextNode: the node in the source tree.
2931 * @list: the nodes of a sequence constructor;
2932 * (plus leading xsl:param elements)
2933 * @templ: the compiled xsl:template declaration;
2934 * NULL if a sequence constructor
2935 * @withParams: a set of caller-parameters (xsl:with-param) or NULL
2938 * - xsltApplyImports()
2939 * - xsltCallTemplate()
2940 * - xsltDefaultProcessOneNode()
2941 * - xsltProcessOneNode()
2944 xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt
,
2945 xmlNodePtr contextNode
,
2947 xsltTemplatePtr templ
,
2948 xsltStackElemPtr withParams
)
2950 int oldVarsBase
= 0;
2953 xsltStackElemPtr tmpParam
= NULL
;
2954 xmlDocPtr oldUserFragmentTop
, oldLocalFragmentTop
;
2956 #ifdef XSLT_REFACTORED
2957 xsltStyleItemParamPtr iparam
;
2959 xsltStylePreCompPtr iparam
;
2962 #ifdef WITH_DEBUGGER
2963 int addCallResult
= 0;
2968 if (templ
== NULL
) {
2969 xsltTransformError(ctxt
, NULL
, list
,
2970 "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
2974 #ifdef WITH_DEBUGGER
2975 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) {
2976 if (xsltDebuggerStartSequenceConstructor(ctxt
, contextNode
,
2977 list
, templ
, &addCallResult
) == NULL
)
2987 * Check for infinite recursion: stop if the maximum of nested templates
2988 * is excceeded. Adjust xsltMaxDepth if you need more.
2990 if (ctxt
->templNr
>= ctxt
->maxTemplateDepth
)
2992 xsltTransformError(ctxt
, NULL
, list
,
2993 "xsltApplyXSLTTemplate: A potential infinite template recursion "
2995 "You can adjust xsltMaxDepth (--maxdepth) in order to "
2996 "raise the maximum number of nested template calls and "
2997 "variables/params (currently set to %d).\n",
2998 ctxt
->maxTemplateDepth
);
2999 xsltDebug(ctxt
, contextNode
, list
, NULL
);
3003 if (ctxt
->varsNr
>= ctxt
->maxTemplateVars
)
3005 xsltTransformError(ctxt
, NULL
, list
,
3006 "xsltApplyXSLTTemplate: A potential infinite template recursion "
3008 "You can adjust maxTemplateVars (--maxvars) in order to "
3009 "raise the maximum number of variables/params (currently set to %d).\n",
3010 ctxt
->maxTemplateVars
);
3011 xsltDebug(ctxt
, contextNode
, list
, NULL
);
3015 oldUserFragmentTop
= ctxt
->tmpRVT
;
3016 ctxt
->tmpRVT
= NULL
;
3017 oldLocalFragmentTop
= ctxt
->localRVT
;
3020 * Initiate a distinct scope of local params/variables.
3022 oldVarsBase
= ctxt
->varsBase
;
3023 ctxt
->varsBase
= ctxt
->varsNr
;
3025 ctxt
->node
= contextNode
;
3026 if (ctxt
->profile
) {
3028 start
= xsltTimestamp();
3030 profCallgraphAdd(templ
, ctxt
->templ
);
3033 * Push the xsl:template declaration onto the stack.
3035 templPush(ctxt
, templ
);
3037 #ifdef WITH_XSLT_DEBUG_PROCESS
3038 if (templ
->name
!= NULL
)
3039 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
3040 "applying xsl:template '%s'\n", templ
->name
));
3043 * Process xsl:param instructions and skip those elements for
3044 * further processing.
3048 if (cur
->type
== XML_TEXT_NODE
) {
3052 if ((cur
->type
!= XML_ELEMENT_NODE
) ||
3053 (cur
->name
[0] != 'p') ||
3054 (cur
->psvi
== NULL
) ||
3055 (! xmlStrEqual(cur
->name
, BAD_CAST
"param")) ||
3056 (! IS_XSLT_ELEM(cur
)))
3063 #ifdef XSLT_REFACTORED
3064 iparam
= (xsltStyleItemParamPtr
) cur
->psvi
;
3066 iparam
= (xsltStylePreCompPtr
) cur
->psvi
;
3070 * Substitute xsl:param for a given xsl:with-param.
3071 * Since the XPath expression will reference the params/vars
3072 * by index, we need to slot the xsl:with-params in the
3073 * order of encountered xsl:params to keep the sequence of
3074 * params/variables in the stack exactly as it was at
3079 tmpParam
= withParams
;
3081 if ((tmpParam
->name
== (iparam
->name
)) &&
3082 (tmpParam
->nameURI
== (iparam
->ns
)))
3085 * Push the caller-parameter.
3087 xsltLocalVariablePush(ctxt
, tmpParam
, -1);
3090 tmpParam
= tmpParam
->next
;
3091 } while (tmpParam
!= NULL
);
3094 * Push the xsl:param.
3096 if (tmpParam
== NULL
) {
3098 * Note that we must assume that the added parameter
3099 * has a @depth of 0.
3101 xsltParseStylesheetParam(ctxt
, cur
);
3104 } while (cur
!= NULL
);
3106 * Process the sequence constructor.
3108 xsltApplySequenceConstructor(ctxt
, contextNode
, list
, templ
);
3111 * Remove remaining xsl:param and xsl:with-param items from
3112 * the stack. Don't free xsl:with-param items.
3114 if (ctxt
->varsNr
> ctxt
->varsBase
)
3115 xsltTemplateParamsCleanup(ctxt
);
3116 ctxt
->varsBase
= oldVarsBase
;
3119 * Clean up remaining local tree fragments.
3120 * This also frees fragments which are the result of
3121 * extension instructions. Should normally not be hit; but
3122 * just for the case xsltExtensionInstructionResultFinalize()
3123 * was not called by the extension author.
3125 if (oldLocalFragmentTop
!= ctxt
->localRVT
) {
3126 xmlDocPtr curdoc
= ctxt
->localRVT
, tmp
;
3130 curdoc
= (xmlDocPtr
) curdoc
->next
;
3131 /* Need to housekeep localRVTBase */
3132 if (tmp
== ctxt
->localRVTBase
)
3133 ctxt
->localRVTBase
= curdoc
;
3135 tmp
->prev
->next
= (xmlNodePtr
) curdoc
;
3137 curdoc
->prev
= tmp
->prev
;
3138 xsltReleaseRVT(ctxt
, tmp
);
3139 } while (curdoc
!= oldLocalFragmentTop
);
3141 ctxt
->localRVT
= oldLocalFragmentTop
;
3144 * Release user-created fragments stored in the scope
3145 * of xsl:template. Note that this mechanism is deprecated:
3146 * user code should now use xsltRegisterLocalRVT() instead
3147 * of the obsolete xsltRegisterTmpRVT().
3150 xmlDocPtr curdoc
= ctxt
->tmpRVT
, tmp
;
3152 while (curdoc
!= NULL
) {
3154 curdoc
= (xmlDocPtr
) curdoc
->next
;
3155 xsltReleaseRVT(ctxt
, tmp
);
3158 ctxt
->tmpRVT
= oldUserFragmentTop
;
3161 * Pop the xsl:template declaration from the stack.
3164 if (ctxt
->profile
) {
3165 long spent
, child
, total
, end
;
3167 end
= xsltTimestamp();
3168 child
= profPop(ctxt
);
3169 total
= end
- start
;
3170 spent
= total
- child
;
3173 * Not possible unless the original calibration failed
3174 * we can try to correct it on the fly.
3176 xsltCalibrateAdjust(spent
);
3180 templ
->time
+= spent
;
3181 if (ctxt
->profNr
> 0)
3182 ctxt
->profTab
[ctxt
->profNr
- 1] += total
;
3185 #ifdef WITH_DEBUGGER
3186 if ((ctxt
->debugStatus
!= XSLT_DEBUG_NONE
) && (addCallResult
)) {
3194 * xsltApplyOneTemplate:
3195 * @ctxt: a XSLT process context
3196 * @contextNode: the node in the source tree.
3197 * @list: the nodes of a sequence constructor
3199 * @params: a set of parameters (xsl:param) or NULL
3201 * Processes a sequence constructor on the current node in the source tree.
3203 * @params are the already computed variable stack items; this function
3204 * pushes them on the variable stack, and pops them before exiting; it's
3205 * left to the caller to free or reuse @params afterwards. The initial
3206 * states of the variable stack will always be restored before this
3208 * NOTE that this does *not* initiate a new distinct variable scope; i.e.
3209 * variables already on the stack are visible to the process. The caller's
3210 * side needs to start a new variable scope if needed (e.g. in exsl:function).
3212 * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
3213 * provide a @templ); a non-NULL @templ might raise an error in the future.
3215 * BIG NOTE: This function is not intended to process the content of an
3216 * xsl:template; it does not expect xsl:param instructions in @list and
3217 * will report errors if found.
3220 * - xsltEvalVariable() (variables.c)
3221 * - exsltFuncFunctionFunction() (libexsl/functions.c)
3224 xsltApplyOneTemplate(xsltTransformContextPtr ctxt
,
3225 xmlNodePtr contextNode
,
3227 xsltTemplatePtr templ ATTRIBUTE_UNUSED
,
3228 xsltStackElemPtr params
)
3230 if ((ctxt
== NULL
) || (list
== NULL
))
3236 * This code should be obsolete - was previously used
3237 * by libexslt/functions.c, but due to bug 381319 the
3238 * logic there was changed.
3240 int oldVarsNr
= ctxt
->varsNr
;
3243 * Push the given xsl:param(s) onto the variable stack.
3245 while (params
!= NULL
) {
3246 xsltLocalVariablePush(ctxt
, params
, -1);
3247 params
= params
->next
;
3249 xsltApplySequenceConstructor(ctxt
, contextNode
, list
, templ
);
3251 * Pop the given xsl:param(s) from the stack but don't free them.
3253 xsltLocalVariablePop(ctxt
, oldVarsNr
, -2);
3255 xsltApplySequenceConstructor(ctxt
, contextNode
, list
, templ
);
3258 /************************************************************************
3260 * XSLT-1.1 extensions *
3262 ************************************************************************/
3266 * @ctxt: an XSLT processing context
3267 * @node: The current node
3268 * @inst: the instruction in the stylesheet
3269 * @castedComp: precomputed information
3271 * Process an EXSLT/XSLT-1.1 document element
3274 xsltDocumentElem(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
3275 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
3277 #ifdef XSLT_REFACTORED
3278 xsltStyleItemDocumentPtr comp
= (xsltStyleItemDocumentPtr
) castedComp
;
3280 xsltStylePreCompPtr comp
= castedComp
;
3282 xsltStylesheetPtr style
= NULL
;
3284 xmlChar
*filename
= NULL
, *prop
, *elements
;
3285 xmlChar
*element
, *end
;
3286 xmlDocPtr res
= NULL
;
3287 xmlDocPtr oldOutput
;
3288 xmlNodePtr oldInsert
, root
;
3289 const char *oldOutputFile
;
3290 xsltOutputType oldType
;
3291 xmlChar
*URL
= NULL
;
3292 const xmlChar
*method
;
3293 const xmlChar
*doctypePublic
;
3294 const xmlChar
*doctypeSystem
;
3295 const xmlChar
*version
;
3296 const xmlChar
*encoding
;
3297 int redirect_write_append
= 0;
3299 if ((ctxt
== NULL
) || (node
== NULL
) || (inst
== NULL
) || (comp
== NULL
))
3302 if (comp
->filename
== NULL
) {
3304 if (xmlStrEqual(inst
->name
, (const xmlChar
*) "output")) {
3306 * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
3307 * (http://icl.com/saxon)
3308 * The @file is in no namespace.
3310 #ifdef WITH_XSLT_DEBUG_EXTRA
3311 xsltGenericDebug(xsltGenericDebugContext
,
3312 "Found saxon:output extension\n");
3314 URL
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3315 (const xmlChar
*) "file",
3316 XSLT_SAXON_NAMESPACE
);
3319 URL
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3320 (const xmlChar
*) "href",
3321 XSLT_SAXON_NAMESPACE
);
3322 } else if (xmlStrEqual(inst
->name
, (const xmlChar
*) "write")) {
3323 #ifdef WITH_XSLT_DEBUG_EXTRA
3324 xsltGenericDebug(xsltGenericDebugContext
,
3325 "Found xalan:write extension\n");
3327 URL
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3330 XSLT_XALAN_NAMESPACE
);
3332 xmlXPathCompExprPtr cmp
;
3336 * Trying to handle bug #59212
3337 * The value of the "select" attribute is an
3339 * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
3341 cmp
= xmlXPathCompile(URL
);
3342 val
= xsltEvalXPathString(ctxt
, cmp
);
3343 xmlXPathFreeCompExpr(cmp
);
3348 URL
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3351 XSLT_XALAN_NAMESPACE
);
3353 URL
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3356 XSLT_XALAN_NAMESPACE
);
3357 } else if (xmlStrEqual(inst
->name
, (const xmlChar
*) "document")) {
3358 URL
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3359 (const xmlChar
*) "href",
3364 URL
= xmlStrdup(comp
->filename
);
3368 xsltTransformError(ctxt
, NULL
, inst
,
3369 "xsltDocumentElem: href/URI-Reference not found\n");
3374 * If the computation failed, it's likely that the URL wasn't escaped
3376 filename
= xmlBuildURI(URL
, (const xmlChar
*) ctxt
->outputFile
);
3377 if (filename
== NULL
) {
3380 escURL
=xmlURIEscapeStr(URL
, BAD_CAST
":/.?,");
3381 if (escURL
!= NULL
) {
3382 filename
= xmlBuildURI(escURL
, (const xmlChar
*) ctxt
->outputFile
);
3387 if (filename
== NULL
) {
3388 xsltTransformError(ctxt
, NULL
, inst
,
3389 "xsltDocumentElem: URL computation failed for %s\n",
3396 * Security checking: can we write to this resource
3398 if (ctxt
->sec
!= NULL
) {
3399 ret
= xsltCheckWrite(ctxt
->sec
, ctxt
, filename
);
3401 xsltTransformError(ctxt
, NULL
, inst
,
3402 "xsltDocumentElem: write rights for %s denied\n",
3410 oldOutputFile
= ctxt
->outputFile
;
3411 oldOutput
= ctxt
->output
;
3412 oldInsert
= ctxt
->insert
;
3413 oldType
= ctxt
->type
;
3414 ctxt
->outputFile
= (const char *) filename
;
3416 style
= xsltNewStylesheet();
3417 if (style
== NULL
) {
3418 xsltTransformError(ctxt
, NULL
, inst
,
3419 "xsltDocumentElem: out of memory\n");
3424 * Version described in 1.1 draft allows full parameterization
3427 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3428 (const xmlChar
*) "version",
3431 if (style
->version
!= NULL
)
3432 xmlFree(style
->version
);
3433 style
->version
= prop
;
3435 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3436 (const xmlChar
*) "encoding",
3439 if (style
->encoding
!= NULL
)
3440 xmlFree(style
->encoding
);
3441 style
->encoding
= prop
;
3443 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3444 (const xmlChar
*) "method",
3449 if (style
->method
!= NULL
)
3450 xmlFree(style
->method
);
3451 style
->method
= NULL
;
3452 if (style
->methodURI
!= NULL
)
3453 xmlFree(style
->methodURI
);
3454 style
->methodURI
= NULL
;
3456 URI
= xsltGetQNameURI(inst
, &prop
);
3458 if (style
!= NULL
) style
->errors
++;
3459 } else if (URI
== NULL
) {
3460 if ((xmlStrEqual(prop
, (const xmlChar
*) "xml")) ||
3461 (xmlStrEqual(prop
, (const xmlChar
*) "html")) ||
3462 (xmlStrEqual(prop
, (const xmlChar
*) "text"))) {
3463 style
->method
= prop
;
3465 xsltTransformError(ctxt
, NULL
, inst
,
3466 "invalid value for method: %s\n", prop
);
3467 if (style
!= NULL
) style
->warnings
++;
3470 style
->method
= prop
;
3471 style
->methodURI
= xmlStrdup(URI
);
3474 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3476 "doctype-system", NULL
);
3478 if (style
->doctypeSystem
!= NULL
)
3479 xmlFree(style
->doctypeSystem
);
3480 style
->doctypeSystem
= prop
;
3482 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3484 "doctype-public", NULL
);
3486 if (style
->doctypePublic
!= NULL
)
3487 xmlFree(style
->doctypePublic
);
3488 style
->doctypePublic
= prop
;
3490 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3491 (const xmlChar
*) "standalone",
3494 if (xmlStrEqual(prop
, (const xmlChar
*) "yes")) {
3495 style
->standalone
= 1;
3496 } else if (xmlStrEqual(prop
, (const xmlChar
*) "no")) {
3497 style
->standalone
= 0;
3499 xsltTransformError(ctxt
, NULL
, inst
,
3500 "invalid value for standalone: %s\n",
3502 if (style
!= NULL
) style
->warnings
++;
3507 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3508 (const xmlChar
*) "indent",
3511 if (xmlStrEqual(prop
, (const xmlChar
*) "yes")) {
3513 } else if (xmlStrEqual(prop
, (const xmlChar
*) "no")) {
3516 xsltTransformError(ctxt
, NULL
, inst
,
3517 "invalid value for indent: %s\n", prop
);
3518 if (style
!= NULL
) style
->warnings
++;
3523 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3525 "omit-xml-declaration",
3528 if (xmlStrEqual(prop
, (const xmlChar
*) "yes")) {
3529 style
->omitXmlDeclaration
= 1;
3530 } else if (xmlStrEqual(prop
, (const xmlChar
*) "no")) {
3531 style
->omitXmlDeclaration
= 0;
3533 xsltTransformError(ctxt
, NULL
, inst
,
3534 "invalid value for omit-xml-declaration: %s\n",
3536 if (style
!= NULL
) style
->warnings
++;
3541 elements
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3543 "cdata-section-elements",
3545 if (elements
!= NULL
) {
3546 if (style
->stripSpaces
== NULL
)
3547 style
->stripSpaces
= xmlHashCreate(10);
3548 if (style
->stripSpaces
== NULL
)
3552 while (*element
!= 0) {
3553 while (IS_BLANK_CH(*element
))
3558 while ((*end
!= 0) && (!IS_BLANK_CH(*end
)))
3560 element
= xmlStrndup(element
, end
- element
);
3564 #ifdef WITH_XSLT_DEBUG_PARSING
3565 xsltGenericDebug(xsltGenericDebugContext
,
3566 "add cdata section output element %s\n",
3569 URI
= xsltGetQNameURI(inst
, &element
);
3571 xmlHashAddEntry2(style
->stripSpaces
, element
, URI
,
3572 (xmlChar
*) "cdata");
3581 * Create a new document tree and process the element template
3583 XSLT_GET_IMPORT_PTR(method
, style
, method
)
3584 XSLT_GET_IMPORT_PTR(doctypePublic
, style
, doctypePublic
)
3585 XSLT_GET_IMPORT_PTR(doctypeSystem
, style
, doctypeSystem
)
3586 XSLT_GET_IMPORT_PTR(version
, style
, version
)
3587 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
3589 if ((method
!= NULL
) &&
3590 (!xmlStrEqual(method
, (const xmlChar
*) "xml"))) {
3591 if (xmlStrEqual(method
, (const xmlChar
*) "html")) {
3592 ctxt
->type
= XSLT_OUTPUT_HTML
;
3593 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
)))
3594 res
= htmlNewDoc(doctypeSystem
, doctypePublic
);
3596 if (version
!= NULL
) {
3597 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3598 xsltGetHTMLIDs(version
, &doctypePublic
, &doctypeSystem
);
3601 res
= htmlNewDocNoDtD(doctypeSystem
, doctypePublic
);
3605 res
->dict
= ctxt
->dict
;
3606 xmlDictReference(res
->dict
);
3607 } else if (xmlStrEqual(method
, (const xmlChar
*) "xhtml")) {
3608 xsltTransformError(ctxt
, NULL
, inst
,
3609 "xsltDocumentElem: unsupported method xhtml\n",
3611 ctxt
->type
= XSLT_OUTPUT_HTML
;
3612 res
= htmlNewDocNoDtD(doctypeSystem
, doctypePublic
);
3615 res
->dict
= ctxt
->dict
;
3616 xmlDictReference(res
->dict
);
3617 } else if (xmlStrEqual(method
, (const xmlChar
*) "text")) {
3618 ctxt
->type
= XSLT_OUTPUT_TEXT
;
3619 res
= xmlNewDoc(style
->version
);
3622 res
->dict
= ctxt
->dict
;
3623 xmlDictReference(res
->dict
);
3624 #ifdef WITH_XSLT_DEBUG
3625 xsltGenericDebug(xsltGenericDebugContext
,
3626 "reusing transformation dict for output\n");
3629 xsltTransformError(ctxt
, NULL
, inst
,
3630 "xsltDocumentElem: unsupported method %s\n",
3635 ctxt
->type
= XSLT_OUTPUT_XML
;
3636 res
= xmlNewDoc(style
->version
);
3639 res
->dict
= ctxt
->dict
;
3640 xmlDictReference(res
->dict
);
3641 #ifdef WITH_XSLT_DEBUG
3642 xsltGenericDebug(xsltGenericDebugContext
,
3643 "reusing transformation dict for output\n");
3646 res
->charset
= XML_CHAR_ENCODING_UTF8
;
3647 if (encoding
!= NULL
)
3648 res
->encoding
= xmlStrdup(encoding
);
3650 ctxt
->insert
= (xmlNodePtr
) res
;
3651 xsltApplySequenceConstructor(ctxt
, node
, inst
->children
, NULL
);
3654 * Do some post processing work depending on the generated output
3656 root
= xmlDocGetRootElement(res
);
3658 const xmlChar
*doctype
= NULL
;
3660 if ((root
->ns
!= NULL
) && (root
->ns
->prefix
!= NULL
))
3661 doctype
= xmlDictQLookup(ctxt
->dict
, root
->ns
->prefix
, root
->name
);
3662 if (doctype
== NULL
)
3663 doctype
= root
->name
;
3666 * Apply the default selection of the method
3668 if ((method
== NULL
) &&
3669 (root
->ns
== NULL
) &&
3670 (!xmlStrcasecmp(root
->name
, (const xmlChar
*) "html"))) {
3673 tmp
= res
->children
;
3674 while ((tmp
!= NULL
) && (tmp
!= root
)) {
3675 if (tmp
->type
== XML_ELEMENT_NODE
)
3677 if ((tmp
->type
== XML_TEXT_NODE
) && (!xmlIsBlankNode(tmp
)))
3682 ctxt
->type
= XSLT_OUTPUT_HTML
;
3683 res
->type
= XML_HTML_DOCUMENT_NODE
;
3684 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
))) {
3685 res
->intSubset
= xmlCreateIntSubset(res
, doctype
,
3688 #ifdef XSLT_GENERATE_HTML_DOCTYPE
3689 } else if (version
!= NULL
) {
3690 xsltGetHTMLIDs(version
, &doctypePublic
,
3692 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
)))
3694 xmlCreateIntSubset(res
, doctype
,
3702 if (ctxt
->type
== XSLT_OUTPUT_XML
) {
3703 XSLT_GET_IMPORT_PTR(doctypePublic
, style
, doctypePublic
)
3704 XSLT_GET_IMPORT_PTR(doctypeSystem
, style
, doctypeSystem
)
3705 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
)))
3706 res
->intSubset
= xmlCreateIntSubset(res
, doctype
,
3713 * Calls to redirect:write also take an optional attribute append.
3714 * Attribute append="true|yes" which will attempt to simply append
3715 * to an existing file instead of always opening a new file. The
3716 * default behavior of always overwriting the file still happens
3717 * if we do not specify append.
3718 * Note that append use will forbid use of remote URI target.
3720 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
, (const xmlChar
*)"append",
3723 if (xmlStrEqual(prop
, (const xmlChar
*) "true") ||
3724 xmlStrEqual(prop
, (const xmlChar
*) "yes")) {
3725 style
->omitXmlDeclaration
= 1;
3726 redirect_write_append
= 1;
3728 style
->omitXmlDeclaration
= 0;
3732 if (redirect_write_append
) {
3735 f
= fopen((const char *) filename
, "ab");
3739 ret
= xsltSaveResultToFile(f
, res
, style
);
3743 ret
= xsltSaveResultToFilename((const char *) filename
, res
, style
, 0);
3746 xsltTransformError(ctxt
, NULL
, inst
,
3747 "xsltDocumentElem: unable to save to %s\n",
3749 ctxt
->state
= XSLT_STATE_ERROR
;
3750 #ifdef WITH_XSLT_DEBUG_EXTRA
3752 xsltGenericDebug(xsltGenericDebugContext
,
3753 "Wrote %d bytes to %s\n", ret
, filename
);
3758 ctxt
->output
= oldOutput
;
3759 ctxt
->insert
= oldInsert
;
3760 ctxt
->type
= oldType
;
3761 ctxt
->outputFile
= oldOutputFile
;
3764 if (filename
!= NULL
)
3767 xsltFreeStylesheet(style
);
3772 /************************************************************************
3774 * Most of the XSLT-1.0 transformations *
3776 ************************************************************************/
3780 * @ctxt: a XSLT process context
3781 * @node: the node in the source tree.
3782 * @inst: the xslt sort node
3783 * @comp: precomputed information
3785 * function attached to xsl:sort nodes, but this should not be
3789 xsltSort(xsltTransformContextPtr ctxt
,
3790 xmlNodePtr node ATTRIBUTE_UNUSED
, xmlNodePtr inst
,
3791 xsltStylePreCompPtr comp
) {
3793 xsltTransformError(ctxt
, NULL
, inst
,
3794 "xsl:sort : compilation failed\n");
3797 xsltTransformError(ctxt
, NULL
, inst
,
3798 "xsl:sort : improper use this should not be reached\n");
3803 * @ctxt: an XSLT process context
3804 * @node: the node in the source tree
3805 * @inst: the element node of the XSLT-copy instruction
3806 * @castedComp: computed information of the XSLT-copy instruction
3808 * Execute the XSLT-copy instruction on the source node.
3811 xsltCopy(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
3812 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
3814 #ifdef XSLT_REFACTORED
3815 xsltStyleItemCopyPtr comp
= (xsltStyleItemCopyPtr
) castedComp
;
3817 xsltStylePreCompPtr comp
= castedComp
;
3819 xmlNodePtr copy
, oldInsert
;
3821 oldInsert
= ctxt
->insert
;
3822 if (ctxt
->insert
!= NULL
) {
3823 switch (node
->type
) {
3825 case XML_CDATA_SECTION_NODE
:
3827 * This text comes from the stylesheet
3828 * For stylesheets, the set of whitespace-preserving
3829 * element names consists of just xsl:text.
3831 #ifdef WITH_XSLT_DEBUG_PROCESS
3832 if (node
->type
== XML_CDATA_SECTION_NODE
) {
3833 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3834 "xsltCopy: CDATA text %s\n", node
->content
));
3836 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3837 "xsltCopy: text %s\n", node
->content
));
3840 xsltCopyText(ctxt
, ctxt
->insert
, node
, 0);
3842 case XML_DOCUMENT_NODE
:
3843 case XML_HTML_DOCUMENT_NODE
:
3845 case XML_ELEMENT_NODE
:
3847 * REVISIT NOTE: The "fake" is a doc-node, not an element node.
3849 * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
3853 #ifdef WITH_XSLT_DEBUG_PROCESS
3854 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3855 "xsltCopy: node %s\n", node
->name
));
3857 copy
= xsltShallowCopyElem(ctxt
, node
, ctxt
->insert
, 0);
3858 ctxt
->insert
= copy
;
3859 if (comp
->use
!= NULL
) {
3860 xsltApplyAttributeSet(ctxt
, node
, inst
, comp
->use
);
3863 case XML_ATTRIBUTE_NODE
: {
3864 #ifdef WITH_XSLT_DEBUG_PROCESS
3865 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3866 "xsltCopy: attribute %s\n", node
->name
));
3869 * REVISIT: We could also raise an error if the parent is not
3871 * OPTIMIZE TODO: Can we set the value/children of the
3872 * attribute without an intermediate copy of the string value?
3874 xsltShallowCopyAttr(ctxt
, inst
, ctxt
->insert
, (xmlAttrPtr
) node
);
3878 #ifdef WITH_XSLT_DEBUG_PROCESS
3879 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3880 "xsltCopy: PI %s\n", node
->name
));
3882 copy
= xmlNewDocPI(ctxt
->insert
->doc
, node
->name
,
3884 copy
= xsltAddChild(ctxt
->insert
, copy
);
3886 case XML_COMMENT_NODE
:
3887 #ifdef WITH_XSLT_DEBUG_PROCESS
3888 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3889 "xsltCopy: comment\n"));
3891 copy
= xmlNewComment(node
->content
);
3892 copy
= xsltAddChild(ctxt
->insert
, copy
);
3894 case XML_NAMESPACE_DECL
:
3895 #ifdef WITH_XSLT_DEBUG_PROCESS
3896 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY
,xsltGenericDebug(xsltGenericDebugContext
,
3897 "xsltCopy: namespace declaration\n"));
3899 xsltShallowCopyNsNode(ctxt
, inst
, ctxt
->insert
, (xmlNsPtr
)node
);
3907 switch (node
->type
) {
3908 case XML_DOCUMENT_NODE
:
3909 case XML_HTML_DOCUMENT_NODE
:
3910 case XML_ELEMENT_NODE
:
3911 xsltApplySequenceConstructor(ctxt
, ctxt
->node
, inst
->children
,
3917 ctxt
->insert
= oldInsert
;
3922 * @ctxt: a XSLT process context
3923 * @node: the node in the source tree.
3924 * @inst: the xslt text node
3925 * @comp: precomputed information
3927 * Process the xslt text node on the source node
3930 xsltText(xsltTransformContextPtr ctxt
, xmlNodePtr node ATTRIBUTE_UNUSED
,
3931 xmlNodePtr inst
, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED
) {
3932 if ((inst
->children
!= NULL
) && (comp
!= NULL
)) {
3933 xmlNodePtr text
= inst
->children
;
3936 while (text
!= NULL
) {
3937 if ((text
->type
!= XML_TEXT_NODE
) &&
3938 (text
->type
!= XML_CDATA_SECTION_NODE
)) {
3939 xsltTransformError(ctxt
, NULL
, inst
,
3940 "xsl:text content problem\n");
3943 copy
= xmlNewDocText(ctxt
->output
, text
->content
);
3944 if (text
->type
!= XML_CDATA_SECTION_NODE
) {
3945 #ifdef WITH_XSLT_DEBUG_PARSING
3946 xsltGenericDebug(xsltGenericDebugContext
,
3947 "Disable escaping: %s\n", text
->content
);
3949 copy
->name
= xmlStringTextNoenc
;
3951 copy
= xsltAddChild(ctxt
->insert
, copy
);
3959 * @ctxt: a XSLT process context
3960 * @node: the node in the source tree.
3961 * @inst: the xslt element node
3962 * @castedComp: precomputed information
3964 * Process the xslt element node on the source node
3967 xsltElement(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
3968 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
) {
3969 #ifdef XSLT_REFACTORED
3970 xsltStyleItemElementPtr comp
= (xsltStyleItemElementPtr
) castedComp
;
3972 xsltStylePreCompPtr comp
= castedComp
;
3974 xmlChar
*prop
= NULL
;
3975 const xmlChar
*name
, *prefix
= NULL
, *nsName
= NULL
;
3977 xmlNodePtr oldInsert
;
3979 if (ctxt
->insert
== NULL
)
3983 * A comp->has_name == 0 indicates that we need to skip this instruction,
3984 * since it was evaluated to be invalid already during compilation.
3986 if (!comp
->has_name
)
3992 oldInsert
= ctxt
->insert
;
3994 if (comp
->name
== NULL
) {
3995 /* TODO: fix attr acquisition wrt to the XSLT namespace */
3996 prop
= xsltEvalAttrValueTemplate(ctxt
, inst
,
3997 (const xmlChar
*) "name", XSLT_NAMESPACE
);
3999 xsltTransformError(ctxt
, NULL
, inst
,
4000 "xsl:element: The attribute 'name' is missing.\n");
4003 if (xmlValidateQName(prop
, 0)) {
4004 xsltTransformError(ctxt
, NULL
, inst
,
4005 "xsl:element: The effective name '%s' is not a "
4006 "valid QName.\n", prop
);
4007 /* we fall through to catch any further errors, if possible */
4009 name
= xsltSplitQName(ctxt
->dict
, prop
, &prefix
);
4013 * The "name" value was static.
4015 #ifdef XSLT_REFACTORED
4016 prefix
= comp
->nsPrefix
;
4019 name
= xsltSplitQName(ctxt
->dict
, comp
->name
, &prefix
);
4024 * Create the new element
4026 if (ctxt
->output
->dict
== ctxt
->dict
) {
4027 copy
= xmlNewDocNodeEatName(ctxt
->output
, NULL
, (xmlChar
*)name
, NULL
);
4029 copy
= xmlNewDocNode(ctxt
->output
, NULL
, (xmlChar
*)name
, NULL
);
4032 xsltTransformError(ctxt
, NULL
, inst
,
4033 "xsl:element : creation of %s failed\n", name
);
4036 copy
= xsltAddChild(ctxt
->insert
, copy
);
4043 if (comp
->ns
!= NULL
) {
4045 * No AVT; just plain text for the namespace name.
4047 if (comp
->ns
[0] != 0)
4054 /* TODO: check attr acquisition wrt to the XSLT namespace */
4055 tmpNsName
= xsltEvalAttrValueTemplate(ctxt
, inst
,
4056 (const xmlChar
*) "namespace", XSLT_NAMESPACE
);
4059 * "If the string is empty, then the expanded-name of the
4060 * attribute has a null namespace URI."
4062 if ((tmpNsName
!= NULL
) && (tmpNsName
[0] != 0))
4063 nsName
= xmlDictLookup(ctxt
->dict
, BAD_CAST tmpNsName
, -1);
4067 if (xmlStrEqual(nsName
, BAD_CAST
"http://www.w3.org/2000/xmlns/")) {
4068 xsltTransformError(ctxt
, NULL
, inst
,
4069 "xsl:attribute: Namespace http://www.w3.org/2000/xmlns/ "
4073 if (xmlStrEqual(nsName
, XML_XML_NAMESPACE
)) {
4074 prefix
= BAD_CAST
"xml";
4075 } else if (xmlStrEqual(prefix
, BAD_CAST
"xml")) {
4082 * "If the namespace attribute is not present, then the QName is
4083 * expanded into an expanded-name using the namespace declarations
4084 * in effect for the xsl:element element, including any default
4085 * namespace declaration.
4087 ns
= xmlSearchNs(inst
->doc
, inst
, prefix
);
4090 * TODO: Check this in the compilation layer in case it's a
4093 if (prefix
!= NULL
) {
4094 xsltTransformError(ctxt
, NULL
, inst
,
4095 "xsl:element: The QName '%s:%s' has no "
4096 "namespace binding in scope in the stylesheet; "
4097 "this is an error, since the namespace was not "
4098 "specified by the instruction itself.\n", prefix
, name
);
4104 * Find/create a matching ns-decl in the result tree.
4106 if (nsName
!= NULL
) {
4107 if (xmlStrEqual(prefix
, BAD_CAST
"xmlns")) {
4108 /* Don't use a prefix of "xmlns" */
4109 xmlChar
*pref
= xmlStrdup(BAD_CAST
"ns_1");
4111 copy
->ns
= xsltGetSpecialNamespace(ctxt
, inst
, nsName
, pref
, copy
);
4115 copy
->ns
= xsltGetSpecialNamespace(ctxt
, inst
, nsName
, prefix
,
4118 } else if ((copy
->parent
!= NULL
) &&
4119 (copy
->parent
->type
== XML_ELEMENT_NODE
) &&
4120 (copy
->parent
->ns
!= NULL
))
4123 * "Undeclare" the default namespace.
4125 xsltGetSpecialNamespace(ctxt
, inst
, NULL
, NULL
, copy
);
4128 ctxt
->insert
= copy
;
4130 if (comp
->has_use
) {
4131 if (comp
->use
!= NULL
) {
4132 xsltApplyAttributeSet(ctxt
, node
, inst
, comp
->use
);
4134 xmlChar
*attrSets
= NULL
;
4136 * BUG TODO: use-attribute-sets is not a value template.
4137 * use-attribute-sets = qnames
4139 attrSets
= xsltEvalAttrValueTemplate(ctxt
, inst
,
4140 (const xmlChar
*)"use-attribute-sets", NULL
);
4141 if (attrSets
!= NULL
) {
4142 xsltApplyAttributeSet(ctxt
, node
, inst
, attrSets
);
4148 * Instantiate the sequence constructor.
4150 if (inst
->children
!= NULL
)
4151 xsltApplySequenceConstructor(ctxt
, ctxt
->node
, inst
->children
,
4155 ctxt
->insert
= oldInsert
;
4162 * @ctxt: a XSLT process context
4163 * @node: the node in the source tree.
4164 * @inst: the xslt comment node
4165 * @comp: precomputed information
4167 * Process the xslt comment node on the source node
4170 xsltComment(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4171 xmlNodePtr inst
, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED
) {
4172 xmlChar
*value
= NULL
;
4173 xmlNodePtr commentNode
;
4176 value
= xsltEvalTemplateString(ctxt
, node
, inst
);
4177 /* TODO: use or generate the compiled form */
4178 len
= xmlStrlen(value
);
4180 if ((value
[len
-1] == '-') ||
4181 (xmlStrstr(value
, BAD_CAST
"--"))) {
4182 xsltTransformError(ctxt
, NULL
, inst
,
4183 "xsl:comment : '--' or ending '-' not allowed in comment\n");
4184 /* fall through to try to catch further errors */
4187 #ifdef WITH_XSLT_DEBUG_PROCESS
4188 if (value
== NULL
) {
4189 XSLT_TRACE(ctxt
,XSLT_TRACE_COMMENT
,xsltGenericDebug(xsltGenericDebugContext
,
4190 "xsltComment: empty\n"));
4192 XSLT_TRACE(ctxt
,XSLT_TRACE_COMMENT
,xsltGenericDebug(xsltGenericDebugContext
,
4193 "xsltComment: content %s\n", value
));
4197 commentNode
= xmlNewComment(value
);
4198 commentNode
= xsltAddChild(ctxt
->insert
, commentNode
);
4205 * xsltProcessingInstruction:
4206 * @ctxt: a XSLT process context
4207 * @node: the node in the source tree.
4208 * @inst: the xslt processing-instruction node
4209 * @castedComp: precomputed information
4211 * Process the xslt processing-instruction node on the source node
4214 xsltProcessingInstruction(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4215 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
) {
4216 #ifdef XSLT_REFACTORED
4217 xsltStyleItemPIPtr comp
= (xsltStyleItemPIPtr
) castedComp
;
4219 xsltStylePreCompPtr comp
= castedComp
;
4221 const xmlChar
*name
;
4222 xmlChar
*value
= NULL
;
4226 if (ctxt
->insert
== NULL
)
4228 if (comp
->has_name
== 0)
4230 if (comp
->name
== NULL
) {
4231 name
= xsltEvalAttrValueTemplate(ctxt
, inst
,
4232 (const xmlChar
*)"name", NULL
);
4234 xsltTransformError(ctxt
, NULL
, inst
,
4235 "xsl:processing-instruction : name is missing\n");
4241 /* TODO: check that it's both an an NCName and a PITarget. */
4244 value
= xsltEvalTemplateString(ctxt
, node
, inst
);
4245 if (xmlStrstr(value
, BAD_CAST
"?>") != NULL
) {
4246 xsltTransformError(ctxt
, NULL
, inst
,
4247 "xsl:processing-instruction: '?>' not allowed within PI content\n");
4250 #ifdef WITH_XSLT_DEBUG_PROCESS
4251 if (value
== NULL
) {
4252 XSLT_TRACE(ctxt
,XSLT_TRACE_PI
,xsltGenericDebug(xsltGenericDebugContext
,
4253 "xsltProcessingInstruction: %s empty\n", name
));
4255 XSLT_TRACE(ctxt
,XSLT_TRACE_PI
,xsltGenericDebug(xsltGenericDebugContext
,
4256 "xsltProcessingInstruction: %s content %s\n", name
, value
));
4260 pi
= xmlNewDocPI(ctxt
->insert
->doc
, name
, value
);
4261 pi
= xsltAddChild(ctxt
->insert
, pi
);
4264 if ((name
!= NULL
) && (name
!= comp
->name
))
4265 xmlFree((xmlChar
*) name
);
4272 * @ctxt: an XSLT transformation context
4273 * @node: the current node in the source tree
4274 * @inst: the element node of the XSLT copy-of instruction
4275 * @castedComp: precomputed information of the XSLT copy-of instruction
4277 * Process the XSLT copy-of instruction.
4280 xsltCopyOf(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4281 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
) {
4282 #ifdef XSLT_REFACTORED
4283 xsltStyleItemCopyOfPtr comp
= (xsltStyleItemCopyOfPtr
) castedComp
;
4285 xsltStylePreCompPtr comp
= castedComp
;
4287 xmlXPathObjectPtr res
= NULL
;
4288 xmlNodeSetPtr list
= NULL
;
4290 xmlDocPtr oldXPContextDoc
;
4291 xmlNsPtr
*oldXPNamespaces
;
4292 xmlNodePtr oldXPContextNode
;
4293 int oldXPProximityPosition
, oldXPContextSize
, oldXPNsNr
;
4294 xmlXPathContextPtr xpctxt
;
4296 if ((ctxt
== NULL
) || (node
== NULL
) || (inst
== NULL
))
4298 if ((comp
== NULL
) || (comp
->select
== NULL
) || (comp
->comp
== NULL
)) {
4299 xsltTransformError(ctxt
, NULL
, inst
,
4300 "xsl:copy-of : compilation failed\n");
4306 * "The xsl:copy-of element can be used to insert a result tree
4307 * fragment into the result tree, without first converting it to
4308 * a string as xsl:value-of does (see [7.6.1 Generating Text with
4309 * xsl:value-of]). The required select attribute contains an
4310 * expression. When the result of evaluating the expression is a
4311 * result tree fragment, the complete fragment is copied into the
4312 * result tree. When the result is a node-set, all the nodes in the
4313 * set are copied in document order into the result tree; copying
4314 * an element node copies the attribute nodes, namespace nodes and
4315 * children of the element node as well as the element node itself;
4316 * a root node is copied by copying its children. When the result
4317 * is neither a node-set nor a result tree fragment, the result is
4318 * converted to a string and then inserted into the result tree,
4319 * as with xsl:value-of.
4322 #ifdef WITH_XSLT_DEBUG_PROCESS
4323 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_OF
,xsltGenericDebug(xsltGenericDebugContext
,
4324 "xsltCopyOf: select %s\n", comp
->select
));
4328 * Evaluate the "select" expression.
4330 xpctxt
= ctxt
->xpathCtxt
;
4331 oldXPContextDoc
= xpctxt
->doc
;
4332 oldXPContextNode
= xpctxt
->node
;
4333 oldXPProximityPosition
= xpctxt
->proximityPosition
;
4334 oldXPContextSize
= xpctxt
->contextSize
;
4335 oldXPNsNr
= xpctxt
->nsNr
;
4336 oldXPNamespaces
= xpctxt
->namespaces
;
4338 xpctxt
->node
= node
;
4341 #ifdef XSLT_REFACTORED
4342 if (comp
->inScopeNs
!= NULL
) {
4343 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
4344 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
4346 xpctxt
->namespaces
= NULL
;
4350 xpctxt
->namespaces
= comp
->nsList
;
4351 xpctxt
->nsNr
= comp
->nsNr
;
4354 xpctxt
->namespaces
= NULL
;
4358 res
= xmlXPathCompiledEval(comp
->comp
, xpctxt
);
4360 xpctxt
->doc
= oldXPContextDoc
;
4361 xpctxt
->node
= oldXPContextNode
;
4362 xpctxt
->contextSize
= oldXPContextSize
;
4363 xpctxt
->proximityPosition
= oldXPProximityPosition
;
4364 xpctxt
->nsNr
= oldXPNsNr
;
4365 xpctxt
->namespaces
= oldXPNamespaces
;
4368 if (res
->type
== XPATH_NODESET
) {
4373 #ifdef WITH_XSLT_DEBUG_PROCESS
4374 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_OF
,xsltGenericDebug(xsltGenericDebugContext
,
4375 "xsltCopyOf: result is a node set\n"));
4377 list
= res
->nodesetval
;
4381 * The list is already sorted in document order by XPath.
4382 * Append everything in this order under ctxt->insert.
4384 for (i
= 0;i
< list
->nodeNr
;i
++) {
4385 cur
= list
->nodeTab
[i
];
4388 if ((cur
->type
== XML_DOCUMENT_NODE
) ||
4389 (cur
->type
== XML_HTML_DOCUMENT_NODE
))
4391 xsltCopyTreeList(ctxt
, inst
,
4392 cur
->children
, ctxt
->insert
, 0, 0);
4393 } else if (cur
->type
== XML_ATTRIBUTE_NODE
) {
4394 xsltShallowCopyAttr(ctxt
, inst
,
4395 ctxt
->insert
, (xmlAttrPtr
) cur
);
4397 xsltCopyTreeInternal(ctxt
, inst
,
4398 cur
, ctxt
->insert
, 0, 0);
4402 } else if (res
->type
== XPATH_XSLT_TREE
) {
4404 * Result tree fragment
4405 * --------------------
4406 * E.g. via <xsl:variable ...><foo/></xsl:variable>
4407 * Note that the root node of such trees is an xmlDocPtr in Libxslt.
4409 #ifdef WITH_XSLT_DEBUG_PROCESS
4410 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_OF
,xsltGenericDebug(xsltGenericDebugContext
,
4411 "xsltCopyOf: result is a result tree fragment\n"));
4413 list
= res
->nodesetval
;
4414 if ((list
!= NULL
) && (list
->nodeTab
!= NULL
) &&
4415 (list
->nodeTab
[0] != NULL
) &&
4416 (IS_XSLT_REAL_NODE(list
->nodeTab
[0])))
4418 xsltCopyTreeList(ctxt
, inst
,
4419 list
->nodeTab
[0]->children
, ctxt
->insert
, 0, 0);
4422 xmlChar
*value
= NULL
;
4424 * Convert to a string.
4426 value
= xmlXPathCastToString(res
);
4427 if (value
== NULL
) {
4428 xsltTransformError(ctxt
, NULL
, inst
,
4429 "Internal error in xsltCopyOf(): "
4430 "failed to cast an XPath object to string.\n");
4431 ctxt
->state
= XSLT_STATE_STOPPED
;
4433 if (value
[0] != 0) {
4435 * Append content as text node.
4437 xsltCopyTextString(ctxt
, ctxt
->insert
, value
, 0);
4441 #ifdef WITH_XSLT_DEBUG_PROCESS
4442 XSLT_TRACE(ctxt
,XSLT_TRACE_COPY_OF
,xsltGenericDebug(xsltGenericDebugContext
,
4443 "xsltCopyOf: result %s\n", res
->stringval
));
4448 ctxt
->state
= XSLT_STATE_STOPPED
;
4452 xmlXPathFreeObject(res
);
4457 * @ctxt: a XSLT process context
4458 * @node: the node in the source tree.
4459 * @inst: the xslt value-of node
4460 * @castedComp: precomputed information
4462 * Process the xslt value-of node on the source node
4465 xsltValueOf(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4466 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
4468 #ifdef XSLT_REFACTORED
4469 xsltStyleItemValueOfPtr comp
= (xsltStyleItemValueOfPtr
) castedComp
;
4471 xsltStylePreCompPtr comp
= castedComp
;
4473 xmlXPathObjectPtr res
= NULL
;
4474 xmlChar
*value
= NULL
;
4475 xmlDocPtr oldXPContextDoc
;
4476 xmlNsPtr
*oldXPNamespaces
;
4477 xmlNodePtr oldXPContextNode
;
4478 int oldXPProximityPosition
, oldXPContextSize
, oldXPNsNr
;
4479 xmlXPathContextPtr xpctxt
;
4481 if ((ctxt
== NULL
) || (node
== NULL
) || (inst
== NULL
))
4484 if ((comp
== NULL
) || (comp
->select
== NULL
) || (comp
->comp
== NULL
)) {
4485 xsltTransformError(ctxt
, NULL
, inst
,
4486 "Internal error in xsltValueOf(): "
4487 "The XSLT 'value-of' instruction was not compiled.\n");
4491 #ifdef WITH_XSLT_DEBUG_PROCESS
4492 XSLT_TRACE(ctxt
,XSLT_TRACE_VALUE_OF
,xsltGenericDebug(xsltGenericDebugContext
,
4493 "xsltValueOf: select %s\n", comp
->select
));
4496 xpctxt
= ctxt
->xpathCtxt
;
4497 oldXPContextDoc
= xpctxt
->doc
;
4498 oldXPContextNode
= xpctxt
->node
;
4499 oldXPProximityPosition
= xpctxt
->proximityPosition
;
4500 oldXPContextSize
= xpctxt
->contextSize
;
4501 oldXPNsNr
= xpctxt
->nsNr
;
4502 oldXPNamespaces
= xpctxt
->namespaces
;
4504 xpctxt
->node
= node
;
4507 #ifdef XSLT_REFACTORED
4508 if (comp
->inScopeNs
!= NULL
) {
4509 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
4510 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
4512 xpctxt
->namespaces
= NULL
;
4516 xpctxt
->namespaces
= comp
->nsList
;
4517 xpctxt
->nsNr
= comp
->nsNr
;
4520 xpctxt
->namespaces
= NULL
;
4524 res
= xmlXPathCompiledEval(comp
->comp
, xpctxt
);
4526 xpctxt
->doc
= oldXPContextDoc
;
4527 xpctxt
->node
= oldXPContextNode
;
4528 xpctxt
->contextSize
= oldXPContextSize
;
4529 xpctxt
->proximityPosition
= oldXPProximityPosition
;
4530 xpctxt
->nsNr
= oldXPNsNr
;
4531 xpctxt
->namespaces
= oldXPNamespaces
;
4534 * Cast the XPath object to string.
4537 value
= xmlXPathCastToString(res
);
4538 if (value
== NULL
) {
4539 xsltTransformError(ctxt
, NULL
, inst
,
4540 "Internal error in xsltValueOf(): "
4541 "failed to cast an XPath object to string.\n");
4542 ctxt
->state
= XSLT_STATE_STOPPED
;
4545 if (value
[0] != 0) {
4546 xsltCopyTextString(ctxt
, ctxt
->insert
, value
, comp
->noescape
);
4549 xsltTransformError(ctxt
, NULL
, inst
,
4550 "XPath evaluation returned no result.\n");
4551 ctxt
->state
= XSLT_STATE_STOPPED
;
4555 #ifdef WITH_XSLT_DEBUG_PROCESS
4557 XSLT_TRACE(ctxt
,XSLT_TRACE_VALUE_OF
,xsltGenericDebug(xsltGenericDebugContext
,
4558 "xsltValueOf: result '%s'\n", value
));
4566 xmlXPathFreeObject(res
);
4571 * @ctxt: a XSLT process context
4572 * @node: the node in the source tree.
4573 * @inst: the xslt number node
4574 * @castedComp: precomputed information
4576 * Process the xslt number node on the source node
4579 xsltNumber(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4580 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
4582 #ifdef XSLT_REFACTORED
4583 xsltStyleItemNumberPtr comp
= (xsltStyleItemNumberPtr
) castedComp
;
4585 xsltStylePreCompPtr comp
= castedComp
;
4588 xsltTransformError(ctxt
, NULL
, inst
,
4589 "xsl:number : compilation failed\n");
4593 if ((ctxt
== NULL
) || (node
== NULL
) || (inst
== NULL
) || (comp
== NULL
))
4596 comp
->numdata
.doc
= inst
->doc
;
4597 comp
->numdata
.node
= inst
;
4599 xsltNumberFormat(ctxt
, &comp
->numdata
, node
);
4604 * @ctxt: an XSLT transformation context
4605 * @contextNode: the current node in the source tree.
4606 * @inst: the element node of the XSLT 'apply-imports' instruction
4607 * @comp: the compiled instruction
4609 * Process the XSLT apply-imports element.
4612 xsltApplyImports(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
,
4614 xsltStylePreCompPtr comp ATTRIBUTE_UNUSED
)
4616 xsltTemplatePtr templ
;
4618 if ((ctxt
== NULL
) || (inst
== NULL
))
4622 xsltTransformError(ctxt
, NULL
, inst
,
4623 "Internal error in xsltApplyImports(): "
4624 "The XSLT 'apply-imports' instruction was not compiled.\n");
4628 * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
4629 * same; the former is the "Current Template Rule" as defined by the
4630 * XSLT spec, the latter is simply the template struct being
4631 * currently processed.
4633 if (ctxt
->currentTemplateRule
== NULL
) {
4636 * "[ERR XTDE0560] It is a non-recoverable dynamic error if
4637 * xsl:apply-imports or xsl:next-match is evaluated when the
4638 * current template rule is null."
4640 xsltTransformError(ctxt
, NULL
, inst
,
4641 "It is an error to call 'apply-imports' "
4642 "when there's no current template rule.\n");
4646 * TODO: Check if this is correct.
4648 templ
= xsltGetTemplate(ctxt
, contextNode
,
4649 ctxt
->currentTemplateRule
->style
);
4651 if (templ
!= NULL
) {
4652 xsltTemplatePtr oldCurTemplRule
= ctxt
->currentTemplateRule
;
4654 * Set the current template rule.
4656 ctxt
->currentTemplateRule
= templ
;
4658 * URGENT TODO: Need xsl:with-param be handled somehow here?
4660 xsltApplyXSLTTemplate(ctxt
, contextNode
, templ
->content
,
4663 ctxt
->currentTemplateRule
= oldCurTemplRule
;
4669 * @ctxt: a XSLT transformation context
4670 * @node: the "current node" in the source tree
4671 * @inst: the XSLT 'call-template' instruction
4672 * @castedComp: the compiled information of the instruction
4674 * Processes the XSLT call-template instruction on the source node.
4677 xsltCallTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4678 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
4680 #ifdef XSLT_REFACTORED
4681 xsltStyleItemCallTemplatePtr comp
=
4682 (xsltStyleItemCallTemplatePtr
) castedComp
;
4684 xsltStylePreCompPtr comp
= castedComp
;
4686 xsltStackElemPtr withParams
= NULL
;
4688 if (ctxt
->insert
== NULL
)
4691 xsltTransformError(ctxt
, NULL
, inst
,
4692 "The XSLT 'call-template' instruction was not compiled.\n");
4697 * The template must have been precomputed
4699 if (comp
->templ
== NULL
) {
4700 comp
->templ
= xsltFindTemplate(ctxt
, comp
->name
, comp
->ns
);
4701 if (comp
->templ
== NULL
) {
4702 if (comp
->ns
!= NULL
) {
4703 xsltTransformError(ctxt
, NULL
, inst
,
4704 "The called template '{%s}%s' was not found.\n",
4705 comp
->ns
, comp
->name
);
4707 xsltTransformError(ctxt
, NULL
, inst
,
4708 "The called template '%s' was not found.\n",
4715 #ifdef WITH_XSLT_DEBUG_PROCESS
4716 if ((comp
!= NULL
) && (comp
->name
!= NULL
))
4717 XSLT_TRACE(ctxt
,XSLT_TRACE_CALL_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
4718 "call-template: name %s\n", comp
->name
));
4721 if (inst
->children
) {
4723 xsltStackElemPtr param
;
4725 cur
= inst
->children
;
4726 while (cur
!= NULL
) {
4727 #ifdef WITH_DEBUGGER
4728 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
)
4729 xslHandleDebugger(cur
, node
, comp
->templ
, ctxt
);
4731 if (ctxt
->state
== XSLT_STATE_STOPPED
) break;
4733 * TODO: The "with-param"s could be part of the "call-template"
4734 * structure. Avoid to "search" for params dynamically
4735 * in the XML tree every time.
4737 if (IS_XSLT_ELEM(cur
)) {
4738 if (IS_XSLT_NAME(cur
, "with-param")) {
4739 param
= xsltParseStylesheetCallerParam(ctxt
, cur
);
4740 if (param
!= NULL
) {
4741 param
->next
= withParams
;
4745 xsltGenericError(xsltGenericErrorContext
,
4746 "xsl:call-template: misplaced xsl:%s\n", cur
->name
);
4749 xsltGenericError(xsltGenericErrorContext
,
4750 "xsl:call-template: misplaced %s element\n", cur
->name
);
4756 * Create a new frame using the params first
4758 xsltApplyXSLTTemplate(ctxt
, node
, comp
->templ
->content
, comp
->templ
,
4760 if (withParams
!= NULL
)
4761 xsltFreeStackElemList(withParams
);
4763 #ifdef WITH_XSLT_DEBUG_PROCESS
4764 if ((comp
!= NULL
) && (comp
->name
!= NULL
))
4765 XSLT_TRACE(ctxt
,XSLT_TRACE_CALL_TEMPLATE
,xsltGenericDebug(xsltGenericDebugContext
,
4766 "call-template returned: name %s\n", comp
->name
));
4771 * xsltApplyTemplates:
4772 * @ctxt: a XSLT transformation context
4773 * @node: the 'current node' in the source tree
4774 * @inst: the element node of an XSLT 'apply-templates' instruction
4775 * @castedComp: the compiled instruction
4777 * Processes the XSLT 'apply-templates' instruction on the current node.
4780 xsltApplyTemplates(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
4781 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
4783 #ifdef XSLT_REFACTORED
4784 xsltStyleItemApplyTemplatesPtr comp
=
4785 (xsltStyleItemApplyTemplatesPtr
) castedComp
;
4787 xsltStylePreCompPtr comp
= castedComp
;
4790 xmlNodePtr cur
, delNode
= NULL
, oldContextNode
;
4791 xmlNodeSetPtr list
= NULL
, oldList
;
4792 xsltStackElemPtr withParams
= NULL
;
4793 int oldXPProximityPosition
, oldXPContextSize
, oldXPNsNr
;
4794 const xmlChar
*oldMode
, *oldModeURI
;
4796 xsltDocumentPtr oldDocInfo
;
4797 xmlXPathContextPtr xpctxt
;
4798 xmlNsPtr
*oldXPNamespaces
;
4801 xsltTransformError(ctxt
, NULL
, inst
,
4802 "xsl:apply-templates : compilation failed\n");
4805 if ((ctxt
== NULL
) || (node
== NULL
) || (inst
== NULL
) || (comp
== NULL
))
4808 #ifdef WITH_XSLT_DEBUG_PROCESS
4809 if ((node
!= NULL
) && (node
->name
!= NULL
))
4810 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
4811 "xsltApplyTemplates: node: '%s'\n", node
->name
));
4814 xpctxt
= ctxt
->xpathCtxt
;
4816 * Save context states.
4818 oldContextNode
= ctxt
->node
;
4819 oldMode
= ctxt
->mode
;
4820 oldModeURI
= ctxt
->modeURI
;
4821 oldDocInfo
= ctxt
->document
;
4822 oldList
= ctxt
->nodeList
;
4825 * The xpath context size and proximity position, as
4826 * well as the xpath and context documents, may be changed
4827 * so we save their initial state and will restore on exit
4829 oldXPContextSize
= xpctxt
->contextSize
;
4830 oldXPProximityPosition
= xpctxt
->proximityPosition
;
4831 oldXPDoc
= xpctxt
->doc
;
4832 oldXPNsNr
= xpctxt
->nsNr
;
4833 oldXPNamespaces
= xpctxt
->namespaces
;
4838 ctxt
->mode
= comp
->mode
;
4839 ctxt
->modeURI
= comp
->modeURI
;
4841 if (comp
->select
!= NULL
) {
4842 xmlXPathObjectPtr res
= NULL
;
4844 if (comp
->comp
== NULL
) {
4845 xsltTransformError(ctxt
, NULL
, inst
,
4846 "xsl:apply-templates : compilation failed\n");
4849 #ifdef WITH_XSLT_DEBUG_PROCESS
4850 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
4851 "xsltApplyTemplates: select %s\n", comp
->select
));
4857 xpctxt
->node
= node
; /* Set the "context node" */
4858 #ifdef XSLT_REFACTORED
4859 if (comp
->inScopeNs
!= NULL
) {
4860 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
4861 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
4863 xpctxt
->namespaces
= NULL
;
4867 xpctxt
->namespaces
= comp
->nsList
;
4868 xpctxt
->nsNr
= comp
->nsNr
;
4870 res
= xmlXPathCompiledEval(comp
->comp
, xpctxt
);
4872 xpctxt
->contextSize
= oldXPContextSize
;
4873 xpctxt
->proximityPosition
= oldXPProximityPosition
;
4875 if (res
->type
== XPATH_NODESET
) {
4876 list
= res
->nodesetval
; /* consume the node set */
4877 res
->nodesetval
= NULL
;
4879 xsltTransformError(ctxt
, NULL
, inst
,
4880 "The 'select' expression did not evaluate to a "
4882 ctxt
->state
= XSLT_STATE_STOPPED
;
4883 xmlXPathFreeObject(res
);
4886 xmlXPathFreeObject(res
);
4888 * Note: An xsl:apply-templates with a 'select' attribute,
4889 * can change the current source doc.
4892 xsltTransformError(ctxt
, NULL
, inst
,
4893 "Failed to evaluate the 'select' expression.\n");
4894 ctxt
->state
= XSLT_STATE_STOPPED
;
4898 #ifdef WITH_XSLT_DEBUG_PROCESS
4899 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
4900 "xsltApplyTemplates: select didn't evaluate to a node list\n"));
4906 * NOTE: Previously a document info (xsltDocument) was
4907 * created and attached to the Result Tree Fragment.
4908 * But such a document info is created on demand in
4909 * xsltKeyFunction() (functions.c), so we need to create
4910 * it here beforehand.
4911 * In order to take care of potential keys we need to
4912 * do some extra work for the case when a Result Tree Fragment
4913 * is converted into a nodeset (e.g. exslt:node-set()) :
4914 * We attach a "pseudo-doc" (xsltDocument) to _private.
4915 * This xsltDocument, together with the keyset, will be freed
4916 * when the Result Tree Fragment is freed.
4920 if ((ctxt
->nbKeys
> 0) &&
4921 (list
->nodeNr
!= 0) &&
4922 (list
->nodeTab
[0]->doc
!= NULL
) &&
4923 XSLT_IS_RES_TREE_FRAG(list
->nodeTab
[0]->doc
))
4926 * NOTE that it's also OK if @effectiveDocInfo will be
4930 effectiveDocInfo
= list
->nodeTab
[0]->doc
->_private
;
4935 * Build an XPath node set with the children
4937 list
= xmlXPathNodeSetCreate(NULL
);
4940 if (node
->type
!= XML_NAMESPACE_DECL
)
4941 cur
= node
->children
;
4944 while (cur
!= NULL
) {
4945 switch (cur
->type
) {
4947 if ((IS_BLANK_NODE(cur
)) &&
4948 (cur
->parent
!= NULL
) &&
4949 (cur
->parent
->type
== XML_ELEMENT_NODE
) &&
4950 (ctxt
->style
->stripSpaces
!= NULL
)) {
4953 if (cur
->parent
->ns
!= NULL
) {
4954 val
= (const xmlChar
*)
4955 xmlHashLookup2(ctxt
->style
->stripSpaces
,
4957 cur
->parent
->ns
->href
);
4959 val
= (const xmlChar
*)
4960 xmlHashLookup2(ctxt
->style
->stripSpaces
,
4962 cur
->parent
->ns
->href
);
4965 val
= (const xmlChar
*)
4966 xmlHashLookup2(ctxt
->style
->stripSpaces
,
4967 cur
->parent
->name
, NULL
);
4969 if ((val
!= NULL
) &&
4970 (xmlStrEqual(val
, (xmlChar
*) "strip"))) {
4975 /* no break on purpose */
4976 case XML_ELEMENT_NODE
:
4977 case XML_DOCUMENT_NODE
:
4978 case XML_HTML_DOCUMENT_NODE
:
4979 case XML_CDATA_SECTION_NODE
:
4981 case XML_COMMENT_NODE
:
4982 xmlXPathNodeSetAddUnique(list
, cur
);
4985 /* Unlink the DTD, it's still reachable
4986 * using doc->intSubset */
4987 if (cur
->next
!= NULL
)
4988 cur
->next
->prev
= cur
->prev
;
4989 if (cur
->prev
!= NULL
)
4990 cur
->prev
->next
= cur
->next
;
4992 case XML_NAMESPACE_DECL
:
4995 #ifdef WITH_XSLT_DEBUG_PROCESS
4996 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
4997 "xsltApplyTemplates: skipping cur type %d\n",
5003 if (delNode
!= NULL
) {
5004 #ifdef WITH_XSLT_DEBUG_PROCESS
5005 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
5006 "xsltApplyTemplates: removing ignorable blank cur\n"));
5008 xmlUnlinkNode(delNode
);
5009 xmlFreeNode(delNode
);
5015 #ifdef WITH_XSLT_DEBUG_PROCESS
5017 XSLT_TRACE(ctxt
,XSLT_TRACE_APPLY_TEMPLATES
,xsltGenericDebug(xsltGenericDebugContext
,
5018 "xsltApplyTemplates: list of %d nodes\n", list
->nodeNr
));
5021 if ((list
== NULL
) || (list
->nodeNr
== 0))
5025 * Set the context's node set and size; this is also needed for
5026 * for xsltDoSortFunction().
5028 ctxt
->nodeList
= list
;
5030 * Process xsl:with-param and xsl:sort instructions.
5031 * (The code became so verbose just to avoid the
5032 * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort)
5033 * BUG TODO: We are not using namespaced potentially defined on the
5034 * xsl:sort or xsl:with-param elements; XPath expression might fail.
5036 if (inst
->children
) {
5037 xsltStackElemPtr param
;
5039 cur
= inst
->children
;
5042 #ifdef WITH_DEBUGGER
5043 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
)
5044 xslHandleDebugger(cur
, node
, NULL
, ctxt
);
5046 if (ctxt
->state
== XSLT_STATE_STOPPED
)
5048 if (cur
->type
== XML_TEXT_NODE
) {
5052 if (! IS_XSLT_ELEM(cur
))
5054 if (IS_XSLT_NAME(cur
, "with-param")) {
5055 param
= xsltParseStylesheetCallerParam(ctxt
, cur
);
5056 if (param
!= NULL
) {
5057 param
->next
= withParams
;
5061 if (IS_XSLT_NAME(cur
, "sort")) {
5062 xsltTemplatePtr oldCurTempRule
=
5063 ctxt
->currentTemplateRule
;
5065 xmlNodePtr sorts
[XSLT_MAX_SORT
];
5067 sorts
[nbsorts
++] = cur
;
5071 #ifdef WITH_DEBUGGER
5072 if (ctxt
->debugStatus
!= XSLT_DEBUG_NONE
)
5073 xslHandleDebugger(cur
, node
, NULL
, ctxt
);
5075 if (ctxt
->state
== XSLT_STATE_STOPPED
)
5078 if (cur
->type
== XML_TEXT_NODE
) {
5083 if (! IS_XSLT_ELEM(cur
))
5085 if (IS_XSLT_NAME(cur
, "with-param")) {
5086 param
= xsltParseStylesheetCallerParam(ctxt
, cur
);
5087 if (param
!= NULL
) {
5088 param
->next
= withParams
;
5092 if (IS_XSLT_NAME(cur
, "sort")) {
5093 if (nbsorts
>= XSLT_MAX_SORT
) {
5094 xsltTransformError(ctxt
, NULL
, cur
,
5095 "The number (%d) of xsl:sort instructions exceeds the "
5096 "maximum allowed by this processor's settings.\n",
5098 ctxt
->state
= XSLT_STATE_STOPPED
;
5101 sorts
[nbsorts
++] = cur
;
5107 * The "current template rule" is cleared for xsl:sort.
5109 ctxt
->currentTemplateRule
= NULL
;
5113 xsltDoSortFunction(ctxt
, sorts
, nbsorts
);
5114 ctxt
->currentTemplateRule
= oldCurTempRule
;
5120 xpctxt
->contextSize
= list
->nodeNr
;
5122 * Apply templates for all selected source nodes.
5124 for (i
= 0; i
< list
->nodeNr
; i
++) {
5125 cur
= list
->nodeTab
[i
];
5127 * The node becomes the "current node".
5131 * An xsl:apply-templates can change the current context doc.
5132 * OPTIMIZE TODO: Get rid of the need to set the context doc.
5134 if ((cur
->type
!= XML_NAMESPACE_DECL
) && (cur
->doc
!= NULL
))
5135 xpctxt
->doc
= cur
->doc
;
5137 xpctxt
->proximityPosition
= i
+ 1;
5139 * Find and apply a template for this node.
5141 xsltProcessOneNode(ctxt
, cur
, withParams
);
5147 * Free the parameter list.
5149 if (withParams
!= NULL
)
5150 xsltFreeStackElemList(withParams
);
5152 xmlXPathFreeNodeSet(list
);
5154 * Restore context states.
5156 xpctxt
->nsNr
= oldXPNsNr
;
5157 xpctxt
->namespaces
= oldXPNamespaces
;
5158 xpctxt
->doc
= oldXPDoc
;
5159 xpctxt
->contextSize
= oldXPContextSize
;
5160 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5162 ctxt
->document
= oldDocInfo
;
5163 ctxt
->nodeList
= oldList
;
5164 ctxt
->node
= oldContextNode
;
5165 ctxt
->mode
= oldMode
;
5166 ctxt
->modeURI
= oldModeURI
;
5172 * @ctxt: a XSLT process context
5173 * @contextNode: the current node in the source tree
5174 * @inst: the xsl:choose instruction
5175 * @comp: compiled information of the instruction
5177 * Processes the xsl:choose instruction on the source node.
5180 xsltChoose(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
,
5181 xmlNodePtr inst
, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED
)
5185 if ((ctxt
== NULL
) || (contextNode
== NULL
) || (inst
== NULL
))
5189 * TODO: Content model checks should be done only at compilation
5192 cur
= inst
->children
;
5194 xsltTransformError(ctxt
, NULL
, inst
,
5195 "xsl:choose: The instruction has no content.\n");
5199 #ifdef XSLT_REFACTORED
5201 * We don't check the content model during transformation.
5204 if ((! IS_XSLT_ELEM(cur
)) || (! IS_XSLT_NAME(cur
, "when"))) {
5205 xsltTransformError(ctxt
, NULL
, inst
,
5206 "xsl:choose: xsl:when expected first\n");
5212 int testRes
= 0, res
= 0;
5213 xmlXPathContextPtr xpctxt
= ctxt
->xpathCtxt
;
5214 xmlDocPtr oldXPContextDoc
= xpctxt
->doc
;
5215 int oldXPProximityPosition
= xpctxt
->proximityPosition
;
5216 int oldXPContextSize
= xpctxt
->contextSize
;
5217 xmlNsPtr
*oldXPNamespaces
= xpctxt
->namespaces
;
5218 int oldXPNsNr
= xpctxt
->nsNr
;
5220 #ifdef XSLT_REFACTORED
5221 xsltStyleItemWhenPtr wcomp
= NULL
;
5223 xsltStylePreCompPtr wcomp
= NULL
;
5227 * Process xsl:when ---------------------------------------------------
5229 while (IS_XSLT_ELEM(cur
) && IS_XSLT_NAME(cur
, "when")) {
5232 if ((wcomp
== NULL
) || (wcomp
->test
== NULL
) ||
5233 (wcomp
->comp
== NULL
))
5235 xsltTransformError(ctxt
, NULL
, cur
,
5236 "Internal error in xsltChoose(): "
5237 "The XSLT 'when' instruction was not compiled.\n");
5242 #ifdef WITH_DEBUGGER
5243 if (xslDebugStatus
!= XSLT_DEBUG_NONE
) {
5245 * TODO: Isn't comp->templ always NULL for xsl:choose?
5247 xslHandleDebugger(cur
, contextNode
, NULL
, ctxt
);
5250 #ifdef WITH_XSLT_DEBUG_PROCESS
5251 XSLT_TRACE(ctxt
,XSLT_TRACE_CHOOSE
,xsltGenericDebug(xsltGenericDebugContext
,
5252 "xsltChoose: test %s\n", wcomp
->test
));
5255 xpctxt
->node
= contextNode
;
5256 xpctxt
->doc
= oldXPContextDoc
;
5257 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5258 xpctxt
->contextSize
= oldXPContextSize
;
5260 #ifdef XSLT_REFACTORED
5261 if (wcomp
->inScopeNs
!= NULL
) {
5262 xpctxt
->namespaces
= wcomp
->inScopeNs
->list
;
5263 xpctxt
->nsNr
= wcomp
->inScopeNs
->xpathNumber
;
5265 xpctxt
->namespaces
= NULL
;
5269 xpctxt
->namespaces
= wcomp
->nsList
;
5270 xpctxt
->nsNr
= wcomp
->nsNr
;
5275 res
= xmlXPathCompiledEvalToBoolean(wcomp
->comp
, xpctxt
);
5278 ctxt
->state
= XSLT_STATE_STOPPED
;
5281 testRes
= (res
== 1) ? 1 : 0;
5283 #else /* XSLT_FAST_IF */
5285 res
= xmlXPathCompiledEval(wcomp
->comp
, xpctxt
);
5288 if (res
->type
!= XPATH_BOOLEAN
)
5289 res
= xmlXPathConvertBoolean(res
);
5290 if (res
->type
== XPATH_BOOLEAN
)
5291 testRes
= res
->boolval
;
5293 #ifdef WITH_XSLT_DEBUG_PROCESS
5294 XSLT_TRACE(ctxt
,XSLT_TRACE_CHOOSE
,xsltGenericDebug(xsltGenericDebugContext
,
5295 "xsltChoose: test didn't evaluate to a boolean\n"));
5299 xmlXPathFreeObject(res
);
5302 ctxt
->state
= XSLT_STATE_STOPPED
;
5306 #endif /* else of XSLT_FAST_IF */
5308 #ifdef WITH_XSLT_DEBUG_PROCESS
5309 XSLT_TRACE(ctxt
,XSLT_TRACE_CHOOSE
,xsltGenericDebug(xsltGenericDebugContext
,
5310 "xsltChoose: test evaluate to %d\n", testRes
));
5319 * Process xsl:otherwise ----------------------------------------------
5321 if (IS_XSLT_ELEM(cur
) && IS_XSLT_NAME(cur
, "otherwise")) {
5323 #ifdef WITH_DEBUGGER
5324 if (xslDebugStatus
!= XSLT_DEBUG_NONE
)
5325 xslHandleDebugger(cur
, contextNode
, NULL
, ctxt
);
5328 #ifdef WITH_XSLT_DEBUG_PROCESS
5329 XSLT_TRACE(ctxt
,XSLT_TRACE_CHOOSE
,xsltGenericDebug(xsltGenericDebugContext
,
5330 "evaluating xsl:otherwise\n"));
5334 xpctxt
->node
= contextNode
;
5335 xpctxt
->doc
= oldXPContextDoc
;
5336 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5337 xpctxt
->contextSize
= oldXPContextSize
;
5338 xpctxt
->namespaces
= oldXPNamespaces
;
5339 xpctxt
->nsNr
= oldXPNsNr
;
5344 xpctxt
->node
= contextNode
;
5345 xpctxt
->doc
= oldXPContextDoc
;
5346 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5347 xpctxt
->contextSize
= oldXPContextSize
;
5348 xpctxt
->namespaces
= oldXPNamespaces
;
5349 xpctxt
->nsNr
= oldXPNsNr
;
5350 goto process_sequence
;
5356 * Instantiate the sequence constructor.
5358 xsltApplySequenceConstructor(ctxt
, ctxt
->node
, cur
->children
,
5368 * @ctxt: a XSLT process context
5369 * @contextNode: the current node in the source tree
5370 * @inst: the xsl:if instruction
5371 * @castedComp: compiled information of the instruction
5373 * Processes the xsl:if instruction on the source node.
5376 xsltIf(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
,
5377 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
5381 #ifdef XSLT_REFACTORED
5382 xsltStyleItemIfPtr comp
= (xsltStyleItemIfPtr
) castedComp
;
5384 xsltStylePreCompPtr comp
= castedComp
;
5387 if ((ctxt
== NULL
) || (contextNode
== NULL
) || (inst
== NULL
))
5389 if ((comp
== NULL
) || (comp
->test
== NULL
) || (comp
->comp
== NULL
)) {
5390 xsltTransformError(ctxt
, NULL
, inst
,
5391 "Internal error in xsltIf(): "
5392 "The XSLT 'if' instruction was not compiled.\n");
5396 #ifdef WITH_XSLT_DEBUG_PROCESS
5397 XSLT_TRACE(ctxt
,XSLT_TRACE_IF
,xsltGenericDebug(xsltGenericDebugContext
,
5398 "xsltIf: test %s\n", comp
->test
));
5403 xmlXPathContextPtr xpctxt
= ctxt
->xpathCtxt
;
5404 xmlDocPtr oldXPContextDoc
= xpctxt
->doc
;
5405 xmlNsPtr
*oldXPNamespaces
= xpctxt
->namespaces
;
5406 xmlNodePtr oldXPContextNode
= xpctxt
->node
;
5407 int oldXPProximityPosition
= xpctxt
->proximityPosition
;
5408 int oldXPContextSize
= xpctxt
->contextSize
;
5409 int oldXPNsNr
= xpctxt
->nsNr
;
5410 xmlDocPtr oldLocalFragmentTop
= ctxt
->localRVT
;
5412 xpctxt
->node
= contextNode
;
5415 #ifdef XSLT_REFACTORED
5416 if (comp
->inScopeNs
!= NULL
) {
5417 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
5418 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
5420 xpctxt
->namespaces
= NULL
;
5424 xpctxt
->namespaces
= comp
->nsList
;
5425 xpctxt
->nsNr
= comp
->nsNr
;
5428 xpctxt
->namespaces
= NULL
;
5432 * This XPath function is optimized for boolean results.
5434 res
= xmlXPathCompiledEvalToBoolean(comp
->comp
, xpctxt
);
5437 * Cleanup fragments created during evaluation of the
5438 * "select" expression.
5440 if (oldLocalFragmentTop
!= ctxt
->localRVT
)
5441 xsltReleaseLocalRVTs(ctxt
, oldLocalFragmentTop
);
5443 xpctxt
->doc
= oldXPContextDoc
;
5444 xpctxt
->node
= oldXPContextNode
;
5445 xpctxt
->contextSize
= oldXPContextSize
;
5446 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5447 xpctxt
->nsNr
= oldXPNsNr
;
5448 xpctxt
->namespaces
= oldXPNamespaces
;
5451 #ifdef WITH_XSLT_DEBUG_PROCESS
5452 XSLT_TRACE(ctxt
,XSLT_TRACE_IF
,xsltGenericDebug(xsltGenericDebugContext
,
5453 "xsltIf: test evaluate to %d\n", res
));
5457 ctxt
->state
= XSLT_STATE_STOPPED
;
5462 * Instantiate the sequence constructor of xsl:if.
5464 xsltApplySequenceConstructor(ctxt
,
5465 contextNode
, inst
->children
, NULL
);
5468 #else /* XSLT_FAST_IF */
5470 xmlXPathObjectPtr xpobj
= NULL
;
5475 xmlXPathContextPtr xpctxt
= ctxt
->xpathCtxt
;
5476 xmlDocPtr oldXPContextDoc
= xpctxt
->doc
;
5477 xmlNsPtr
*oldXPNamespaces
= xpctxt
->namespaces
;
5478 xmlNodePtr oldXPContextNode
= xpctxt
->node
;
5479 int oldXPProximityPosition
= xpctxt
->proximityPosition
;
5480 int oldXPContextSize
= xpctxt
->contextSize
;
5481 int oldXPNsNr
= xpctxt
->nsNr
;
5483 xpctxt
->node
= contextNode
;
5486 #ifdef XSLT_REFACTORED
5487 if (comp
->inScopeNs
!= NULL
) {
5488 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
5489 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
5491 xpctxt
->namespaces
= NULL
;
5495 xpctxt
->namespaces
= comp
->nsList
;
5496 xpctxt
->nsNr
= comp
->nsNr
;
5499 xpctxt
->namespaces
= NULL
;
5504 * This XPath function is optimized for boolean results.
5506 xpobj
= xmlXPathCompiledEval(comp
->comp
, xpctxt
);
5508 xpctxt
->doc
= oldXPContextDoc
;
5509 xpctxt
->node
= oldXPContextNode
;
5510 xpctxt
->contextSize
= oldXPContextSize
;
5511 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5512 xpctxt
->nsNr
= oldXPNsNr
;
5513 xpctxt
->namespaces
= oldXPNamespaces
;
5515 if (xpobj
!= NULL
) {
5516 if (xpobj
->type
!= XPATH_BOOLEAN
)
5517 xpobj
= xmlXPathConvertBoolean(xpobj
);
5518 if (xpobj
->type
== XPATH_BOOLEAN
) {
5519 res
= xpobj
->boolval
;
5521 #ifdef WITH_XSLT_DEBUG_PROCESS
5522 XSLT_TRACE(ctxt
,XSLT_TRACE_IF
,xsltGenericDebug(xsltGenericDebugContext
,
5523 "xsltIf: test evaluate to %d\n", res
));
5526 xsltApplySequenceConstructor(ctxt
,
5527 contextNode
, inst
->children
, NULL
);
5531 #ifdef WITH_XSLT_DEBUG_PROCESS
5532 XSLT_TRACE(ctxt
, XSLT_TRACE_IF
,
5533 xsltGenericDebug(xsltGenericDebugContext
,
5534 "xsltIf: test didn't evaluate to a boolean\n"));
5536 ctxt
->state
= XSLT_STATE_STOPPED
;
5538 xmlXPathFreeObject(xpobj
);
5540 ctxt
->state
= XSLT_STATE_STOPPED
;
5543 #endif /* else of XSLT_FAST_IF */
5551 * @ctxt: an XSLT transformation context
5552 * @contextNode: the "current node" in the source tree
5553 * @inst: the element node of the xsl:for-each instruction
5554 * @castedComp: the compiled information of the instruction
5556 * Process the xslt for-each node on the source node
5559 xsltForEach(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
,
5560 xmlNodePtr inst
, xsltStylePreCompPtr castedComp
)
5562 #ifdef XSLT_REFACTORED
5563 xsltStyleItemForEachPtr comp
= (xsltStyleItemForEachPtr
) castedComp
;
5565 xsltStylePreCompPtr comp
= castedComp
;
5568 xmlXPathObjectPtr res
= NULL
;
5569 xmlNodePtr cur
, curInst
;
5570 xmlNodeSetPtr list
= NULL
;
5571 xmlNodeSetPtr oldList
;
5572 int oldXPProximityPosition
, oldXPContextSize
;
5573 xmlNodePtr oldContextNode
;
5574 xsltTemplatePtr oldCurTemplRule
;
5576 xsltDocumentPtr oldDocInfo
;
5577 xmlXPathContextPtr xpctxt
;
5579 if ((ctxt
== NULL
) || (contextNode
== NULL
) || (inst
== NULL
)) {
5580 xsltGenericError(xsltGenericErrorContext
,
5581 "xsltForEach(): Bad arguments.\n");
5586 xsltTransformError(ctxt
, NULL
, inst
,
5587 "Internal error in xsltForEach(): "
5588 "The XSLT 'for-each' instruction was not compiled.\n");
5591 if ((comp
->select
== NULL
) || (comp
->comp
== NULL
)) {
5592 xsltTransformError(ctxt
, NULL
, inst
,
5593 "Internal error in xsltForEach(): "
5594 "The selecting expression of the XSLT 'for-each' "
5595 "instruction was not compiled correctly.\n");
5598 xpctxt
= ctxt
->xpathCtxt
;
5600 #ifdef WITH_XSLT_DEBUG_PROCESS
5601 XSLT_TRACE(ctxt
,XSLT_TRACE_FOR_EACH
,xsltGenericDebug(xsltGenericDebugContext
,
5602 "xsltForEach: select %s\n", comp
->select
));
5606 * Save context states.
5608 oldDocInfo
= ctxt
->document
;
5609 oldList
= ctxt
->nodeList
;
5610 oldContextNode
= ctxt
->node
;
5612 * The "current template rule" is cleared for the instantiation of
5615 oldCurTemplRule
= ctxt
->currentTemplateRule
;
5616 ctxt
->currentTemplateRule
= NULL
;
5618 oldXPDoc
= xpctxt
->doc
;
5619 oldXPProximityPosition
= xpctxt
->proximityPosition
;
5620 oldXPContextSize
= xpctxt
->contextSize
;
5624 xpctxt
->node
= contextNode
;
5625 #ifdef XSLT_REFACTORED
5626 if (comp
->inScopeNs
!= NULL
) {
5627 xpctxt
->namespaces
= comp
->inScopeNs
->list
;
5628 xpctxt
->nsNr
= comp
->inScopeNs
->xpathNumber
;
5630 xpctxt
->namespaces
= NULL
;
5634 xpctxt
->namespaces
= comp
->nsList
;
5635 xpctxt
->nsNr
= comp
->nsNr
;
5639 * Evaluate the 'select' expression.
5641 res
= xmlXPathCompiledEval(comp
->comp
, ctxt
->xpathCtxt
);
5644 if (res
->type
== XPATH_NODESET
)
5645 list
= res
->nodesetval
;
5647 xsltTransformError(ctxt
, NULL
, inst
,
5648 "The 'select' expression does not evaluate to a node set.\n");
5650 #ifdef WITH_XSLT_DEBUG_PROCESS
5651 XSLT_TRACE(ctxt
,XSLT_TRACE_FOR_EACH
,xsltGenericDebug(xsltGenericDebugContext
,
5652 "xsltForEach: select didn't evaluate to a node list\n"));
5657 xsltTransformError(ctxt
, NULL
, inst
,
5658 "Failed to evaluate the 'select' expression.\n");
5659 ctxt
->state
= XSLT_STATE_STOPPED
;
5663 if ((list
== NULL
) || (list
->nodeNr
<= 0))
5666 #ifdef WITH_XSLT_DEBUG_PROCESS
5667 XSLT_TRACE(ctxt
,XSLT_TRACE_FOR_EACH
,xsltGenericDebug(xsltGenericDebugContext
,
5668 "xsltForEach: select evaluates to %d nodes\n", list
->nodeNr
));
5672 * Restore XPath states for the "current node".
5674 xpctxt
->contextSize
= oldXPContextSize
;
5675 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5676 xpctxt
->node
= contextNode
;
5679 * Set the list; this has to be done already here for xsltDoSortFunction().
5681 ctxt
->nodeList
= list
;
5683 * Handle xsl:sort instructions and skip them for further processing.
5684 * BUG TODO: We are not using namespaced potentially defined on the
5685 * xsl:sort element; XPath expression might fail.
5687 curInst
= inst
->children
;
5688 if (IS_XSLT_ELEM(curInst
) && IS_XSLT_NAME(curInst
, "sort")) {
5690 xmlNodePtr sorts
[XSLT_MAX_SORT
];
5692 sorts
[nbsorts
++] = curInst
;
5694 #ifdef WITH_DEBUGGER
5695 if (xslDebugStatus
!= XSLT_DEBUG_NONE
)
5696 xslHandleDebugger(curInst
, contextNode
, NULL
, ctxt
);
5699 curInst
= curInst
->next
;
5700 while (IS_XSLT_ELEM(curInst
) && IS_XSLT_NAME(curInst
, "sort")) {
5701 if (nbsorts
>= XSLT_MAX_SORT
) {
5702 xsltTransformError(ctxt
, NULL
, curInst
,
5703 "The number of xsl:sort instructions exceeds the "
5704 "maximum (%d) allowed by this processor.\n",
5708 sorts
[nbsorts
++] = curInst
;
5711 #ifdef WITH_DEBUGGER
5712 if (xslDebugStatus
!= XSLT_DEBUG_NONE
)
5713 xslHandleDebugger(curInst
, contextNode
, NULL
, ctxt
);
5715 curInst
= curInst
->next
;
5717 xsltDoSortFunction(ctxt
, sorts
, nbsorts
);
5719 xpctxt
->contextSize
= list
->nodeNr
;
5721 * Instantiate the sequence constructor for each selected node.
5723 for (i
= 0; i
< list
->nodeNr
; i
++) {
5724 cur
= list
->nodeTab
[i
];
5726 * The selected node becomes the "current node".
5730 * An xsl:for-each can change the current context doc.
5731 * OPTIMIZE TODO: Get rid of the need to set the context doc.
5733 if ((cur
->type
!= XML_NAMESPACE_DECL
) && (cur
->doc
!= NULL
))
5734 xpctxt
->doc
= cur
->doc
;
5736 xpctxt
->proximityPosition
= i
+ 1;
5738 xsltApplySequenceConstructor(ctxt
, cur
, curInst
, NULL
);
5744 xmlXPathFreeObject(res
);
5746 * Restore old states.
5748 ctxt
->document
= oldDocInfo
;
5749 ctxt
->nodeList
= oldList
;
5750 ctxt
->node
= oldContextNode
;
5751 ctxt
->currentTemplateRule
= oldCurTemplRule
;
5753 xpctxt
->doc
= oldXPDoc
;
5754 xpctxt
->contextSize
= oldXPContextSize
;
5755 xpctxt
->proximityPosition
= oldXPProximityPosition
;
5758 /************************************************************************
5760 * Generic interface *
5762 ************************************************************************/
5764 #ifdef XSLT_GENERATE_HTML_DOCTYPE
5765 typedef struct xsltHTMLVersion
{
5766 const char *version
;
5771 static xsltHTMLVersion xsltHTMLVersions
[] = {
5772 { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
5773 "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
5774 { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
5775 "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
5776 { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
5777 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
5778 { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
5779 "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
5780 { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
5781 "http://www.w3.org/TR/html4/strict.dtd"},
5782 { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
5783 "http://www.w3.org/TR/html4/loose.dtd"},
5784 { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
5785 "http://www.w3.org/TR/html4/frameset.dtd"},
5786 { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
5787 "http://www.w3.org/TR/html4/loose.dtd"},
5788 { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL
}
5793 * @version: the version string
5794 * @publicID: used to return the public ID
5795 * @systemID: used to return the system ID
5797 * Returns -1 if not found, 0 otherwise and the system and public
5798 * Identifier for this given verion of HTML
5801 xsltGetHTMLIDs(const xmlChar
*version
, const xmlChar
**publicID
,
5802 const xmlChar
**systemID
) {
5804 if (version
== NULL
)
5806 for (i
= 0;i
< (sizeof(xsltHTMLVersions
)/sizeof(xsltHTMLVersions
[1]));
5808 if (!xmlStrcasecmp(version
,
5809 (const xmlChar
*) xsltHTMLVersions
[i
].version
)) {
5810 if (publicID
!= NULL
)
5811 *publicID
= (const xmlChar
*) xsltHTMLVersions
[i
].public;
5812 if (systemID
!= NULL
)
5813 *systemID
= (const xmlChar
*) xsltHTMLVersions
[i
].system
;
5822 * xsltApplyStripSpaces:
5823 * @ctxt: a XSLT process context
5824 * @node: the root of the XML tree
5826 * Strip the unwanted ignorable spaces from the input tree
5829 xsltApplyStripSpaces(xsltTransformContextPtr ctxt
, xmlNodePtr node
) {
5831 #ifdef WITH_XSLT_DEBUG_PROCESS
5837 while (current
!= NULL
) {
5839 * Cleanup children empty nodes if asked for
5841 if ((IS_XSLT_REAL_NODE(current
)) &&
5842 (current
->children
!= NULL
) &&
5843 (xsltFindElemSpaceHandling(ctxt
, current
))) {
5844 xmlNodePtr
delete = NULL
, cur
= current
->children
;
5846 while (cur
!= NULL
) {
5847 if (IS_BLANK_NODE(cur
))
5851 if (delete != NULL
) {
5852 xmlUnlinkNode(delete);
5853 xmlFreeNode(delete);
5855 #ifdef WITH_XSLT_DEBUG_PROCESS
5863 * Skip to next node in document order.
5865 if (node
->type
== XML_ENTITY_REF_NODE
) {
5866 /* process deep in entities */
5867 xsltApplyStripSpaces(ctxt
, node
->children
);
5869 if ((current
->children
!= NULL
) &&
5870 (current
->type
!= XML_ENTITY_REF_NODE
)) {
5871 current
= current
->children
;
5872 } else if (current
->next
!= NULL
) {
5873 current
= current
->next
;
5876 current
= current
->parent
;
5877 if (current
== NULL
)
5879 if (current
== node
)
5881 if (current
->next
!= NULL
) {
5882 current
= current
->next
;
5885 } while (current
!= NULL
);
5890 #ifdef WITH_XSLT_DEBUG_PROCESS
5891 XSLT_TRACE(ctxt
,XSLT_TRACE_STRIP_SPACES
,xsltGenericDebug(xsltGenericDebugContext
,
5892 "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb
));
5898 xsltCountKeys(xsltTransformContextPtr ctxt
)
5900 xsltStylesheetPtr style
;
5907 * Do we have those nastly templates with a key() in the match pattern?
5909 ctxt
->hasTemplKeyPatterns
= 0;
5910 style
= ctxt
->style
;
5911 while (style
!= NULL
) {
5912 if (style
->keyMatch
!= NULL
) {
5913 ctxt
->hasTemplKeyPatterns
= 1;
5916 style
= xsltNextImport(style
);
5919 * Count number of key declarations.
5922 style
= ctxt
->style
;
5923 while (style
!= NULL
) {
5929 style
= xsltNextImport(style
);
5931 return(ctxt
->nbKeys
);
5935 * xsltApplyStylesheetInternal:
5936 * @style: a parsed XSLT stylesheet
5937 * @doc: a parsed XML document
5938 * @params: a NULL terminated array of parameters names/values tuples
5939 * @output: the targetted output
5940 * @profile: profile FILE * output or NULL
5941 * @user: user provided parameter
5943 * Apply the stylesheet to the document
5944 * NOTE: This may lead to a non-wellformed output XML wise !
5946 * Returns the result document or NULL in case of error
5949 xsltApplyStylesheetInternal(xsltStylesheetPtr style
, xmlDocPtr doc
,
5950 const char **params
, const char *output
,
5951 FILE * profile
, xsltTransformContextPtr userCtxt
)
5953 xmlDocPtr res
= NULL
;
5954 xsltTransformContextPtr ctxt
= NULL
;
5955 xmlNodePtr root
, node
;
5956 const xmlChar
*method
;
5957 const xmlChar
*doctypePublic
;
5958 const xmlChar
*doctypeSystem
;
5959 const xmlChar
*version
;
5960 const xmlChar
*encoding
;
5961 xsltStackElemPtr variables
;
5962 xsltStackElemPtr vptr
;
5966 if ((style
== NULL
) || (doc
== NULL
))
5969 if (style
->internalized
== 0) {
5970 #ifdef WITH_XSLT_DEBUG
5971 xsltGenericDebug(xsltGenericDebugContext
,
5972 "Stylesheet was not fully internalized !\n");
5975 if (doc
->intSubset
!= NULL
) {
5977 * Avoid hitting the DTD when scanning nodes
5978 * but keep it linked as doc->intSubset
5980 xmlNodePtr cur
= (xmlNodePtr
) doc
->intSubset
;
5981 if (cur
->next
!= NULL
)
5982 cur
->next
->prev
= cur
->prev
;
5983 if (cur
->prev
!= NULL
)
5984 cur
->prev
->next
= cur
->next
;
5985 if (doc
->children
== cur
)
5986 doc
->children
= cur
->next
;
5987 if (doc
->last
== cur
)
5988 doc
->last
= cur
->prev
;
5989 cur
->prev
= cur
->next
= NULL
;
5993 * Check for XPath document order availability
5995 root
= xmlDocGetRootElement(doc
);
5997 if (((long) root
->content
) >= 0 && (xslDebugStatus
== XSLT_DEBUG_NONE
))
5998 xmlXPathOrderDocElems(doc
);
6001 if (userCtxt
!= NULL
)
6004 ctxt
= xsltNewTransformContext(style
, doc
);
6009 ctxt
->initialContextDoc
= doc
;
6010 ctxt
->initialContextNode
= (xmlNodePtr
) doc
;
6012 if (profile
!= NULL
)
6016 ctxt
->outputFile
= output
;
6018 ctxt
->outputFile
= NULL
;
6021 * internalize the modes if needed
6023 if (ctxt
->dict
!= NULL
) {
6024 if (ctxt
->mode
!= NULL
)
6025 ctxt
->mode
= xmlDictLookup(ctxt
->dict
, ctxt
->mode
, -1);
6026 if (ctxt
->modeURI
!= NULL
)
6027 ctxt
->modeURI
= xmlDictLookup(ctxt
->dict
, ctxt
->modeURI
, -1);
6030 XSLT_GET_IMPORT_PTR(method
, style
, method
)
6031 XSLT_GET_IMPORT_PTR(doctypePublic
, style
, doctypePublic
)
6032 XSLT_GET_IMPORT_PTR(doctypeSystem
, style
, doctypeSystem
)
6033 XSLT_GET_IMPORT_PTR(version
, style
, version
)
6034 XSLT_GET_IMPORT_PTR(encoding
, style
, encoding
)
6036 if ((method
!= NULL
) &&
6037 (!xmlStrEqual(method
, (const xmlChar
*) "xml")))
6039 if (xmlStrEqual(method
, (const xmlChar
*) "html")) {
6040 ctxt
->type
= XSLT_OUTPUT_HTML
;
6041 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
))) {
6042 res
= htmlNewDoc(doctypeSystem
, doctypePublic
);
6044 if (version
== NULL
) {
6047 res
= htmlNewDoc(NULL
, NULL
);
6049 * Make sure no DTD node is generated in this case
6052 dtd
= xmlGetIntSubset(res
);
6054 xmlUnlinkNode((xmlNodePtr
) dtd
);
6057 res
->intSubset
= NULL
;
6058 res
->extSubset
= NULL
;
6062 #ifdef XSLT_GENERATE_HTML_DOCTYPE
6063 xsltGetHTMLIDs(version
, &doctypePublic
, &doctypeSystem
);
6065 res
= htmlNewDoc(doctypeSystem
, doctypePublic
);
6070 res
->dict
= ctxt
->dict
;
6071 xmlDictReference(res
->dict
);
6073 #ifdef WITH_XSLT_DEBUG
6074 xsltGenericDebug(xsltGenericDebugContext
,
6075 "reusing transformation dict for output\n");
6077 } else if (xmlStrEqual(method
, (const xmlChar
*) "xhtml")) {
6078 xsltTransformError(ctxt
, NULL
, (xmlNodePtr
) doc
,
6079 "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n",
6081 ctxt
->type
= XSLT_OUTPUT_HTML
;
6082 res
= htmlNewDoc(doctypeSystem
, doctypePublic
);
6085 res
->dict
= ctxt
->dict
;
6086 xmlDictReference(res
->dict
);
6088 #ifdef WITH_XSLT_DEBUG
6089 xsltGenericDebug(xsltGenericDebugContext
,
6090 "reusing transformation dict for output\n");
6092 } else if (xmlStrEqual(method
, (const xmlChar
*) "text")) {
6093 ctxt
->type
= XSLT_OUTPUT_TEXT
;
6094 res
= xmlNewDoc(style
->version
);
6097 res
->dict
= ctxt
->dict
;
6098 xmlDictReference(res
->dict
);
6100 #ifdef WITH_XSLT_DEBUG
6101 xsltGenericDebug(xsltGenericDebugContext
,
6102 "reusing transformation dict for output\n");
6105 xsltTransformError(ctxt
, NULL
, (xmlNodePtr
) doc
,
6106 "xsltApplyStylesheetInternal: unsupported method %s\n",
6111 ctxt
->type
= XSLT_OUTPUT_XML
;
6112 res
= xmlNewDoc(style
->version
);
6115 res
->dict
= ctxt
->dict
;
6116 xmlDictReference(ctxt
->dict
);
6117 #ifdef WITH_XSLT_DEBUG
6118 xsltGenericDebug(xsltGenericDebugContext
,
6119 "reusing transformation dict for output\n");
6122 res
->charset
= XML_CHAR_ENCODING_UTF8
;
6123 if (encoding
!= NULL
)
6124 res
->encoding
= xmlStrdup(encoding
);
6125 variables
= style
->variables
;
6128 * Start the evaluation, evaluate the params, the stylesheets globals
6129 * and start by processing the top node.
6131 if (xsltNeedElemSpaceHandling(ctxt
))
6132 xsltApplyStripSpaces(ctxt
, xmlDocGetRootElement(doc
));
6134 * Evaluate global params and user-provided params.
6136 ctxt
->node
= (xmlNodePtr
) doc
;
6137 if (ctxt
->globalVars
== NULL
)
6138 ctxt
->globalVars
= xmlHashCreate(20);
6139 if (params
!= NULL
) {
6140 xsltEvalUserParams(ctxt
, params
);
6143 /* need to be called before evaluating global variables */
6144 xsltCountKeys(ctxt
);
6146 xsltEvalGlobalVariables(ctxt
);
6148 ctxt
->node
= (xmlNodePtr
) doc
;
6150 ctxt
->insert
= (xmlNodePtr
) res
;
6151 ctxt
->varsBase
= ctxt
->varsNr
- 1;
6153 ctxt
->xpathCtxt
->contextSize
= 1;
6154 ctxt
->xpathCtxt
->proximityPosition
= 1;
6155 ctxt
->xpathCtxt
->node
= NULL
; /* TODO: Set the context node here? */
6157 * Start processing the source tree -----------------------------------
6159 xsltProcessOneNode(ctxt
, ctxt
->node
, NULL
);
6161 * Remove all remaining vars from the stack.
6163 xsltLocalVariablePop(ctxt
, 0, -2);
6164 xsltShutdownCtxtExts(ctxt
);
6166 xsltCleanupTemplates(style
); /* TODO: <- style should be read only */
6169 * Now cleanup our variables so stylesheet can be re-used
6171 * TODO: this is not needed anymore global variables are copied
6172 * and not evaluated directly anymore, keep this as a check
6174 if (style
->variables
!= variables
) {
6175 vptr
= style
->variables
;
6176 while (vptr
->next
!= variables
)
6179 xsltFreeStackElemList(style
->variables
);
6180 style
->variables
= variables
;
6182 vptr
= style
->variables
;
6183 while (vptr
!= NULL
) {
6184 if (vptr
->computed
) {
6185 if (vptr
->value
!= NULL
) {
6186 xmlXPathFreeObject(vptr
->value
);
6195 * code disabled by wmb; awaiting kb's review
6196 * problem is that global variable(s) may contain xpath objects
6197 * from doc associated with RVT, so can't be freed at this point.
6198 * xsltFreeTransformContext includes a call to xsltFreeRVTs, so
6199 * I assume this shouldn't be required at this point.
6202 * Free all remaining tree fragments.
6207 * Do some post processing work depending on the generated output
6209 root
= xmlDocGetRootElement(res
);
6211 const xmlChar
*doctype
= NULL
;
6213 if ((root
->ns
!= NULL
) && (root
->ns
->prefix
!= NULL
))
6214 doctype
= xmlDictQLookup(ctxt
->dict
, root
->ns
->prefix
, root
->name
);
6215 if (doctype
== NULL
)
6216 doctype
= root
->name
;
6219 * Apply the default selection of the method
6221 if ((method
== NULL
) &&
6222 (root
->ns
== NULL
) &&
6223 (!xmlStrcasecmp(root
->name
, (const xmlChar
*) "html"))) {
6226 tmp
= res
->children
;
6227 while ((tmp
!= NULL
) && (tmp
!= root
)) {
6228 if (tmp
->type
== XML_ELEMENT_NODE
)
6230 if ((tmp
->type
== XML_TEXT_NODE
) && (!xmlIsBlankNode(tmp
)))
6235 ctxt
->type
= XSLT_OUTPUT_HTML
;
6237 * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
6238 * transformation on the doc, but functions like
6240 res
->type
= XML_HTML_DOCUMENT_NODE
;
6241 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
))) {
6242 res
->intSubset
= xmlCreateIntSubset(res
, doctype
,
6245 #ifdef XSLT_GENERATE_HTML_DOCTYPE
6246 } else if (version
!= NULL
) {
6247 xsltGetHTMLIDs(version
, &doctypePublic
,
6249 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
)))
6251 xmlCreateIntSubset(res
, doctype
,
6259 if (ctxt
->type
== XSLT_OUTPUT_XML
) {
6260 XSLT_GET_IMPORT_PTR(doctypePublic
, style
, doctypePublic
)
6261 XSLT_GET_IMPORT_PTR(doctypeSystem
, style
, doctypeSystem
)
6262 if (((doctypePublic
!= NULL
) || (doctypeSystem
!= NULL
))) {
6264 /* Need a small "hack" here to assure DTD comes before
6265 possible comment nodes */
6266 node
= res
->children
;
6268 res
->children
= NULL
;
6270 res
->intSubset
= xmlCreateIntSubset(res
, doctype
,
6273 if (res
->children
!= NULL
) {
6274 res
->children
->next
= node
;
6275 node
->prev
= res
->children
;
6278 res
->children
= node
;
6284 xmlXPathFreeNodeSet(ctxt
->nodeList
);
6285 if (profile
!= NULL
) {
6286 xsltSaveProfiling(ctxt
, profile
);
6292 if ((ctxt
!= NULL
) && (ctxt
->state
== XSLT_STATE_ERROR
)) {
6296 if ((res
!= NULL
) && (ctxt
!= NULL
) && (output
!= NULL
)) {
6299 ret
= xsltCheckWrite(ctxt
->sec
, ctxt
, (const xmlChar
*) output
);
6301 xsltTransformError(ctxt
, NULL
, NULL
,
6302 "xsltApplyStylesheet: forbidden to save to %s\n",
6304 } else if (ret
< 0) {
6305 xsltTransformError(ctxt
, NULL
, NULL
,
6306 "xsltApplyStylesheet: saving to %s may not be possible\n",
6311 #ifdef XSLT_DEBUG_PROFILE_CACHE
6312 printf("# Cache:\n");
6313 printf("# Reused tree fragments: %d\n", ctxt
->cache
->dbgReusedRVTs
);
6314 printf("# Reused variables : %d\n", ctxt
->cache
->dbgReusedVars
);
6317 if ((ctxt
!= NULL
) && (userCtxt
== NULL
))
6318 xsltFreeTransformContext(ctxt
);
6326 #ifdef XSLT_DEBUG_PROFILE_CACHE
6327 printf("# Cache:\n");
6328 printf("# Reused tree fragments: %d\n", ctxt
->cache
->dbgReusedRVTs
);
6329 printf("# Reused variables : %d\n", ctxt
->cache
->dbgReusedVars
);
6332 if ((ctxt
!= NULL
) && (userCtxt
== NULL
))
6333 xsltFreeTransformContext(ctxt
);
6338 * xsltApplyStylesheet:
6339 * @style: a parsed XSLT stylesheet
6340 * @doc: a parsed XML document
6341 * @params: a NULL terminated arry of parameters names/values tuples
6343 * Apply the stylesheet to the document
6344 * NOTE: This may lead to a non-wellformed output XML wise !
6346 * Returns the result document or NULL in case of error
6349 xsltApplyStylesheet(xsltStylesheetPtr style
, xmlDocPtr doc
,
6350 const char **params
)
6352 return (xsltApplyStylesheetInternal(style
, doc
, params
, NULL
, NULL
, NULL
));
6356 * xsltProfileStylesheet:
6357 * @style: a parsed XSLT stylesheet
6358 * @doc: a parsed XML document
6359 * @params: a NULL terminated arry of parameters names/values tuples
6360 * @output: a FILE * for the profiling output
6362 * Apply the stylesheet to the document and dump the profiling to
6365 * Returns the result document or NULL in case of error
6368 xsltProfileStylesheet(xsltStylesheetPtr style
, xmlDocPtr doc
,
6369 const char **params
, FILE * output
)
6373 res
= xsltApplyStylesheetInternal(style
, doc
, params
, NULL
, output
, NULL
);
6378 * xsltApplyStylesheetUser:
6379 * @style: a parsed XSLT stylesheet
6380 * @doc: a parsed XML document
6381 * @params: a NULL terminated array of parameters names/values tuples
6382 * @output: the targetted output
6383 * @profile: profile FILE * output or NULL
6384 * @userCtxt: user provided transform context
6386 * Apply the stylesheet to the document and allow the user to provide
6387 * its own transformation context.
6389 * Returns the result document or NULL in case of error
6392 xsltApplyStylesheetUser(xsltStylesheetPtr style
, xmlDocPtr doc
,
6393 const char **params
, const char *output
,
6394 FILE * profile
, xsltTransformContextPtr userCtxt
)
6398 res
= xsltApplyStylesheetInternal(style
, doc
, params
, output
,
6404 * xsltRunStylesheetUser:
6405 * @style: a parsed XSLT stylesheet
6406 * @doc: a parsed XML document
6407 * @params: a NULL terminated array of parameters names/values tuples
6408 * @output: the URL/filename ot the generated resource if available
6409 * @SAX: a SAX handler for progressive callback output (not implemented yet)
6410 * @IObuf: an output buffer for progressive output (not implemented yet)
6411 * @profile: profile FILE * output or NULL
6412 * @userCtxt: user provided transform context
6414 * Apply the stylesheet to the document and generate the output according
6415 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
6417 * NOTE: This may lead to a non-wellformed output XML wise !
6418 * NOTE: This may also result in multiple files being generated
6419 * NOTE: using IObuf, the result encoding used will be the one used for
6420 * creating the output buffer, use the following macro to read it
6421 * from the stylesheet
6422 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
6423 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
6424 * since the interface uses only UTF8
6426 * Returns the number of by written to the main resource or -1 in case of
6430 xsltRunStylesheetUser(xsltStylesheetPtr style
, xmlDocPtr doc
,
6431 const char **params
, const char *output
,
6432 xmlSAXHandlerPtr SAX
, xmlOutputBufferPtr IObuf
,
6433 FILE * profile
, xsltTransformContextPtr userCtxt
)
6438 if ((output
== NULL
) && (SAX
== NULL
) && (IObuf
== NULL
))
6440 if ((SAX
!= NULL
) && (IObuf
!= NULL
))
6443 /* unsupported yet */
6445 XSLT_TODO
/* xsltRunStylesheet xmlSAXHandlerPtr SAX */
6449 tmp
= xsltApplyStylesheetInternal(style
, doc
, params
, output
, profile
,
6452 xsltTransformError(NULL
, NULL
, (xmlNodePtr
) doc
,
6453 "xsltRunStylesheet : run failed\n");
6456 if (IObuf
!= NULL
) {
6457 /* TODO: incomplete, IObuf output not progressive */
6458 ret
= xsltSaveResultTo(IObuf
, tmp
, style
);
6460 ret
= xsltSaveResultToFilename(output
, tmp
, style
, 0);
6467 * xsltRunStylesheet:
6468 * @style: a parsed XSLT stylesheet
6469 * @doc: a parsed XML document
6470 * @params: a NULL terminated array of parameters names/values tuples
6471 * @output: the URL/filename ot the generated resource if available
6472 * @SAX: a SAX handler for progressive callback output (not implemented yet)
6473 * @IObuf: an output buffer for progressive output (not implemented yet)
6475 * Apply the stylesheet to the document and generate the output according
6476 * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
6478 * NOTE: This may lead to a non-wellformed output XML wise !
6479 * NOTE: This may also result in multiple files being generated
6480 * NOTE: using IObuf, the result encoding used will be the one used for
6481 * creating the output buffer, use the following macro to read it
6482 * from the stylesheet
6483 * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
6484 * NOTE: using SAX, any encoding specified in the stylesheet will be lost
6485 * since the interface uses only UTF8
6487 * Returns the number of bytes written to the main resource or -1 in case of
6491 xsltRunStylesheet(xsltStylesheetPtr style
, xmlDocPtr doc
,
6492 const char **params
, const char *output
,
6493 xmlSAXHandlerPtr SAX
, xmlOutputBufferPtr IObuf
)
6495 return(xsltRunStylesheetUser(style
, doc
, params
, output
, SAX
, IObuf
,
6500 * xsltRegisterAllElement:
6501 * @ctxt: the XPath context
6503 * Registers all default XSLT elements in this context
6506 xsltRegisterAllElement(xsltTransformContextPtr ctxt
)
6508 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "apply-templates",
6510 (xsltTransformFunction
) xsltApplyTemplates
);
6511 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "apply-imports",
6513 (xsltTransformFunction
) xsltApplyImports
);
6514 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "call-template",
6516 (xsltTransformFunction
) xsltCallTemplate
);
6517 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "element",
6519 (xsltTransformFunction
) xsltElement
);
6520 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "attribute",
6522 (xsltTransformFunction
) xsltAttribute
);
6523 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "text",
6525 (xsltTransformFunction
) xsltText
);
6526 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "processing-instruction",
6528 (xsltTransformFunction
) xsltProcessingInstruction
);
6529 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "comment",
6531 (xsltTransformFunction
) xsltComment
);
6532 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "copy",
6534 (xsltTransformFunction
) xsltCopy
);
6535 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "value-of",
6537 (xsltTransformFunction
) xsltValueOf
);
6538 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "number",
6540 (xsltTransformFunction
) xsltNumber
);
6541 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "for-each",
6543 (xsltTransformFunction
) xsltForEach
);
6544 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "if",
6546 (xsltTransformFunction
) xsltIf
);
6547 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "choose",
6549 (xsltTransformFunction
) xsltChoose
);
6550 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "sort",
6552 (xsltTransformFunction
) xsltSort
);
6553 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "copy-of",
6555 (xsltTransformFunction
) xsltCopyOf
);
6556 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "message",
6558 (xsltTransformFunction
) xsltMessage
);
6561 * Those don't have callable entry points but are registered anyway
6563 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "variable",
6565 (xsltTransformFunction
) xsltDebug
);
6566 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "param",
6568 (xsltTransformFunction
) xsltDebug
);
6569 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "with-param",
6571 (xsltTransformFunction
) xsltDebug
);
6572 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "decimal-format",
6574 (xsltTransformFunction
) xsltDebug
);
6575 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "when",
6577 (xsltTransformFunction
) xsltDebug
);
6578 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "otherwise",
6580 (xsltTransformFunction
) xsltDebug
);
6581 xsltRegisterExtElement(ctxt
, (const xmlChar
*) "fallback",
6583 (xsltTransformFunction
) xsltDebug
);