2 * pattern.c: Implemetation of the template match compilation and lookup
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/tree.h>
18 #include <libxml/valid.h>
19 #include <libxml/hash.h>
20 #include <libxml/xmlerror.h>
21 #include <libxml/parserInternals.h>
23 #include "xsltInternals.h"
24 #include "xsltutils.h"
26 /* #define DEBUG_PARSING */
52 typedef struct _xsltStepOp xsltStepOp;
53 typedef xsltStepOp *xsltStepOpPtr;
60 typedef struct _xsltCompMatch xsltCompMatch;
61 typedef xsltCompMatch *xsltCompMatchPtr;
62 struct _xsltCompMatch {
63 struct _xsltCompMatch *next; /* siblings in the name hash */
64 float priority; /* the priority */
65 xsltTemplatePtr template; /* the associated template */
67 /* TODO fix the statically allocated size steps[] */
70 xsltStepOp steps[20]; /* ops for computation */
73 typedef struct _xsltParserContext xsltParserContext;
74 typedef xsltParserContext *xsltParserContextPtr;
75 struct _xsltParserContext {
76 const xmlChar *cur; /* the current char being parsed */
77 const xmlChar *base; /* the full expression */
78 int error; /* error code */
79 xsltCompMatchPtr comp; /* the result */
82 /************************************************************************
86 ************************************************************************/
91 * Create a new XSLT CompMatch
93 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
96 xsltNewCompMatch(void) {
99 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
101 xsltGenericError(xsltGenericErrorContext,
102 "xsltNewCompMatch : malloc failed\n");
105 memset(cur, 0, sizeof(xsltCompMatch));
112 * @comp: an XSLT comp
114 * Free up the memory allocated by @comp
117 xsltFreeCompMatch(xsltCompMatchPtr comp) {
123 for (i = 0;i < comp->nbStep;i++) {
124 op = &comp->steps[i];
125 if (op->value != NULL)
127 if (op->value2 != NULL)
130 memset(comp, -1, sizeof(xsltCompMatch));
135 * xsltFreeCompMatchList:
136 * @comp: an XSLT comp list
138 * Free up the memory allocated by all the elements of @comp
141 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
142 xsltCompMatchPtr cur;
144 while (comp != NULL) {
147 xsltFreeCompMatch(cur);
152 * xsltNewParserContext:
154 * Create a new XSLT ParserContext
156 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
159 xsltNewParserContext(void) {
160 xsltParserContextPtr cur;
162 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
164 xsltGenericError(xsltGenericErrorContext,
165 "xsltNewParserContext : malloc failed\n");
168 memset(cur, 0, sizeof(xsltParserContext));
173 * xsltFreeParserContext:
174 * @ctxt: an XSLT parser context
176 * Free up the memory allocated by @ctxt
179 xsltFreeParserContext(xsltParserContextPtr ctxt) {
182 memset(ctxt, -1, sizeof(xsltParserContext));
188 * @comp: the compiled match expression
190 * @value: the first value
191 * @value2: the second value
193 * Add an step to an XSLT Compiled Match
195 * Returns -1 in case of failure, 0 otherwise.
198 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
200 if (comp->nbStep >= 20) {
201 xsltGenericError(xsltGenericErrorContext,
202 "xsltCompMatchAddOp: overflow\n");
205 comp->steps[comp->nbStep].op = op;
206 comp->steps[comp->nbStep].value = value;
207 comp->steps[comp->nbStep].value2 = value2;
213 * xsltReverseCompMatch:
214 * @comp: the compiled match expression
216 * reverse all the stack of expressions
219 xsltReverseCompMatch(xsltCompMatchPtr comp) {
221 int j = comp->nbStep - 1;
224 register xmlChar *tmp;
226 tmp = comp->steps[i].value;
227 comp->steps[i].value = comp->steps[j].value;
228 comp->steps[j].value = tmp;
229 tmp = comp->steps[i].value2;
230 comp->steps[i].value2 = comp->steps[j].value2;
231 comp->steps[j].value2 = tmp;
232 op = comp->steps[i].op;
233 comp->steps[i].op = comp->steps[j].op;
234 comp->steps[j].op = op;
238 comp->steps[comp->nbStep++].op = XSLT_OP_END;
241 /************************************************************************
243 * The interpreter for the precompiled patterns *
245 ************************************************************************/
249 * @comp: the precompiled pattern
252 * Test wether the node matches the pattern
254 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
257 xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) {
261 if ((comp == NULL) || (node == NULL)) {
262 xsltGenericError(xsltGenericErrorContext,
263 "xsltTestCompMatch: null arg\n");
266 for (i = 0;i < comp->nbStep;i++) {
267 step = &comp->steps[i];
272 if ((node->type != XML_DOCUMENT_NODE) &&
273 (node->type != XML_HTML_DOCUMENT_NODE))
277 if (node->type != XML_ELEMENT_NODE)
279 if (step->value == NULL)
281 if (!xmlStrEqual(step->value, node->name))
285 if (node->ns == NULL) {
286 if (step->value2 != NULL)
288 } else if (node->ns->href != NULL) {
289 if (step->value2 == NULL)
291 if (!xmlStrEqual(step->value2, node->ns->href))
296 TODO /* Handle OP_CHILD */
299 if (node->type != XML_ATTRIBUTE_NODE)
301 if (step->value == NULL)
303 if (!xmlStrEqual(step->value, node->name))
307 if (node->ns == NULL) {
308 if (step->value2 != NULL)
310 } else if (node->ns->href != NULL) {
311 if (step->value2 == NULL)
313 if (!xmlStrEqual(step->value2, node->ns->href))
321 if (step->value == NULL)
323 if (!xmlStrEqual(step->value, node->name))
326 if (node->ns == NULL) {
327 if (step->value2 != NULL)
329 } else if (node->ns->href != NULL) {
330 if (step->value2 == NULL)
332 if (!xmlStrEqual(step->value2, node->ns->href))
336 case XSLT_OP_ANCESTOR:
337 /* TODO: implement coalescing of ANCESTOR/NODE ops */
338 if (step->value == NULL) {
340 step = &comp->steps[i];
341 if (step->op == XSLT_OP_ROOT)
343 if (step->op != XSLT_OP_ELEM)
345 if (step->value == NULL)
351 while (node != NULL) {
354 if (xmlStrEqual(step->value, node->name)) {
356 if (node->ns == NULL) {
357 if (step->value2 == NULL)
359 } else if (node->ns->href != NULL) {
360 if ((step->value2 != NULL) &&
361 (xmlStrEqual(step->value2, node->ns->href)))
370 TODO /* Handle IDs, might be done differently */
373 TODO /* Handle Keys, might be done differently */
377 if (node->ns == NULL) {
378 if (step->value != NULL)
380 } else if (node->ns->href != NULL) {
381 if (step->value == NULL)
383 if (!xmlStrEqual(step->value, node->ns->href))
388 switch (node->type) {
389 case XML_DOCUMENT_NODE:
390 case XML_HTML_DOCUMENT_NODE:
391 case XML_ELEMENT_NODE:
397 case XSLT_OP_PREDICATE:
398 TODO /* Handle Predicate */
401 if (node->type != XML_PI_NODE)
403 if (step->value == NULL) {
404 if (!xmlStrEqual(step->value, node->name))
408 case XSLT_OP_COMMENT:
409 if (node->type != XML_COMMENT_NODE)
413 if ((node->type != XML_TEXT_NODE) &&
414 (node->type != XML_CDATA_SECTION_NODE))
418 switch (node->type) {
419 case XML_DOCUMENT_NODE:
420 case XML_HTML_DOCUMENT_NODE:
421 case XML_ELEMENT_NODE:
422 case XML_CDATA_SECTION_NODE:
424 case XML_COMMENT_NODE:
426 case XML_ATTRIBUTE_NODE:
437 /************************************************************************
439 * Dedicated parser for templates *
441 ************************************************************************/
443 #define CUR (*ctxt->cur)
444 #define SKIP(val) ctxt->cur += (val)
445 #define NXT(val) ctxt->cur[(val)]
446 #define CUR_PTR ctxt->cur
448 #define SKIP_BLANKS \
449 while (IS_BLANK(CUR)) NEXT
451 #define CURRENT (*ctxt->cur)
452 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
455 #define PUSH(op, val, val2) \
456 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
458 #define XSLT_ERROR(X) \
459 { xsltError(ctxt, __FILE__, __LINE__, X); \
460 ctxt->error = (X); return; }
462 #define XSLT_ERROR0(X) \
463 { xsltError(ctxt, __FILE__, __LINE__, X); \
464 ctxt->error = (X); return(0); }
468 * @ctxt: the XPath Parser context
470 * Parse an XPath Litteral:
472 * [29] Literal ::= '"' [^"]* '"'
475 * Returns the Literal parsed or NULL
479 xsltScanLiteral(xsltParserContextPtr ctxt) {
487 while ((IS_CHAR(CUR)) && (CUR != '"'))
490 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
494 ret = xmlStrndup(q, CUR_PTR - q);
497 } else if (CUR == '\'') {
500 while ((IS_CHAR(CUR)) && (CUR != '\''))
503 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
507 ret = xmlStrndup(q, CUR_PTR - q);
511 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
520 * @ctxt: the XPath Parser context
522 * Trickery: parse an XML name but without consuming the input flow
523 * Needed to avoid insanity in the parser state.
525 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
526 * CombiningChar | Extender
528 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
530 * [6] Names ::= Name (S Name)*
532 * Returns the Name parsed or NULL
536 xsltScanName(xsltParserContextPtr ctxt) {
537 xmlChar buf[XML_MAX_NAMELEN];
541 if (!IS_LETTER(CUR) && (CUR != '_') &&
546 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
547 (NXT(len) == '.') || (NXT(len) == '-') ||
548 (NXT(len) == '_') || (NXT(len) == ':') ||
549 (IS_COMBINING(NXT(len))) ||
550 (IS_EXTENDER(NXT(len)))) {
553 if (len >= XML_MAX_NAMELEN) {
554 xmlGenericError(xmlGenericErrorContext,
555 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
556 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
557 (NXT(len) == '.') || (NXT(len) == '-') ||
558 (NXT(len) == '_') || (NXT(len) == ':') ||
559 (IS_COMBINING(NXT(len))) ||
560 (IS_EXTENDER(NXT(len))))
566 return(xmlStrndup(buf, len));
569 * xsltCompileIdKeyPattern:
570 * @comp: the compilation context
571 * @name: a preparsed name
572 * @aid: whether id/key are allowed there
574 * Compile the XSLT LocationIdKeyPattern
575 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
576 * | 'key' '(' Literal ',' Literal ')'
578 * also handle NodeType and PI from:
580 * [7] NodeTest ::= NameTest
582 * | 'processing-instruction' '(' Literal ')'
585 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
587 xmlChar *lit2 = NULL;
590 xsltGenericError(xsltGenericErrorContext,
591 "xsltCompileIdKeyPattern : ( expected\n");
595 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
598 lit = xsltScanLiteral(ctxt);
603 xsltGenericError(xsltGenericErrorContext,
604 "xsltCompileIdKeyPattern : ) expected\n");
609 PUSH(XSLT_OP_ID, lit, NULL);
610 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
613 lit = xsltScanLiteral(ctxt);
618 xsltGenericError(xsltGenericErrorContext,
619 "xsltCompileIdKeyPattern : , expected\n");
625 lit2 = xsltScanLiteral(ctxt);
630 xsltGenericError(xsltGenericErrorContext,
631 "xsltCompileIdKeyPattern : ) expected\n");
636 PUSH(XSLT_OP_KEY, lit, NULL);
637 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
641 lit = xsltScanLiteral(ctxt);
646 xsltGenericError(xsltGenericErrorContext,
647 "xsltCompileIdKeyPattern : ) expected\n");
653 PUSH(XSLT_OP_PI, lit, NULL);
654 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
658 xsltGenericError(xsltGenericErrorContext,
659 "xsltCompileIdKeyPattern : ) expected\n");
664 PUSH(XSLT_OP_TEXT, NULL, NULL);
665 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
669 xsltGenericError(xsltGenericErrorContext,
670 "xsltCompileIdKeyPattern : ) expected\n");
675 PUSH(XSLT_OP_COMMENT, NULL, NULL);
676 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
680 xsltGenericError(xsltGenericErrorContext,
681 "xsltCompileIdKeyPattern : ) expected\n");
686 PUSH(XSLT_OP_NODE, NULL, NULL);
688 xsltGenericError(xsltGenericErrorContext,
689 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
693 xsltGenericError(xsltGenericErrorContext,
694 "xsltCompileIdKeyPattern : node type\n");
708 * xsltCompileStepPattern:
709 * @comp: the compilation context
710 * @token: a posible precompiled name
712 * Compile the XSLT StepPattern and generates a precompiled
713 * form suitable for fast matching.
715 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
716 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
717 * | ('child' | 'attribute') '::'
719 * [7] NodeTest ::= NameTest
721 * | 'processing-instruction' '(' Literal ')'
722 * [8] Predicate ::= '[' PredicateExpr ']'
723 * [9] PredicateExpr ::= Expr
724 * [13] AbbreviatedAxisSpecifier ::= '@'?
725 * [37] NameTest ::= '*' | NCName ':' '*' | QName
729 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
730 xmlChar *name = NULL;
733 if ((token == NULL) && (CUR == '@')) {
737 PUSH(XSLT_OP_ATTR, NULL, NULL);
740 token = xsltScanName(ctxt);
742 xsltGenericError(xsltGenericErrorContext,
743 "xsltCompileStepPattern : Name expected\n");
747 PUSH(XSLT_OP_ATTR, token, NULL);
751 token = xsltScanName(ctxt);
755 PUSH(XSLT_OP_ALL, token, NULL);
756 goto parse_predicate;
758 xsltGenericError(xsltGenericErrorContext,
759 "xsltCompileStepPattern : Name expected\n");
766 xsltCompileIdKeyPattern(ctxt, token, 0);
769 } else if (CUR == ':') {
772 xsltGenericError(xsltGenericErrorContext,
773 "xsltCompileStepPattern : sequence '::' expected\n");
778 if (xmlStrEqual(token, (const xmlChar *) "child")) {
779 /* TODO: handle namespace */
780 name = xsltScanName(ctxt);
782 xsltGenericError(xsltGenericErrorContext,
783 "xsltCompileStepPattern : QName expected\n");
787 PUSH(XSLT_OP_CHILD, name, NULL);
788 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
789 /* TODO: handle namespace */
790 name = xsltScanName(ctxt);
792 xsltGenericError(xsltGenericErrorContext,
793 "xsltCompileStepPattern : QName expected\n");
797 PUSH(XSLT_OP_ATTR, name, NULL);
799 xsltGenericError(xsltGenericErrorContext,
800 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
805 } else if (CUR == '*') {
807 PUSH(XSLT_OP_ALL, token, NULL);
809 /* TODO: handle namespace */
810 PUSH(XSLT_OP_ELEM, token, NULL);
820 /* TODO: avoid breaking in strings ... */
821 while ((IS_CHAR(CUR)) && (CUR != ']'))
824 xsltGenericError(xsltGenericErrorContext,
825 "xsltCompileStepPattern : ']' expected\n");
830 ret = xmlStrndup(q, CUR_PTR - q);
831 PUSH(XSLT_OP_PREDICATE, ret, NULL);
842 * xsltCompileRelativePathPattern:
843 * @comp: the compilation context
844 * @token: a posible precompiled name
846 * Compile the XSLT RelativePathPattern and generates a precompiled
847 * form suitable for fast matching.
849 * [4] RelativePathPattern ::= StepPattern
850 * | RelativePathPattern '/' StepPattern
851 * | RelativePathPattern '//' StepPattern
854 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
855 xsltCompileStepPattern(ctxt, token);
859 while ((CUR != 0) && (CUR != '|')) {
860 if ((CUR == '/') && (NXT(1) == '/')) {
861 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
865 xsltCompileStepPattern(ctxt, NULL);
866 } else if (CUR == '/') {
867 PUSH(XSLT_OP_PARENT, NULL, NULL);
870 if ((CUR != 0) || (CUR == '|')) {
871 xsltCompileRelativePathPattern(ctxt, NULL);
885 * xsltCompileLocationPathPattern:
886 * @comp: the compilation context
888 * Compile the XSLT LocationPathPattern and generates a precompiled
889 * form suitable for fast matching.
891 * [2] LocationPathPattern ::= '/' RelativePathPattern?
892 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
893 * | '//'? RelativePathPattern
896 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
898 if ((CUR == '/') && (NXT(1) == '/')) {
900 * since we reverse the query
901 * a leading // can be safely ignored
905 xsltCompileRelativePathPattern(ctxt, NULL);
906 } else if (CUR == '/') {
908 * We need to find root as the parent
912 PUSH(XSLT_OP_ROOT, NULL, NULL);
913 if ((CUR != 0) || (CUR == '|')) {
914 PUSH(XSLT_OP_PARENT, NULL, NULL);
915 xsltCompileRelativePathPattern(ctxt, NULL);
917 } else if (CUR == '*') {
918 xsltCompileRelativePathPattern(ctxt, NULL);
919 } else if (CUR == '@') {
920 xsltCompileRelativePathPattern(ctxt, NULL);
923 name = xsltScanName(ctxt);
925 xsltGenericError(xsltGenericErrorContext,
926 "xsltCompileLocationPathPattern : Name expected\n");
932 xsltCompileIdKeyPattern(ctxt, name, 1);
933 if ((CUR == '/') && (NXT(1) == '/')) {
934 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
938 xsltCompileRelativePathPattern(ctxt, NULL);
939 } else if (CUR == '/') {
940 PUSH(XSLT_OP_PARENT, NULL, NULL);
943 xsltCompileRelativePathPattern(ctxt, NULL);
947 xsltCompileRelativePathPattern(ctxt, name);
954 * xsltCompilePattern:
955 * @pattern an XSLT pattern
957 * Compile the XSLT pattern and generates a precompiled form suitable
959 * Note that the splitting as union of patterns is expected to be handled
962 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
964 * Returns the generated xsltCompMatchPtr or NULL in case of failure
968 xsltCompilePattern(const xmlChar *pattern) {
969 xsltParserContextPtr ctxt;
970 xsltCompMatchPtr ret;
973 if (pattern == NULL) {
974 xsltGenericError(xsltGenericErrorContext,
975 "xsltCompilePattern : NULL pattern\n");
980 xsltGenericDebug(xsltGenericDebugContext,
981 "xsltCompilePattern : parsing '%s'\n", pattern);
985 while (IS_BLANK(*cur)) cur++;
987 xsltGenericError(xsltGenericErrorContext,
988 "xsltCompilePattern : NULL pattern\n");
991 ctxt = xsltNewParserContext();
994 ret = xsltNewCompMatch();
996 xsltFreeParserContext(ctxt);
1001 ctxt->base = pattern;
1003 xsltCompileLocationPathPattern(ctxt);
1008 * Reverse for faster interpretation.
1010 xsltReverseCompMatch(ret);
1013 * Set-up the priority
1015 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
1016 (ret->steps[0].op == XSLT_OP_ATTR)) &&
1017 (ret->steps[0].value != NULL) &&
1018 (ret->steps[1].op == XSLT_OP_END)) {
1020 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
1021 (ret->steps[0].value != NULL) &&
1022 (ret->steps[1].op == XSLT_OP_END)) {
1024 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
1025 (ret->steps[0].value != NULL) &&
1026 (ret->steps[1].op == XSLT_OP_END)) {
1027 ret->priority = -0.25;
1028 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
1029 (ret->steps[0].op == XSLT_OP_TEXT) ||
1030 (ret->steps[0].op == XSLT_OP_NODE) ||
1031 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
1032 (ret->steps[1].op == XSLT_OP_END)) {
1033 ret->priority = -0.5;
1035 ret->priority = 0.5;
1038 xsltFreeParserContext(ctxt);
1042 xsltFreeParserContext(ctxt);
1043 xsltFreeCompMatch(ret);
1049 /************************************************************************
1051 * Module interfaces *
1053 ************************************************************************/
1057 * @style: an XSLT stylesheet
1058 * @cur: an XSLT template
1060 * Register the XSLT pattern associated to @cur
1062 * Returns -1 in case of error, 0 otherwise
1065 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
1066 xsltCompMatchPtr pat, list, *top;
1067 const xmlChar *name = NULL;
1068 xmlChar *p, *pattern, tmp;
1070 if ((style == NULL) || (cur == NULL))
1081 * get a compiled form of the pattern
1084 while ((*p != 0) && (*p != '|')) {
1085 /* TODO: handle string escaping "a | b" in patterns ... */
1091 pat = xsltCompilePattern(pattern);
1097 pat->template = cur;
1098 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1099 pat->priority = cur->priority;
1102 * insert it in the hash table list corresponding to its lookup name
1104 switch (pat->steps[0].op) {
1106 if (pat->steps[0].value != NULL)
1107 name = pat->steps[0].value;
1109 top = (xsltCompMatchPtr *) &(style->attrMatch);
1113 case XSLT_OP_PARENT:
1114 case XSLT_OP_ANCESTOR:
1118 name = pat->steps[0].value;
1121 top = (xsltCompMatchPtr *) &(style->rootMatch);
1124 top = (xsltCompMatchPtr *) &(style->elemMatch);
1127 case XSLT_OP_PREDICATE:
1128 xsltGenericError(xsltGenericErrorContext,
1129 "xsltAddTemplate: invalid compiled pattern\n");
1130 xsltFreeCompMatch(pat);
1133 * TODO: some flags at the top level about type based patterns
1134 * would be faster than inclusion in the hash table.
1137 if (pat->steps[0].value != NULL)
1138 name = pat->steps[0].value;
1140 top = (xsltCompMatchPtr *) &(style->piMatch);
1142 case XSLT_OP_COMMENT:
1143 top = (xsltCompMatchPtr *) &(style->commentMatch);
1146 top = (xsltCompMatchPtr *) &(style->textMatch);
1149 if (pat->steps[0].value != NULL)
1150 name = pat->steps[0].value;
1152 top = (xsltCompMatchPtr *) &(style->elemMatch);
1157 if (style->templatesHash == NULL) {
1158 style->templatesHash = xmlHashCreate(0);
1159 if (style->templatesHash == NULL) {
1160 xsltFreeCompMatch(pat);
1163 #ifdef DEBUG_PARSING
1164 xsltGenericDebug(xsltGenericDebugContext,
1165 "xsltAddTemplate: created template hash\n");
1167 xmlHashAddEntry(style->templatesHash, name, pat);
1168 #ifdef DEBUG_PARSING
1169 xsltGenericDebug(xsltGenericDebugContext,
1170 "xsltAddTemplate: added new hash %s\n", name);
1173 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1175 xmlHashAddEntry(style->templatesHash, name, pat);
1176 #ifdef DEBUG_PARSING
1177 xsltGenericDebug(xsltGenericDebugContext,
1178 "xsltAddTemplate: added new hash %s\n", name);
1182 * Note '<=' since one must choose among the matching
1183 * template rules that are left, the one that occurs
1184 * last in the stylesheet
1186 if (list->priority <= pat->priority) {
1188 xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
1189 #ifdef DEBUG_PARSING
1190 xsltGenericDebug(xsltGenericDebugContext,
1191 "xsltAddTemplate: added head hash for %s\n", name);
1194 while (list->next != NULL) {
1195 if (list->next->priority <= pat->priority)
1198 pat->next = list->next;
1203 } else if (top != NULL) {
1208 } else if (list->priority <= pat->priority) {
1212 while (list->next != NULL) {
1213 if (list->next->priority <= pat->priority)
1216 pat->next = list->next;
1220 xsltGenericError(xsltGenericErrorContext,
1221 "xsltAddTemplate: invalid compiled pattern\n");
1222 xsltFreeCompMatch(pat);
1232 * @style: an XSLT stylesheet
1233 * @node: an XML Node
1235 * Finds the template applying to this node
1237 * Returns the xsltTemplatePtr or NULL if not found
1240 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {
1241 xsltTemplatePtr ret = NULL;
1242 const xmlChar *name = NULL;
1243 xsltCompMatchPtr list = NULL;
1245 if ((style == NULL) || (node == NULL))
1248 /* TODO : handle IDs/keys here ! */
1249 if (style->templatesHash != NULL) {
1251 * Use the top name as selector
1253 switch (node->type) {
1254 case XML_ELEMENT_NODE:
1255 case XML_ATTRIBUTE_NODE:
1259 case XML_DOCUMENT_NODE:
1260 case XML_HTML_DOCUMENT_NODE:
1262 case XML_CDATA_SECTION_NODE:
1263 case XML_COMMENT_NODE:
1264 case XML_ENTITY_REF_NODE:
1265 case XML_ENTITY_NODE:
1266 case XML_DOCUMENT_TYPE_NODE:
1267 case XML_DOCUMENT_FRAG_NODE:
1268 case XML_NOTATION_NODE:
1270 case XML_ELEMENT_DECL:
1271 case XML_ATTRIBUTE_DECL:
1272 case XML_ENTITY_DECL:
1273 case XML_NAMESPACE_DECL:
1274 case XML_XINCLUDE_START:
1275 case XML_XINCLUDE_END:
1284 * find the list of appliable expressions based on the name
1286 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1288 while (list != NULL) {
1289 if (xsltTestCompMatch(list, node)) {
1290 ret = list->template;
1298 * find alternate generic matches
1300 switch (node->type) {
1301 case XML_ELEMENT_NODE:
1302 list = style->elemMatch;
1304 case XML_ATTRIBUTE_NODE:
1305 list = style->attrMatch;
1308 list = style->piMatch;
1310 case XML_DOCUMENT_NODE:
1311 case XML_HTML_DOCUMENT_NODE:
1312 list = style->rootMatch;
1315 case XML_CDATA_SECTION_NODE:
1316 list = style->textMatch;
1318 case XML_COMMENT_NODE:
1319 list = style->commentMatch;
1321 case XML_ENTITY_REF_NODE:
1322 case XML_ENTITY_NODE:
1323 case XML_DOCUMENT_TYPE_NODE:
1324 case XML_DOCUMENT_FRAG_NODE:
1325 case XML_NOTATION_NODE:
1327 case XML_ELEMENT_DECL:
1328 case XML_ATTRIBUTE_DECL:
1329 case XML_ENTITY_DECL:
1330 case XML_NAMESPACE_DECL:
1331 case XML_XINCLUDE_START:
1332 case XML_XINCLUDE_END:
1338 while ((list != NULL) &&
1339 ((ret == NULL) || (list->priority > ret->priority))) {
1340 if (xsltTestCompMatch(list, node)) {
1341 ret = list->template;
1350 * xsltFreeTemplateHashes:
1351 * @style: an XSLT stylesheet
1353 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1356 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1357 if (style->templatesHash != NULL)
1358 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1359 (xmlHashDeallocator) xsltFreeCompMatchList);
1360 if (style->rootMatch != NULL)
1361 xsltFreeCompMatchList(style->rootMatch);
1362 if (style->elemMatch != NULL)
1363 xsltFreeCompMatchList(style->elemMatch);
1364 if (style->attrMatch != NULL)
1365 xsltFreeCompMatchList(style->attrMatch);
1366 if (style->parentMatch != NULL)
1367 xsltFreeCompMatchList(style->parentMatch);
1368 if (style->textMatch != NULL)
1369 xsltFreeCompMatchList(style->textMatch);
1370 if (style->piMatch != NULL)
1371 xsltFreeCompMatchList(style->piMatch);
1372 if (style->commentMatch != NULL)
1373 xsltFreeCompMatchList(style->commentMatch);
1378 * @style: an XSLT stylesheet
1379 * @name: the template name
1380 * @nameURI: the template name URI
1382 * Finds the named template.
1384 * Returns the xsltTemplatePtr or NULL if not found
1387 xsltFindTemplate(xsltStylesheetPtr style, const xmlChar *name,
1388 const xmlChar *nameURI) {
1389 xsltTemplatePtr cur;
1391 if ((style == NULL) || (name == NULL))
1394 /* TODO: apply stylesheet import order */
1395 cur = style->templates;
1396 while (cur != NULL) {
1397 if (xmlStrEqual(name, cur->name)) {
1398 if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
1399 ((nameURI != NULL) && (cur->nameURI != NULL) &&
1400 (xmlStrEqual(nameURI, cur->nameURI)))) {