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))
283 /* TODO: Handle namespace ... */
286 TODO /* Handle OP_CHILD */
289 if (node->type != XML_ATTRIBUTE_NODE)
291 if (step->value == NULL)
293 if (!xmlStrEqual(step->value, node->name))
295 /* TODO: Handle namespace ... */
301 if (step->value == NULL)
303 if (!xmlStrEqual(step->value, node->name))
305 /* TODO: Handle namespace ... */
307 case XSLT_OP_ANCESTOR:
308 /* TODO: implement coalescing of ANCESTOR/NODE ops */
309 if (step->value == NULL) {
311 step = &comp->steps[i];
312 if (step->op == XSLT_OP_ROOT)
314 if (step->op != XSLT_OP_ELEM)
316 if (step->value == NULL)
322 while (node != NULL) {
325 if (xmlStrEqual(step->value, node->name)) {
326 /* TODO: Handle namespace ... */
334 TODO /* Handle IDs, might be done differently */
337 TODO /* Handle Keys, might be done differently */
340 TODO /* Handle Namespace */
345 case XSLT_OP_PREDICATE:
346 TODO /* Handle Predicate */
349 TODO /* Handle PI() */
351 case XSLT_OP_COMMENT:
352 TODO /* Handle Comments() */
355 TODO /* Handle Text() */
358 TODO /* Handle Node() */
365 /************************************************************************
367 * Dedicated parser for templates *
369 ************************************************************************/
371 #define CUR (*ctxt->cur)
372 #define SKIP(val) ctxt->cur += (val)
373 #define NXT(val) ctxt->cur[(val)]
374 #define CUR_PTR ctxt->cur
376 #define SKIP_BLANKS \
377 while (IS_BLANK(CUR)) NEXT
379 #define CURRENT (*ctxt->cur)
380 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
383 #define PUSH(op, val, val2) \
384 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
386 #define XSLT_ERROR(X) \
387 { xsltError(ctxt, __FILE__, __LINE__, X); \
388 ctxt->error = (X); return; }
390 #define XSLT_ERROR0(X) \
391 { xsltError(ctxt, __FILE__, __LINE__, X); \
392 ctxt->error = (X); return(0); }
396 * @ctxt: the XPath Parser context
398 * Parse an XPath Litteral:
400 * [29] Literal ::= '"' [^"]* '"'
403 * Returns the Literal parsed or NULL
407 xsltScanLiteral(xsltParserContextPtr ctxt) {
415 while ((IS_CHAR(CUR)) && (CUR != '"'))
418 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
422 ret = xmlStrndup(q, CUR_PTR - q);
425 } else if (CUR == '\'') {
428 while ((IS_CHAR(CUR)) && (CUR != '\''))
431 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
435 ret = xmlStrndup(q, CUR_PTR - q);
439 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
448 * @ctxt: the XPath Parser context
450 * Trickery: parse an XML name but without consuming the input flow
451 * Needed to avoid insanity in the parser state.
453 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
454 * CombiningChar | Extender
456 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
458 * [6] Names ::= Name (S Name)*
460 * Returns the Name parsed or NULL
464 xsltScanName(xsltParserContextPtr ctxt) {
465 xmlChar buf[XML_MAX_NAMELEN];
469 if (!IS_LETTER(CUR) && (CUR != '_') &&
474 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
475 (NXT(len) == '.') || (NXT(len) == '-') ||
476 (NXT(len) == '_') || (NXT(len) == ':') ||
477 (IS_COMBINING(NXT(len))) ||
478 (IS_EXTENDER(NXT(len)))) {
481 if (len >= XML_MAX_NAMELEN) {
482 xmlGenericError(xmlGenericErrorContext,
483 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
484 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
485 (NXT(len) == '.') || (NXT(len) == '-') ||
486 (NXT(len) == '_') || (NXT(len) == ':') ||
487 (IS_COMBINING(NXT(len))) ||
488 (IS_EXTENDER(NXT(len))))
494 return(xmlStrndup(buf, len));
497 * xsltCompileIdKeyPattern:
498 * @comp: the compilation context
499 * @name: a preparsed name
500 * @aid: whether id/key are allowed there
502 * Compile the XSLT LocationIdKeyPattern
503 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
504 * | 'key' '(' Literal ',' Literal ')'
506 * also handle NodeType and PI from:
508 * [7] NodeTest ::= NameTest
510 * | 'processing-instruction' '(' Literal ')'
513 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
515 xmlChar *lit2 = NULL;
518 xsltGenericError(xsltGenericErrorContext,
519 "xsltCompileIdKeyPattern : ( expected\n");
523 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
526 lit = xsltScanLiteral(ctxt);
531 xsltGenericError(xsltGenericErrorContext,
532 "xsltCompileIdKeyPattern : ) expected\n");
537 PUSH(XSLT_OP_ID, lit, NULL);
538 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
541 lit = xsltScanLiteral(ctxt);
546 xsltGenericError(xsltGenericErrorContext,
547 "xsltCompileIdKeyPattern : , expected\n");
553 lit2 = xsltScanLiteral(ctxt);
558 xsltGenericError(xsltGenericErrorContext,
559 "xsltCompileIdKeyPattern : ) expected\n");
564 PUSH(XSLT_OP_KEY, lit, NULL);
565 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
569 lit = xsltScanLiteral(ctxt);
574 xsltGenericError(xsltGenericErrorContext,
575 "xsltCompileIdKeyPattern : ) expected\n");
581 PUSH(XSLT_OP_PI, lit, NULL);
582 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
586 xsltGenericError(xsltGenericErrorContext,
587 "xsltCompileIdKeyPattern : ) expected\n");
592 PUSH(XSLT_OP_TEXT, NULL, NULL);
593 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
597 xsltGenericError(xsltGenericErrorContext,
598 "xsltCompileIdKeyPattern : ) expected\n");
603 PUSH(XSLT_OP_COMMENT, NULL, NULL);
604 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
608 xsltGenericError(xsltGenericErrorContext,
609 "xsltCompileIdKeyPattern : ) expected\n");
614 PUSH(XSLT_OP_NODE, NULL, NULL);
616 xsltGenericError(xsltGenericErrorContext,
617 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
621 xsltGenericError(xsltGenericErrorContext,
622 "xsltCompileIdKeyPattern : node type\n");
634 * xsltCompileStepPattern:
635 * @comp: the compilation context
636 * @token: a posible precompiled name
638 * Compile the XSLT StepPattern and generates a precompiled
639 * form suitable for fast matching.
641 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
642 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
643 * | ('child' | 'attribute') '::'
645 * [7] NodeTest ::= NameTest
647 * | 'processing-instruction' '(' Literal ')'
648 * [8] Predicate ::= '[' PredicateExpr ']'
649 * [9] PredicateExpr ::= Expr
650 * [13] AbbreviatedAxisSpecifier ::= '@'?
651 * [37] NameTest ::= '*' | NCName ':' '*' | QName
655 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
656 xmlChar *name = NULL;
659 if ((token == NULL) && (CUR == '@')) {
660 token = xsltScanName(ctxt);
662 xsltGenericError(xsltGenericErrorContext,
663 "xsltCompilePattern : Name expected\n");
667 PUSH(XSLT_OP_ATTR, token, NULL);
671 token = xsltScanName(ctxt);
673 xsltGenericError(xsltGenericErrorContext,
674 "xsltCompilePattern : Name expected\n");
680 xsltCompileIdKeyPattern(ctxt, token, 0);
683 } else if (CUR == ':') {
686 xsltGenericError(xsltGenericErrorContext,
687 "xsltCompilePattern : sequence '::' expected\n");
692 if (xmlStrEqual(token, (const xmlChar *) "child")) {
693 /* TODO: handle namespace */
694 name = xsltScanName(ctxt);
696 xsltGenericError(xsltGenericErrorContext,
697 "xsltCompilePattern : QName expected\n");
701 PUSH(XSLT_OP_CHILD, name, NULL);
702 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
703 /* TODO: handle namespace */
704 name = xsltScanName(ctxt);
706 xsltGenericError(xsltGenericErrorContext,
707 "xsltCompilePattern : QName expected\n");
711 PUSH(XSLT_OP_ATTR, name, NULL);
713 xsltGenericError(xsltGenericErrorContext,
714 "xsltCompilePattern : 'child' or 'attribute' expected\n");
719 } else if (CUR == '*') {
721 PUSH(XSLT_OP_ALL, token, NULL);
723 /* TODO: handle namespace */
724 PUSH(XSLT_OP_ELEM, token, NULL);
733 /* TODO: avoid breaking in strings ... */
734 while ((IS_CHAR(CUR)) && (CUR != ']'))
737 xsltGenericError(xsltGenericErrorContext,
738 "xsltCompilePattern : ']' expected\n");
743 ret = xmlStrndup(q, CUR_PTR - q);
744 PUSH(XSLT_OP_PREDICATE, ret, NULL);
755 * xsltCompileRelativePathPattern:
756 * @comp: the compilation context
757 * @token: a posible precompiled name
759 * Compile the XSLT RelativePathPattern and generates a precompiled
760 * form suitable for fast matching.
762 * [4] RelativePathPattern ::= StepPattern
763 * | RelativePathPattern '/' StepPattern
764 * | RelativePathPattern '//' StepPattern
767 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
768 xsltCompileStepPattern(ctxt, token);
772 while ((CUR != 0) && (CUR != '|')) {
773 if ((CUR == '/') && (NXT(1) == '/')) {
774 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
778 xsltCompileStepPattern(ctxt, NULL);
779 } else if (CUR == '/') {
780 PUSH(XSLT_OP_PARENT, NULL, NULL);
783 if ((CUR != 0) || (CUR == '|')) {
784 xsltCompileRelativePathPattern(ctxt, NULL);
798 * xsltCompileLocationPathPattern:
799 * @comp: the compilation context
801 * Compile the XSLT LocationPathPattern and generates a precompiled
802 * form suitable for fast matching.
804 * [2] LocationPathPattern ::= '/' RelativePathPattern?
805 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
806 * | '//'? RelativePathPattern
809 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
811 if ((CUR == '/') && (NXT(1) == '/')) {
813 * since we reverse the query
814 * a leading // can be safely ignored
818 xsltCompileRelativePathPattern(ctxt, NULL);
819 } else if (CUR == '/') {
821 * We need to find root as the parent
825 PUSH(XSLT_OP_ROOT, NULL, NULL);
826 if ((CUR != 0) || (CUR == '|')) {
827 PUSH(XSLT_OP_PARENT, NULL, NULL);
828 xsltCompileRelativePathPattern(ctxt, NULL);
832 name = xsltScanName(ctxt);
834 xsltGenericError(xsltGenericErrorContext,
835 "xsltCompilePattern : Name expected\n");
841 xsltCompileIdKeyPattern(ctxt, name, 1);
842 if ((CUR == '/') && (NXT(1) == '/')) {
843 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
847 xsltCompileRelativePathPattern(ctxt, NULL);
848 } else if (CUR == '/') {
849 PUSH(XSLT_OP_PARENT, NULL, NULL);
852 xsltCompileRelativePathPattern(ctxt, NULL);
856 xsltCompileRelativePathPattern(ctxt, name);
863 * xsltCompilePattern:
864 * @pattern an XSLT pattern
866 * Compile the XSLT pattern and generates a precompiled form suitable
868 * Note that the splitting as union of patterns is expected to be handled
871 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
873 * Returns the generated xsltCompMatchPtr or NULL in case of failure
877 xsltCompilePattern(const xmlChar *pattern) {
878 xsltParserContextPtr ctxt;
879 xsltCompMatchPtr ret;
882 if (pattern == NULL) {
883 xsltGenericError(xsltGenericErrorContext,
884 "xsltCompilePattern : NULL pattern\n");
889 xsltGenericDebug(xsltGenericDebugContext,
890 "xsltCompilePattern : parsing '%s'\n", pattern);
894 while (IS_BLANK(*cur)) cur++;
896 xsltGenericError(xsltGenericErrorContext,
897 "xsltCompilePattern : NULL pattern\n");
900 ctxt = xsltNewParserContext();
903 ret = xsltNewCompMatch();
905 xsltFreeParserContext(ctxt);
910 ctxt->base = pattern;
912 xsltCompileLocationPathPattern(ctxt);
917 * Reverse for faster interpretation.
919 xsltReverseCompMatch(ret);
922 * Set-up the priority
924 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
925 (ret->steps[0].op == XSLT_OP_ATTR)) &&
926 (ret->steps[0].value != NULL) &&
927 (ret->steps[1].op == XSLT_OP_END)) {
929 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
930 (ret->steps[0].value != NULL) &&
931 (ret->steps[1].op == XSLT_OP_END)) {
933 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
934 (ret->steps[0].value != NULL) &&
935 (ret->steps[1].op == XSLT_OP_END)) {
936 ret->priority = -0.25;
937 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
938 (ret->steps[0].op == XSLT_OP_TEXT) ||
939 (ret->steps[0].op == XSLT_OP_NODE) ||
940 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
941 (ret->steps[1].op == XSLT_OP_END)) {
942 ret->priority = -0.5;
947 xsltFreeParserContext(ctxt);
951 xsltFreeParserContext(ctxt);
952 xsltFreeCompMatch(ret);
958 /************************************************************************
960 * Module interfaces *
962 ************************************************************************/
966 * @style: an XSLT stylesheet
967 * @cur: an XSLT template
969 * Register the XSLT pattern associated to @cur
971 * Returns -1 in case of error, 0 otherwise
974 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
975 xsltCompMatchPtr pat, list;
979 * get a compiled form of the pattern
981 /* TODO : handle | in patterns as multple pat !!! */
982 pat = xsltCompilePattern(cur->match);
986 if (cur->priority != XSLT_PAT_NO_PRIORITY)
987 pat->priority = cur->priority;
990 * insert it in the hash table list corresponding to its lookup name
992 switch (pat->steps[0].op) {
997 case XSLT_OP_ANCESTOR:
1001 name = pat->steps[0].value;
1004 name = (const xmlChar *) "/";
1007 name = (const xmlChar *) "*";
1010 case XSLT_OP_PREDICATE:
1011 xsltGenericError(xsltGenericErrorContext,
1012 "xsltAddTemplate: invalid compiled pattern\n");
1013 xsltFreeCompMatch(pat);
1016 * TODO: some flags at the top level about type based patterns
1017 * would be faster than inclusion in the hash table.
1020 name = (const xmlChar *) "processing-instruction()";
1022 case XSLT_OP_COMMENT:
1023 name = (const xmlChar *) "comment()";
1026 name = (const xmlChar *) "text()";
1029 name = (const xmlChar *) "node()";
1033 xsltGenericError(xsltGenericErrorContext,
1034 "xsltAddTemplate: invalid compiled pattern\n");
1035 xsltFreeCompMatch(pat);
1038 if (style->templatesHash == NULL) {
1039 style->templatesHash = xmlHashCreate(0);
1040 if (style->templatesHash == NULL) {
1041 xsltFreeCompMatch(pat);
1044 #ifdef DEBUG_PARSING
1045 xsltGenericDebug(xsltGenericDebugContext,
1046 "xsltAddTemplate: created template hash\n");
1048 xmlHashAddEntry(style->templatesHash, name, pat);
1049 #ifdef DEBUG_PARSING
1050 xsltGenericDebug(xsltGenericDebugContext,
1051 "xsltAddTemplate: added new hash %s\n", name);
1054 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1056 xmlHashAddEntry(style->templatesHash, name, pat);
1057 #ifdef DEBUG_PARSING
1058 xsltGenericDebug(xsltGenericDebugContext,
1059 "xsltAddTemplate: added new hash %s\n", name);
1063 * Note '<=' since one must choose among the matching template
1064 * rules that are left, the one that occurs last in the stylesheet
1066 if (list->priority <= pat->priority) {
1068 xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
1069 #ifdef DEBUG_PARSING
1070 xsltGenericDebug(xsltGenericDebugContext,
1071 "xsltAddTemplate: added head hash for %s\n", name);
1074 while (list->next != NULL) {
1075 if (list->next->priority <= pat->priority)
1078 pat->next = list->next;
1088 * @style: an XSLT stylesheet
1089 * @node: an XML Node
1091 * Finds the template applying to this node
1093 * Returns the xsltTemplatePtr or NULL if not found
1096 xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {
1097 const xmlChar *name;
1098 xsltCompMatchPtr list;
1100 if ((style == NULL) || (node == NULL))
1103 /* TODO : handle IDs/keys here ! */
1104 if (style->templatesHash == NULL)
1108 * Use a name as selector
1110 switch (node->type) {
1111 case XML_ELEMENT_NODE:
1112 case XML_ATTRIBUTE_NODE:
1116 case XML_DOCUMENT_NODE:
1117 case XML_HTML_DOCUMENT_NODE:
1118 name = (const xmlChar *)"/";
1121 case XML_CDATA_SECTION_NODE:
1122 case XML_ENTITY_REF_NODE:
1123 case XML_ENTITY_NODE:
1124 case XML_COMMENT_NODE:
1125 case XML_DOCUMENT_TYPE_NODE:
1126 case XML_DOCUMENT_FRAG_NODE:
1127 case XML_NOTATION_NODE:
1129 case XML_ELEMENT_DECL:
1130 case XML_ATTRIBUTE_DECL:
1131 case XML_ENTITY_DECL:
1132 case XML_NAMESPACE_DECL:
1133 case XML_XINCLUDE_START:
1134 case XML_XINCLUDE_END:
1144 * find the list of appliable expressions based on the name
1146 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1148 #ifdef DEBUG_MATCHING
1149 xsltGenericDebug(xsltGenericDebugContext,
1150 "xsltGetTemplate: empty set for %s\n", name);
1154 while (list != NULL) {
1155 if (xsltTestCompMatch(list, node))
1156 return(list->template);
1165 * xsltFreeTemplateHashes:
1166 * @style: an XSLT stylesheet
1168 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1171 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1172 if (style->templatesHash != NULL)
1173 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1174 (xmlHashDeallocator) xsltFreeCompMatchList);