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;
574 switch (node->type) {
575 case XML_DOCUMENT_NODE:
576 case XML_HTML_DOCUMENT_NODE:
577 case XML_ELEMENT_NODE:
580 copy = xmlCopyNode(node, 0);
582 xmlAddChild(ctxt->insert, copy);
584 xsltGenericError(xsltGenericErrorContext,
585 "xsltDefaultProcessOneNode: text copy failed\n");
591 node = node->children;
592 while (node != NULL) {
593 switch (node->type) {
594 case XML_DOCUMENT_NODE:
595 case XML_HTML_DOCUMENT_NODE:
596 case XML_ELEMENT_NODE:
597 xsltProcessOneNode(ctxt, node);
600 /* TODO: check the whitespace stripping rules ! */
601 if ((IS_BLANK_NODE(node)) &&
602 (node->parent != NULL) &&
603 (ctxt->style->stripSpaces != NULL)) {
606 if (strip_spaces == -1) {
607 /* TODO: add namespaces support */
608 val = (const xmlChar *)
609 xmlHashLookup(ctxt->style->stripSpaces,
612 if (xmlStrEqual(val, (xmlChar *) "strip"))
614 if (xmlStrEqual(val, (xmlChar *) "preserve"))
617 if (strip_spaces == -1) {
618 val = (const xmlChar *)
619 xmlHashLookup(ctxt->style->stripSpaces,
620 (const xmlChar *)"*");
622 (xmlStrEqual(val, (xmlChar *) "strip")))
628 if (strip_spaces == 1) {
633 /* no break on purpose */
634 case XML_CDATA_SECTION_NODE:
635 copy = xmlCopyNode(node, 0);
637 xmlAddChild(ctxt->insert, copy);
639 xsltGenericError(xsltGenericErrorContext,
640 "xsltDefaultProcessOneNode: text copy failed\n");
645 xsltGenericDebug(xsltGenericDebugContext,
646 "xsltDefaultProcessOneNode: skipping node type %d\n",
652 if (delete != NULL) {
654 xsltGenericDebug(xsltGenericDebugContext,
655 "xsltDefaultProcessOneNode: removing ignorable blank node\n");
657 xmlUnlinkNode(delete);
666 * @ctxt: a XSLT process context
667 * @node: the node in the source tree.
668 * @inst: the xslt call-template node
670 * Process the xslt call-template node on the source node
673 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
675 xmlChar *prop = NULL;
676 xmlChar *ncname = NULL;
677 xmlChar *prefix = NULL;
679 xsltTemplatePtr template;
680 xmlNodePtr cur = NULL;
684 if (ctxt->insert == NULL)
686 prop = xmlGetNsProp(inst, (const xmlChar *)"name", XSLT_NAMESPACE);
688 xsltGenericError(xsltGenericErrorContext,
689 "xslt:call-template : name is missing\n");
693 ncname = xmlSplitQName2(prop, &prefix);
694 if (ncname == NULL) {
699 if ((prefix != NULL) && (ns == NULL)) {
700 ns = xmlSearchNs(ctxt->insert->doc, ctxt->insert, prefix);
702 xsltGenericError(xsltGenericErrorContext,
703 "no namespace bound to prefix %s\n", prefix);
707 template = xsltFindTemplate(ctxt->style, ncname, ns->href);
709 template = xsltFindTemplate(ctxt->style, ncname, NULL);
710 if (template == NULL) {
711 xsltGenericError(xsltGenericDebugContext,
712 "xslt:call-template: template %s not found\n", cur->name);
715 cur = inst->children;
716 while (cur != NULL) {
717 if (IS_XSLT_ELEM(cur)) {
718 if (IS_XSLT_NAME(cur, "with-param")) {
719 if (has_param == 0) {
723 xsltParseStylesheetParam(ctxt, cur);
725 xsltGenericError(xsltGenericDebugContext,
726 "xslt:call-template: misplaced xslt:%s\n", cur->name);
729 xsltGenericError(xsltGenericDebugContext,
730 "xslt:call-template: misplaced %s element\n", cur->name);
734 xsltApplyOneTemplate(ctxt, node, template->content);
748 * xsltApplyTemplates:
749 * @ctxt: a XSLT process context
750 * @node: the node in the source tree.
751 * @inst: the apply-templates node
753 * Process the apply-templates node on the source node
756 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
758 xmlChar *prop = NULL;
759 xmlNodePtr cur, delete = NULL;
760 xmlXPathObjectPtr res = NULL, tmp;
761 xmlNodePtr replacement;
762 xmlNodeSetPtr list = NULL, oldlist;
763 xmlXPathParserContextPtr xpathParserCtxt = NULL;
764 int i, oldProximityPosition, oldContextSize;
766 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
770 xsltGenericDebug(xsltGenericDebugContext,
771 "xsltApplyTemplates: node: %s\n", node->name);
773 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
776 xsltGenericDebug(xsltGenericDebugContext,
777 "xsltApplyTemplates: select %s\n", prop);
780 if (ctxt->xpathCtxt == NULL) {
782 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
783 if (ctxt->xpathCtxt == NULL)
785 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
788 xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
789 if (xpathParserCtxt == NULL)
791 ctxt->xpathCtxt->node = node;
792 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
793 xmlXPathEvalExpr(xpathParserCtxt);
794 res = valuePop(xpathParserCtxt);
796 tmp = valuePop(xpathParserCtxt);
798 xmlXPathFreeObject(tmp);
800 } while (tmp != NULL);
802 if (res->type == XPATH_NODESET) {
803 list = res->nodesetval;
804 res->nodesetval = NULL;
807 xsltGenericDebug(xsltGenericDebugContext,
808 "xsltApplyTemplates: select didn't evaluate to a node list\n");
815 * Build an XPath nodelist with the children
817 list = xmlXPathNodeSetCreate(NULL);
818 cur = node->children;
819 while (cur != NULL) {
822 /* TODO: check the whitespace stripping rules ! */
823 if ((IS_BLANK_NODE(cur)) &&
824 (cur->parent != NULL) &&
825 (ctxt->style->stripSpaces != NULL)) {
828 val = (const xmlChar *)
829 xmlHashLookup(ctxt->style->stripSpaces,
832 (xmlStrEqual(val, (xmlChar *) "strip"))) {
837 /* no break on purpose */
838 case XML_DOCUMENT_NODE:
839 case XML_HTML_DOCUMENT_NODE:
840 case XML_ELEMENT_NODE:
841 case XML_CDATA_SECTION_NODE:
842 xmlXPathNodeSetAdd(list, cur);
846 xsltGenericDebug(xsltGenericDebugContext,
847 "xsltApplyTemplates: skipping cur type %d\n",
853 if (delete != NULL) {
855 xsltGenericDebug(xsltGenericDebugContext,
856 "xsltApplyTemplates: removing ignorable blank cur\n");
858 xmlUnlinkNode(delete);
866 xsltGenericDebug(xsltGenericDebugContext,
867 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
870 oldlist = ctxt->nodeList;
871 ctxt->nodeList = list;
872 oldContextSize = ctxt->xpathCtxt->contextSize;
873 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
874 ctxt->xpathCtxt->contextSize = list->nodeNr;
877 * handle and skip the xsl:sort
879 replacement = inst->children;
880 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
881 xsltSort(ctxt, node, replacement);
882 replacement = replacement->next;
885 for (i = 0;i < list->nodeNr;i++) {
886 ctxt->node = list->nodeTab[i];
887 ctxt->xpathCtxt->proximityPosition = i + 1;
888 xsltProcessOneNode(ctxt, list->nodeTab[i]);
890 ctxt->nodeList = oldlist;
891 ctxt->xpathCtxt->contextSize = oldContextSize;
892 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
895 if (xpathParserCtxt != NULL)
896 xmlXPathFreeParserContext(xpathParserCtxt);
900 xmlXPathFreeObject(res);
902 xmlXPathFreeNodeSet(list);
906 * xsltApplyOneTemplate:
907 * @ctxt: a XSLT process context
908 * @node: the node in the source tree.
909 * @list: the template replacement nodelist
911 * Process the apply-templates node on the source node
914 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
916 xmlNodePtr cur = NULL, insert, copy = NULL;
917 xmlNodePtr oldInsert;
918 int has_variables = 0;
920 oldInsert = insert = ctxt->insert;
922 * Insert all non-XSLT nodes found in the template
925 while (cur != NULL) {
927 * test, we must have a valid insertion point
929 if (insert == NULL) {
931 xsltGenericDebug(xsltGenericDebugContext,
932 "xsltApplyOneTemplate: insert == NULL !\n");
937 if (IS_XSLT_ELEM(cur)) {
938 if (IS_XSLT_NAME(cur, "apply-templates")) {
939 ctxt->insert = insert;
940 xsltApplyTemplates(ctxt, node, cur);
941 ctxt->insert = oldInsert;
942 } else if (IS_XSLT_NAME(cur, "value-of")) {
943 ctxt->insert = insert;
944 xsltValueOf(ctxt, node, cur);
945 ctxt->insert = oldInsert;
946 } else if (IS_XSLT_NAME(cur, "if")) {
947 ctxt->insert = insert;
948 xsltIf(ctxt, node, cur);
949 ctxt->insert = oldInsert;
950 } else if (IS_XSLT_NAME(cur, "for-each")) {
951 ctxt->insert = insert;
952 xsltForEach(ctxt, node, cur);
953 ctxt->insert = oldInsert;
954 } else if (IS_XSLT_NAME(cur, "attribute")) {
955 ctxt->insert = insert;
956 xsltAttribute(ctxt, node, cur);
957 ctxt->insert = oldInsert;
959 } else if (IS_XSLT_NAME(cur, "element")) {
960 ctxt->insert = insert;
961 xsltElement(ctxt, node, cur);
962 ctxt->insert = oldInsert;
964 } else if (IS_XSLT_NAME(cur, "comment")) {
965 ctxt->insert = insert;
966 xsltComment(ctxt, node, cur);
967 ctxt->insert = oldInsert;
968 } else if (IS_XSLT_NAME(cur, "processing-instruction")) {
969 ctxt->insert = insert;
970 xsltProcessingInstruction(ctxt, node, cur);
971 ctxt->insert = oldInsert;
972 } else if (IS_XSLT_NAME(cur, "variable")) {
973 if (has_variables == 0) {
977 xsltParseStylesheetVariable(ctxt, cur);
978 } else if (IS_XSLT_NAME(cur, "param")) {
979 if (has_variables == 0) {
983 xsltParseStylesheetParam(ctxt, cur);
984 } else if (IS_XSLT_NAME(cur, "call-template")) {
985 if (has_variables == 0) {
989 xsltCallTemplate(ctxt, node, cur);
992 xsltGenericError(xsltGenericDebugContext,
993 "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
998 } else if (cur->type == XML_TEXT_NODE) {
1000 * This text comes from the stylesheet
1001 * For stylesheets, the set of whitespace-preserving
1002 * element names consists of just xsl:text.
1004 #ifdef DEBUG_PROCESS
1005 xsltGenericDebug(xsltGenericDebugContext,
1006 "xsltApplyOneTemplate: copy text %s\n", cur->content);
1008 copy = xmlCopyNode(cur, 0);
1010 xmlAddChild(insert, copy);
1012 xsltGenericError(xsltGenericErrorContext,
1013 "xsltApplyOneTemplate: text copy failed\n");
1015 } else if (cur->type == XML_ELEMENT_NODE) {
1016 #ifdef DEBUG_PROCESS
1017 xsltGenericDebug(xsltGenericDebugContext,
1018 "xsltApplyOneTemplate: copy node %s\n", cur->name);
1020 copy = xsltCopyNode(ctxt, cur, insert);
1022 * all the attributes are directly inherited
1023 * TODO: Do the substitution of {} XPath expressions !!!
1025 if (cur->properties != NULL)
1026 copy->properties = xsltAttrListTemplateProcess(ctxt,
1027 copy, cur->properties);
1031 * Skip to next node, in document order.
1033 if (cur->children != NULL) {
1034 if (cur->children->type != XML_ENTITY_DECL) {
1035 cur = cur->children;
1042 if (cur->next != NULL) {
1049 insert = insert->parent;
1052 if (cur == list->parent) {
1056 if (cur->next != NULL) {
1060 } while (cur != NULL);
1062 if (has_variables != 0) {
1069 * @ctxt: a XSLT process context
1070 * @node: the node in the source tree.
1071 * @inst: the xslt if node
1073 * Process the xslt if node on the source node
1076 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
1079 xmlXPathObjectPtr res = NULL, tmp;
1080 xmlXPathParserContextPtr xpathParserCtxt = NULL;
1083 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1086 prop = xmlGetNsProp(inst, (const xmlChar *)"test", XSLT_NAMESPACE);
1088 xsltGenericError(xsltGenericErrorContext,
1089 "xsltIf: test is not defined\n");
1092 #ifdef DEBUG_PROCESS
1093 xsltGenericDebug(xsltGenericDebugContext,
1094 "xsltIf: test %s\n", prop);
1097 if (ctxt->xpathCtxt == NULL) {
1099 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
1100 if (ctxt->xpathCtxt == NULL)
1102 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
1104 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1105 if (xpathParserCtxt == NULL)
1107 ctxt->xpathCtxt->node = node;
1108 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
1109 xmlXPathEvalExpr(xpathParserCtxt);
1110 xmlXPathBooleanFunction(xpathParserCtxt, 1);
1111 res = valuePop(xpathParserCtxt);
1113 tmp = valuePop(xpathParserCtxt);
1115 xmlXPathFreeObject(tmp);
1117 } while (tmp != NULL);
1120 if (res->type == XPATH_BOOLEAN)
1121 doit = res->boolval;
1123 #ifdef DEBUG_PROCESS
1124 xsltGenericDebug(xsltGenericDebugContext,
1125 "xsltIf: test didn't evaluate to a boolean\n");
1131 #ifdef DEBUG_PROCESS
1132 xsltGenericDebug(xsltGenericDebugContext,
1133 "xsltIf: test evaluate to %d\n", doit);
1136 xsltApplyOneTemplate(ctxt, ctxt->node, inst->children);
1140 if (xpathParserCtxt != NULL)
1141 xmlXPathFreeParserContext(xpathParserCtxt);
1145 xmlXPathFreeObject(res);
1150 * @ctxt: a XSLT process context
1151 * @node: the node in the source tree.
1152 * @inst: the xslt for-each node
1154 * Process the xslt for-each node on the source node
1157 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
1160 xmlXPathObjectPtr res = NULL, tmp;
1161 xmlNodePtr replacement;
1162 xmlNodeSetPtr list = NULL, oldlist;
1163 xmlXPathParserContextPtr xpathParserCtxt = NULL;
1164 int i, oldProximityPosition, oldContextSize;
1166 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1169 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
1171 xsltGenericError(xsltGenericErrorContext,
1172 "xsltForEach: select is not defined\n");
1175 #ifdef DEBUG_PROCESS
1176 xsltGenericDebug(xsltGenericDebugContext,
1177 "xsltForEach: select %s\n", prop);
1180 if (ctxt->xpathCtxt == NULL) {
1182 ctxt->xpathCtxt = xmlXPathNewContext(ctxt->doc);
1183 if (ctxt->xpathCtxt == NULL)
1185 XSLT_REGISTER_VARIABLE_LOOKUP(ctxt);
1187 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1188 if (xpathParserCtxt == NULL)
1190 ctxt->xpathCtxt->node = node;
1191 valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node));
1192 xmlXPathEvalExpr(xpathParserCtxt);
1193 res = valuePop(xpathParserCtxt);
1195 tmp = valuePop(xpathParserCtxt);
1197 xmlXPathFreeObject(tmp);
1199 } while (tmp != NULL);
1202 if (res->type == XPATH_NODESET)
1203 list = res->nodesetval;
1205 #ifdef DEBUG_PROCESS
1206 xsltGenericDebug(xsltGenericDebugContext,
1207 "xsltForEach: select didn't evaluate to a node list\n");
1213 #ifdef DEBUG_PROCESS
1214 xsltGenericDebug(xsltGenericDebugContext,
1215 "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
1218 oldlist = ctxt->nodeList;
1219 ctxt->nodeList = list;
1220 oldContextSize = ctxt->xpathCtxt->contextSize;
1221 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1222 ctxt->xpathCtxt->contextSize = list->nodeNr;
1225 * handle and skip the xsl:sort
1227 replacement = inst->children;
1228 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
1229 xsltSort(ctxt, node, replacement);
1230 replacement = replacement->next;
1233 for (i = 0;i < list->nodeNr;i++) {
1234 ctxt->node = list->nodeTab[i];
1235 ctxt->xpathCtxt->proximityPosition = i + 1;
1236 xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement);
1238 ctxt->nodeList = oldlist;
1239 ctxt->xpathCtxt->contextSize = oldContextSize;
1240 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1243 if (xpathParserCtxt != NULL)
1244 xmlXPathFreeParserContext(xpathParserCtxt);
1248 xmlXPathFreeObject(res);
1252 * xsltProcessOneNode:
1253 * @ctxt: a XSLT process context
1254 * @node: the node in the source tree.
1256 * Process the source node.
1259 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1260 xsltTemplatePtr template;
1262 template = xsltGetTemplate(ctxt->style, node);
1264 * If no template is found, apply the default rule.
1266 if (template == NULL) {
1267 #ifdef DEBUG_PROCESS
1268 if (node->type == XML_DOCUMENT_NODE)
1269 xsltGenericDebug(xsltGenericDebugContext,
1270 "xsltProcessOneNode: no template found for /\n");
1272 xsltGenericDebug(xsltGenericDebugContext,
1273 "xsltProcessOneNode: no template found for %s\n", node->name);
1275 xsltDefaultProcessOneNode(ctxt, node);
1279 xsltApplyOneTemplate(ctxt, node, template->content);
1283 * xsltApplyStylesheet:
1284 * @style: a parsed XSLT stylesheet
1285 * @doc: a parsed XML document
1287 * Apply the stylesheet to the document
1288 * NOTE: This may lead to a non-wellformed output XML wise !
1290 * Returns the result document or NULL in case of error
1293 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
1294 xmlDocPtr res = NULL;
1295 xsltTransformContextPtr ctxt = NULL;
1298 if ((style == NULL) || (doc == NULL))
1300 ctxt = xsltNewTransformContext();
1304 ctxt->style = style;
1305 xsltEvalGlobalVariables(ctxt);
1306 if ((style->method != NULL) &&
1307 (!xmlStrEqual(style->method, (const xmlChar *) "xml"))) {
1308 if (xmlStrEqual(style->method, (const xmlChar *) "html")) {
1309 ctxt->type = XSLT_OUTPUT_HTML;
1310 res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
1313 } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
1314 ctxt->type = XSLT_OUTPUT_TEXT;
1315 res = xmlNewDoc(style->version);
1319 xsltGenericError(xsltGenericErrorContext,
1320 "xsltApplyStylesheet: insupported method %s\n",
1325 ctxt->type = XSLT_OUTPUT_XML;
1326 res = xmlNewDoc(style->version);
1330 res->charset = XML_CHAR_ENCODING_UTF8;
1331 if (style->encoding != NULL)
1332 res->encoding = xmlStrdup(style->encoding);
1338 ctxt->insert = (xmlNodePtr) res;
1339 ctxt->node = (xmlNodePtr) doc;
1340 xsltProcessOneNode(ctxt, ctxt->node);
1343 if ((ctxt->type = XSLT_OUTPUT_XML) &&
1344 ((style->doctypePublic != NULL) ||
1345 (style->doctypeSystem != NULL))) {
1346 root = xmlDocGetRootElement(res);
1348 res->intSubset = xmlCreateIntSubset(res, root->name,
1349 style->doctypePublic, style->doctypeSystem);
1351 xmlXPathFreeNodeSet(ctxt->nodeList);
1352 xsltFreeTransformContext(ctxt);
1359 xsltFreeTransformContext(ctxt);