2 * transform.c: Implemetation of the XSL Transformation 1.0 engine
3 * transform part, i.e. applying a Stylesheet to a document
6 * http://www.w3.org/TR/1999/REC-xslt-19991116
8 * See Copyright for the status of this software.
10 * Daniel.Veillard@imag.fr
13 #include "xsltconfig.h"
17 #include <libxml/xmlmemory.h>
18 #include <libxml/parser.h>
19 #include <libxml/tree.h>
20 #include <libxml/valid.h>
21 #include <libxml/hash.h>
22 #include <libxml/encoding.h>
23 #include <libxml/xmlerror.h>
24 #include <libxml/xpath.h>
25 #include <libxml/xpathInternals.h>
26 #include <libxml/HTMLtree.h>
28 #include "xsltInternals.h"
29 #include "xsltutils.h"
31 #include "transform.h"
32 #include "variables.h"
33 #include "namespaces.h"
34 #include "templates.h"
42 #define IS_BLANK_NODE(n) \
43 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
46 /************************************************************************
50 ************************************************************************/
53 * xsltNewTransformContext:
55 * Create a new XSLT TransformContext
57 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
59 xsltTransformContextPtr
60 xsltNewTransformContext(void) {
61 xsltTransformContextPtr cur;
63 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
65 xsltGenericError(xsltGenericErrorContext,
66 "xsltNewTransformContext : malloc failed\n");
69 memset(cur, 0, sizeof(xsltTransformContext));
74 * xsltFreeTransformContext:
75 * @ctxt: an XSLT parser context
77 * Free up the memory allocated by @ctxt
80 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
85 doc = ctxt->extraDocs;
87 next = (xmlDocPtr) doc->next;
91 if (ctxt->xpathCtxt != NULL)
92 xmlXPathFreeContext(ctxt->xpathCtxt);
93 xsltFreeVariableHashes(ctxt);
94 memset(ctxt, -1, sizeof(xsltTransformContext));
98 /************************************************************************
102 ************************************************************************/
104 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
105 void xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
107 void xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
111 * @ctxt: a XSLT process context
112 * @node: the node in the source tree.
113 * @inst: the xslt sort node
115 * Process the xslt sort node on the source node
118 xsltSort(xsltTransformContextPtr ctxt, xmlNodePtr node,
120 xmlXPathObjectPtr *results = NULL;
121 xmlNodeSetPtr list = NULL;
122 xmlXPathParserContextPtr xpathParserCtxt = NULL;
123 xmlChar *prop = NULL;
124 xmlXPathObjectPtr res, tmp;
125 const xmlChar *start;
131 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
134 list = ctxt->nodeList;
135 if ((list == NULL) || (list->nodeNr <= 1))
136 goto error; /* nothing to do */
140 /* TODO: process attributes as attribute value templates */
141 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"data-type");
143 if (xmlStrEqual(prop, (const xmlChar *) "text"))
145 else if (xmlStrEqual(prop, (const xmlChar *) "number"))
148 xsltGenericError(xsltGenericErrorContext,
149 "xsltSort: no support for data-type = %s\n", prop);
154 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"order");
156 if (xmlStrEqual(prop, (const xmlChar *) "ascending"))
158 else if (xmlStrEqual(prop, (const xmlChar *) "descending"))
161 xsltGenericError(xsltGenericErrorContext,
162 "xsltSort: invalid value %s for order\n", prop);
167 /* TODO: xsl:sort lang attribute */
168 /* TODO: xsl:sort case-order attribute */
170 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
172 prop = xmlNodeGetContent(inst);
174 xsltGenericError(xsltGenericErrorContext,
175 "xsltSort: select is not defined\n");
180 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
181 if (xpathParserCtxt == NULL)
183 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
184 if (results == NULL) {
185 xsltGenericError(xsltGenericErrorContext,
186 "xsltSort: memory allocation failure\n");
190 start = xpathParserCtxt->cur;
191 for (i = 0;i < len;i++) {
192 xpathParserCtxt->cur = start;
193 node = ctxt->node = list->nodeTab[i];
194 ctxt->xpathCtxt->proximityPosition = i + 1;
195 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
196 xmlXPathEvalExpr(xpathParserCtxt);
197 xmlXPathStringFunction(xpathParserCtxt, 1);
199 xmlXPathNumberFunction(xpathParserCtxt, 1);
200 res = valuePop(xpathParserCtxt);
202 tmp = valuePop(xpathParserCtxt);
204 xmlXPathFreeObject(tmp);
206 } while (tmp != NULL);
210 if (res->type == XPATH_NUMBER) {
214 xsltGenericDebug(xsltGenericDebugContext,
215 "xsltSort: select didn't evaluate to a number\n");
220 if (res->type == XPATH_STRING) {
224 xsltGenericDebug(xsltGenericDebugContext,
225 "xsltSort: select didn't evaluate to a string\n");
233 xsltSortFunction(list, &results[0], descending, number);
236 if (xpathParserCtxt != NULL)
237 xmlXPathFreeParserContext(xpathParserCtxt);
240 if (results != NULL) {
241 for (i = 0;i < len;i++)
242 xmlXPathFreeObject(results[i]);
249 * @ctxt: a XSLT process context
250 * @node: the node in the source tree.
251 * @inst: the xslt comment node
253 * Process the xslt comment node on the source node
256 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
258 xmlChar *value = NULL;
261 value = xsltEvalTemplateString(ctxt, node, inst);
262 /* TODO: check that there is no -- sequence and doesn't end up with - */
265 xsltGenericDebug(xsltGenericDebugContext,
266 "xsl:comment: empty\n");
268 xsltGenericDebug(xsltGenericDebugContext,
269 "xsl:comment: content %s\n", value);
272 comment = xmlNewComment(value);
273 xmlAddChild(ctxt->insert, comment);
280 * xsltProcessingInstruction:
281 * @ctxt: a XSLT process context
282 * @node: the node in the source tree.
283 * @inst: the xslt processing-instruction node
285 * Process the xslt processing-instruction node on the source node
288 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
290 xmlChar *ncname = NULL;
291 xmlChar *value = NULL;
295 if (ctxt->insert == NULL)
297 ncname = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
298 if (ncname == NULL) {
299 xsltGenericError(xsltGenericErrorContext,
300 "xslt:processing-instruction : name is missing\n");
303 /* TODO: check that it's both an an NCName and a PITarget. */
306 value = xsltEvalTemplateString(ctxt, node, inst);
307 /* TODO: check that there is no ?> sequence */
310 xsltGenericDebug(xsltGenericDebugContext,
311 "xsl:processing-instruction: %s empty\n", ncname);
313 xsltGenericDebug(xsltGenericDebugContext,
314 "xsl:processing-instruction: %s content %s\n", ncname, value);
317 pi = xmlNewPI(ncname, value);
318 xmlAddChild(ctxt->insert, pi);
329 * @ctxt: a XSLT process context
330 * @node: the node in the source tree.
331 * @inst: the xslt attribute node
333 * Process the xslt attribute node on the source node
336 xsltAttribute(xsltTransformContextPtr ctxt, xmlNodePtr node,
338 xmlChar *prop = NULL;
339 xmlChar *ncname = NULL;
340 xmlChar *prefix = NULL;
341 xmlChar *value = NULL;
346 if (ctxt->insert == NULL)
348 if (ctxt->insert->children != NULL) {
349 xsltGenericError(xsltGenericErrorContext,
350 "xslt:attribute : node has already children\n");
353 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
355 xsltGenericError(xsltGenericErrorContext,
356 "xslt:attribute : name is missing\n");
360 ncname = xmlSplitQName2(prop, &prefix);
361 if (ncname == NULL) {
366 if (xmlStrEqual(ncname, (const xmlChar *) "xmlns")) {
367 xsltGenericError(xsltGenericErrorContext,
368 "xslt:attribute : xmlns forbidden\n");
371 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
373 TODO /* xsl:attribute namespace */
377 if (prefix != NULL) {
378 ns = xmlSearchNs(inst->doc, inst, prefix);
380 xsltGenericError(xsltGenericErrorContext,
381 "no namespace bound to prefix %s\n", prefix);
383 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
389 value = xsltEvalTemplateString(ctxt, node, inst);
392 attr = xmlSetNsProp(ctxt->insert, ns, ncname,
393 (const xmlChar *)"");
395 attr = xmlSetProp(ctxt->insert, ncname, (const xmlChar *)"");
398 attr = xmlSetNsProp(ctxt->insert, ns, ncname, value);
400 attr = xmlSetProp(ctxt->insert, ncname, value);
417 * @ctxt: a XSLT process context
418 * @node: the node in the source tree.
419 * @inst: the xsltValueOf node
421 * Process the xsltValueOf node on the source node
424 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
427 int disableEscaping = 0;
428 xmlXPathObjectPtr res = NULL, tmp;
429 xmlXPathParserContextPtr xpathParserCtxt = NULL;
430 xmlNodePtr copy = NULL;
432 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
435 prop = xmlGetNsProp(inst, (const xmlChar *)"disable-output-escaping",
438 if (xmlStrEqual(prop, (const xmlChar *)"yes"))
440 else if (xmlStrEqual(prop, (const xmlChar *)"no"))
443 xsltGenericError(xsltGenericErrorContext,
444 "invalud value %s for disable-output-escaping\n", prop);
447 if (disableEscaping) {
448 TODO /* disable-output-escaping */
451 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
453 xsltGenericError(xsltGenericErrorContext,
454 "xsltValueOf: select is not defined\n");
458 xsltGenericDebug(xsltGenericDebugContext,
459 "xsltValueOf: select %s\n", prop);
462 if (ctxt->xpathCtxt == NULL) {
464 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
465 if (ctxt->xpathCtxt == NULL)
467 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
470 xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
471 if (xpathParserCtxt == NULL)
473 ctxt->xpathCtxt->node = node;
474 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
475 xmlXPathEvalExpr(xpathParserCtxt);
476 xmlXPathStringFunction(xpathParserCtxt, 1);
477 res = valuePop(xpathParserCtxt);
479 tmp = valuePop(xpathParserCtxt);
481 xmlXPathFreeObject(tmp);
483 } while (tmp != NULL);
485 if (res->type == XPATH_STRING) {
486 copy = xmlNewText(res->stringval);
488 xmlAddChild(ctxt->insert, copy);
493 xsltGenericError(xsltGenericErrorContext,
494 "xsltDefaultProcessOneNode: text copy failed\n");
498 xsltGenericDebug(xsltGenericDebugContext,
499 "xsltValueOf: result %s\n", res->stringval);
502 if (xpathParserCtxt != NULL) {
503 xmlXPathFreeParserContext(xpathParserCtxt);
504 xpathParserCtxt = NULL;
509 xmlXPathFreeObject(res);
514 * @ctxt: a XSLT process context
515 * @node: the element node in the source tree.
516 * @insert: the parent in the result tree.
518 * Make a copy of the element node @node
519 * and insert it as last child of @insert
521 * Returns a pointer to the new node, or NULL in case of error
524 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
528 copy = xmlCopyNode(node, 0);
529 copy->doc = ctxt->output;
531 xmlAddChild(insert, copy);
533 * Add namespaces as they are needed
535 if (node->nsDef != NULL)
536 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
537 if (node->ns != NULL) {
538 copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
541 xsltGenericError(xsltGenericErrorContext,
542 "xsltCopyNode: copy %s failed\n", node->name);
548 * xsltDefaultProcessOneNode:
549 * @ctxt: a XSLT process context
550 * @node: the node in the source tree.
552 * Process the source node with the default built-in template rule:
553 * <xsl:template match="*|/">
554 * <xsl:apply-templates/>
559 * <xsl:template match="text()|@*">
560 * <xsl:value-of select="."/>
563 * Note also that namespaces declarations are copied directly:
565 * the built-in template rule is the only template rule that is applied
566 * for namespace nodes.
569 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
571 xmlNodePtr delete = NULL;
572 int strip_spaces = -1;
575 switch (node->type) {
576 case XML_DOCUMENT_NODE:
577 case XML_HTML_DOCUMENT_NODE:
578 case XML_ELEMENT_NODE:
581 copy = xmlCopyNode(node, 0);
583 xmlAddChild(ctxt->insert, copy);
585 xsltGenericError(xsltGenericErrorContext,
586 "xsltDefaultProcessOneNode: text copy failed\n");
592 node = node->children;
593 while (node != NULL) {
594 switch (node->type) {
595 case XML_DOCUMENT_NODE:
596 case XML_HTML_DOCUMENT_NODE:
597 case XML_ELEMENT_NODE:
598 xsltProcessOneNode(ctxt, node);
601 /* TODO: check the whitespace stripping rules ! */
602 if ((IS_BLANK_NODE(node)) &&
603 (node->parent != NULL) &&
604 (ctxt->style->stripSpaces != NULL)) {
607 if (strip_spaces == -1) {
608 /* TODO: add namespaces support */
609 val = (const xmlChar *)
610 xmlHashLookup(ctxt->style->stripSpaces,
613 if (xmlStrEqual(val, (xmlChar *) "strip"))
615 if (xmlStrEqual(val, (xmlChar *) "preserve"))
618 if (strip_spaces == -1) {
619 val = (const xmlChar *)
620 xmlHashLookup(ctxt->style->stripSpaces,
621 (const xmlChar *)"*");
623 (xmlStrEqual(val, (xmlChar *) "strip")))
629 if (strip_spaces == 1) {
634 /* no break on purpose */
635 case XML_CDATA_SECTION_NODE:
636 copy = xmlCopyNode(node, 0);
638 xmlAddChild(ctxt->insert, copy);
640 xsltGenericError(xsltGenericErrorContext,
641 "xsltDefaultProcessOneNode: text copy failed\n");
646 xsltGenericDebug(xsltGenericDebugContext,
647 "xsltDefaultProcessOneNode: skipping node type %d\n",
653 if (delete != NULL) {
655 xsltGenericDebug(xsltGenericDebugContext,
656 "xsltDefaultProcessOneNode: removing ignorable blank node\n");
658 xmlUnlinkNode(delete);
667 * @ctxt: a XSLT process context
668 * @node: the node in the source tree.
669 * @inst: the xslt call-template node
671 * Process the xslt call-template node on the source node
674 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
676 xmlChar *prop = NULL;
677 xmlChar *ncname = NULL;
678 xmlChar *prefix = NULL;
680 xsltTemplatePtr template;
681 xmlNodePtr cur = NULL;
685 if (ctxt->insert == NULL)
687 prop = xmlGetNsProp(inst, (const xmlChar *)"name", XSLT_NAMESPACE);
689 xsltGenericError(xsltGenericErrorContext,
690 "xslt:call-template : name is missing\n");
694 ncname = xmlSplitQName2(prop, &prefix);
695 if (ncname == NULL) {
700 if ((prefix != NULL) && (ns == NULL)) {
701 ns = xmlSearchNs(ctxt->insert->doc, ctxt->insert, prefix);
703 xsltGenericError(xsltGenericErrorContext,
704 "no namespace bound to prefix %s\n", prefix);
708 template = xsltFindTemplate(ctxt->style, ncname, ns->href);
710 template = xsltFindTemplate(ctxt->style, ncname, NULL);
711 if (template == NULL) {
712 xsltGenericError(xsltGenericDebugContext,
713 "xslt:call-template: template %s not found\n", cur->name);
716 cur = inst->children;
717 while (cur != NULL) {
719 if (IS_XSLT_ELEM(cur)) {
720 if (IS_XSLT_NAME(cur, "with-param")) {
721 if (has_param == 0) {
725 xsltParseStylesheetParam(ctxt, cur);
727 xsltGenericError(xsltGenericDebugContext,
728 "xslt:call-template: misplaced xslt:%s\n", cur->name);
731 xsltGenericError(xsltGenericDebugContext,
732 "xslt:call-template: misplaced %s element\n", cur->name);
736 xsltApplyOneTemplate(ctxt, node, template->content);
750 * xsltApplyTemplates:
751 * @ctxt: a XSLT process context
752 * @node: the node in the source tree.
753 * @inst: the apply-templates node
755 * Process the apply-templates node on the source node
758 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
760 xmlChar *prop = NULL;
761 xmlNodePtr cur, delete = NULL;
762 xmlXPathObjectPtr res = NULL, tmp;
763 xmlNodePtr replacement;
764 xmlNodeSetPtr list = NULL, oldlist;
765 xmlXPathParserContextPtr xpathParserCtxt = NULL;
766 int i, oldProximityPosition, oldContextSize;
768 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
772 xsltGenericDebug(xsltGenericDebugContext,
773 "xsltApplyTemplates: node: %s\n", node->name);
775 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
778 xsltGenericDebug(xsltGenericDebugContext,
779 "xsltApplyTemplates: select %s\n", prop);
782 if (ctxt->xpathCtxt == NULL) {
784 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
785 if (ctxt->xpathCtxt == NULL)
787 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
790 xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
791 if (xpathParserCtxt == NULL)
793 ctxt->xpathCtxt->node = node;
794 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
795 xmlXPathEvalExpr(xpathParserCtxt);
796 res = valuePop(xpathParserCtxt);
798 tmp = valuePop(xpathParserCtxt);
800 xmlXPathFreeObject(tmp);
802 } while (tmp != NULL);
804 if (res->type == XPATH_NODESET) {
805 list = res->nodesetval;
806 res->nodesetval = NULL;
809 xsltGenericDebug(xsltGenericDebugContext,
810 "xsltApplyTemplates: select didn't evaluate to a node list\n");
817 * Build an XPath nodelist with the children
819 list = xmlXPathNodeSetCreate(NULL);
820 cur = node->children;
821 while (cur != NULL) {
824 /* TODO: check the whitespace stripping rules ! */
825 if ((IS_BLANK_NODE(cur)) &&
826 (cur->parent != NULL) &&
827 (ctxt->style->stripSpaces != NULL)) {
830 val = (const xmlChar *)
831 xmlHashLookup(ctxt->style->stripSpaces,
834 (xmlStrEqual(val, (xmlChar *) "strip"))) {
839 /* no break on purpose */
840 case XML_DOCUMENT_NODE:
841 case XML_HTML_DOCUMENT_NODE:
842 case XML_ELEMENT_NODE:
843 case XML_CDATA_SECTION_NODE:
844 xmlXPathNodeSetAdd(list, cur);
848 xsltGenericDebug(xsltGenericDebugContext,
849 "xsltApplyTemplates: skipping cur type %d\n",
855 if (delete != NULL) {
857 xsltGenericDebug(xsltGenericDebugContext,
858 "xsltApplyTemplates: removing ignorable blank cur\n");
860 xmlUnlinkNode(delete);
868 xsltGenericDebug(xsltGenericDebugContext,
869 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
872 oldlist = ctxt->nodeList;
873 ctxt->nodeList = list;
874 oldContextSize = ctxt->xpathCtxt->contextSize;
875 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
876 ctxt->xpathCtxt->contextSize = list->nodeNr;
879 * handle and skip the xsl:sort
881 replacement = inst->children;
882 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
883 xsltSort(ctxt, node, replacement);
884 replacement = replacement->next;
887 for (i = 0;i < list->nodeNr;i++) {
888 ctxt->node = list->nodeTab[i];
889 ctxt->xpathCtxt->proximityPosition = i + 1;
890 xsltProcessOneNode(ctxt, list->nodeTab[i]);
892 ctxt->nodeList = oldlist;
893 ctxt->xpathCtxt->contextSize = oldContextSize;
894 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
897 if (xpathParserCtxt != NULL)
898 xmlXPathFreeParserContext(xpathParserCtxt);
902 xmlXPathFreeObject(res);
904 xmlXPathFreeNodeSet(list);
908 * xsltApplyOneTemplate:
909 * @ctxt: a XSLT process context
910 * @node: the node in the source tree.
911 * @list: the template replacement nodelist
913 * Process the apply-templates node on the source node
916 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
918 xmlNodePtr cur = NULL, insert, copy = NULL;
919 xmlNodePtr oldInsert;
920 int has_variables = 0;
923 oldInsert = insert = ctxt->insert;
925 * Insert all non-XSLT nodes found in the template
928 while (cur != NULL) {
930 * test, we must have a valid insertion point
932 if (insert == NULL) {
934 xsltGenericDebug(xsltGenericDebugContext,
935 "xsltApplyOneTemplate: insert == NULL !\n");
940 if (IS_XSLT_ELEM(cur)) {
941 if (IS_XSLT_NAME(cur, "apply-templates")) {
942 ctxt->insert = insert;
943 xsltApplyTemplates(ctxt, node, cur);
944 ctxt->insert = oldInsert;
945 } else if (IS_XSLT_NAME(cur, "value-of")) {
946 ctxt->insert = insert;
947 xsltValueOf(ctxt, node, cur);
948 ctxt->insert = oldInsert;
949 } else if (IS_XSLT_NAME(cur, "if")) {
950 ctxt->insert = insert;
951 xsltIf(ctxt, node, cur);
952 ctxt->insert = oldInsert;
953 } else if (IS_XSLT_NAME(cur, "for-each")) {
954 ctxt->insert = insert;
955 xsltForEach(ctxt, node, cur);
956 ctxt->insert = oldInsert;
957 } else if (IS_XSLT_NAME(cur, "attribute")) {
958 ctxt->insert = insert;
959 xsltAttribute(ctxt, node, cur);
960 ctxt->insert = oldInsert;
962 } else if (IS_XSLT_NAME(cur, "element")) {
963 ctxt->insert = insert;
964 xsltElement(ctxt, node, cur);
965 ctxt->insert = oldInsert;
967 } else if (IS_XSLT_NAME(cur, "comment")) {
968 ctxt->insert = insert;
969 xsltComment(ctxt, node, cur);
970 ctxt->insert = oldInsert;
971 } else if (IS_XSLT_NAME(cur, "processing-instruction")) {
972 ctxt->insert = insert;
973 xsltProcessingInstruction(ctxt, node, cur);
974 ctxt->insert = oldInsert;
975 } else if (IS_XSLT_NAME(cur, "variable")) {
976 if (has_variables == 0) {
980 xsltParseStylesheetVariable(ctxt, cur);
981 } else if (IS_XSLT_NAME(cur, "param")) {
982 if (has_variables == 0) {
986 xsltParseStylesheetParam(ctxt, cur);
987 } else if (IS_XSLT_NAME(cur, "call-template")) {
988 if (has_variables == 0) {
992 xsltCallTemplate(ctxt, node, cur);
993 } else if (IS_XSLT_NAME(cur, "message")) {
996 xsltGenericError(xsltGenericDebugContext,
997 "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
1003 } else if (cur->type == XML_TEXT_NODE) {
1005 * This text comes from the stylesheet
1006 * For stylesheets, the set of whitespace-preserving
1007 * element names consists of just xsl:text.
1009 #ifdef DEBUG_PROCESS
1010 xsltGenericDebug(xsltGenericDebugContext,
1011 "xsltApplyOneTemplate: copy text %s\n", cur->content);
1013 copy = xmlCopyNode(cur, 0);
1015 xmlAddChild(insert, copy);
1017 xsltGenericError(xsltGenericErrorContext,
1018 "xsltApplyOneTemplate: text copy failed\n");
1020 } else if (cur->type == XML_ELEMENT_NODE) {
1021 #ifdef DEBUG_PROCESS
1022 xsltGenericDebug(xsltGenericDebugContext,
1023 "xsltApplyOneTemplate: copy node %s\n", cur->name);
1025 copy = xsltCopyNode(ctxt, cur, insert);
1027 * all the attributes are directly inherited
1028 * TODO: Do the substitution of {} XPath expressions !!!
1030 if (cur->properties != NULL)
1031 copy->properties = xsltAttrListTemplateProcess(ctxt,
1032 copy, cur->properties);
1036 * Skip to next node, in document order.
1038 if (cur->children != NULL) {
1039 if (cur->children->type != XML_ENTITY_DECL) {
1040 cur = cur->children;
1047 if (cur->next != NULL) {
1054 insert = insert->parent;
1057 if (cur == list->parent) {
1061 if (cur->next != NULL) {
1065 } while (cur != NULL);
1067 if (has_variables != 0) {
1074 * @ctxt: a XSLT process context
1075 * @node: the node in the source tree.
1076 * @inst: the xslt if node
1078 * Process the xslt if node on the source node
1081 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
1084 xmlXPathObjectPtr res = NULL, tmp;
1085 xmlXPathParserContextPtr xpathParserCtxt = NULL;
1088 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1091 prop = xmlGetNsProp(inst, (const xmlChar *)"test", XSLT_NAMESPACE);
1093 xsltGenericError(xsltGenericErrorContext,
1094 "xsltIf: test is not defined\n");
1097 #ifdef DEBUG_PROCESS
1098 xsltGenericDebug(xsltGenericDebugContext,
1099 "xsltIf: test %s\n", prop);
1102 if (ctxt->xpathCtxt == NULL) {
1104 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
1105 if (ctxt->xpathCtxt == NULL)
1107 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
1109 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1110 if (xpathParserCtxt == NULL)
1112 ctxt->xpathCtxt->node = node;
1113 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
1114 xmlXPathEvalExpr(xpathParserCtxt);
1115 xmlXPathBooleanFunction(xpathParserCtxt, 1);
1116 res = valuePop(xpathParserCtxt);
1118 tmp = valuePop(xpathParserCtxt);
1120 xmlXPathFreeObject(tmp);
1122 } while (tmp != NULL);
1125 if (res->type == XPATH_BOOLEAN)
1126 doit = res->boolval;
1128 #ifdef DEBUG_PROCESS
1129 xsltGenericDebug(xsltGenericDebugContext,
1130 "xsltIf: test didn't evaluate to a boolean\n");
1136 #ifdef DEBUG_PROCESS
1137 xsltGenericDebug(xsltGenericDebugContext,
1138 "xsltIf: test evaluate to %d\n", doit);
1141 xsltApplyOneTemplate(ctxt, ctxt->node, inst->children);
1145 if (xpathParserCtxt != NULL)
1146 xmlXPathFreeParserContext(xpathParserCtxt);
1150 xmlXPathFreeObject(res);
1155 * @ctxt: a XSLT process context
1156 * @node: the node in the source tree.
1157 * @inst: the xslt for-each node
1159 * Process the xslt for-each node on the source node
1162 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
1165 xmlXPathObjectPtr res = NULL, tmp;
1166 xmlNodePtr replacement;
1167 xmlNodeSetPtr list = NULL, oldlist;
1168 xmlXPathParserContextPtr xpathParserCtxt = NULL;
1169 int i, oldProximityPosition, oldContextSize;
1171 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1174 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
1176 xsltGenericError(xsltGenericErrorContext,
1177 "xsltForEach: select is not defined\n");
1180 #ifdef DEBUG_PROCESS
1181 xsltGenericDebug(xsltGenericDebugContext,
1182 "xsltForEach: select %s\n", prop);
1185 if (ctxt->xpathCtxt == NULL) {
1187 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
1188 if (ctxt->xpathCtxt == NULL)
1190 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
1192 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1193 if (xpathParserCtxt == NULL)
1195 ctxt->xpathCtxt->node = node;
1196 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
1197 xmlXPathEvalExpr(xpathParserCtxt);
1198 res = valuePop(xpathParserCtxt);
1200 tmp = valuePop(xpathParserCtxt);
1202 xmlXPathFreeObject(tmp);
1204 } while (tmp != NULL);
1207 if (res->type == XPATH_NODESET)
1208 list = res->nodesetval;
1210 #ifdef DEBUG_PROCESS
1211 xsltGenericDebug(xsltGenericDebugContext,
1212 "xsltForEach: select didn't evaluate to a node list\n");
1218 #ifdef DEBUG_PROCESS
1219 xsltGenericDebug(xsltGenericDebugContext,
1220 "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
1223 oldlist = ctxt->nodeList;
1224 ctxt->nodeList = list;
1225 oldContextSize = ctxt->xpathCtxt->contextSize;
1226 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1227 ctxt->xpathCtxt->contextSize = list->nodeNr;
1230 * handle and skip the xsl:sort
1232 replacement = inst->children;
1233 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
1234 xsltSort(ctxt, node, replacement);
1235 replacement = replacement->next;
1238 for (i = 0;i < list->nodeNr;i++) {
1239 ctxt->node = list->nodeTab[i];
1240 ctxt->xpathCtxt->proximityPosition = i + 1;
1241 xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement);
1243 ctxt->nodeList = oldlist;
1244 ctxt->xpathCtxt->contextSize = oldContextSize;
1245 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1248 if (xpathParserCtxt != NULL)
1249 xmlXPathFreeParserContext(xpathParserCtxt);
1253 xmlXPathFreeObject(res);
1257 * xsltProcessOneNode:
1258 * @ctxt: a XSLT process context
1259 * @node: the node in the source tree.
1261 * Process the source node.
1264 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1265 xsltTemplatePtr template;
1267 template = xsltGetTemplate(ctxt->style, node);
1269 * If no template is found, apply the default rule.
1271 if (template == NULL) {
1272 #ifdef DEBUG_PROCESS
1273 if (node->type == XML_DOCUMENT_NODE)
1274 xsltGenericDebug(xsltGenericDebugContext,
1275 "xsltProcessOneNode: no template found for /\n");
1277 xsltGenericDebug(xsltGenericDebugContext,
1278 "xsltProcessOneNode: no template found for %s\n", node->name);
1280 xsltDefaultProcessOneNode(ctxt, node);
1284 xsltApplyOneTemplate(ctxt, node, template->content);
1288 * xsltApplyStylesheet:
1289 * @style: a parsed XSLT stylesheet
1290 * @doc: a parsed XML document
1292 * Apply the stylesheet to the document
1293 * NOTE: This may lead to a non-wellformed output XML wise !
1295 * Returns the result document or NULL in case of error
1298 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
1299 xmlDocPtr res = NULL;
1300 xsltTransformContextPtr ctxt = NULL;
1303 if ((style == NULL) || (doc == NULL))
1305 ctxt = xsltNewTransformContext();
1309 ctxt->style = style;
1310 xsltEvalGlobalVariables(ctxt);
1311 if ((style->method != NULL) &&
1312 (!xmlStrEqual(style->method, (const xmlChar *) "xml"))) {
1313 if (xmlStrEqual(style->method, (const xmlChar *) "html")) {
1314 ctxt->type = XSLT_OUTPUT_HTML;
1315 res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
1318 } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
1319 ctxt->type = XSLT_OUTPUT_TEXT;
1320 res = xmlNewDoc(style->version);
1324 xsltGenericError(xsltGenericErrorContext,
1325 "xsltApplyStylesheet: insupported method %s\n",
1330 ctxt->type = XSLT_OUTPUT_XML;
1331 res = xmlNewDoc(style->version);
1335 res->charset = XML_CHAR_ENCODING_UTF8;
1336 if (style->encoding != NULL)
1337 res->encoding = xmlStrdup(style->encoding);
1343 ctxt->insert = (xmlNodePtr) res;
1344 ctxt->node = (xmlNodePtr) doc;
1345 xsltProcessOneNode(ctxt, ctxt->node);
1348 if ((ctxt->type = XSLT_OUTPUT_XML) &&
1349 ((style->doctypePublic != NULL) ||
1350 (style->doctypeSystem != NULL))) {
1351 root = xmlDocGetRootElement(res);
1353 res->intSubset = xmlCreateIntSubset(res, root->name,
1354 style->doctypePublic, style->doctypeSystem);
1356 xmlXPathFreeNodeSet(ctxt->nodeList);
1357 xsltFreeTransformContext(ctxt);
1364 xsltFreeTransformContext(ctxt);