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");
706 * xsltCompileStepPattern:
707 * @comp: the compilation context
708 * @token: a posible precompiled name
710 * Compile the XSLT StepPattern and generates a precompiled
711 * form suitable for fast matching.
713 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
714 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
715 * | ('child' | 'attribute') '::'
717 * [7] NodeTest ::= NameTest
719 * | 'processing-instruction' '(' Literal ')'
720 * [8] Predicate ::= '[' PredicateExpr ']'
721 * [9] PredicateExpr ::= Expr
722 * [13] AbbreviatedAxisSpecifier ::= '@'?
723 * [37] NameTest ::= '*' | NCName ':' '*' | QName
727 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
728 xmlChar *name = NULL;
731 if ((token == NULL) && (CUR == '@')) {
732 token = xsltScanName(ctxt);
734 xsltGenericError(xsltGenericErrorContext,
735 "xsltCompileStepPattern : Name expected\n");
739 PUSH(XSLT_OP_ATTR, token, NULL);
743 token = xsltScanName(ctxt);
747 PUSH(XSLT_OP_ALL, token, NULL);
748 goto parse_predicate;
750 xsltGenericError(xsltGenericErrorContext,
751 "xsltCompileStepPattern : Name expected\n");
758 xsltCompileIdKeyPattern(ctxt, token, 0);
761 } else if (CUR == ':') {
764 xsltGenericError(xsltGenericErrorContext,
765 "xsltCompileStepPattern : sequence '::' expected\n");
770 if (xmlStrEqual(token, (const xmlChar *) "child")) {
771 /* TODO: handle namespace */
772 name = xsltScanName(ctxt);
774 xsltGenericError(xsltGenericErrorContext,
775 "xsltCompileStepPattern : QName expected\n");
779 PUSH(XSLT_OP_CHILD, name, NULL);
780 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
781 /* TODO: handle namespace */
782 name = xsltScanName(ctxt);
784 xsltGenericError(xsltGenericErrorContext,
785 "xsltCompileStepPattern : QName expected\n");
789 PUSH(XSLT_OP_ATTR, name, NULL);
791 xsltGenericError(xsltGenericErrorContext,
792 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
797 } else if (CUR == '*') {
799 PUSH(XSLT_OP_ALL, token, NULL);
801 /* TODO: handle namespace */
802 PUSH(XSLT_OP_ELEM, token, NULL);
812 /* TODO: avoid breaking in strings ... */
813 while ((IS_CHAR(CUR)) && (CUR != ']'))
816 xsltGenericError(xsltGenericErrorContext,
817 "xsltCompileStepPattern : ']' expected\n");
822 ret = xmlStrndup(q, CUR_PTR - q);
823 PUSH(XSLT_OP_PREDICATE, ret, NULL);
834 * xsltCompileRelativePathPattern:
835 * @comp: the compilation context
836 * @token: a posible precompiled name
838 * Compile the XSLT RelativePathPattern and generates a precompiled
839 * form suitable for fast matching.
841 * [4] RelativePathPattern ::= StepPattern
842 * | RelativePathPattern '/' StepPattern
843 * | RelativePathPattern '//' StepPattern
846 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
847 xsltCompileStepPattern(ctxt, token);
851 while ((CUR != 0) && (CUR != '|')) {
852 if ((CUR == '/') && (NXT(1) == '/')) {
853 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
857 xsltCompileStepPattern(ctxt, NULL);
858 } else if (CUR == '/') {
859 PUSH(XSLT_OP_PARENT, NULL, NULL);
862 if ((CUR != 0) || (CUR == '|')) {
863 xsltCompileRelativePathPattern(ctxt, NULL);
877 * xsltCompileLocationPathPattern:
878 * @comp: the compilation context
880 * Compile the XSLT LocationPathPattern and generates a precompiled
881 * form suitable for fast matching.
883 * [2] LocationPathPattern ::= '/' RelativePathPattern?
884 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
885 * | '//'? RelativePathPattern
888 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
890 if ((CUR == '/') && (NXT(1) == '/')) {
892 * since we reverse the query
893 * a leading // can be safely ignored
897 xsltCompileRelativePathPattern(ctxt, NULL);
898 } else if (CUR == '/') {
900 * We need to find root as the parent
904 PUSH(XSLT_OP_ROOT, NULL, NULL);
905 if ((CUR != 0) || (CUR == '|')) {
906 PUSH(XSLT_OP_PARENT, NULL, NULL);
907 xsltCompileRelativePathPattern(ctxt, NULL);
909 } else if (CUR == '*') {
910 xsltCompileRelativePathPattern(ctxt, NULL);
913 name = xsltScanName(ctxt);
915 xsltGenericError(xsltGenericErrorContext,
916 "xsltCompilePattern : Name expected\n");
922 xsltCompileIdKeyPattern(ctxt, name, 1);
923 if ((CUR == '/') && (NXT(1) == '/')) {
924 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
928 xsltCompileRelativePathPattern(ctxt, NULL);
929 } else if (CUR == '/') {
930 PUSH(XSLT_OP_PARENT, NULL, NULL);
933 xsltCompileRelativePathPattern(ctxt, NULL);
937 xsltCompileRelativePathPattern(ctxt, name);
944 * xsltCompilePattern:
945 * @pattern an XSLT pattern
947 * Compile the XSLT pattern and generates a precompiled form suitable
949 * Note that the splitting as union of patterns is expected to be handled
952 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
954 * Returns the generated xsltCompMatchPtr or NULL in case of failure
958 xsltCompilePattern(const xmlChar *pattern) {
959 xsltParserContextPtr ctxt;
960 xsltCompMatchPtr ret;
963 if (pattern == NULL) {
964 xsltGenericError(xsltGenericErrorContext,
965 "xsltCompilePattern : NULL pattern\n");
970 xsltGenericDebug(xsltGenericDebugContext,
971 "xsltCompilePattern : parsing '%s'\n", pattern);
975 while (IS_BLANK(*cur)) cur++;
977 xsltGenericError(xsltGenericErrorContext,
978 "xsltCompilePattern : NULL pattern\n");
981 ctxt = xsltNewParserContext();
984 ret = xsltNewCompMatch();
986 xsltFreeParserContext(ctxt);
991 ctxt->base = pattern;
993 xsltCompileLocationPathPattern(ctxt);
998 * Reverse for faster interpretation.
1000 xsltReverseCompMatch(ret);
1003 * Set-up the priority
1005 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
1006 (ret->steps[0].op == XSLT_OP_ATTR)) &&
1007 (ret->steps[0].value != NULL) &&
1008 (ret->steps[1].op == XSLT_OP_END)) {
1010 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
1011 (ret->steps[0].value != NULL) &&
1012 (ret->steps[1].op == XSLT_OP_END)) {
1014 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
1015 (ret->steps[0].value != NULL) &&
1016 (ret->steps[1].op == XSLT_OP_END)) {
1017 ret->priority = -0.25;
1018 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
1019 (ret->steps[0].op == XSLT_OP_TEXT) ||
1020 (ret->steps[0].op == XSLT_OP_NODE) ||
1021 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
1022 (ret->steps[1].op == XSLT_OP_END)) {
1023 ret->priority = -0.5;
1025 ret->priority = 0.5;
1028 xsltFreeParserContext(ctxt);
1032 xsltFreeParserContext(ctxt);
1033 xsltFreeCompMatch(ret);
1039 /************************************************************************
1041 * Module interfaces *
1043 ************************************************************************/
1047 * @style: an XSLT stylesheet
1048 * @cur: an XSLT template
1050 * Register the XSLT pattern associated to @cur
1052 * Returns -1 in case of error, 0 otherwise
1055 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
1056 xsltCompMatchPtr pat, list, *top;
1057 const xmlChar *name = NULL;
1058 xmlChar *p, *pattern, tmp;
1060 if ((style == NULL) || (cur == NULL))
1071 * get a compiled form of the pattern
1074 while ((*p != 0) && (*p != '|')) {
1075 /* TODO: handle string escaping "a | b" in patterns ... */
1081 pat = xsltCompilePattern(pattern);
1087 pat->template = cur;
1088 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1089 pat->priority = cur->priority;
1092 * insert it in the hash table list corresponding to its lookup name
1094 switch (pat->steps[0].op) {
1096 if (pat->steps[0].value != NULL)
1097 name = pat->steps[0].value;
1099 top = (xsltCompMatchPtr *) &(style->attrMatch);
1103 case XSLT_OP_PARENT:
1104 case XSLT_OP_ANCESTOR:
1108 name = pat->steps[0].value;
1111 top = (xsltCompMatchPtr *) &(style->rootMatch);
1114 top = (xsltCompMatchPtr *) &(style->elemMatch);
1117 case XSLT_OP_PREDICATE:
1118 xsltGenericError(xsltGenericErrorContext,
1119 "xsltAddTemplate: invalid compiled pattern\n");
1120 xsltFreeCompMatch(pat);
1123 * TODO: some flags at the top level about type based patterns
1124 * would be faster than inclusion in the hash table.
1127 if (pat->steps[0].value != NULL)
1128 name = pat->steps[0].value;
1130 top = (xsltCompMatchPtr *) &(style->piMatch);
1132 case XSLT_OP_COMMENT:
1133 top = (xsltCompMatchPtr *) &(style->commentMatch);
1136 top = (xsltCompMatchPtr *) &(style->textMatch);
1139 if (pat->steps[0].value != NULL)
1140 name = pat->steps[0].value;
1142 top = (xsltCompMatchPtr *) &(style->elemMatch);
1147 if (style->templatesHash == NULL) {
1148 style->templatesHash = xmlHashCreate(0);
1149 if (style->templatesHash == NULL) {
1150 xsltFreeCompMatch(pat);
1153 #ifdef DEBUG_PARSING
1154 xsltGenericDebug(xsltGenericDebugContext,
1155 "xsltAddTemplate: created template hash\n");
1157 xmlHashAddEntry(style->templatesHash, name, pat);
1158 #ifdef DEBUG_PARSING
1159 xsltGenericDebug(xsltGenericDebugContext,
1160 "xsltAddTemplate: added new hash %s\n", name);
1163 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1165 xmlHashAddEntry(style->templatesHash, name, pat);
1166 #ifdef DEBUG_PARSING
1167 xsltGenericDebug(xsltGenericDebugContext,
1168 "xsltAddTemplate: added new hash %s\n", name);
1172 * Note '<=' since one must choose among the matching
1173 * template rules that are left, the one that occurs
1174 * last in the stylesheet
1176 if (list->priority <= pat->priority) {
1178 xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
1179 #ifdef DEBUG_PARSING
1180 xsltGenericDebug(xsltGenericDebugContext,
1181 "xsltAddTemplate: added head hash for %s\n", name);
1184 while (list->next != NULL) {
1185 if (list->next->priority <= pat->priority)
1188 pat->next = list->next;
1193 } else if (top != NULL) {
1198 } else if (list->priority <= pat->priority) {
1202 while (list->next != NULL) {
1203 if (list->next->priority <= pat->priority)
1206 pat->next = list->next;
1210 xsltGenericError(xsltGenericErrorContext,
1211 "xsltAddTemplate: invalid compiled pattern\n");
1212 xsltFreeCompMatch(pat);
1222 * @style: an XSLT stylesheet
1223 * @node: an XML Node
1225 * Finds the template applying to this node
1227 * Returns the xsltTemplatePtr or NULL if not found
1230 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {
1231 xsltTemplatePtr ret = NULL;
1232 const xmlChar *name = NULL;
1233 xsltCompMatchPtr list = NULL;
1235 if ((style == NULL) || (node == NULL))
1238 /* TODO : handle IDs/keys here ! */
1239 if (style->templatesHash != NULL) {
1241 * Use the top name as selector
1243 switch (node->type) {
1244 case XML_ELEMENT_NODE:
1245 case XML_ATTRIBUTE_NODE:
1249 case XML_DOCUMENT_NODE:
1250 case XML_HTML_DOCUMENT_NODE:
1252 case XML_CDATA_SECTION_NODE:
1253 case XML_COMMENT_NODE:
1254 case XML_ENTITY_REF_NODE:
1255 case XML_ENTITY_NODE:
1256 case XML_DOCUMENT_TYPE_NODE:
1257 case XML_DOCUMENT_FRAG_NODE:
1258 case XML_NOTATION_NODE:
1260 case XML_ELEMENT_DECL:
1261 case XML_ATTRIBUTE_DECL:
1262 case XML_ENTITY_DECL:
1263 case XML_NAMESPACE_DECL:
1264 case XML_XINCLUDE_START:
1265 case XML_XINCLUDE_END:
1274 * find the list of appliable expressions based on the name
1276 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1278 while (list != NULL) {
1279 if (xsltTestCompMatch(list, node)) {
1280 ret = list->template;
1288 * find alternate generic matches
1290 switch (node->type) {
1291 case XML_ELEMENT_NODE:
1292 list = style->elemMatch;
1294 case XML_ATTRIBUTE_NODE:
1295 list = style->attrMatch;
1298 list = style->piMatch;
1300 case XML_DOCUMENT_NODE:
1301 case XML_HTML_DOCUMENT_NODE:
1302 list = style->rootMatch;
1305 case XML_CDATA_SECTION_NODE:
1306 list = style->textMatch;
1308 case XML_COMMENT_NODE:
1309 list = style->commentMatch;
1311 case XML_ENTITY_REF_NODE:
1312 case XML_ENTITY_NODE:
1313 case XML_DOCUMENT_TYPE_NODE:
1314 case XML_DOCUMENT_FRAG_NODE:
1315 case XML_NOTATION_NODE:
1317 case XML_ELEMENT_DECL:
1318 case XML_ATTRIBUTE_DECL:
1319 case XML_ENTITY_DECL:
1320 case XML_NAMESPACE_DECL:
1321 case XML_XINCLUDE_START:
1322 case XML_XINCLUDE_END:
1328 while ((list != NULL) &&
1329 ((ret == NULL) || (list->priority > ret->priority))) {
1330 if (xsltTestCompMatch(list, node)) {
1331 ret = list->template;
1340 * xsltFreeTemplateHashes:
1341 * @style: an XSLT stylesheet
1343 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1346 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1347 if (style->templatesHash != NULL)
1348 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1349 (xmlHashDeallocator) xsltFreeCompMatchList);
1350 if (style->rootMatch != NULL)
1351 xsltFreeCompMatchList(style->rootMatch);
1352 if (style->elemMatch != NULL)
1353 xsltFreeCompMatchList(style->elemMatch);
1354 if (style->attrMatch != NULL)
1355 xsltFreeCompMatchList(style->attrMatch);
1356 if (style->parentMatch != NULL)
1357 xsltFreeCompMatchList(style->parentMatch);
1358 if (style->textMatch != NULL)
1359 xsltFreeCompMatchList(style->textMatch);
1360 if (style->piMatch != NULL)
1361 xsltFreeCompMatchList(style->piMatch);
1362 if (style->commentMatch != NULL)
1363 xsltFreeCompMatchList(style->commentMatch);
1368 * @style: an XSLT stylesheet
1369 * @name: the template name
1370 * @nameURI: the template name URI
1372 * Finds the named template.
1374 * Returns the xsltTemplatePtr or NULL if not found
1377 xsltFindTemplate(xsltStylesheetPtr style, const xmlChar *name,
1378 const xmlChar *nameURI) {
1379 xsltTemplatePtr cur;
1381 if ((style == NULL) || (name == NULL))
1384 /* TODO: apply stylesheet import order */
1385 cur = style->templates;
1386 while (cur != NULL) {
1387 if (xmlStrEqual(name, cur->name)) {
1388 if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
1389 ((nameURI != NULL) && (cur->nameURI != NULL) &&
1390 (xmlStrEqual(nameURI, cur->nameURI)))) {