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 "xsltCompilePattern : Name expected\n");
739 PUSH(XSLT_OP_ATTR, token, NULL);
743 token = xsltScanName(ctxt);
745 xsltGenericError(xsltGenericErrorContext,
746 "xsltCompilePattern : Name expected\n");
752 xsltCompileIdKeyPattern(ctxt, token, 0);
755 } else if (CUR == ':') {
758 xsltGenericError(xsltGenericErrorContext,
759 "xsltCompilePattern : sequence '::' expected\n");
764 if (xmlStrEqual(token, (const xmlChar *) "child")) {
765 /* TODO: handle namespace */
766 name = xsltScanName(ctxt);
768 xsltGenericError(xsltGenericErrorContext,
769 "xsltCompilePattern : QName expected\n");
773 PUSH(XSLT_OP_CHILD, name, NULL);
774 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
775 /* TODO: handle namespace */
776 name = xsltScanName(ctxt);
778 xsltGenericError(xsltGenericErrorContext,
779 "xsltCompilePattern : QName expected\n");
783 PUSH(XSLT_OP_ATTR, name, NULL);
785 xsltGenericError(xsltGenericErrorContext,
786 "xsltCompilePattern : 'child' or 'attribute' expected\n");
791 } else if (CUR == '*') {
793 PUSH(XSLT_OP_ALL, token, NULL);
795 /* TODO: handle namespace */
796 PUSH(XSLT_OP_ELEM, token, NULL);
805 /* TODO: avoid breaking in strings ... */
806 while ((IS_CHAR(CUR)) && (CUR != ']'))
809 xsltGenericError(xsltGenericErrorContext,
810 "xsltCompilePattern : ']' expected\n");
815 ret = xmlStrndup(q, CUR_PTR - q);
816 PUSH(XSLT_OP_PREDICATE, ret, NULL);
827 * xsltCompileRelativePathPattern:
828 * @comp: the compilation context
829 * @token: a posible precompiled name
831 * Compile the XSLT RelativePathPattern and generates a precompiled
832 * form suitable for fast matching.
834 * [4] RelativePathPattern ::= StepPattern
835 * | RelativePathPattern '/' StepPattern
836 * | RelativePathPattern '//' StepPattern
839 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
840 xsltCompileStepPattern(ctxt, token);
844 while ((CUR != 0) && (CUR != '|')) {
845 if ((CUR == '/') && (NXT(1) == '/')) {
846 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
850 xsltCompileStepPattern(ctxt, NULL);
851 } else if (CUR == '/') {
852 PUSH(XSLT_OP_PARENT, NULL, NULL);
855 if ((CUR != 0) || (CUR == '|')) {
856 xsltCompileRelativePathPattern(ctxt, NULL);
870 * xsltCompileLocationPathPattern:
871 * @comp: the compilation context
873 * Compile the XSLT LocationPathPattern and generates a precompiled
874 * form suitable for fast matching.
876 * [2] LocationPathPattern ::= '/' RelativePathPattern?
877 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
878 * | '//'? RelativePathPattern
881 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
883 if ((CUR == '/') && (NXT(1) == '/')) {
885 * since we reverse the query
886 * a leading // can be safely ignored
890 xsltCompileRelativePathPattern(ctxt, NULL);
891 } else if (CUR == '/') {
893 * We need to find root as the parent
897 PUSH(XSLT_OP_ROOT, NULL, NULL);
898 if ((CUR != 0) || (CUR == '|')) {
899 PUSH(XSLT_OP_PARENT, NULL, NULL);
900 xsltCompileRelativePathPattern(ctxt, NULL);
904 name = xsltScanName(ctxt);
906 xsltGenericError(xsltGenericErrorContext,
907 "xsltCompilePattern : Name expected\n");
913 xsltCompileIdKeyPattern(ctxt, name, 1);
914 if ((CUR == '/') && (NXT(1) == '/')) {
915 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
919 xsltCompileRelativePathPattern(ctxt, NULL);
920 } else if (CUR == '/') {
921 PUSH(XSLT_OP_PARENT, NULL, NULL);
924 xsltCompileRelativePathPattern(ctxt, NULL);
928 xsltCompileRelativePathPattern(ctxt, name);
935 * xsltCompilePattern:
936 * @pattern an XSLT pattern
938 * Compile the XSLT pattern and generates a precompiled form suitable
940 * Note that the splitting as union of patterns is expected to be handled
943 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
945 * Returns the generated xsltCompMatchPtr or NULL in case of failure
949 xsltCompilePattern(const xmlChar *pattern) {
950 xsltParserContextPtr ctxt;
951 xsltCompMatchPtr ret;
954 if (pattern == NULL) {
955 xsltGenericError(xsltGenericErrorContext,
956 "xsltCompilePattern : NULL pattern\n");
961 xsltGenericDebug(xsltGenericDebugContext,
962 "xsltCompilePattern : parsing '%s'\n", pattern);
966 while (IS_BLANK(*cur)) cur++;
968 xsltGenericError(xsltGenericErrorContext,
969 "xsltCompilePattern : NULL pattern\n");
972 ctxt = xsltNewParserContext();
975 ret = xsltNewCompMatch();
977 xsltFreeParserContext(ctxt);
982 ctxt->base = pattern;
984 xsltCompileLocationPathPattern(ctxt);
989 * Reverse for faster interpretation.
991 xsltReverseCompMatch(ret);
994 * Set-up the priority
996 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
997 (ret->steps[0].op == XSLT_OP_ATTR)) &&
998 (ret->steps[0].value != NULL) &&
999 (ret->steps[1].op == XSLT_OP_END)) {
1001 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
1002 (ret->steps[0].value != NULL) &&
1003 (ret->steps[1].op == XSLT_OP_END)) {
1005 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
1006 (ret->steps[0].value != NULL) &&
1007 (ret->steps[1].op == XSLT_OP_END)) {
1008 ret->priority = -0.25;
1009 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
1010 (ret->steps[0].op == XSLT_OP_TEXT) ||
1011 (ret->steps[0].op == XSLT_OP_NODE) ||
1012 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
1013 (ret->steps[1].op == XSLT_OP_END)) {
1014 ret->priority = -0.5;
1016 ret->priority = 0.5;
1019 xsltFreeParserContext(ctxt);
1023 xsltFreeParserContext(ctxt);
1024 xsltFreeCompMatch(ret);
1030 /************************************************************************
1032 * Module interfaces *
1034 ************************************************************************/
1038 * @style: an XSLT stylesheet
1039 * @cur: an XSLT template
1041 * Register the XSLT pattern associated to @cur
1043 * Returns -1 in case of error, 0 otherwise
1046 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
1047 xsltCompMatchPtr pat, list;
1048 const xmlChar *name = NULL;
1049 xmlChar *p, *pattern, tmp;
1051 if ((style == NULL) || (cur == NULL))
1062 * get a compiled form of the pattern
1065 while ((*p != 0) && (*p != '|')) {
1066 /* TODO: handle string escaping "a | b" in patterns ... */
1072 pat = xsltCompilePattern(pattern);
1078 pat->template = cur;
1079 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1080 pat->priority = cur->priority;
1083 * insert it in the hash table list corresponding to its lookup name
1085 switch (pat->steps[0].op) {
1089 case XSLT_OP_PARENT:
1090 case XSLT_OP_ANCESTOR:
1094 name = pat->steps[0].value;
1097 name = (const xmlChar *) "/";
1100 name = (const xmlChar *) "*";
1103 case XSLT_OP_PREDICATE:
1104 xsltGenericError(xsltGenericErrorContext,
1105 "xsltAddTemplate: invalid compiled pattern\n");
1106 xsltFreeCompMatch(pat);
1109 * TODO: some flags at the top level about type based patterns
1110 * would be faster than inclusion in the hash table.
1113 name = (const xmlChar *) "processing-instruction()";
1115 case XSLT_OP_COMMENT:
1116 name = (const xmlChar *) "comment()";
1119 name = (const xmlChar *) "text()";
1122 name = (const xmlChar *) "node()";
1126 xsltGenericError(xsltGenericErrorContext,
1127 "xsltAddTemplate: invalid compiled pattern\n");
1128 xsltFreeCompMatch(pat);
1131 if (style->templatesHash == NULL) {
1132 style->templatesHash = xmlHashCreate(0);
1133 if (style->templatesHash == NULL) {
1134 xsltFreeCompMatch(pat);
1137 #ifdef DEBUG_PARSING
1138 xsltGenericDebug(xsltGenericDebugContext,
1139 "xsltAddTemplate: created template hash\n");
1141 xmlHashAddEntry(style->templatesHash, name, pat);
1142 #ifdef DEBUG_PARSING
1143 xsltGenericDebug(xsltGenericDebugContext,
1144 "xsltAddTemplate: added new hash %s\n", name);
1147 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1149 xmlHashAddEntry(style->templatesHash, name, pat);
1150 #ifdef DEBUG_PARSING
1151 xsltGenericDebug(xsltGenericDebugContext,
1152 "xsltAddTemplate: added new hash %s\n", name);
1156 * Note '<=' since one must choose among the matching template
1157 * rules that are left, the one that occurs last in the stylesheet
1159 if (list->priority <= pat->priority) {
1161 xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
1162 #ifdef DEBUG_PARSING
1163 xsltGenericDebug(xsltGenericDebugContext,
1164 "xsltAddTemplate: added head hash for %s\n", name);
1167 while (list->next != NULL) {
1168 if (list->next->priority <= pat->priority)
1171 pat->next = list->next;
1183 * @style: an XSLT stylesheet
1184 * @node: an XML Node
1186 * Finds the template applying to this node
1188 * Returns the xsltTemplatePtr or NULL if not found
1191 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {
1192 const xmlChar *name;
1193 xsltCompMatchPtr list;
1195 if ((style == NULL) || (node == NULL))
1198 /* TODO : handle IDs/keys here ! */
1199 if (style->templatesHash == NULL)
1203 * Use a name as selector
1205 switch (node->type) {
1206 case XML_ELEMENT_NODE:
1207 case XML_ATTRIBUTE_NODE:
1211 case XML_DOCUMENT_NODE:
1212 case XML_HTML_DOCUMENT_NODE:
1213 name = (const xmlChar *)"/";
1216 case XML_CDATA_SECTION_NODE:
1217 case XML_ENTITY_REF_NODE:
1218 case XML_ENTITY_NODE:
1219 case XML_COMMENT_NODE:
1220 case XML_DOCUMENT_TYPE_NODE:
1221 case XML_DOCUMENT_FRAG_NODE:
1222 case XML_NOTATION_NODE:
1224 case XML_ELEMENT_DECL:
1225 case XML_ATTRIBUTE_DECL:
1226 case XML_ENTITY_DECL:
1227 case XML_NAMESPACE_DECL:
1228 case XML_XINCLUDE_START:
1229 case XML_XINCLUDE_END:
1239 * find the list of appliable expressions based on the name
1241 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1243 #ifdef DEBUG_MATCHING
1244 xsltGenericDebug(xsltGenericDebugContext,
1245 "xsltGetTemplate: empty set for %s\n", name);
1249 while (list != NULL) {
1250 if (xsltTestCompMatch(list, node))
1251 return(list->template);
1260 * xsltFreeTemplateHashes:
1261 * @style: an XSLT stylesheet
1263 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1266 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1267 if (style->templatesHash != NULL)
1268 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1269 (xmlHashDeallocator) xsltFreeCompMatchList);
1274 * @style: an XSLT stylesheet
1275 * @name: the template name
1276 * @nameURI: the template name URI
1278 * Finds the named template.
1280 * Returns the xsltTemplatePtr or NULL if not found
1283 xsltFindTemplate(xsltStylesheetPtr style, const xmlChar *name,
1284 const xmlChar *nameURI) {
1285 xsltTemplatePtr cur;
1287 if ((style == NULL) || (name == NULL))
1290 /* TODO: apply stylesheet import order */
1291 cur = style->templates;
1292 while (cur != NULL) {
1293 if (xmlStrEqual(name, cur->name)) {
1294 if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
1295 ((nameURI != NULL) && (cur->nameURI != NULL) &&
1296 (xmlStrEqual(nameURI, cur->nameURI)))) {