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"
25 /* #define DEBUG_PARSING */
28 xsltGenericError(xsltGenericErrorContext, \
29 "Unimplemented block at %s:%d\n", \
35 xmlChar *xmlSplitQName2(const xmlChar *name, xmlChar **prefix);
38 * There is no XSLT specific error reporting module yet
40 #define xsltGenericError xmlGenericError
41 #define xsltGenericErrorContext xmlGenericErrorContext
67 typedef struct _xsltStepOp xsltStepOp;
68 typedef xsltStepOp *xsltStepOpPtr;
75 typedef struct _xsltCompMatch xsltCompMatch;
76 typedef xsltCompMatch *xsltCompMatchPtr;
77 struct _xsltCompMatch {
78 struct _xsltCompMatch *next; /* siblings in the name hash */
79 float priority; /* the priority */
80 xsltTemplatePtr template; /* the associated template */
82 /* TODO fix the statically allocated size steps[] */
85 xsltStepOp steps[20]; /* ops for computation */
88 typedef struct _xsltParserContext xsltParserContext;
89 typedef xsltParserContext *xsltParserContextPtr;
90 struct _xsltParserContext {
91 const xmlChar *cur; /* the current char being parsed */
92 const xmlChar *base; /* the full expression */
93 int error; /* error code */
94 xsltCompMatchPtr comp; /* the result */
97 /************************************************************************
101 ************************************************************************/
106 * Create a new XSLT CompMatch
108 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
111 xsltNewCompMatch(void) {
112 xsltCompMatchPtr cur;
114 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
116 xsltGenericError(xsltGenericErrorContext,
117 "xsltNewCompMatch : malloc failed\n");
120 memset(cur, 0, sizeof(xsltCompMatch));
127 * @comp: an XSLT comp
129 * Free up the memory allocated by @comp
132 xsltFreeCompMatch(xsltCompMatchPtr comp) {
138 for (i = 0;i < comp->nbStep;i++) {
139 op = &comp->steps[i];
140 if (op->value != NULL)
142 if (op->value2 != NULL)
145 memset(comp, -1, sizeof(xsltCompMatch));
150 * xsltFreeCompMatchList:
151 * @comp: an XSLT comp list
153 * Free up the memory allocated by all the elements of @comp
156 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
157 xsltCompMatchPtr cur;
159 while (comp != NULL) {
162 xsltFreeCompMatch(cur);
167 * xsltNewParserContext:
169 * Create a new XSLT ParserContext
171 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
174 xsltNewParserContext(void) {
175 xsltParserContextPtr cur;
177 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
179 xsltGenericError(xsltGenericErrorContext,
180 "xsltNewParserContext : malloc failed\n");
183 memset(cur, 0, sizeof(xsltParserContext));
188 * xsltFreeParserContext:
189 * @ctxt: an XSLT parser context
191 * Free up the memory allocated by @ctxt
194 xsltFreeParserContext(xsltParserContextPtr ctxt) {
197 memset(ctxt, -1, sizeof(xsltParserContext));
203 * @comp: the compiled match expression
205 * @value: the first value
206 * @value2: the second value
208 * Add an step to an XSLT Compiled Match
210 * Returns -1 in case of failure, 0 otherwise.
213 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
215 if (comp->nbStep >= 20) {
216 xsltGenericError(xsltGenericErrorContext,
217 "xsltCompMatchAddOp: overflow\n");
220 comp->steps[comp->nbStep].op = op;
221 comp->steps[comp->nbStep].value = value;
222 comp->steps[comp->nbStep].value2 = value2;
228 * xsltReverseCompMatch:
229 * @comp: the compiled match expression
231 * reverse all the stack of expressions
234 xsltReverseCompMatch(xsltCompMatchPtr comp) {
236 int j = comp->nbStep - 1;
239 register xmlChar *tmp;
241 tmp = comp->steps[i].value;
242 comp->steps[i].value = comp->steps[j].value;
243 comp->steps[j].value = tmp;
244 tmp = comp->steps[i].value2;
245 comp->steps[i].value2 = comp->steps[j].value2;
246 comp->steps[j].value2 = tmp;
247 op = comp->steps[i].op;
248 comp->steps[i].op = comp->steps[j].op;
249 comp->steps[j].op = op;
253 comp->steps[comp->nbStep++].op = XSLT_OP_END;
256 /************************************************************************
258 * The interpreter for the precompiled patterns *
260 ************************************************************************/
264 * @comp: the precompiled pattern
267 * Test wether the node matches the pattern
269 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
272 xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) {
276 if ((comp == NULL) || (node == NULL)) {
277 xsltGenericError(xsltGenericErrorContext,
278 "xsltTestCompMatch: null arg\n");
281 for (i = 0;i < comp->nbStep;i++) {
282 step = &comp->steps[i];
287 if ((node->type != XML_DOCUMENT_NODE) &&
288 (node->type != XML_HTML_DOCUMENT_NODE))
292 if (node->type != XML_ELEMENT_NODE)
294 if (step->value == NULL)
296 if (!xmlStrEqual(step->value, node->name))
298 /* TODO: Handle namespace ... */
301 TODO /* Handle OP_CHILD */
304 if (node->type != XML_ATTRIBUTE_NODE)
306 if (step->value == NULL)
308 if (!xmlStrEqual(step->value, node->name))
310 /* TODO: Handle namespace ... */
316 if (step->value == NULL)
318 if (!xmlStrEqual(step->value, node->name))
320 /* TODO: Handle namespace ... */
322 case XSLT_OP_ANCESTOR:
323 /* TODO: implement coalescing of ANCESTOR/NODE ops */
324 if (step->value == NULL) {
326 step = &comp->steps[i];
327 if (step->op == XSLT_OP_ROOT)
329 if (step->op != XSLT_OP_ELEM)
331 if (step->value == NULL)
337 while (node != NULL) {
340 if (xmlStrEqual(step->value, node->name)) {
341 /* TODO: Handle namespace ... */
349 TODO /* Handle IDs, might be done differently */
352 TODO /* Handle Keys, might be done differently */
355 TODO /* Handle Namespace */
360 case XSLT_OP_PREDICATE:
361 TODO /* Handle Predicate */
364 TODO /* Handle PI() */
366 case XSLT_OP_COMMENT:
367 TODO /* Handle Comments() */
370 TODO /* Handle Text() */
373 TODO /* Handle Node() */
380 /************************************************************************
382 * Dedicated parser for templates *
384 ************************************************************************/
386 #define CUR (*ctxt->cur)
387 #define SKIP(val) ctxt->cur += (val)
388 #define NXT(val) ctxt->cur[(val)]
389 #define CUR_PTR ctxt->cur
391 #define SKIP_BLANKS \
392 while (IS_BLANK(CUR)) NEXT
394 #define CURRENT (*ctxt->cur)
395 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
398 #define PUSH(op, val, val2) \
399 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
401 #define XSLT_ERROR(X) \
402 { xsltError(ctxt, __FILE__, __LINE__, X); \
403 ctxt->error = (X); return; }
405 #define XSLT_ERROR0(X) \
406 { xsltError(ctxt, __FILE__, __LINE__, X); \
407 ctxt->error = (X); return(0); }
411 * @ctxt: the XPath Parser context
413 * Parse an XPath Litteral:
415 * [29] Literal ::= '"' [^"]* '"'
418 * Returns the Literal parsed or NULL
422 xsltScanLiteral(xsltParserContextPtr ctxt) {
430 while ((IS_CHAR(CUR)) && (CUR != '"'))
433 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
437 ret = xmlStrndup(q, CUR_PTR - q);
440 } else if (CUR == '\'') {
443 while ((IS_CHAR(CUR)) && (CUR != '\''))
446 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
450 ret = xmlStrndup(q, CUR_PTR - q);
454 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
463 * @ctxt: the XPath Parser context
465 * Trickery: parse an XML name but without consuming the input flow
466 * Needed to avoid insanity in the parser state.
468 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
469 * CombiningChar | Extender
471 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
473 * [6] Names ::= Name (S Name)*
475 * Returns the Name parsed or NULL
479 xsltScanName(xsltParserContextPtr ctxt) {
480 xmlChar buf[XML_MAX_NAMELEN];
484 if (!IS_LETTER(CUR) && (CUR != '_') &&
489 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
490 (NXT(len) == '.') || (NXT(len) == '-') ||
491 (NXT(len) == '_') || (NXT(len) == ':') ||
492 (IS_COMBINING(NXT(len))) ||
493 (IS_EXTENDER(NXT(len)))) {
496 if (len >= XML_MAX_NAMELEN) {
497 xmlGenericError(xmlGenericErrorContext,
498 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
499 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
500 (NXT(len) == '.') || (NXT(len) == '-') ||
501 (NXT(len) == '_') || (NXT(len) == ':') ||
502 (IS_COMBINING(NXT(len))) ||
503 (IS_EXTENDER(NXT(len))))
509 return(xmlStrndup(buf, len));
512 * xsltCompileIdKeyPattern:
513 * @comp: the compilation context
514 * @name: a preparsed name
515 * @aid: whether id/key are allowed there
517 * Compile the XSLT LocationIdKeyPattern
518 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
519 * | 'key' '(' Literal ',' Literal ')'
521 * also handle NodeType and PI from:
523 * [7] NodeTest ::= NameTest
525 * | 'processing-instruction' '(' Literal ')'
528 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
530 xmlChar *lit2 = NULL;
533 xsltGenericError(xsltGenericErrorContext,
534 "xsltCompileIdKeyPattern : ( expected\n");
538 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
541 lit = xsltScanLiteral(ctxt);
546 xsltGenericError(xsltGenericErrorContext,
547 "xsltCompileIdKeyPattern : ) expected\n");
552 PUSH(XSLT_OP_ID, lit, NULL);
553 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
556 lit = xsltScanLiteral(ctxt);
561 xsltGenericError(xsltGenericErrorContext,
562 "xsltCompileIdKeyPattern : , expected\n");
568 lit2 = xsltScanLiteral(ctxt);
573 xsltGenericError(xsltGenericErrorContext,
574 "xsltCompileIdKeyPattern : ) expected\n");
579 PUSH(XSLT_OP_KEY, lit, NULL);
580 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
584 lit = xsltScanLiteral(ctxt);
589 xsltGenericError(xsltGenericErrorContext,
590 "xsltCompileIdKeyPattern : ) expected\n");
596 PUSH(XSLT_OP_PI, lit, NULL);
597 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
601 xsltGenericError(xsltGenericErrorContext,
602 "xsltCompileIdKeyPattern : ) expected\n");
607 PUSH(XSLT_OP_TEXT, NULL, NULL);
608 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
612 xsltGenericError(xsltGenericErrorContext,
613 "xsltCompileIdKeyPattern : ) expected\n");
618 PUSH(XSLT_OP_COMMENT, NULL, NULL);
619 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
623 xsltGenericError(xsltGenericErrorContext,
624 "xsltCompileIdKeyPattern : ) expected\n");
629 PUSH(XSLT_OP_NODE, NULL, NULL);
631 xsltGenericError(xsltGenericErrorContext,
632 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
636 xsltGenericError(xsltGenericErrorContext,
637 "xsltCompileIdKeyPattern : node type\n");
649 * xsltCompileStepPattern:
650 * @comp: the compilation context
651 * @token: a posible precompiled name
653 * Compile the XSLT StepPattern and generates a precompiled
654 * form suitable for fast matching.
656 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
657 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
658 * | ('child' | 'attribute') '::'
660 * [7] NodeTest ::= NameTest
662 * | 'processing-instruction' '(' Literal ')'
663 * [8] Predicate ::= '[' PredicateExpr ']'
664 * [9] PredicateExpr ::= Expr
665 * [13] AbbreviatedAxisSpecifier ::= '@'?
666 * [37] NameTest ::= '*' | NCName ':' '*' | QName
670 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
671 xmlChar *name = NULL;
674 if ((token == NULL) && (CUR == '@')) {
675 token = xsltScanName(ctxt);
677 xsltGenericError(xsltGenericErrorContext,
678 "xsltCompilePattern : Name expected\n");
682 PUSH(XSLT_OP_ATTR, token, NULL);
686 token = xsltScanName(ctxt);
688 xsltGenericError(xsltGenericErrorContext,
689 "xsltCompilePattern : Name expected\n");
695 xsltCompileIdKeyPattern(ctxt, token, 0);
698 } else if (CUR == ':') {
701 xsltGenericError(xsltGenericErrorContext,
702 "xsltCompilePattern : sequence '::' expected\n");
707 if (xmlStrEqual(token, (const xmlChar *) "child")) {
708 /* TODO: handle namespace */
709 name = xsltScanName(ctxt);
711 xsltGenericError(xsltGenericErrorContext,
712 "xsltCompilePattern : QName expected\n");
716 PUSH(XSLT_OP_CHILD, name, NULL);
717 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
718 /* TODO: handle namespace */
719 name = xsltScanName(ctxt);
721 xsltGenericError(xsltGenericErrorContext,
722 "xsltCompilePattern : QName expected\n");
726 PUSH(XSLT_OP_ATTR, name, NULL);
728 xsltGenericError(xsltGenericErrorContext,
729 "xsltCompilePattern : 'child' or 'attribute' expected\n");
734 } else if (CUR == '*') {
736 PUSH(XSLT_OP_ALL, token, NULL);
738 /* TODO: handle namespace */
739 PUSH(XSLT_OP_ELEM, token, NULL);
748 /* TODO: avoid breaking in strings ... */
749 while ((IS_CHAR(CUR)) && (CUR != ']'))
752 xsltGenericError(xsltGenericErrorContext,
753 "xsltCompilePattern : ']' expected\n");
758 ret = xmlStrndup(q, CUR_PTR - q);
759 PUSH(XSLT_OP_PREDICATE, ret, NULL);
770 * xsltCompileRelativePathPattern:
771 * @comp: the compilation context
772 * @token: a posible precompiled name
774 * Compile the XSLT RelativePathPattern and generates a precompiled
775 * form suitable for fast matching.
777 * [4] RelativePathPattern ::= StepPattern
778 * | RelativePathPattern '/' StepPattern
779 * | RelativePathPattern '//' StepPattern
782 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
783 xsltCompileStepPattern(ctxt, token);
787 while ((CUR != 0) && (CUR != '|')) {
788 if ((CUR == '/') && (NXT(1) == '/')) {
789 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
793 xsltCompileStepPattern(ctxt, NULL);
794 } else if (CUR == '/') {
795 PUSH(XSLT_OP_PARENT, NULL, NULL);
798 if ((CUR != 0) || (CUR == '|')) {
799 xsltCompileRelativePathPattern(ctxt, NULL);
813 * xsltCompileLocationPathPattern:
814 * @comp: the compilation context
816 * Compile the XSLT LocationPathPattern and generates a precompiled
817 * form suitable for fast matching.
819 * [2] LocationPathPattern ::= '/' RelativePathPattern?
820 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
821 * | '//'? RelativePathPattern
824 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
826 if ((CUR == '/') && (NXT(1) == '/')) {
828 * since we reverse the query
829 * a leading // can be safely ignored
833 xsltCompileRelativePathPattern(ctxt, NULL);
834 } else if (CUR == '/') {
836 * We need to find root as the parent
840 PUSH(XSLT_OP_ROOT, NULL, NULL);
841 if ((CUR != 0) || (CUR == '|')) {
842 PUSH(XSLT_OP_PARENT, NULL, NULL);
843 xsltCompileRelativePathPattern(ctxt, NULL);
847 name = xsltScanName(ctxt);
849 xsltGenericError(xsltGenericErrorContext,
850 "xsltCompilePattern : Name expected\n");
856 xsltCompileIdKeyPattern(ctxt, name, 1);
857 if ((CUR == '/') && (NXT(1) == '/')) {
858 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
862 xsltCompileRelativePathPattern(ctxt, NULL);
863 } else if (CUR == '/') {
864 PUSH(XSLT_OP_PARENT, NULL, NULL);
867 xsltCompileRelativePathPattern(ctxt, NULL);
871 xsltCompileRelativePathPattern(ctxt, name);
878 * xsltCompilePattern:
879 * @pattern an XSLT pattern
881 * Compile the XSLT pattern and generates a precompiled form suitable
883 * Note that the splitting as union of patterns is expected to be handled
886 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
888 * Returns the generated xsltCompMatchPtr or NULL in case of failure
892 xsltCompilePattern(const xmlChar *pattern) {
893 xsltParserContextPtr ctxt;
894 xsltCompMatchPtr ret;
897 if (pattern == NULL) {
898 xsltGenericError(xsltGenericErrorContext,
899 "xsltCompilePattern : NULL pattern\n");
904 xsltGenericError(xsltGenericErrorContext,
905 "xsltCompilePattern : parsing '%s'\n", pattern);
909 while (IS_BLANK(*cur)) cur++;
911 xsltGenericError(xsltGenericErrorContext,
912 "xsltCompilePattern : NULL pattern\n");
915 ctxt = xsltNewParserContext();
918 ret = xsltNewCompMatch();
920 xsltFreeParserContext(ctxt);
925 ctxt->base = pattern;
927 xsltCompileLocationPathPattern(ctxt);
932 * Reverse for faster interpretation.
934 xsltReverseCompMatch(ret);
937 * Set-up the priority
939 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
940 (ret->steps[0].op == XSLT_OP_ATTR)) &&
941 (ret->steps[0].value != NULL) &&
942 (ret->steps[1].op == XSLT_OP_END)) {
944 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
945 (ret->steps[0].value != NULL) &&
946 (ret->steps[1].op == XSLT_OP_END)) {
948 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
949 (ret->steps[0].value != NULL) &&
950 (ret->steps[1].op == XSLT_OP_END)) {
951 ret->priority = -0.25;
952 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
953 (ret->steps[0].op == XSLT_OP_TEXT) ||
954 (ret->steps[0].op == XSLT_OP_NODE) ||
955 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
956 (ret->steps[1].op == XSLT_OP_END)) {
957 ret->priority = -0.5;
962 xsltFreeParserContext(ctxt);
966 xsltFreeParserContext(ctxt);
967 xsltFreeCompMatch(ret);
973 /************************************************************************
975 * Module interfaces *
977 ************************************************************************/
981 * @style: an XSLT stylesheet
982 * @cur: an XSLT template
984 * Register the XSLT pattern associated to @cur
986 * Returns -1 in case of error, 0 otherwise
989 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
990 xsltCompMatchPtr pat, list;
994 * get a compiled form of the pattern
996 /* TODO : handle | in patterns as multple pat !!! */
997 pat = xsltCompilePattern(cur->match);
1000 pat->template = cur;
1001 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1002 pat->priority = cur->priority;
1005 * insert it in the hash table list corresponding to its lookup name
1007 switch (pat->steps[0].op) {
1011 case XSLT_OP_PARENT:
1012 case XSLT_OP_ANCESTOR:
1016 name = pat->steps[0].value;
1019 name = (const xmlChar *) "/";
1022 name = (const xmlChar *) "*";
1025 case XSLT_OP_PREDICATE:
1026 xsltGenericError(xsltGenericErrorContext,
1027 "xsltAddTemplate: invalid compiled pattern\n");
1028 xsltFreeCompMatch(pat);
1031 * TODO: some flags at the top level about type based patterns
1032 * would be faster than inclusion in the hash table.
1035 name = (const xmlChar *) "processing-instruction()";
1037 case XSLT_OP_COMMENT:
1038 name = (const xmlChar *) "comment()";
1041 name = (const xmlChar *) "text()";
1044 name = (const xmlChar *) "node()";
1048 xsltGenericError(xsltGenericErrorContext,
1049 "xsltAddTemplate: invalid compiled pattern\n");
1050 xsltFreeCompMatch(pat);
1053 if (style->templatesHash == NULL) {
1054 style->templatesHash = xmlHashCreate(0);
1055 if (style->templatesHash == NULL) {
1056 xsltFreeCompMatch(pat);
1059 #ifdef DEBUG_PARSING
1060 xsltGenericError(xsltGenericErrorContext,
1061 "xsltAddTemplate: created template hash\n");
1063 xmlHashAddEntry(style->templatesHash, name, pat);
1064 #ifdef DEBUG_PARSING
1065 xsltGenericError(xsltGenericErrorContext,
1066 "xsltAddTemplate: added new hash %s\n", name);
1069 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1071 xmlHashAddEntry(style->templatesHash, name, pat);
1072 #ifdef DEBUG_PARSING
1073 xsltGenericError(xsltGenericErrorContext,
1074 "xsltAddTemplate: added new hash %s\n", name);
1078 * Note '<=' since one must choose among the matching template
1079 * rules that are left, the one that occurs last in the stylesheet
1081 if (list->priority <= pat->priority) {
1083 xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
1084 #ifdef DEBUG_PARSING
1085 xsltGenericError(xsltGenericErrorContext,
1086 "xsltAddTemplate: added head hash for %s\n", name);
1089 while (list->next != NULL) {
1090 if (list->next->priority <= pat->priority)
1093 pat->next = list->next;
1103 * @style: an XSLT stylesheet
1104 * @node: an XML Node
1106 * Finds the template applying to this node
1108 * Returns the xsltTemplatePtr or NULL if not found
1111 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {
1112 const xmlChar *name;
1113 xsltCompMatchPtr list;
1115 if ((style == NULL) || (node == NULL))
1118 /* TODO : handle IDs/keys here ! */
1119 if (style->templatesHash == NULL)
1123 * Use a name as selector
1125 switch (node->type) {
1126 case XML_ELEMENT_NODE:
1127 case XML_ATTRIBUTE_NODE:
1131 case XML_DOCUMENT_NODE:
1132 case XML_HTML_DOCUMENT_NODE:
1133 name = (const xmlChar *)"/";
1136 case XML_CDATA_SECTION_NODE:
1137 case XML_ENTITY_REF_NODE:
1138 case XML_ENTITY_NODE:
1139 case XML_COMMENT_NODE:
1140 case XML_DOCUMENT_TYPE_NODE:
1141 case XML_DOCUMENT_FRAG_NODE:
1142 case XML_NOTATION_NODE:
1144 case XML_ELEMENT_DECL:
1145 case XML_ATTRIBUTE_DECL:
1146 case XML_ENTITY_DECL:
1147 case XML_NAMESPACE_DECL:
1148 case XML_XINCLUDE_START:
1149 case XML_XINCLUDE_END:
1159 * find the list of appliable expressions based on the name
1161 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1163 #ifdef DEBUG_MATCHING
1164 xsltGenericError(xsltGenericErrorContext,
1165 "xsltGetTemplate: empty set for %s\n", name);
1169 while (list != NULL) {
1170 if (xsltTestCompMatch(list, node))
1171 return(list->template);
1180 * xsltFreeTemplateHashes:
1181 * @style: an XSLT stylesheet
1183 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1186 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1187 if (style->templatesHash != NULL)
1188 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1189 (xmlHashDeallocator) xsltFreeCompMatchList);