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/parserInternals.h>
26 #include <libxml/xpathInternals.h>
27 #include <libxml/HTMLtree.h>
29 #include "xsltInternals.h"
30 #include "xsltutils.h"
32 #include "transform.h"
33 #include "variables.h"
34 #include "numbersInternals.h"
35 #include "namespaces.h"
36 #include "attributes.h"
37 #include "templates.h"
40 #include "documents.h"
44 int xsltMaxDepth = 250;
51 # define FALSE (0 == 1)
52 # define TRUE (!FALSE)
55 #define IS_BLANK_NODE(n) \
56 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
59 * Generic function for accessing stacks in the transform Context
62 #define PUSH_AND_POP(scope, type, name) \
63 scope int name##Push(xsltTransformContextPtr ctxt, type value) { \
64 if (ctxt->name##Nr >= ctxt->name##Max) { \
65 ctxt->name##Max *= 2; \
66 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
67 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
68 if (ctxt->name##Tab == NULL) { \
69 xmlGenericError(xmlGenericErrorContext, \
70 "realloc failed !\n"); \
74 ctxt->name##Tab[ctxt->name##Nr] = value; \
76 return(ctxt->name##Nr++); \
78 scope type name##Pop(xsltTransformContextPtr ctxt) { \
80 if (ctxt->name##Nr <= 0) return(0); \
82 if (ctxt->name##Nr > 0) \
83 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
86 ret = ctxt->name##Tab[ctxt->name##Nr]; \
87 ctxt->name##Tab[ctxt->name##Nr] = 0; \
92 * Those macros actually generate the functions
94 PUSH_AND_POP(extern, xsltTemplatePtr, templ)
95 PUSH_AND_POP(extern, xsltStackElemPtr, vars)
97 /************************************************************************
99 * handling of transformation contexts *
101 ************************************************************************/
104 * xsltNewTransformContext:
105 * @style: a parsed XSLT stylesheet
106 * @doc: the input document
108 * Create a new XSLT TransformContext
110 * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
112 xsltTransformContextPtr
113 xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
114 xsltTransformContextPtr cur;
115 xsltDocumentPtr docu;
117 cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
119 xsltGenericError(xsltGenericErrorContext,
120 "xsltNewTransformContext : malloc failed\n");
123 memset(cur, 0, sizeof(xsltTransformContext));
126 * initialize the template stack
128 cur->templTab = (xsltTemplatePtr *)
129 xmlMalloc(10 * sizeof(xsltTemplatePtr));
130 if (cur->templTab == NULL) {
131 xmlGenericError(xmlGenericErrorContext,
132 "xsltNewTransformContext: out of memory\n");
141 * initialize the variables stack
143 cur->varsTab = (xsltStackElemPtr *)
144 xmlMalloc(10 * sizeof(xsltStackElemPtr));
145 if (cur->varsTab == NULL) {
146 xmlGenericError(xmlGenericErrorContext,
147 "xsltNewTransformContext: out of memory\n");
148 xmlFree(cur->templTab);
158 cur->xpathCtxt = xmlXPathNewContext(doc);
159 if (cur->xpathCtxt == NULL) {
160 xsltGenericError(xsltGenericErrorContext,
161 "xsltNewTransformContext : xmlXPathNewContext failed\n");
162 xmlFree(cur->templTab);
163 xmlFree(cur->varsTab);
167 XSLT_REGISTER_VARIABLE_LOOKUP(cur);
168 cur->xpathCtxt->nsHash = style->nsHash;
169 docu = xsltNewDocument(cur, doc);
171 xsltGenericError(xsltGenericErrorContext,
172 "xsltNewTransformContext : xsltNewDocument failed\n");
173 xmlFree(cur->templTab);
174 xmlFree(cur->varsTab);
179 cur->document = docu;
184 * xsltFreeTransformContext:
185 * @ctxt: an XSLT parser context
187 * Free up the memory allocated by @ctxt
190 xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
193 if (ctxt->xpathCtxt != NULL) {
194 ctxt->xpathCtxt->nsHash = NULL;
195 xmlXPathFreeContext(ctxt->xpathCtxt);
197 if (ctxt->templTab != NULL)
198 xmlFree(ctxt->templTab);
199 if (ctxt->varsTab != NULL)
200 xmlFree(ctxt->varsTab);
201 xsltFreeDocuments(ctxt);
202 memset(ctxt, -1, sizeof(xsltTransformContext));
206 /************************************************************************
208 * Copy of Nodes in an XSLT fashion *
210 ************************************************************************/
212 xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
217 * @ctxt: a XSLT process context
218 * @node: the element node in the source tree.
219 * @insert: the parent in the result tree.
221 * Make a copy of the element node @node
222 * and insert it as last child of @insert
224 * Returns a pointer to the new node, or NULL in case of error
227 xsltCopyNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
231 copy = xmlCopyNode(node, 0);
232 copy->doc = ctxt->output;
234 xmlAddChild(insert, copy);
235 if (node->type == XML_ELEMENT_NODE) {
237 * Add namespaces as they are needed
239 if (node->nsDef != NULL)
240 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
242 if (node->ns != NULL) {
243 copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
246 xsltGenericError(xsltGenericErrorContext,
247 "xsltCopyNode: copy %s failed\n", node->name);
254 * @ctxt: a XSLT process context
255 * @list: the list of element node in the source tree.
256 * @insert: the parent in the result tree.
258 * Make a copy of the full list of tree @list
259 * and insert them as last children of @insert
261 * Returns a pointer to the new list, or NULL in case of error
264 xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr list,
266 xmlNodePtr copy, ret = NULL;
268 while (list != NULL) {
269 copy = xsltCopyTree(ctxt, list, insert);
279 * @ctxt: a XSLT process context
280 * @node: the element node in the source tree.
281 * @insert: the parent in the result tree.
283 * Make a copy of the full tree under the element node @node
284 * and insert it as last child of @insert
286 * Returns a pointer to the new tree, or NULL in case of error
289 xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
293 copy = xmlCopyNode(node, 0);
294 copy->doc = ctxt->output;
296 xmlAddChild(insert, copy);
298 * Add namespaces as they are needed
300 if (node->nsDef != NULL)
301 xsltCopyNamespaceList(ctxt, copy, node->nsDef);
302 if (node->ns != NULL) {
303 copy->ns = xsltGetNamespace(ctxt, node, node->ns, insert);
305 if (node->children != NULL)
306 copy->children = xsltCopyTreeList(ctxt, node->children, copy);
308 xsltGenericError(xsltGenericErrorContext,
309 "xsltCopyTree: copy %s failed\n", node->name);
313 /************************************************************************
317 ************************************************************************/
319 void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node);
320 void xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
322 void xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
323 void xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr inst);
327 * @ctxt: a XSLT process context
328 * @node: the node in the source tree.
329 * @inst: the xslt sort node
331 * Process the xslt sort node on the source node
334 xsltSort(xsltTransformContextPtr ctxt, xmlNodePtr node,
336 xmlXPathObjectPtr *results = NULL;
337 xmlNodeSetPtr list = NULL;
338 xmlXPathParserContextPtr xpathParserCtxt = NULL;
339 xmlChar *prop = NULL;
340 xmlXPathObjectPtr res, tmp;
341 const xmlChar *start;
348 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
351 list = ctxt->nodeList;
352 if ((list == NULL) || (list->nodeNr <= 1))
353 goto error; /* nothing to do */
357 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"data-type");
359 if (xmlStrEqual(prop, (const xmlChar *) "text"))
361 else if (xmlStrEqual(prop, (const xmlChar *) "number"))
364 xsltGenericError(xsltGenericErrorContext,
365 "xsltSort: no support for data-type = %s\n", prop);
370 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"order");
372 if (xmlStrEqual(prop, (const xmlChar *) "ascending"))
374 else if (xmlStrEqual(prop, (const xmlChar *) "descending"))
377 xsltGenericError(xsltGenericErrorContext,
378 "xsltSort: invalid value %s for order\n", prop);
383 /* TODO: xsl:sort lang attribute */
384 /* TODO: xsl:sort case-order attribute */
386 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
388 prop = xmlNodeGetContent(inst);
390 xsltGenericError(xsltGenericErrorContext,
391 "xsltSort: select is not defined\n");
396 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
397 if (xpathParserCtxt == NULL)
399 results = xmlMalloc(len * sizeof(xmlXPathObjectPtr));
400 if (results == NULL) {
401 xsltGenericError(xsltGenericErrorContext,
402 "xsltSort: memory allocation failure\n");
406 start = xpathParserCtxt->cur;
407 oldNode = ctxt->node;
408 for (i = 0;i < len;i++) {
409 xpathParserCtxt->cur = start;
410 ctxt->xpathCtxt->contextSize = len;
411 ctxt->xpathCtxt->proximityPosition = i + 1;
412 ctxt->node = list->nodeTab[i];
413 ctxt->xpathCtxt->node = ctxt->node;
414 xmlXPathEvalExpr(xpathParserCtxt);
415 xmlXPathStringFunction(xpathParserCtxt, 1);
417 xmlXPathNumberFunction(xpathParserCtxt, 1);
418 res = valuePop(xpathParserCtxt);
420 tmp = valuePop(xpathParserCtxt);
422 xmlXPathFreeObject(tmp);
424 } while (tmp != NULL);
428 if (res->type == XPATH_NUMBER) {
432 xsltGenericDebug(xsltGenericDebugContext,
433 "xsltSort: select didn't evaluate to a number\n");
438 if (res->type == XPATH_STRING) {
442 xsltGenericDebug(xsltGenericDebugContext,
443 "xsltSort: select didn't evaluate to a string\n");
450 ctxt->node = oldNode;
452 xsltSortFunction(list, &results[0], descending, number);
455 if (xpathParserCtxt != NULL)
456 xmlXPathFreeParserContext(xpathParserCtxt);
459 if (results != NULL) {
460 for (i = 0;i < len;i++)
461 xmlXPathFreeObject(results[i]);
468 * @ctxt: a XSLT process context
469 * @node: the node in the source tree.
470 * @inst: the xslt copy node
472 * Process the xslt copy node on the source node
475 xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
478 xmlNodePtr copy, oldInsert;
480 oldInsert = ctxt->insert;
481 if (ctxt->insert != NULL) {
482 switch (node->type) {
483 case XML_DOCUMENT_NODE:
484 case XML_HTML_DOCUMENT_NODE:
486 case XML_ELEMENT_NODE:
488 xsltGenericDebug(xsltGenericDebugContext,
489 "xsl:copy: node %s\n", node->name);
491 copy = xsltCopyNode(ctxt, node, ctxt->insert);
493 prop = xmlGetNsProp(inst, (const xmlChar *)"use-attribute-sets",
496 xsltApplyAttributeSet(ctxt, node, inst, prop);
500 case XML_ATTRIBUTE_NODE: {
502 xsltGenericDebug(xsltGenericDebugContext,
503 "xsl:copy: attribute %s\n", node->name);
505 if (ctxt->insert->type == XML_ELEMENT_NODE) {
506 xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
507 if (attr->ns != NULL) {
508 if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
509 (xmlStrncasecmp(attr->ns->prefix,
510 (xmlChar *)"xml", 3))) {
511 ret = xmlCopyProp(ctxt->insert, attr);
512 ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
516 ret = xmlCopyProp(ctxt->insert, attr);
518 cur = ctxt->insert->properties;
520 while (cur->next != NULL)
525 ctxt->insert->properties = ret;
535 switch (node->type) {
536 case XML_DOCUMENT_NODE:
537 case XML_HTML_DOCUMENT_NODE:
538 case XML_ELEMENT_NODE:
539 varsPush(ctxt, NULL);
540 xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
541 xsltFreeStackElemList(varsPop(ctxt));
546 ctxt->insert = oldInsert;
551 * @ctxt: a XSLT process context
552 * @node: the node in the source tree.
553 * @inst: the xslt text node
555 * Process the xslt text node on the source node
558 xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node,
562 if (inst->children != NULL) {
563 if ((inst->children->type != XML_TEXT_NODE) ||
564 (inst->children->next != NULL)) {
565 xsltGenericError(xsltGenericErrorContext,
566 "xslt:text has content problem !\n");
569 xmlNodePtr text = inst->children;
571 copy = xmlNewDocText(ctxt->output, text->content);
572 prop = xmlGetNsProp(inst,
573 (const xmlChar *)"disable-output-escaping",
577 xsltGenericDebug(xsltGenericDebugContext,
578 "Disable escaping: %s\n", text->content);
580 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
581 copy->name = xmlStringTextNoenc;
582 } else if (!xmlStrEqual(prop,
583 (const xmlChar *)"no")){
584 xsltGenericError(xsltGenericErrorContext,
585 "xslt:text: disable-output-escaping allow only yes or no\n");
590 xmlAddChild(ctxt->insert, copy);
597 * @ctxt: a XSLT process context
598 * @node: the node in the source tree.
599 * @inst: the xslt element node
601 * Process the xslt element node on the source node
604 xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
606 xmlChar *prop = NULL, *attributes = NULL;
607 xmlChar *ncname = NULL;
608 xmlChar *prefix = NULL;
609 xmlChar *value = NULL;
612 xmlNodePtr oldInsert;
615 if (ctxt->insert == NULL)
620 oldInsert = ctxt->insert;
622 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
624 xsltGenericError(xsltGenericErrorContext,
625 "xslt:element : name is missing\n");
629 ncname = xmlSplitQName2(prop, &prefix);
630 if (ncname == NULL) {
635 prop = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"namespace");
637 ns = xsltGetSpecialNamespace(ctxt, inst, prop, prefix, ctxt->insert);
639 if (prefix != NULL) {
640 if (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)) {
642 xsltGenericDebug(xsltGenericDebugContext,
643 "xslt:element : xml prefix forbidden\n");
647 ns = xmlSearchNs(inst->doc, inst, prefix);
649 xsltGenericError(xsltGenericErrorContext,
650 "no namespace bound to prefix %s\n", prefix);
652 ns = xsltGetNamespace(ctxt, inst, ns, ctxt->insert);
657 copy = xmlNewDocNode(ctxt->output, ns, ncname, NULL);
659 xsltGenericError(xsltGenericErrorContext,
660 "xsl:element : creation of %s failed\n", ncname);
663 xmlAddChild(ctxt->insert, copy);
665 attributes = xsltEvalAttrValueTemplate(ctxt, inst,
666 (const xmlChar *)"use-attribute-sets");
667 if (attributes != NULL) {
668 xsltApplyAttributeSet(ctxt, node, inst, attributes);
672 varsPush(ctxt, NULL);
673 xsltApplyOneTemplate(ctxt, ctxt->node, inst->children, 0);
674 xsltFreeStackElemList(varsPop(ctxt));
676 ctxt->insert = oldInsert;
691 * @ctxt: a XSLT process context
692 * @node: the node in the source tree.
693 * @inst: the xslt comment node
695 * Process the xslt comment node on the source node
698 xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
700 xmlChar *value = NULL;
703 value = xsltEvalTemplateString(ctxt, node, inst);
704 /* TODO: check that there is no -- sequence and doesn't end up with - */
707 xsltGenericDebug(xsltGenericDebugContext,
708 "xsl:comment: empty\n");
710 xsltGenericDebug(xsltGenericDebugContext,
711 "xsl:comment: content %s\n", value);
714 comment = xmlNewComment(value);
715 xmlAddChild(ctxt->insert, comment);
722 * xsltProcessingInstruction:
723 * @ctxt: a XSLT process context
724 * @node: the node in the source tree.
725 * @inst: the xslt processing-instruction node
727 * Process the xslt processing-instruction node on the source node
730 xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
732 xmlChar *ncname = NULL;
733 xmlChar *value = NULL;
737 if (ctxt->insert == NULL)
739 ncname = xsltEvalAttrValueTemplate(ctxt, inst, (const xmlChar *)"name");
740 if (ncname == NULL) {
741 xsltGenericError(xsltGenericErrorContext,
742 "xslt:processing-instruction : name is missing\n");
745 /* TODO: check that it's both an an NCName and a PITarget. */
748 value = xsltEvalTemplateString(ctxt, node, inst);
749 /* TODO: check that there is no ?> sequence */
752 xsltGenericDebug(xsltGenericDebugContext,
753 "xsl:processing-instruction: %s empty\n", ncname);
755 xsltGenericDebug(xsltGenericDebugContext,
756 "xsl:processing-instruction: %s content %s\n", ncname, value);
759 pi = xmlNewPI(ncname, value);
760 xmlAddChild(ctxt->insert, pi);
771 * @ctxt: a XSLT process context
772 * @node: the node in the source tree.
773 * @inst: the xslt copy-of node
775 * Process the xslt copy-of node on the source node
778 xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
780 xmlChar *prop = NULL;
781 xmlXPathObjectPtr res = NULL, tmp;
782 xmlXPathParserContextPtr xpathParserCtxt = NULL;
783 xmlNodePtr copy = NULL;
784 xmlNodeSetPtr list = NULL;
787 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
790 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
792 xsltGenericError(xsltGenericErrorContext,
793 "xslt:copy-of : select is missing\n");
797 xsltGenericDebug(xsltGenericDebugContext,
798 "xsltCopyOf: select %s\n", prop);
802 xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
803 if (xpathParserCtxt == NULL)
805 ctxt->xpathCtxt->node = node;
806 xmlXPathEvalExpr(xpathParserCtxt);
807 res = valuePop(xpathParserCtxt);
809 tmp = valuePop(xpathParserCtxt);
811 xmlXPathFreeObject(tmp);
813 } while (tmp != NULL);
815 if ((res->type == XPATH_NODESET) || (res->type == XPATH_XSLT_TREE)) {
816 list = res->nodesetval;
818 /* sort the list in document order */
819 xsltDocumentSortFunction(list);
820 /* append everything in this order under ctxt->insert */
821 for (i = 0;i < list->nodeNr;i++) {
822 if (list->nodeTab[i] == NULL)
824 if ((list->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
825 (list->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) {
826 xsltCopyTreeList(ctxt, list->nodeTab[i]->children,
829 xsltCopyTree(ctxt, list->nodeTab[i], ctxt->insert);
834 /* convert to a string */
835 valuePush(xpathParserCtxt, res);
836 xmlXPathStringFunction(xpathParserCtxt, 1);
837 res = valuePop(xpathParserCtxt);
838 if ((res != NULL) && (res->type == XPATH_STRING)) {
839 /* append content as text node */
840 copy = xmlNewText(res->stringval);
842 xmlAddChild(ctxt->insert, copy);
846 tmp = valuePop(xpathParserCtxt);
848 xmlXPathFreeObject(tmp);
850 } while (tmp != NULL);
852 xsltGenericError(xsltGenericErrorContext,
853 "xsltDefaultProcessOneNode: text copy failed\n");
857 xsltGenericDebug(xsltGenericDebugContext,
858 "xslcopyOf: result %s\n", res->stringval);
864 if (xpathParserCtxt != NULL) {
865 xmlXPathFreeParserContext(xpathParserCtxt);
866 xpathParserCtxt = NULL;
871 xmlXPathFreeObject(res);
876 * @ctxt: a XSLT process context
877 * @node: the node in the source tree.
878 * @inst: the xslt value-of node
880 * Process the xslt value-of node on the source node
883 xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
886 int disableEscaping = 0;
887 xmlXPathObjectPtr res = NULL, tmp;
888 xmlXPathParserContextPtr xpathParserCtxt = NULL;
889 xmlNodePtr copy = NULL;
891 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
894 prop = xmlGetNsProp(inst, (const xmlChar *)"disable-output-escaping",
897 if (xmlStrEqual(prop, (const xmlChar *)"yes"))
899 else if (xmlStrEqual(prop, (const xmlChar *)"no"))
902 xsltGenericError(xsltGenericErrorContext,
903 "invalud value %s for disable-output-escaping\n", prop);
907 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
909 xsltGenericError(xsltGenericErrorContext,
910 "xsltValueOf: select is not defined\n");
914 xsltGenericDebug(xsltGenericDebugContext,
915 "xsltValueOf: select %s\n", prop);
919 xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
920 if (xpathParserCtxt == NULL)
922 ctxt->xpathCtxt->node = node;
923 xmlXPathEvalExpr(xpathParserCtxt);
924 xmlXPathStringFunction(xpathParserCtxt, 1);
925 res = valuePop(xpathParserCtxt);
927 tmp = valuePop(xpathParserCtxt);
929 xmlXPathFreeObject(tmp);
931 } while (tmp != NULL);
933 if (res->type == XPATH_STRING) {
934 copy = xmlNewText(res->stringval);
937 copy->name = xmlStringTextNoenc;
938 xmlAddChild(ctxt->insert, copy);
943 xsltGenericError(xsltGenericErrorContext,
944 "xsltDefaultProcessOneNode: text copy failed\n");
948 xsltGenericDebug(xsltGenericDebugContext,
949 "xsltValueOf: result %s\n", res->stringval);
952 if (xpathParserCtxt != NULL) {
953 xmlXPathFreeParserContext(xpathParserCtxt);
954 xpathParserCtxt = NULL;
959 xmlXPathFreeObject(res);
964 * @ctxt: a XSLT process context
965 * @node: the node in the source tree.
966 * @cur: the xslt number node
968 * Process the xslt number node on the source node
971 xsltNumber(xsltTransformContextPtr ctxt,
976 xsltNumberData numdata;
978 if ((ctxt == NULL) || (cur == NULL))
981 memset(&numdata, 0, sizeof(numdata));
983 numdata.doc = cur->doc;
985 numdata.value = xmlGetNsProp(cur, (const xmlChar *)"value", XSLT_NAMESPACE);
987 prop = xmlGetNsProp(cur, (const xmlChar *)"format", XSLT_NAMESPACE);
989 numdata.format = prop;
991 numdata.format = xmlStrdup(BAD_CAST("1"));
994 numdata.count = xmlGetNsProp(cur, (const xmlChar *)"count", XSLT_NAMESPACE);
995 numdata.from = xmlGetNsProp(cur, (const xmlChar *)"from", XSLT_NAMESPACE);
997 prop = xmlGetNsProp(cur, (const xmlChar *)"level", XSLT_NAMESPACE);
999 if (xmlStrEqual(prop, BAD_CAST("single")) ||
1000 xmlStrEqual(prop, BAD_CAST("multiple")) ||
1001 xmlStrEqual(prop, BAD_CAST("any"))) {
1002 numdata.level = prop;
1004 xsltGenericError(xsltGenericErrorContext,
1005 "invalid value %s for level\n", prop);
1010 prop = xmlGetNsProp(cur, (const xmlChar *)"lang", XSLT_NAMESPACE);
1016 prop = xmlGetNsProp(cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE);
1018 if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) {
1020 } else if (xmlStrEqual(prop, BAD_CAST("traditional"))) {
1023 xsltGenericError(xsltGenericErrorContext,
1024 "invalid value %s for letter-value\n", prop);
1029 prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", XSLT_NAMESPACE);
1031 numdata.groupingCharacter = prop[0];
1035 prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE);
1037 sscanf((char *)prop, "%d", &numdata.digitsPerGroup);
1040 numdata.groupingCharacter = 0;
1043 /* Set default values */
1044 if (numdata.value == NULL) {
1045 if (numdata.level == NULL) {
1046 numdata.level = xmlStrdup(BAD_CAST("single"));
1050 xsltNumberFormat(ctxt, &numdata, node);
1052 if (numdata.level != NULL)
1053 xmlFree(numdata.level);
1054 if (numdata.count != NULL)
1055 xmlFree(numdata.count);
1056 if (numdata.from != NULL)
1057 xmlFree(numdata.from);
1058 if (numdata.value != NULL)
1059 xmlFree(numdata.value);
1060 if (numdata.format != NULL)
1061 xmlFree(numdata.format);
1065 * xsltDefaultProcessOneNode:
1066 * @ctxt: a XSLT process context
1067 * @node: the node in the source tree.
1069 * Process the source node with the default built-in template rule:
1070 * <xsl:template match="*|/">
1071 * <xsl:apply-templates/>
1076 * <xsl:template match="text()|@*">
1077 * <xsl:value-of select="."/>
1080 * Note also that namespaces declarations are copied directly:
1082 * the built-in template rule is the only template rule that is applied
1083 * for namespace nodes.
1086 xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1089 xmlNodePtr delete = NULL, cur;
1090 int strip_spaces = -1;
1091 int nbchild = 0, oldSize;
1092 int childno = 0, oldPos;
1093 xsltTemplatePtr template;
1097 * Handling of leaves
1099 switch (node->type) {
1100 case XML_DOCUMENT_NODE:
1101 case XML_HTML_DOCUMENT_NODE:
1102 case XML_ELEMENT_NODE:
1104 case XML_CDATA_SECTION_NODE:
1105 template = xsltGetTemplate(ctxt, node, NULL);
1109 #ifdef DEBUG_PROCESS
1110 xsltGenericDebug(xsltGenericDebugContext,
1111 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
1114 oldNode = ctxt->node;
1116 templPush(ctxt, template);
1117 varsPush(ctxt, NULL);
1118 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1119 xsltFreeStackElemList(varsPop(ctxt));
1121 ctxt->node = oldNode;
1122 } else /* if (ctxt->mode == NULL) */ {
1123 #ifdef DEBUG_PROCESS
1124 xsltGenericDebug(xsltGenericDebugContext,
1125 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1128 copy = xmlNewDocText(ctxt->output, node->content);
1130 xmlAddChild(ctxt->insert, copy);
1132 xsltGenericError(xsltGenericErrorContext,
1133 "xsltDefaultProcessOneNode: cdata copy failed\n");
1138 template = xsltGetTemplate(ctxt, node, NULL);
1142 #ifdef DEBUG_PROCESS
1143 xsltGenericDebug(xsltGenericDebugContext,
1144 "xsltDefaultProcessOneNode: applying template for text %s\n",
1147 oldNode = ctxt->node;
1149 templPush(ctxt, template);
1150 varsPush(ctxt, NULL);
1151 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1152 xsltFreeStackElemList(varsPop(ctxt));
1154 ctxt->node = oldNode;
1155 } else /* if (ctxt->mode == NULL) */ {
1156 #ifdef DEBUG_PROCESS
1157 if (node->content == NULL)
1158 xsltGenericDebug(xsltGenericDebugContext,
1159 "xsltDefaultProcessOneNode: copy empty text\n");
1161 xsltGenericDebug(xsltGenericDebugContext,
1162 "xsltDefaultProcessOneNode: copy text %s\n",
1165 copy = xmlCopyNode(node, 0);
1167 xmlAddChild(ctxt->insert, copy);
1169 xsltGenericError(xsltGenericErrorContext,
1170 "xsltDefaultProcessOneNode: text copy failed\n");
1174 case XML_ATTRIBUTE_NODE:
1175 if (ctxt->insert->type == XML_ELEMENT_NODE) {
1176 xmlAttrPtr attr = (xmlAttrPtr) node, ret = NULL, cur;
1177 template = xsltGetTemplate(ctxt, node, NULL);
1181 oldNode = ctxt->node;
1183 templPush(ctxt, template);
1184 varsPush(ctxt, NULL);
1185 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1186 xsltFreeStackElemList(varsPop(ctxt));
1188 ctxt->node = oldNode;
1189 } else if (ctxt->mode == NULL) {
1190 if (attr->ns != NULL) {
1191 if ((!xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) &&
1192 (xmlStrncasecmp(attr->ns->prefix,
1193 (xmlChar *)"xml", 3))) {
1194 ret = xmlCopyProp(ctxt->insert, attr);
1195 ret->ns = xsltGetNamespace(ctxt, node, attr->ns,
1199 ret = xmlCopyProp(ctxt->insert, attr);
1201 cur = ctxt->insert->properties;
1203 while (cur->next != NULL)
1208 ctxt->insert->properties = ret;
1216 * Handling of Elements: first pass, cleanup and counting
1218 cur = node->children;
1219 while (cur != NULL) {
1220 switch (cur->type) {
1222 if ((IS_BLANK_NODE(cur)) &&
1223 (cur->parent != NULL) &&
1224 (ctxt->style->stripSpaces != NULL)) {
1225 if (strip_spaces == -1)
1227 xsltFindElemSpaceHandling(ctxt, cur->parent);
1228 if (strip_spaces == 1) {
1233 /* no break on purpose */
1234 case XML_CDATA_SECTION_NODE:
1235 case XML_DOCUMENT_NODE:
1236 case XML_HTML_DOCUMENT_NODE:
1237 case XML_ELEMENT_NODE:
1241 case XML_COMMENT_NODE:
1245 #ifdef DEBUG_PROCESS
1246 xsltGenericDebug(xsltGenericDebugContext,
1247 "xsltDefaultProcessOneNode: skipping node type %d\n",
1253 if (delete != NULL) {
1254 #ifdef DEBUG_PROCESS
1255 xsltGenericDebug(xsltGenericDebugContext,
1256 "xsltDefaultProcessOneNode: removing ignorable blank node\n");
1258 xmlUnlinkNode(delete);
1259 xmlFreeNode(delete);
1264 * Handling of Elements: second pass, actual processing
1266 attrs = node->properties;
1267 while (attrs != NULL) {
1268 template = xsltGetTemplate(ctxt, (xmlNodePtr) attrs, NULL);
1272 oldNode = ctxt->node;
1274 templPush(ctxt, template);
1275 varsPush(ctxt, NULL);
1276 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1277 xsltFreeStackElemList(varsPop(ctxt));
1279 ctxt->node = oldNode;
1281 attrs = attrs->next;
1283 oldSize = ctxt->xpathCtxt->contextSize;
1284 oldPos = ctxt->xpathCtxt->proximityPosition;
1285 cur = node->children;
1286 while (cur != NULL) {
1288 switch (cur->type) {
1289 case XML_DOCUMENT_NODE:
1290 case XML_HTML_DOCUMENT_NODE:
1291 case XML_ELEMENT_NODE:
1292 ctxt->xpathCtxt->contextSize = nbchild;
1293 ctxt->xpathCtxt->proximityPosition = childno;
1294 xsltProcessOneNode(ctxt, cur);
1296 case XML_CDATA_SECTION_NODE:
1297 template = xsltGetTemplate(ctxt, node, NULL);
1301 #ifdef DEBUG_PROCESS
1302 xsltGenericDebug(xsltGenericDebugContext,
1303 "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
1306 oldNode = ctxt->node;
1308 templPush(ctxt, template);
1309 varsPush(ctxt, NULL);
1310 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1311 xsltFreeStackElemList(varsPop(ctxt));
1313 ctxt->node = oldNode;
1314 } else /* if (ctxt->mode == NULL) */ {
1315 #ifdef DEBUG_PROCESS
1316 xsltGenericDebug(xsltGenericDebugContext,
1317 "xsltDefaultProcessOneNode: copy CDATA %s\n",
1320 copy = xmlNewDocText(ctxt->output, node->content);
1322 xmlAddChild(ctxt->insert, copy);
1324 xsltGenericError(xsltGenericErrorContext,
1325 "xsltDefaultProcessOneNode: cdata copy failed\n");
1330 template = xsltGetTemplate(ctxt, cur, NULL);
1334 #ifdef DEBUG_PROCESS
1335 xsltGenericDebug(xsltGenericDebugContext,
1336 "xsltDefaultProcessOneNode: applying template for text %s\n",
1339 oldNode = ctxt->node;
1341 ctxt->xpathCtxt->contextSize = nbchild;
1342 ctxt->xpathCtxt->proximityPosition = childno;
1343 templPush(ctxt, template);
1344 varsPush(ctxt, NULL);
1345 xsltApplyOneTemplate(ctxt, cur, template->content, 1);
1346 xsltFreeStackElemList(varsPop(ctxt));
1348 ctxt->node = oldNode;
1349 } else /* if (ctxt->mode == NULL) */ {
1350 #ifdef DEBUG_PROCESS
1351 if (cur->content == NULL)
1352 xsltGenericDebug(xsltGenericDebugContext,
1353 "xsltDefaultProcessOneNode: copy empty text\n");
1355 xsltGenericDebug(xsltGenericDebugContext,
1356 "xsltDefaultProcessOneNode: copy text %s\n",
1359 copy = xmlCopyNode(cur, 0);
1361 xmlAddChild(ctxt->insert, copy);
1363 xsltGenericError(xsltGenericErrorContext,
1364 "xsltDefaultProcessOneNode: text copy failed\n");
1369 case XML_COMMENT_NODE:
1370 template = xsltGetTemplate(ctxt, cur, NULL);
1374 oldNode = ctxt->node;
1376 ctxt->xpathCtxt->contextSize = nbchild;
1377 ctxt->xpathCtxt->proximityPosition = childno;
1378 templPush(ctxt, template);
1379 varsPush(ctxt, NULL);
1380 xsltApplyOneTemplate(ctxt, cur, template->content, 1);
1381 xsltFreeStackElemList(varsPop(ctxt));
1383 ctxt->node = oldNode;
1391 ctxt->xpathCtxt->contextSize = oldSize;
1392 ctxt->xpathCtxt->proximityPosition = oldPos;
1397 * @ctxt: a XSLT process context
1398 * @node: the node in the source tree.
1399 * @inst: the xslt apply-imports node
1401 * Process the xslt apply-imports node on the source node
1404 xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr node,
1406 xsltTemplatePtr template;
1408 if ((ctxt->templ == NULL) || (ctxt->templ->style == NULL)) {
1409 xsltGenericError(xsltGenericErrorContext,
1410 "xslt:apply-imports : internal error no current template\n");
1413 template = xsltGetTemplate(ctxt, node, ctxt->templ->style);
1414 if (template != NULL) {
1415 templPush(ctxt, template);
1416 varsPush(ctxt, NULL);
1417 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1418 xsltFreeStackElemList(varsPop(ctxt));
1425 * @ctxt: a XSLT process context
1426 * @node: the node in the source tree.
1427 * @inst: the xslt call-template node
1429 * Process the xslt call-template node on the source node
1432 xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
1434 xmlChar *prop = NULL;
1435 xmlChar *ncname = NULL;
1436 xmlChar *prefix = NULL;
1438 xsltTemplatePtr template;
1439 xmlNodePtr cur = NULL;
1442 if (ctxt->insert == NULL)
1444 prop = xmlGetNsProp(inst, (const xmlChar *)"name", XSLT_NAMESPACE);
1446 xsltGenericError(xsltGenericErrorContext,
1447 "xslt:call-template : name is missing\n");
1451 ncname = xmlSplitQName2(prop, &prefix);
1452 if (ncname == NULL) {
1457 if ((prefix != NULL) && (ns == NULL)) {
1458 ns = xmlSearchNs(ctxt->insert->doc, ctxt->insert, prefix);
1460 xsltGenericError(xsltGenericErrorContext,
1461 "no namespace bound to prefix %s\n", prefix);
1465 template = xsltFindTemplate(ctxt, ncname, ns->href);
1467 template = xsltFindTemplate(ctxt, ncname, NULL);
1468 if (template == NULL) {
1469 xsltGenericError(xsltGenericDebugContext,
1470 "xslt:call-template: template %s not found\n", ncname);
1473 templPush(ctxt, template);
1474 varsPush(ctxt, NULL);
1475 cur = inst->children;
1476 while (cur != NULL) {
1477 if (ctxt->state == XSLT_STATE_STOPPED) break;
1478 if (IS_XSLT_ELEM(cur)) {
1479 if (IS_XSLT_NAME(cur, "with-param")) {
1480 xsltParseStylesheetParam(ctxt, cur);
1482 xsltGenericError(xsltGenericDebugContext,
1483 "xslt:call-template: misplaced xslt:%s\n", cur->name);
1486 xsltGenericError(xsltGenericDebugContext,
1487 "xslt:call-template: misplaced %s element\n", cur->name);
1491 xsltApplyOneTemplate(ctxt, node, template->content, 1);
1493 xsltFreeStackElemList(varsPop(ctxt));
1506 * xsltApplyTemplates:
1507 * @ctxt: a XSLT process context
1508 * @node: the node in the source tree.
1509 * @inst: the apply-templates node
1511 * Process the apply-templates node on the source node
1514 xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
1516 xmlChar *prop = NULL;
1517 xmlNodePtr cur, delete = NULL;
1518 xmlXPathObjectPtr res = NULL, tmp;
1519 xmlNodePtr replacement;
1520 xmlNodeSetPtr list = NULL, oldlist;
1521 xmlXPathParserContextPtr xpathParserCtxt = NULL;
1522 int i, oldProximityPosition, oldContextSize;
1523 xmlChar *mode, *modeURI;
1524 const xmlChar *oldmode, *oldmodeURI;
1526 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1529 #ifdef DEBUG_PROCESS
1530 xsltGenericDebug(xsltGenericDebugContext,
1531 "xsltApplyTemplates: node: %s\n", node->name);
1537 oldmode = ctxt->mode;
1538 oldmodeURI = ctxt->modeURI;
1539 prop = xmlGetNsProp(inst, (const xmlChar *)"mode", XSLT_NAMESPACE);
1541 xmlChar *prefix = NULL;
1543 mode = xmlSplitQName2(prop, &prefix);
1545 if (prefix != NULL) {
1548 ns = xmlSearchNs(inst->doc, inst, prefix);
1550 xsltGenericError(xsltGenericErrorContext,
1551 "no namespace bound to prefix %s\n", prefix);
1557 modeURI = xmlStrdup(ns->href);
1569 #ifdef DEBUG_PROCESS
1570 xsltGenericDebug(xsltGenericDebugContext,
1571 "xsltApplyTemplates: mode %s\n", mode);
1578 ctxt->modeURI = modeURI;
1580 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
1582 #ifdef DEBUG_PROCESS
1583 xsltGenericDebug(xsltGenericDebugContext,
1584 "xsltApplyTemplates: select %s\n", prop);
1587 if (ctxt->xpathCtxt == NULL) {
1590 xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1591 if (xpathParserCtxt == NULL)
1593 ctxt->xpathCtxt->node = node;
1594 xmlXPathEvalExpr(xpathParserCtxt);
1595 res = valuePop(xpathParserCtxt);
1597 tmp = valuePop(xpathParserCtxt);
1599 xmlXPathFreeObject(tmp);
1601 } while (tmp != NULL);
1603 if (res->type == XPATH_NODESET) {
1604 list = res->nodesetval;
1605 res->nodesetval = NULL;
1607 #ifdef DEBUG_PROCESS
1608 xsltGenericDebug(xsltGenericDebugContext,
1609 "xsltApplyTemplates: select didn't evaluate to a node list\n");
1616 * Build an XPath nodelist with the children
1618 list = xmlXPathNodeSetCreate(NULL);
1619 cur = node->children;
1620 while (cur != NULL) {
1621 switch (cur->type) {
1623 if ((IS_BLANK_NODE(cur)) &&
1624 (cur->parent != NULL) &&
1625 (ctxt->style->stripSpaces != NULL)) {
1628 val = (const xmlChar *)
1629 xmlHashLookup(ctxt->style->stripSpaces,
1631 if ((val != NULL) &&
1632 (xmlStrEqual(val, (xmlChar *) "strip"))) {
1637 /* no break on purpose */
1638 case XML_DOCUMENT_NODE:
1639 case XML_HTML_DOCUMENT_NODE:
1640 case XML_ELEMENT_NODE:
1641 case XML_CDATA_SECTION_NODE:
1642 xmlXPathNodeSetAdd(list, cur);
1645 #ifdef DEBUG_PROCESS
1646 xsltGenericDebug(xsltGenericDebugContext,
1647 "xsltApplyTemplates: skipping cur type %d\n",
1653 if (delete != NULL) {
1654 #ifdef DEBUG_PROCESS
1655 xsltGenericDebug(xsltGenericDebugContext,
1656 "xsltApplyTemplates: removing ignorable blank cur\n");
1658 xmlUnlinkNode(delete);
1659 xmlFreeNode(delete);
1665 #ifdef DEBUG_PROCESS
1666 xsltGenericDebug(xsltGenericDebugContext,
1667 "xsltApplyTemplates: list of %d nodes\n", list->nodeNr);
1670 oldlist = ctxt->nodeList;
1671 ctxt->nodeList = list;
1672 oldContextSize = ctxt->xpathCtxt->contextSize;
1673 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
1674 ctxt->xpathCtxt->contextSize = list->nodeNr;
1677 * handle and skip the xsl:sort
1679 replacement = inst->children;
1680 if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
1681 xsltSort(ctxt, node, replacement);
1682 replacement = replacement->next;
1683 while (IS_XSLT_ELEM(replacement) &&
1684 (IS_XSLT_NAME(replacement, "sort"))) {
1685 TODO /* imbricated sorts */
1686 replacement = replacement->next;
1690 for (i = 0;i < list->nodeNr;i++) {
1691 ctxt->node = list->nodeTab[i];
1692 ctxt->xpathCtxt->proximityPosition = i + 1;
1693 xsltProcessOneNode(ctxt, list->nodeTab[i]);
1695 ctxt->nodeList = oldlist;
1696 ctxt->xpathCtxt->contextSize = oldContextSize;
1697 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
1700 ctxt->mode = oldmode;
1701 ctxt->modeURI = oldmodeURI;
1702 if (xpathParserCtxt != NULL)
1703 xmlXPathFreeParserContext(xpathParserCtxt);
1707 xmlXPathFreeObject(res);
1709 xmlXPathFreeNodeSet(list);
1712 if (modeURI != NULL)
1718 * xsltApplyOneTemplate:
1719 * @ctxt: a XSLT process context
1720 * @node: the node in the source tree.
1721 * @list: the template replacement nodelist
1722 * @real: is this a real template processing
1724 * Process the apply-templates node on the source node
1727 xsltApplyOneTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
1728 xmlNodePtr list, int real) {
1729 xmlNodePtr cur = NULL, insert, copy = NULL;
1730 xmlNodePtr oldInsert;
1731 xmlNodePtr oldCurrent;
1738 if (ctxt->templNr >= xsltMaxDepth) {
1739 xsltGenericError(xsltGenericErrorContext,
1740 "xsltApplyOneTemplate: loop found ???\n");
1741 xsltGenericError(xsltGenericErrorContext,
1742 "try increasing xsltMaxDepth (--maxdepth)\n");
1743 xsltDebug(ctxt, node);
1750 oldInsert = insert = ctxt->insert;
1752 oldCurrent = ctxt->current;
1753 ctxt->current = node;
1757 * Insert all non-XSLT nodes found in the template
1760 while (cur != NULL) {
1762 * test, we must have a valid insertion point
1764 if (insert == NULL) {
1765 #ifdef DEBUG_PROCESS
1766 xsltGenericDebug(xsltGenericDebugContext,
1767 "xsltApplyOneTemplate: insert == NULL !\n");
1770 ctxt->current = oldCurrent;
1774 if (IS_XSLT_ELEM(cur)) {
1775 if (IS_XSLT_NAME(cur, "apply-templates")) {
1776 ctxt->insert = insert;
1777 xsltApplyTemplates(ctxt, node, cur);
1778 ctxt->insert = oldInsert;
1779 } else if (IS_XSLT_NAME(cur, "value-of")) {
1780 ctxt->insert = insert;
1781 xsltValueOf(ctxt, node, cur);
1782 ctxt->insert = oldInsert;
1783 } else if (IS_XSLT_NAME(cur, "copy")) {
1784 ctxt->insert = insert;
1785 xsltCopy(ctxt, node, cur);
1786 ctxt->insert = oldInsert;
1787 } else if (IS_XSLT_NAME(cur, "copy-of")) {
1788 ctxt->insert = insert;
1789 xsltCopyOf(ctxt, node, cur);
1790 ctxt->insert = oldInsert;
1791 } else if (IS_XSLT_NAME(cur, "if")) {
1792 ctxt->insert = insert;
1793 xsltIf(ctxt, node, cur);
1794 ctxt->insert = oldInsert;
1795 } else if (IS_XSLT_NAME(cur, "choose")) {
1796 ctxt->insert = insert;
1797 xsltChoose(ctxt, node, cur);
1798 ctxt->insert = oldInsert;
1799 } else if (IS_XSLT_NAME(cur, "for-each")) {
1800 ctxt->insert = insert;
1801 xsltForEach(ctxt, node, cur);
1802 ctxt->insert = oldInsert;
1803 } else if (IS_XSLT_NAME(cur, "apply-imports")) {
1804 ctxt->insert = insert;
1805 xsltApplyImports(ctxt, node, cur);
1806 ctxt->insert = oldInsert;
1807 } else if (IS_XSLT_NAME(cur, "attribute")) {
1808 ctxt->insert = insert;
1809 xsltAttribute(ctxt, node, cur);
1810 ctxt->insert = oldInsert;
1811 } else if (IS_XSLT_NAME(cur, "element")) {
1812 ctxt->insert = insert;
1813 xsltElement(ctxt, node, cur);
1814 ctxt->insert = oldInsert;
1815 } else if (IS_XSLT_NAME(cur, "text")) {
1816 #ifdef DEBUG_PROCESS
1817 xsltGenericDebug(xsltGenericDebugContext,
1818 "xsltApplyOneTemplate: found xslt:text, not expected\n");
1820 xsltText(ctxt, node, cur);
1821 } else if (IS_XSLT_NAME(cur, "comment")) {
1822 ctxt->insert = insert;
1823 xsltComment(ctxt, node, cur);
1824 ctxt->insert = oldInsert;
1825 } else if (IS_XSLT_NAME(cur, "number")) {
1826 ctxt->insert = insert;
1827 xsltNumber(ctxt, node, cur);
1828 ctxt->insert = oldInsert;
1829 } else if (IS_XSLT_NAME(cur, "processing-instruction")) {
1830 ctxt->insert = insert;
1831 xsltProcessingInstruction(ctxt, node, cur);
1832 ctxt->insert = oldInsert;
1833 } else if (IS_XSLT_NAME(cur, "variable")) {
1834 xsltParseStylesheetVariable(ctxt, cur);
1835 } else if (IS_XSLT_NAME(cur, "param")) {
1836 xsltParseStylesheetParam(ctxt, cur);
1837 } else if (IS_XSLT_NAME(cur, "call-template")) {
1838 ctxt->insert = insert;
1839 xsltCallTemplate(ctxt, node, cur);
1840 ctxt->insert = oldInsert;
1841 } else if (IS_XSLT_NAME(cur, "message")) {
1842 xsltMessage(ctxt, node, cur);
1844 xsltGenericError(xsltGenericDebugContext,
1845 "xsltApplyOneTemplate: found xslt:%s\n", cur->name);
1850 } else if (cur->type == XML_TEXT_NODE) {
1852 * This text comes from the stylesheet
1853 * For stylesheets, the set of whitespace-preserving
1854 * element names consists of just xsl:text.
1856 #ifdef DEBUG_PROCESS
1857 if (cur->name == xmlStringTextNoenc)
1858 xsltGenericDebug(xsltGenericDebugContext,
1859 "xsltApplyOneTemplate: copy unescaped text %s\n",
1862 xsltGenericDebug(xsltGenericDebugContext,
1863 "xsltApplyOneTemplate: copy text %s\n", cur->content);
1865 copy = xmlCopyNode(cur, 0);
1867 xmlAddChild(insert, copy);
1869 xsltGenericError(xsltGenericErrorContext,
1870 "xsltApplyOneTemplate: text copy failed\n");
1872 } else if ((cur->type == XML_ELEMENT_NODE) &&
1873 (xmlStrEqual(cur->name, (const xmlChar *)"xsltdebug"))) {
1874 xsltDebug(ctxt, cur);
1875 } else if (cur->type == XML_ELEMENT_NODE) {
1876 #ifdef DEBUG_PROCESS
1877 xsltGenericDebug(xsltGenericDebugContext,
1878 "xsltApplyOneTemplate: copy node %s\n", cur->name);
1880 copy = xsltCopyNode(ctxt, cur, insert);
1882 * all the attributes are directly inherited
1884 if (cur->properties != NULL) {
1885 attrs = xsltAttrListTemplateProcess(ctxt, copy,
1887 if (copy->properties != NULL) {
1888 xmlAttrPtr cur = copy->properties;
1889 while (cur->next != NULL)
1893 copy->properties = attrs;
1898 * Skip to next node, in document order.
1900 if (cur->children != NULL) {
1901 if (cur->children->type != XML_ENTITY_DECL) {
1902 cur = cur->children;
1909 if (cur->next != NULL) {
1916 insert = insert->parent;
1919 if (cur == list->parent) {
1923 if (cur->next != NULL) {
1927 } while (cur != NULL);
1930 ctxt->current = oldCurrent;
1935 * @ctxt: a XSLT process context
1936 * @node: the node in the source tree.
1937 * @inst: the xslt choose node
1939 * Process the xslt choose node on the source node
1942 xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr node,
1944 xmlChar *prop = NULL;
1945 xmlXPathObjectPtr res = NULL, tmp;
1946 xmlXPathParserContextPtr xpathParserCtxt = NULL;
1947 xmlNodePtr replacement, when;
1950 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
1956 replacement = inst->children;
1957 if (replacement == NULL) {
1958 xsltGenericError(xsltGenericErrorContext,
1959 "xslt:choose: empty content not allowed\n");
1962 if ((!IS_XSLT_ELEM(replacement)) ||
1963 (!IS_XSLT_NAME(replacement, "when"))) {
1964 xsltGenericError(xsltGenericErrorContext,
1965 "xslt:choose: xsl:when expected first\n");
1968 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "when"))) {
1971 prop = xmlGetNsProp(when, (const xmlChar *)"test", XSLT_NAMESPACE);
1973 xsltGenericError(xsltGenericErrorContext,
1974 "xsl:when: test is not defined\n");
1977 #ifdef DEBUG_PROCESS
1978 xsltGenericDebug(xsltGenericDebugContext,
1979 "xsl:when: test %s\n", prop);
1982 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
1983 if (xpathParserCtxt == NULL)
1985 ctxt->xpathCtxt->node = node;
1986 xmlXPathEvalExpr(xpathParserCtxt);
1987 xmlXPathBooleanFunction(xpathParserCtxt, 1);
1988 res = valuePop(xpathParserCtxt);
1990 tmp = valuePop(xpathParserCtxt);
1992 xmlXPathFreeObject(tmp);
1994 } while (tmp != NULL);
1997 if (res->type == XPATH_BOOLEAN)
1998 doit = res->boolval;
2000 #ifdef DEBUG_PROCESS
2001 xsltGenericDebug(xsltGenericDebugContext,
2002 "xsl:when: test didn't evaluate to a boolean\n");
2008 #ifdef DEBUG_PROCESS
2009 xsltGenericDebug(xsltGenericDebugContext,
2010 "xsl:when: test evaluate to %d\n", doit);
2013 varsPush(ctxt, NULL);
2014 xsltApplyOneTemplate(ctxt, ctxt->node, when->children, 0);
2015 xsltFreeStackElemList(varsPop(ctxt));
2018 if (xpathParserCtxt != NULL)
2019 xmlXPathFreeParserContext(xpathParserCtxt);
2020 xpathParserCtxt = NULL;
2025 xmlXPathFreeObject(res);
2027 replacement = replacement->next;
2029 if (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "otherwise"))) {
2030 varsPush(ctxt, NULL);
2031 xsltApplyOneTemplate(ctxt, ctxt->node, replacement->children, 0);
2032 xsltFreeStackElemList(varsPop(ctxt));
2033 replacement = replacement->next;
2035 if (replacement != NULL) {
2036 #ifdef DEBUG_PROCESS
2037 xsltGenericDebug(xsltGenericDebugContext,
2038 "xsl:otherwise: applying default fallback\n");
2040 xsltGenericError(xsltGenericErrorContext,
2041 "xslt:choose: unexpected content %s\n", replacement->name);
2047 if (xpathParserCtxt != NULL)
2048 xmlXPathFreeParserContext(xpathParserCtxt);
2052 xmlXPathFreeObject(res);
2057 * @ctxt: a XSLT process context
2058 * @node: the node in the source tree.
2059 * @inst: the xslt if node
2061 * Process the xslt if node on the source node
2064 xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr node,
2067 xmlXPathObjectPtr res = NULL, tmp;
2068 xmlXPathParserContextPtr xpathParserCtxt = NULL;
2071 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2074 prop = xmlGetNsProp(inst, (const xmlChar *)"test", XSLT_NAMESPACE);
2076 xsltGenericError(xsltGenericErrorContext,
2077 "xsltIf: test is not defined\n");
2080 #ifdef DEBUG_PROCESS
2081 xsltGenericDebug(xsltGenericDebugContext,
2082 "xsltIf: test %s\n", prop);
2085 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
2086 if (xpathParserCtxt == NULL)
2088 ctxt->xpathCtxt->node = node;
2089 xmlXPathEvalExpr(xpathParserCtxt);
2090 xmlXPathBooleanFunction(xpathParserCtxt, 1);
2091 res = valuePop(xpathParserCtxt);
2093 tmp = valuePop(xpathParserCtxt);
2095 xmlXPathFreeObject(tmp);
2097 } while (tmp != NULL);
2100 if (res->type == XPATH_BOOLEAN)
2101 doit = res->boolval;
2103 #ifdef DEBUG_PROCESS
2104 xsltGenericDebug(xsltGenericDebugContext,
2105 "xsltIf: test didn't evaluate to a boolean\n");
2111 #ifdef DEBUG_PROCESS
2112 xsltGenericDebug(xsltGenericDebugContext,
2113 "xsltIf: test evaluate to %d\n", doit);
2116 varsPush(ctxt, NULL);
2117 xsltApplyOneTemplate(ctxt, node, inst->children, 0);
2118 xsltFreeStackElemList(varsPop(ctxt));
2122 if (xpathParserCtxt != NULL)
2123 xmlXPathFreeParserContext(xpathParserCtxt);
2127 xmlXPathFreeObject(res);
2132 * @ctxt: a XSLT process context
2133 * @node: the node in the source tree.
2134 * @inst: the xslt for-each node
2136 * Process the xslt for-each node on the source node
2139 xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node,
2142 xmlXPathObjectPtr res = NULL, tmp;
2143 xmlNodePtr replacement;
2144 xmlNodeSetPtr list = NULL, oldlist;
2145 xmlXPathParserContextPtr xpathParserCtxt = NULL;
2146 int i, oldProximityPosition, oldContextSize;
2148 if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
2151 prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE);
2153 xsltGenericError(xsltGenericErrorContext,
2154 "xsltForEach: select is not defined\n");
2157 #ifdef DEBUG_PROCESS
2158 xsltGenericDebug(xsltGenericDebugContext,
2159 "xsltForEach: select %s\n", prop);
2162 xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt);
2163 if (xpathParserCtxt == NULL)
2165 ctxt->xpathCtxt->node = node;
2166 xmlXPathEvalExpr(xpathParserCtxt);
2167 res = valuePop(xpathParserCtxt);
2169 tmp = valuePop(xpathParserCtxt);
2171 xmlXPathFreeObject(tmp);
2173 } while (tmp != NULL);
2176 if (res->type == XPATH_NODESET)
2177 list = res->nodesetval;
2179 #ifdef DEBUG_PROCESS
2180 xsltGenericDebug(xsltGenericDebugContext,
2181 "xsltForEach: select didn't evaluate to a node list\n");
2187 #ifdef DEBUG_PROCESS
2188 xsltGenericDebug(xsltGenericDebugContext,
2189 "xsltForEach: select evaluate to %d nodes\n", list->nodeNr);
2192 oldlist = ctxt->nodeList;
2193 ctxt->nodeList = list;
2194 oldContextSize = ctxt->xpathCtxt->contextSize;
2195 oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
2196 ctxt->xpathCtxt->contextSize = list->nodeNr;
2199 * handle and skip the xsl:sort
2201 replacement = inst->children;
2202 while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) {
2203 xsltSort(ctxt, node, replacement);
2204 replacement = replacement->next;
2207 for (i = 0;i < list->nodeNr;i++) {
2208 ctxt->node = list->nodeTab[i];
2209 ctxt->xpathCtxt->proximityPosition = i + 1;
2210 varsPush(ctxt, NULL);
2211 xsltApplyOneTemplate(ctxt, list->nodeTab[i], replacement, 0);
2212 xsltFreeStackElemList(varsPop(ctxt));
2214 ctxt->nodeList = oldlist;
2215 ctxt->xpathCtxt->contextSize = oldContextSize;
2216 ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
2219 if (xpathParserCtxt != NULL)
2220 xmlXPathFreeParserContext(xpathParserCtxt);
2224 xmlXPathFreeObject(res);
2228 * xsltProcessOneNode:
2229 * @ctxt: a XSLT process context
2230 * @node: the node in the source tree.
2232 * Process the source node.
2235 xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node) {
2236 xsltTemplatePtr template;
2239 template = xsltGetTemplate(ctxt, node, NULL);
2241 * If no template is found, apply the default rule.
2243 if (template == NULL) {
2244 #ifdef DEBUG_PROCESS
2245 if (node->type == XML_DOCUMENT_NODE)
2246 xsltGenericDebug(xsltGenericDebugContext,
2247 "xsltProcessOneNode: no template found for /\n");
2248 else if (node->type == XML_CDATA_SECTION_NODE)
2249 xsltGenericDebug(xsltGenericDebugContext,
2250 "xsltProcessOneNode: no template found for CDATA\n");
2252 xsltGenericDebug(xsltGenericDebugContext,
2253 "xsltProcessOneNode: no template found for %s\n", node->name);
2255 oldNode = ctxt->node;
2257 xsltDefaultProcessOneNode(ctxt, node);
2258 ctxt->node = oldNode;
2262 if (node->type == XML_ATTRIBUTE_NODE) {
2263 #ifdef DEBUG_PROCESS
2264 xsltGenericDebug(xsltGenericDebugContext,
2265 "xsltProcessOneNode: applying template for attribute %s\n",
2268 templPush(ctxt, template);
2269 varsPush(ctxt, NULL);
2270 xsltApplyOneTemplate(ctxt, node, template->content, 1);
2271 xsltFreeStackElemList(varsPop(ctxt));
2274 #ifdef DEBUG_PROCESS
2275 if (node->type == XML_DOCUMENT_NODE)
2276 xsltGenericDebug(xsltGenericDebugContext,
2277 "xsltProcessOneNode: applying template for /\n");
2279 xsltGenericDebug(xsltGenericDebugContext,
2280 "xsltProcessOneNode: applying template for %s\n", node->name);
2282 oldNode = ctxt->node;
2284 templPush(ctxt, template);
2285 varsPush(ctxt, NULL);
2286 xsltApplyOneTemplate(ctxt, node, template->content, 1);
2287 xsltFreeStackElemList(varsPop(ctxt));
2289 ctxt->node = oldNode;
2294 * xsltApplyStylesheet:
2295 * @style: a parsed XSLT stylesheet
2296 * @doc: a parsed XML document
2298 * Apply the stylesheet to the document
2299 * NOTE: This may lead to a non-wellformed output XML wise !
2301 * Returns the result document or NULL in case of error
2304 xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc) {
2305 xmlDocPtr res = NULL;
2306 xsltTransformContextPtr ctxt = NULL;
2309 if ((style == NULL) || (doc == NULL))
2311 ctxt = xsltNewTransformContext(style, doc);
2314 if ((style->method != NULL) &&
2315 (!xmlStrEqual(style->method, (const xmlChar *) "xml"))) {
2316 if (xmlStrEqual(style->method, (const xmlChar *) "html")) {
2317 ctxt->type = XSLT_OUTPUT_HTML;
2318 res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
2321 } else if (xmlStrEqual(style->method, (const xmlChar *) "xhtml")) {
2322 xsltGenericError(xsltGenericErrorContext,
2323 "xsltApplyStylesheet: insupported method xhtml, using html\n",
2325 ctxt->type = XSLT_OUTPUT_HTML;
2326 res = htmlNewDoc(style->doctypePublic, style->doctypeSystem);
2329 } else if (xmlStrEqual(style->method, (const xmlChar *) "text")) {
2330 ctxt->type = XSLT_OUTPUT_TEXT;
2331 res = xmlNewDoc(style->version);
2335 xsltGenericError(xsltGenericErrorContext,
2336 "xsltApplyStylesheet: insupported method %s\n",
2341 ctxt->type = XSLT_OUTPUT_XML;
2342 res = xmlNewDoc(style->version);
2346 res->charset = XML_CHAR_ENCODING_UTF8;
2347 if (style->encoding != NULL)
2348 res->encoding = xmlStrdup(style->encoding);
2354 ctxt->insert = (xmlNodePtr) res;
2355 xsltEvalGlobalVariables(ctxt);
2356 ctxt->node = (xmlNodePtr) doc;
2357 varsPush(ctxt, NULL);
2358 xsltProcessOneNode(ctxt, ctxt->node);
2359 xsltFreeStackElemList(varsPop(ctxt));
2362 if ((ctxt->type = XSLT_OUTPUT_XML) &&
2363 ((style->doctypePublic != NULL) ||
2364 (style->doctypeSystem != NULL))) {
2365 root = xmlDocGetRootElement(res);
2367 res->intSubset = xmlCreateIntSubset(res, root->name,
2368 style->doctypePublic, style->doctypeSystem);
2370 xmlXPathFreeNodeSet(ctxt->nodeList);
2371 xsltFreeTransformContext(ctxt);
2378 xsltFreeTransformContext(ctxt);