2 * xslt.c: Implemetation of an XSL Transformation 1.0 engine
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * Daniel.Veillard@imag.fr
12 #include "xsltconfig.h"
16 #include <libxml/xmlmemory.h>
17 #include <libxml/parser.h>
18 #include <libxml/tree.h>
19 #include <libxml/valid.h>
20 #include <libxml/hash.h>
21 #include <libxml/xmlerror.h>
23 #include "xsltInternals.h"
25 #include "xsltutils.h"
33 #define IS_BLANK(c) (((c) == 0x20) || ((c) == 0x09) || ((c) == 0xA) || \
36 #define IS_BLANK_NODE(n) \
37 (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
40 /************************************************************************
44 ************************************************************************/
50 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
53 xsltIsBlank(xmlChar *str) {
57 if (!(IS_BLANK(*str))) return(0);
63 /************************************************************************
65 * Routines to handle XSLT data structures *
67 ************************************************************************/
72 * Create a new XSLT Template
74 * Returns the newly allocated xsltTemplatePtr or NULL in case of error
77 xsltNewTemplate(void) {
80 cur = (xsltTemplatePtr) xmlMalloc(sizeof(xsltTemplate));
82 xsltGenericError(xsltGenericErrorContext,
83 "xsltNewTemplate : malloc failed\n");
86 memset(cur, 0, sizeof(xsltTemplate));
87 cur->priority = XSLT_PAT_NO_PRIORITY;
93 * @template: an XSLT template
95 * Free up the memory allocated by @template
98 xsltFreeTemplate(xsltTemplatePtr template) {
101 if (template->match) xmlFree(template->match);
102 if (template->name) xmlFree(template->name);
103 if (template->nameURI) xmlFree(template->nameURI);
104 if (template->mode) xmlFree(template->mode);
105 if (template->modeURI) xmlFree(template->modeURI);
106 memset(template, -1, sizeof(xsltTemplate));
111 * xsltFreeTemplateList:
112 * @template: an XSLT template list
114 * Free up the memory allocated by all the elements of @template
117 xsltFreeTemplateList(xsltTemplatePtr template) {
120 while (template != NULL) {
122 template = template->next;
123 xsltFreeTemplate(cur);
130 * Create a new XSLT Stylesheet
132 * Returns the newly allocated xsltStylesheetPtr or NULL in case of error
135 xsltNewStylesheet(void) {
136 xsltStylesheetPtr cur;
138 cur = (xsltStylesheetPtr) xmlMalloc(sizeof(xsltStylesheet));
140 xsltGenericError(xsltGenericErrorContext,
141 "xsltNewStylesheet : malloc failed\n");
144 memset(cur, 0, sizeof(xsltStylesheet));
145 cur->omitXmlDeclaration = -1;
146 cur->standalone = -1;
152 * xsltFreeStylesheet:
153 * @sheet: an XSLT stylesheet
155 * Free up the memory allocated by @sheet
158 xsltFreeStylesheet(xsltStylesheetPtr sheet) {
162 xsltFreeTemplateHashes(sheet);
163 xsltFreeTemplateList(sheet->templates);
164 if (sheet->doc != NULL)
165 xmlFreeDoc(sheet->doc);
166 if (sheet->stripSpaces != NULL)
167 xmlHashFree(sheet->stripSpaces, NULL);
169 if (sheet->method != NULL) xmlFree(sheet->method);
170 if (sheet->methodURI != NULL) xmlFree(sheet->methodURI);
171 if (sheet->version != NULL) xmlFree(sheet->version);
172 if (sheet->encoding != NULL) xmlFree(sheet->encoding);
173 if (sheet->doctypePublic != NULL) xmlFree(sheet->doctypePublic);
174 if (sheet->doctypeSystem != NULL) xmlFree(sheet->doctypeSystem);
175 if (sheet->mediaType != NULL) xmlFree(sheet->mediaType);
177 memset(sheet, -1, sizeof(xsltStylesheet));
181 /************************************************************************
183 * Parsing of an XSLT Stylesheet *
185 ************************************************************************/
188 * xsltParseStylesheetOutput:
189 * @style: the XSLT stylesheet
190 * @template: the "output" element
192 * parse an XSLT stylesheet output element and record
193 * information related to the stylesheet output
197 xsltParseStylesheetOutput(xsltStylesheetPtr style, xmlNodePtr cur) {
198 xmlChar *elements, *prop;
199 xmlChar *element, *end;
201 if ((cur == NULL) || (style == NULL))
204 prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
206 if (style->version != NULL) xmlFree(style->version);
207 style->version = prop;
210 prop = xmlGetNsProp(cur, (const xmlChar *)"encoding", XSLT_NAMESPACE);
212 if (style->encoding != NULL) xmlFree(style->encoding);
213 style->encoding = prop;
216 prop = xmlGetNsProp(cur, (const xmlChar *)"method", XSLT_NAMESPACE);
219 xmlChar *prefix = NULL;
221 if (style->method != NULL) xmlFree(style->method);
222 style->method = NULL;
223 if (style->methodURI != NULL) xmlFree(style->methodURI);
224 style->methodURI = NULL;
226 ncname = xmlSplitQName2(prop, &prefix);
227 if (ncname != NULL) {
228 if (prefix != NULL) {
231 ns = xmlSearchNs(cur->doc, cur, prefix);
233 xsltGenericError(xsltGenericErrorContext,
234 "no namespace bound to prefix %s\n", prefix);
237 style->method = prop;
239 style->methodURI = xmlStrdup(ns->href);
240 style->method = ncname;
245 style->method = ncname;
249 if ((xmlStrEqual(prop, (const xmlChar *)"xml")) ||
250 (xmlStrEqual(prop, (const xmlChar *)"html")) ||
251 (xmlStrEqual(prop, (const xmlChar *)"text"))) {
252 style->method = prop;
254 xsltGenericError(xsltGenericErrorContext,
255 "invalid value for method: %s\n", prop);
260 prop = xmlGetNsProp(cur, (const xmlChar *)"doctype-system", XSLT_NAMESPACE);
262 if (style->doctypeSystem != NULL) xmlFree(style->doctypeSystem);
263 style->doctypeSystem = prop;
266 prop = xmlGetNsProp(cur, (const xmlChar *)"doctype-public", XSLT_NAMESPACE);
268 if (style->doctypePublic != NULL) xmlFree(style->doctypePublic);
269 style->doctypePublic = prop;
272 prop = xmlGetNsProp(cur, (const xmlChar *)"standalone",
275 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
276 style->standalone = 1;
277 } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
278 style->standalone = 0;
280 xsltGenericError(xsltGenericErrorContext,
281 "invalid value for standalone: %s\n", prop);
286 prop = xmlGetNsProp(cur, (const xmlChar *)"indent",
289 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
291 } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
294 xsltGenericError(xsltGenericErrorContext,
295 "invalid value for indent: %s\n", prop);
300 prop = xmlGetNsProp(cur, (const xmlChar *)"omit-xml-declaration",
303 if (xmlStrEqual(prop, (const xmlChar *)"yes")) {
304 style->omitXmlDeclaration = 1;
305 } else if (xmlStrEqual(prop, (const xmlChar *)"no")) {
306 style->omitXmlDeclaration = 0;
308 xsltGenericError(xsltGenericErrorContext,
309 "invalid value for omit-xml-declaration: %s\n", prop);
314 elements = xmlGetNsProp(cur, (const xmlChar *)"cdata-section-elements",
316 if (elements != NULL) {
317 if (style->stripSpaces == NULL)
318 style->stripSpaces = xmlHashCreate(10);
319 if (style->stripSpaces == NULL)
323 while (*element != 0) {
324 while (IS_BLANK(*element)) element++;
328 while ((*end != 0) && (!IS_BLANK(*end))) end++;
329 element = xmlStrndup(element, end - element);
332 xsltGenericDebug(xsltGenericDebugContext,
333 "add cdata section output element %s\n", element);
335 xmlHashAddEntry(style->stripSpaces, element, "cdata");
345 * xsltParseStylesheetPreserveSpace:
346 * @style: the XSLT stylesheet
347 * @template: the "preserve-space" element
349 * parse an XSLT stylesheet preserve-space element and record
350 * elements needing preserving
354 xsltParseStylesheetPreserveSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
356 xmlChar *element, *end;
358 if ((cur == NULL) || (style == NULL))
361 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", XSLT_NAMESPACE);
362 if (elements == NULL) {
363 xsltGenericError(xsltGenericErrorContext,
364 "xsltParseStylesheetPreserveSpace: missing elements attribute\n");
368 if (style->stripSpaces == NULL)
369 style->stripSpaces = xmlHashCreate(10);
370 if (style->stripSpaces == NULL)
374 while (*element != 0) {
375 while (IS_BLANK(*element)) element++;
379 while ((*end != 0) && (!IS_BLANK(*end))) end++;
380 element = xmlStrndup(element, end - element);
383 xsltGenericDebug(xsltGenericDebugContext,
384 "add preserved space element %s\n", element);
386 xmlHashAddEntry(style->stripSpaces, element, "preserve");
395 * xsltParseStylesheetStripSpace:
396 * @style: the XSLT stylesheet
397 * @template: the "strip-space" element
399 * parse an XSLT stylesheet strip-space element and record
400 * elements needing stripping
404 xsltParseStylesheetStripSpace(xsltStylesheetPtr style, xmlNodePtr cur) {
406 xmlChar *element, *end;
408 if ((cur == NULL) || (style == NULL))
411 elements = xmlGetNsProp(cur, (const xmlChar *)"elements", XSLT_NAMESPACE);
412 if (elements == NULL) {
413 xsltGenericError(xsltGenericErrorContext,
414 "xsltParseStylesheetStripSpace: missing elements attribute\n");
418 if (style->stripSpaces == NULL)
419 style->stripSpaces = xmlHashCreate(10);
420 if (style->stripSpaces == NULL)
424 while (*element != 0) {
425 while (IS_BLANK(*element)) element++;
429 while ((*end != 0) && (!IS_BLANK(*end))) end++;
430 element = xmlStrndup(element, end - element);
433 xsltGenericDebug(xsltGenericDebugContext,
434 "add stripped space element %s\n", element);
436 xmlHashAddEntry(style->stripSpaces, element, "strip");
445 * xsltParseTemplateContent:
446 * @style: the XSLT stylesheet
447 * @ret: the "template" structure
448 * @template: the container node (can be a document for literal results)
450 * parse an XSLT template element content
451 * Clean-up the template content from unwanted ignorable blank nodes
452 * and process xslt:text
456 xsltParseTemplateContent(xsltStylesheetPtr style, xsltTemplatePtr ret,
457 xmlNodePtr template) {
458 xmlNodePtr cur, delete;
460 * This content comes from the stylesheet
461 * For stylesheets, the set of whitespace-preserving
462 * element names consists of just xsl:text.
464 cur = template->children;
466 while (cur != NULL) {
467 if (delete != NULL) {
469 xsltGenericDebug(xsltGenericDebugContext,
470 "xsltParseStylesheetTemplate: removing ignorable blank node\n");
472 xmlUnlinkNode(delete);
476 if (IS_XSLT_ELEM(cur)) {
477 if (IS_XSLT_NAME(cur, "text")) {
478 if (cur->children != NULL) {
479 if ((cur->children->type != XML_TEXT_NODE) ||
480 (cur->children->next != NULL)) {
481 xsltGenericError(xsltGenericErrorContext,
482 "xsltParseStylesheetTemplate: xslt:text content problem\n");
484 xmlNodePtr text = cur->children;
486 xmlAddPrevSibling(cur, text);
492 } else if (cur->type == XML_TEXT_NODE) {
493 if (IS_BLANK_NODE(cur)) {
496 } else if (cur->type != XML_ELEMENT_NODE) {
503 if (cur->children != NULL) {
504 if (cur->children->type != XML_ENTITY_DECL) {
510 if (cur->next != NULL) {
519 if (cur == template) {
523 if (cur->next != NULL) {
527 } while (cur != NULL);
529 if (delete != NULL) {
531 xsltGenericDebug(xsltGenericDebugContext,
532 "xsltParseStylesheetTemplate: removing ignorable blank node\n");
534 xmlUnlinkNode(delete);
540 * Find and handle the params
542 cur = template->children;
543 while (cur != NULL) {
545 * Remove Blank nodes found at this level.
547 if (IS_BLANK_NODE(cur)) {
548 xmlNodePtr blank = cur;
551 xmlUnlinkNode(blank);
555 if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
556 TODO /* Handle param */
563 * Browse the remaining of the template
565 while (cur != NULL) {
567 * Remove Blank nodes found at this level.
569 if (IS_BLANK_NODE(cur)) {
570 xmlNodePtr blank = cur;
573 xmlUnlinkNode(blank);
577 if ((IS_XSLT_ELEM(cur)) && (IS_XSLT_NAME(cur, "param"))) {
578 xmlNodePtr param = cur;
581 xsltGenericError(xsltGenericErrorContext,
582 "xsltParseStylesheetTop: ignoring misplaced param element\n");
583 xmlUnlinkNode(param);
591 ret->content = template->children;
595 * xsltParseStylesheetTemplate:
596 * @style: the XSLT stylesheet
597 * @template: the "template" element
599 * parse an XSLT stylesheet template building the associated structures
603 xsltParseStylesheetTemplate(xsltStylesheetPtr style, xmlNodePtr template) {
607 if (template == NULL)
611 * Create and link the structure
613 ret = xsltNewTemplate();
616 ret->next = style->templates;
617 style->templates = ret;
622 prop = xmlGetNsProp(template, (const xmlChar *)"match", XSLT_NAMESPACE);
624 if (ret->match != NULL) xmlFree(ret->match);
628 prop = xmlGetNsProp(template, (const xmlChar *)"name", XSLT_NAMESPACE);
631 xmlChar *prefix = NULL;
633 if (ret->name != NULL) xmlFree(ret->name);
635 if (ret->nameURI != NULL) xmlFree(ret->nameURI);
638 ncname = xmlSplitQName2(prop, &prefix);
639 if (ncname != NULL) {
640 if (prefix != NULL) {
643 ns = xmlSearchNs(template->doc, template, prefix);
645 xsltGenericError(xsltGenericErrorContext,
646 "no namespace bound to prefix %s\n", prefix);
651 ret->nameURI = xmlStrdup(ns->href);
666 * parse the content and register the pattern
668 xsltParseTemplateContent(style, ret, template);
669 xsltAddTemplate(style, ret);
673 * xsltParseStylesheetTop:
674 * @style: the XSLT stylesheet
675 * @top: the top level "stylesheet" element
677 * scan the top level elements of an XSL stylesheet
681 xsltParseStylesheetTop(xsltStylesheetPtr style, xmlNodePtr top) {
691 while (cur != NULL) {
692 if (IS_BLANK_NODE(cur)) {
696 if (!(IS_XSLT_ELEM(cur))) {
698 xsltGenericDebug(xsltGenericDebugContext,
699 "xsltParseStylesheetTop : found foreign element %s\n",
705 if (IS_XSLT_NAME(cur, "import")) {
706 TODO /* Handle import */
711 while (cur != NULL) {
712 if (IS_BLANK_NODE(cur)) {
716 if (!(IS_XSLT_ELEM(cur))) {
718 xsltGenericDebug(xsltGenericDebugContext,
719 "xsltParseStylesheetTop : found foreign element %s\n",
725 if (IS_XSLT_NAME(cur, "import")) {
726 xsltGenericError(xsltGenericErrorContext,
727 "xsltParseStylesheetTop: ignoring misplaced import element\n");
728 } else if (IS_XSLT_NAME(cur, "include")) {
729 TODO /* Handle include */
730 } else if (IS_XSLT_NAME(cur, "strip-space")) {
731 xsltParseStylesheetStripSpace(style, cur);
732 } else if (IS_XSLT_NAME(cur, "preserve-space")) {
733 xsltParseStylesheetPreserveSpace(style, cur);
734 } else if (IS_XSLT_NAME(cur, "output")) {
735 xsltParseStylesheetOutput(style, cur);
736 } else if (IS_XSLT_NAME(cur, "key")) {
737 TODO /* Handle key */
738 } else if (IS_XSLT_NAME(cur, "decimal-format")) {
739 TODO /* Handle decimal-format */
740 } else if (IS_XSLT_NAME(cur, "attribute-set")) {
741 TODO /* Handle attribute-set */
742 } else if (IS_XSLT_NAME(cur, "variable")) {
743 TODO /* Handle variable */
744 } else if (IS_XSLT_NAME(cur, "param")) {
745 TODO /* Handle param */
746 } else if (IS_XSLT_NAME(cur, "template")) {
750 xsltParseStylesheetTemplate(style, cur);
751 } else if (IS_XSLT_NAME(cur, "namespace-alias")) {
752 TODO /* Handle namespace-alias */
754 xsltGenericError(xsltGenericErrorContext,
755 "xsltParseStylesheetTop: ignoring unknown %s element\n",
761 xsltGenericDebug(xsltGenericDebugContext,
762 "parsed %d templates\n", templates);
767 * xsltParseStylesheetDoc:
768 * @doc: and xmlDoc parsed XML
770 * parse an XSLT stylesheet building the associated structures
772 * Returns a new XSLT stylesheet structure.
776 xsltParseStylesheetDoc(xmlDocPtr doc) {
777 xsltStylesheetPtr ret;
783 ret = xsltNewStylesheet();
788 * First step, locate the xsl:stylesheet element and the
789 * namespace declaration.
791 cur = xmlDocGetRootElement(doc);
793 xsltGenericError(xsltGenericErrorContext,
794 "xsltParseStylesheetDoc : empty stylesheet\n");
795 xsltFreeStylesheet(ret);
800 if ((IS_XSLT_ELEM(cur)) &&
801 ((IS_XSLT_NAME(cur, "stylesheet")) ||
802 (IS_XSLT_NAME(cur, "transform")))) {
804 xsltGenericDebug(xsltGenericDebugContext,
805 "xsltParseStylesheetDoc : found stylesheet\n");
808 xsltParseStylesheetTop(ret, cur);
811 xsltTemplatePtr template;
814 * the document itself might be the template, check xsl:version
816 prop = xmlGetNsProp(cur, (const xmlChar *)"version", XSLT_NAMESPACE);
818 xsltGenericError(xsltGenericErrorContext,
819 "xsltParseStylesheetDoc : document is not a stylesheet\n");
820 xsltFreeStylesheet(ret);
825 xsltGenericDebug(xsltGenericDebugContext,
826 "xsltParseStylesheetDoc : document is stylesheet\n");
829 /* TODO: check the version */
833 * Create and link the template
835 template = xsltNewTemplate();
836 if (template == NULL) {
837 xsltFreeStylesheet(ret);
840 template->next = ret->templates;
841 ret->templates = template;
842 template->match = xmlStrdup((const xmlChar *)"/");
845 * parse the content and register the pattern
847 xsltParseTemplateContent(ret, template, (xmlNodePtr) doc);
848 xsltAddTemplate(ret, template);
855 * xsltParseStylesheetFile:
856 * @filename: the filename/URL to the stylesheet
858 * Load and parse an XSLT stylesheet
860 * Returns a new XSLT stylesheet structure.
864 xsltParseStylesheetFile(const xmlChar* filename) {
865 xsltStylesheetPtr ret;
869 if (filename == NULL)
873 xsltGenericDebug(xsltGenericDebugContext,
874 "xsltParseStylesheetFile : parse %s\n", filename);
877 doc = xmlParseFile((const char *) filename);
879 xsltGenericError(xsltGenericErrorContext,
880 "xsltParseStylesheetFile : cannot parse %s\n", filename);
883 ret = xsltParseStylesheetDoc(doc);