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 #include "templates.h"
28 /* #define DEBUG_PARSING */
54 typedef struct _xsltStepOp xsltStepOp;
55 typedef xsltStepOp *xsltStepOpPtr;
62 typedef struct _xsltCompMatch xsltCompMatch;
63 typedef xsltCompMatch *xsltCompMatchPtr;
64 struct _xsltCompMatch {
65 struct _xsltCompMatch *next; /* siblings in the name hash */
66 float priority; /* the priority */
67 xsltTemplatePtr template; /* the associated template */
69 /* TODO fix the statically allocated size steps[] */
72 xsltStepOp steps[20]; /* ops for computation */
75 typedef struct _xsltParserContext xsltParserContext;
76 typedef xsltParserContext *xsltParserContextPtr;
77 struct _xsltParserContext {
78 const xmlChar *cur; /* the current char being parsed */
79 const xmlChar *base; /* the full expression */
80 int error; /* error code */
81 xsltCompMatchPtr comp; /* the result */
84 /************************************************************************
88 ************************************************************************/
93 * Create a new XSLT CompMatch
95 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
98 xsltNewCompMatch(void) {
101 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
103 xsltGenericError(xsltGenericErrorContext,
104 "xsltNewCompMatch : malloc failed\n");
107 memset(cur, 0, sizeof(xsltCompMatch));
114 * @comp: an XSLT comp
116 * Free up the memory allocated by @comp
119 xsltFreeCompMatch(xsltCompMatchPtr comp) {
125 for (i = 0;i < comp->nbStep;i++) {
126 op = &comp->steps[i];
127 if (op->value != NULL)
129 if (op->value2 != NULL)
132 memset(comp, -1, sizeof(xsltCompMatch));
137 * xsltFreeCompMatchList:
138 * @comp: an XSLT comp list
140 * Free up the memory allocated by all the elements of @comp
143 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
144 xsltCompMatchPtr cur;
146 while (comp != NULL) {
149 xsltFreeCompMatch(cur);
154 * xsltNewParserContext:
156 * Create a new XSLT ParserContext
158 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
161 xsltNewParserContext(void) {
162 xsltParserContextPtr cur;
164 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
166 xsltGenericError(xsltGenericErrorContext,
167 "xsltNewParserContext : malloc failed\n");
170 memset(cur, 0, sizeof(xsltParserContext));
175 * xsltFreeParserContext:
176 * @ctxt: an XSLT parser context
178 * Free up the memory allocated by @ctxt
181 xsltFreeParserContext(xsltParserContextPtr ctxt) {
184 memset(ctxt, -1, sizeof(xsltParserContext));
190 * @comp: the compiled match expression
192 * @value: the first value
193 * @value2: the second value
195 * Add an step to an XSLT Compiled Match
197 * Returns -1 in case of failure, 0 otherwise.
200 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
202 if (comp->nbStep >= 20) {
203 xsltGenericError(xsltGenericErrorContext,
204 "xsltCompMatchAddOp: overflow\n");
207 comp->steps[comp->nbStep].op = op;
208 comp->steps[comp->nbStep].value = value;
209 comp->steps[comp->nbStep].value2 = value2;
215 * xsltSwapTopCompMatch:
216 * @comp: the compiled match expression
218 * reverse the two top steps.
221 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
223 int j = comp->nbStep - 1;
226 register xmlChar *tmp;
229 tmp = comp->steps[i].value;
230 comp->steps[i].value = comp->steps[j].value;
231 comp->steps[j].value = tmp;
232 tmp = comp->steps[i].value2;
233 comp->steps[i].value2 = comp->steps[j].value2;
234 comp->steps[j].value2 = tmp;
235 op = comp->steps[i].op;
236 comp->steps[i].op = comp->steps[j].op;
237 comp->steps[j].op = op;
242 * xsltReverseCompMatch:
243 * @comp: the compiled match expression
245 * reverse all the stack of expressions
248 xsltReverseCompMatch(xsltCompMatchPtr comp) {
250 int j = comp->nbStep - 1;
253 register xmlChar *tmp;
255 tmp = comp->steps[i].value;
256 comp->steps[i].value = comp->steps[j].value;
257 comp->steps[j].value = tmp;
258 tmp = comp->steps[i].value2;
259 comp->steps[i].value2 = comp->steps[j].value2;
260 comp->steps[j].value2 = tmp;
261 op = comp->steps[i].op;
262 comp->steps[i].op = comp->steps[j].op;
263 comp->steps[j].op = op;
267 comp->steps[comp->nbStep++].op = XSLT_OP_END;
270 /************************************************************************
272 * The interpreter for the precompiled patterns *
274 ************************************************************************/
278 * @ctxt: a XSLT process context
279 * @comp: the precompiled pattern
282 * Test wether the node matches the pattern
284 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
287 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
290 xsltStepOpPtr step, select = NULL;
292 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
293 xsltGenericError(xsltGenericErrorContext,
294 "xsltTestCompMatch: null arg\n");
297 for (i = 0;i < comp->nbStep;i++) {
298 step = &comp->steps[i];
299 if (step->op != XSLT_OP_PREDICATE)
305 if ((node->type != XML_DOCUMENT_NODE) &&
306 (node->type != XML_HTML_DOCUMENT_NODE))
310 if (node->type != XML_ELEMENT_NODE)
312 if (step->value == NULL)
314 if (!xmlStrEqual(step->value, node->name))
318 if (node->ns == NULL) {
319 if (step->value2 != NULL)
321 } else if (node->ns->href != NULL) {
322 if (step->value2 == NULL)
324 if (!xmlStrEqual(step->value2, node->ns->href))
329 TODO /* Handle OP_CHILD */
332 if (node->type != XML_ATTRIBUTE_NODE)
334 if (step->value == NULL)
336 if (!xmlStrEqual(step->value, node->name))
340 if (node->ns == NULL) {
341 if (step->value2 != NULL)
343 } else if (node->ns->href != NULL) {
344 if (step->value2 == NULL)
346 if (!xmlStrEqual(step->value2, node->ns->href))
354 if (step->value == NULL)
356 if (!xmlStrEqual(step->value, node->name))
359 if (node->ns == NULL) {
360 if (step->value2 != NULL)
362 } else if (node->ns->href != NULL) {
363 if (step->value2 == NULL)
365 if (!xmlStrEqual(step->value2, node->ns->href))
369 case XSLT_OP_ANCESTOR:
370 /* TODO: implement coalescing of ANCESTOR/NODE ops */
371 if (step->value == NULL) {
373 step = &comp->steps[i];
374 if (step->op == XSLT_OP_ROOT)
376 if (step->op != XSLT_OP_ELEM)
378 if (step->value == NULL)
384 while (node != NULL) {
387 if (xmlStrEqual(step->value, node->name)) {
389 if (node->ns == NULL) {
390 if (step->value2 == NULL)
392 } else if (node->ns->href != NULL) {
393 if ((step->value2 != NULL) &&
394 (xmlStrEqual(step->value2, node->ns->href)))
404 /* TODO Handle IDs decently, must be done differently */
407 id = xmlGetID(node->doc, step->value);
408 if ((id == NULL) || (id->parent != node))
413 TODO /* Handle Keys, might be done differently */
417 if (node->ns == NULL) {
418 if (step->value != NULL)
420 } else if (node->ns->href != NULL) {
421 if (step->value == NULL)
423 if (!xmlStrEqual(step->value, node->ns->href))
428 switch (node->type) {
429 case XML_DOCUMENT_NODE:
430 case XML_HTML_DOCUMENT_NODE:
431 case XML_ELEMENT_NODE:
437 case XSLT_OP_PREDICATE: {
440 int pos = 0, len = 0;
442 * Depending on the last selection, one may need to
443 * recompute contextSize and proximityPosition.
445 if ((select != NULL) &&
446 (select->op == XSLT_OP_ELEM) &&
447 (select->value != NULL) &&
448 (node->type == XML_ELEMENT_NODE) &&
449 (node->parent != NULL)) {
451 /* TODO: cache those informations ?!? */
452 xmlNodePtr siblings = node->parent->children;
454 oldCS = ctxt->xpathCtxt->contextSize;
455 oldCP = ctxt->xpathCtxt->proximityPosition;
456 while (siblings != NULL) {
457 if (siblings->type == XML_ELEMENT_NODE) {
458 if (siblings == node) {
461 } else if (xmlStrEqual(node->name,
466 siblings = siblings->next;
469 ctxt->xpathCtxt->contextSize = len;
470 ctxt->xpathCtxt->proximityPosition = pos;
473 oldNode = ctxt->node;
476 if ((step->value == NULL) ||
477 (!xsltEvalXPathPredicate(ctxt, step->value))) {
479 ctxt->xpathCtxt->contextSize = oldCS;
480 ctxt->xpathCtxt->proximityPosition = oldCP;
482 ctxt->node = oldNode;
486 ctxt->xpathCtxt->contextSize = oldCS;
487 ctxt->xpathCtxt->proximityPosition = oldCP;
489 ctxt->node = oldNode;
493 if (node->type != XML_PI_NODE)
495 if (step->value != NULL) {
496 if (!xmlStrEqual(step->value, node->name))
500 case XSLT_OP_COMMENT:
501 if (node->type != XML_COMMENT_NODE)
505 if ((node->type != XML_TEXT_NODE) &&
506 (node->type != XML_CDATA_SECTION_NODE))
510 switch (node->type) {
511 case XML_DOCUMENT_NODE:
512 case XML_HTML_DOCUMENT_NODE:
513 case XML_ELEMENT_NODE:
514 case XML_CDATA_SECTION_NODE:
516 case XML_COMMENT_NODE:
518 case XML_ATTRIBUTE_NODE:
529 /************************************************************************
531 * Dedicated parser for templates *
533 ************************************************************************/
535 #define CUR (*ctxt->cur)
536 #define SKIP(val) ctxt->cur += (val)
537 #define NXT(val) ctxt->cur[(val)]
538 #define CUR_PTR ctxt->cur
540 #define SKIP_BLANKS \
541 while (IS_BLANK(CUR)) NEXT
543 #define CURRENT (*ctxt->cur)
544 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
547 #define PUSH(op, val, val2) \
548 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
551 xsltSwapTopCompMatch(ctxt->comp);
553 #define XSLT_ERROR(X) \
554 { xsltError(ctxt, __FILE__, __LINE__, X); \
555 ctxt->error = (X); return; }
557 #define XSLT_ERROR0(X) \
558 { xsltError(ctxt, __FILE__, __LINE__, X); \
559 ctxt->error = (X); return(0); }
563 * @ctxt: the XPath Parser context
565 * Parse an XPath Litteral:
567 * [29] Literal ::= '"' [^"]* '"'
570 * Returns the Literal parsed or NULL
574 xsltScanLiteral(xsltParserContextPtr ctxt) {
582 while ((IS_CHAR(CUR)) && (CUR != '"'))
585 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
589 ret = xmlStrndup(q, CUR_PTR - q);
592 } else if (CUR == '\'') {
595 while ((IS_CHAR(CUR)) && (CUR != '\''))
598 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
602 ret = xmlStrndup(q, CUR_PTR - q);
606 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
615 * @ctxt: the XPath Parser context
617 * Trickery: parse an XML name but without consuming the input flow
618 * Needed to avoid insanity in the parser state.
620 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
621 * CombiningChar | Extender
623 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
625 * [6] Names ::= Name (S Name)*
627 * Returns the Name parsed or NULL
631 xsltScanName(xsltParserContextPtr ctxt) {
632 xmlChar buf[XML_MAX_NAMELEN];
636 if (!IS_LETTER(CUR) && (CUR != '_') &&
641 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
642 (NXT(len) == '.') || (NXT(len) == '-') ||
643 (NXT(len) == '_') || (NXT(len) == ':') ||
644 (IS_COMBINING(NXT(len))) ||
645 (IS_EXTENDER(NXT(len)))) {
648 if (len >= XML_MAX_NAMELEN) {
649 xmlGenericError(xmlGenericErrorContext,
650 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
651 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
652 (NXT(len) == '.') || (NXT(len) == '-') ||
653 (NXT(len) == '_') || (NXT(len) == ':') ||
654 (IS_COMBINING(NXT(len))) ||
655 (IS_EXTENDER(NXT(len))))
661 return(xmlStrndup(buf, len));
664 * xsltCompileIdKeyPattern:
665 * @comp: the compilation context
666 * @name: a preparsed name
667 * @aid: whether id/key are allowed there
669 * Compile the XSLT LocationIdKeyPattern
670 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
671 * | 'key' '(' Literal ',' Literal ')'
673 * also handle NodeType and PI from:
675 * [7] NodeTest ::= NameTest
677 * | 'processing-instruction' '(' Literal ')'
680 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
682 xmlChar *lit2 = NULL;
685 xsltGenericError(xsltGenericErrorContext,
686 "xsltCompileIdKeyPattern : ( expected\n");
690 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
693 lit = xsltScanLiteral(ctxt);
698 xsltGenericError(xsltGenericErrorContext,
699 "xsltCompileIdKeyPattern : ) expected\n");
704 PUSH(XSLT_OP_ID, lit, NULL);
705 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
708 lit = xsltScanLiteral(ctxt);
713 xsltGenericError(xsltGenericErrorContext,
714 "xsltCompileIdKeyPattern : , expected\n");
720 lit2 = xsltScanLiteral(ctxt);
725 xsltGenericError(xsltGenericErrorContext,
726 "xsltCompileIdKeyPattern : ) expected\n");
731 PUSH(XSLT_OP_KEY, lit, lit2);
732 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
736 lit = xsltScanLiteral(ctxt);
741 xsltGenericError(xsltGenericErrorContext,
742 "xsltCompileIdKeyPattern : ) expected\n");
748 PUSH(XSLT_OP_PI, lit, NULL);
749 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
753 xsltGenericError(xsltGenericErrorContext,
754 "xsltCompileIdKeyPattern : ) expected\n");
759 PUSH(XSLT_OP_TEXT, NULL, NULL);
760 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
764 xsltGenericError(xsltGenericErrorContext,
765 "xsltCompileIdKeyPattern : ) expected\n");
770 PUSH(XSLT_OP_COMMENT, NULL, NULL);
771 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
775 xsltGenericError(xsltGenericErrorContext,
776 "xsltCompileIdKeyPattern : ) expected\n");
781 PUSH(XSLT_OP_NODE, NULL, NULL);
783 xsltGenericError(xsltGenericErrorContext,
784 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
788 xsltGenericError(xsltGenericErrorContext,
789 "xsltCompileIdKeyPattern : node type\n");
799 * xsltCompileStepPattern:
800 * @comp: the compilation context
801 * @token: a posible precompiled name
803 * Compile the XSLT StepPattern and generates a precompiled
804 * form suitable for fast matching.
806 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
807 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
808 * | ('child' | 'attribute') '::'
810 * [7] NodeTest ::= NameTest
812 * | 'processing-instruction' '(' Literal ')'
813 * [8] Predicate ::= '[' PredicateExpr ']'
814 * [9] PredicateExpr ::= Expr
815 * [13] AbbreviatedAxisSpecifier ::= '@'?
816 * [37] NameTest ::= '*' | NCName ':' '*' | QName
820 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
821 xmlChar *name = NULL;
824 if ((token == NULL) && (CUR == '@')) {
828 PUSH(XSLT_OP_ATTR, NULL, NULL);
831 token = xsltScanName(ctxt);
833 xsltGenericError(xsltGenericErrorContext,
834 "xsltCompileStepPattern : Name expected\n");
838 PUSH(XSLT_OP_ATTR, token, NULL);
842 token = xsltScanName(ctxt);
846 PUSH(XSLT_OP_ALL, token, NULL);
847 goto parse_predicate;
849 xsltGenericError(xsltGenericErrorContext,
850 "xsltCompileStepPattern : Name expected\n");
857 xsltCompileIdKeyPattern(ctxt, token, 0);
860 } else if (CUR == ':') {
863 xsltGenericError(xsltGenericErrorContext,
864 "xsltCompileStepPattern : sequence '::' expected\n");
869 if (xmlStrEqual(token, (const xmlChar *) "child")) {
870 /* TODO: handle namespace */
871 name = xsltScanName(ctxt);
873 xsltGenericError(xsltGenericErrorContext,
874 "xsltCompileStepPattern : QName expected\n");
878 PUSH(XSLT_OP_CHILD, name, NULL);
879 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
880 /* TODO: handle namespace */
881 name = xsltScanName(ctxt);
883 xsltGenericError(xsltGenericErrorContext,
884 "xsltCompileStepPattern : QName expected\n");
888 PUSH(XSLT_OP_ATTR, name, NULL);
890 xsltGenericError(xsltGenericErrorContext,
891 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
896 } else if (CUR == '*') {
898 PUSH(XSLT_OP_ALL, token, NULL);
900 /* TODO: handle namespace */
901 PUSH(XSLT_OP_ELEM, token, NULL);
911 /* TODO: avoid breaking in strings ... */
912 while ((IS_CHAR(CUR)) && (CUR != ']'))
915 xsltGenericError(xsltGenericErrorContext,
916 "xsltCompileStepPattern : ']' expected\n");
920 ret = xmlStrndup(q, CUR_PTR - q);
921 PUSH(XSLT_OP_PREDICATE, ret, NULL);
922 /* push the predicate lower than local test */
935 * xsltCompileRelativePathPattern:
936 * @comp: the compilation context
937 * @token: a posible precompiled name
939 * Compile the XSLT RelativePathPattern and generates a precompiled
940 * form suitable for fast matching.
942 * [4] RelativePathPattern ::= StepPattern
943 * | RelativePathPattern '/' StepPattern
944 * | RelativePathPattern '//' StepPattern
947 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
948 xsltCompileStepPattern(ctxt, token);
952 while ((CUR != 0) && (CUR != '|')) {
953 if ((CUR == '/') && (NXT(1) == '/')) {
954 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
958 xsltCompileStepPattern(ctxt, NULL);
959 } else if (CUR == '/') {
960 PUSH(XSLT_OP_PARENT, NULL, NULL);
963 if ((CUR != 0) || (CUR == '|')) {
964 xsltCompileRelativePathPattern(ctxt, NULL);
978 * xsltCompileLocationPathPattern:
979 * @comp: the compilation context
981 * Compile the XSLT LocationPathPattern and generates a precompiled
982 * form suitable for fast matching.
984 * [2] LocationPathPattern ::= '/' RelativePathPattern?
985 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
986 * | '//'? RelativePathPattern
989 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
991 if ((CUR == '/') && (NXT(1) == '/')) {
993 * since we reverse the query
994 * a leading // can be safely ignored
998 xsltCompileRelativePathPattern(ctxt, NULL);
999 } else if (CUR == '/') {
1001 * We need to find root as the parent
1005 PUSH(XSLT_OP_ROOT, NULL, NULL);
1006 if ((CUR != 0) || (CUR == '|')) {
1007 PUSH(XSLT_OP_PARENT, NULL, NULL);
1008 xsltCompileRelativePathPattern(ctxt, NULL);
1010 } else if (CUR == '*') {
1011 xsltCompileRelativePathPattern(ctxt, NULL);
1012 } else if (CUR == '@') {
1013 xsltCompileRelativePathPattern(ctxt, NULL);
1016 name = xsltScanName(ctxt);
1018 xsltGenericError(xsltGenericErrorContext,
1019 "xsltCompileLocationPathPattern : Name expected\n");
1025 xsltCompileIdKeyPattern(ctxt, name, 1);
1026 if ((CUR == '/') && (NXT(1) == '/')) {
1027 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1031 xsltCompileRelativePathPattern(ctxt, NULL);
1032 } else if (CUR == '/') {
1033 PUSH(XSLT_OP_PARENT, NULL, NULL);
1036 xsltCompileRelativePathPattern(ctxt, NULL);
1040 xsltCompileRelativePathPattern(ctxt, name);
1047 * xsltCompilePattern:
1048 * @pattern an XSLT pattern
1050 * Compile the XSLT pattern and generates a precompiled form suitable
1051 * for fast matching.
1052 * Note that the splitting as union of patterns is expected to be handled
1055 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1057 * Returns the generated xsltCompMatchPtr or NULL in case of failure
1061 xsltCompilePattern(const xmlChar *pattern) {
1062 xsltParserContextPtr ctxt;
1063 xsltCompMatchPtr ret;
1066 if (pattern == NULL) {
1067 xsltGenericError(xsltGenericErrorContext,
1068 "xsltCompilePattern : NULL pattern\n");
1072 #ifdef DEBUG_PARSING
1073 xsltGenericDebug(xsltGenericDebugContext,
1074 "xsltCompilePattern : parsing '%s'\n", pattern);
1078 while (IS_BLANK(*cur)) cur++;
1080 xsltGenericError(xsltGenericErrorContext,
1081 "xsltCompilePattern : NULL pattern\n");
1084 ctxt = xsltNewParserContext();
1087 ret = xsltNewCompMatch();
1089 xsltFreeParserContext(ctxt);
1094 ctxt->base = pattern;
1096 xsltCompileLocationPathPattern(ctxt);
1101 * Reverse for faster interpretation.
1103 xsltReverseCompMatch(ret);
1106 * Set-up the priority
1108 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
1109 (ret->steps[0].op == XSLT_OP_ATTR)) &&
1110 (ret->steps[0].value != NULL) &&
1111 (ret->steps[1].op == XSLT_OP_END)) {
1113 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
1114 (ret->steps[0].value != NULL) &&
1115 (ret->steps[1].op == XSLT_OP_END)) {
1117 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
1118 (ret->steps[0].value != NULL) &&
1119 (ret->steps[1].op == XSLT_OP_END)) {
1120 ret->priority = -0.25;
1121 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
1122 (ret->steps[0].op == XSLT_OP_TEXT) ||
1123 (ret->steps[0].op == XSLT_OP_NODE) ||
1124 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
1125 (ret->steps[1].op == XSLT_OP_END)) {
1126 ret->priority = -0.5;
1128 ret->priority = 0.5;
1131 xsltFreeParserContext(ctxt);
1135 xsltFreeParserContext(ctxt);
1136 xsltFreeCompMatch(ret);
1142 /************************************************************************
1144 * Module interfaces *
1146 ************************************************************************/
1150 * @style: an XSLT stylesheet
1151 * @cur: an XSLT template
1153 * Register the XSLT pattern associated to @cur
1155 * Returns -1 in case of error, 0 otherwise
1158 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
1159 xsltCompMatchPtr pat, list, *top;
1160 const xmlChar *name = NULL;
1161 xmlChar *p, *pattern, tmp;
1163 if ((style == NULL) || (cur == NULL))
1174 * get a compiled form of the pattern
1177 while ((*p != 0) && (*p != '|')) {
1178 /* TODO: handle string escaping "a | b" in patterns ... */
1184 pat = xsltCompilePattern(pattern);
1190 pat->template = cur;
1191 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1192 pat->priority = cur->priority;
1195 * insert it in the hash table list corresponding to its lookup name
1197 switch (pat->steps[0].op) {
1199 if (pat->steps[0].value != NULL)
1200 name = pat->steps[0].value;
1202 top = (xsltCompMatchPtr *) &(style->attrMatch);
1206 case XSLT_OP_PARENT:
1207 case XSLT_OP_ANCESTOR:
1209 name = pat->steps[0].value;
1212 top = (xsltCompMatchPtr *) &(style->rootMatch);
1216 /* TODO optimize ID/KEY !!! */
1218 top = (xsltCompMatchPtr *) &(style->elemMatch);
1221 case XSLT_OP_PREDICATE:
1222 xsltGenericError(xsltGenericErrorContext,
1223 "xsltAddTemplate: invalid compiled pattern\n");
1224 xsltFreeCompMatch(pat);
1227 * TODO: some flags at the top level about type based patterns
1228 * would be faster than inclusion in the hash table.
1231 if (pat->steps[0].value != NULL)
1232 name = pat->steps[0].value;
1234 top = (xsltCompMatchPtr *) &(style->piMatch);
1236 case XSLT_OP_COMMENT:
1237 top = (xsltCompMatchPtr *) &(style->commentMatch);
1240 top = (xsltCompMatchPtr *) &(style->textMatch);
1243 if (pat->steps[0].value != NULL)
1244 name = pat->steps[0].value;
1246 top = (xsltCompMatchPtr *) &(style->elemMatch);
1251 if (style->templatesHash == NULL) {
1252 style->templatesHash = xmlHashCreate(0);
1253 if (style->templatesHash == NULL) {
1254 xsltFreeCompMatch(pat);
1257 #ifdef DEBUG_PARSING
1258 xsltGenericDebug(xsltGenericDebugContext,
1259 "xsltAddTemplate: created template hash\n");
1261 xmlHashAddEntry(style->templatesHash, name, pat);
1262 #ifdef DEBUG_PARSING
1263 xsltGenericDebug(xsltGenericDebugContext,
1264 "xsltAddTemplate: added new hash %s\n", name);
1267 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1269 xmlHashAddEntry(style->templatesHash, name, pat);
1270 #ifdef DEBUG_PARSING
1271 xsltGenericDebug(xsltGenericDebugContext,
1272 "xsltAddTemplate: added new hash %s\n", name);
1276 * Note '<=' since one must choose among the matching
1277 * template rules that are left, the one that occurs
1278 * last in the stylesheet
1280 if (list->priority <= pat->priority) {
1282 xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
1283 #ifdef DEBUG_PARSING
1284 xsltGenericDebug(xsltGenericDebugContext,
1285 "xsltAddTemplate: added head hash for %s\n", name);
1288 while (list->next != NULL) {
1289 if (list->next->priority <= pat->priority)
1293 pat->next = list->next;
1298 } else if (top != NULL) {
1303 } else if (list->priority <= pat->priority) {
1307 while (list->next != NULL) {
1308 if (list->next->priority <= pat->priority)
1312 pat->next = list->next;
1316 xsltGenericError(xsltGenericErrorContext,
1317 "xsltAddTemplate: invalid compiled pattern\n");
1318 xsltFreeCompMatch(pat);
1328 * @ctxt: a XSLT process context
1329 * @node: an XML Node
1331 * Finds the template applying to this node
1333 * Returns the xsltTemplatePtr or NULL if not found
1336 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1337 xsltStylesheetPtr style;
1338 xsltTemplatePtr ret = NULL;
1339 const xmlChar *name = NULL;
1340 xsltCompMatchPtr list = NULL;
1342 if ((ctxt == NULL) || (node == NULL))
1345 style = ctxt->style;
1346 while (style != NULL) {
1347 /* TODO : handle IDs/keys here ! */
1348 if (style->templatesHash != NULL) {
1350 * Use the top name as selector
1352 switch (node->type) {
1353 case XML_ELEMENT_NODE:
1354 case XML_ATTRIBUTE_NODE:
1358 case XML_DOCUMENT_NODE:
1359 case XML_HTML_DOCUMENT_NODE:
1361 case XML_CDATA_SECTION_NODE:
1362 case XML_COMMENT_NODE:
1363 case XML_ENTITY_REF_NODE:
1364 case XML_ENTITY_NODE:
1365 case XML_DOCUMENT_TYPE_NODE:
1366 case XML_DOCUMENT_FRAG_NODE:
1367 case XML_NOTATION_NODE:
1369 case XML_ELEMENT_DECL:
1370 case XML_ATTRIBUTE_DECL:
1371 case XML_ENTITY_DECL:
1372 case XML_NAMESPACE_DECL:
1373 case XML_XINCLUDE_START:
1374 case XML_XINCLUDE_END:
1383 * find the list of appliable expressions based on the name
1385 list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
1387 while (list != NULL) {
1388 if (xsltTestCompMatch(ctxt, list, node)) {
1389 ret = list->template;
1397 * find alternate generic matches
1399 switch (node->type) {
1400 case XML_ELEMENT_NODE:
1401 list = style->elemMatch;
1403 case XML_ATTRIBUTE_NODE:
1404 list = style->attrMatch;
1407 list = style->piMatch;
1409 case XML_DOCUMENT_NODE:
1410 case XML_HTML_DOCUMENT_NODE:
1411 list = style->rootMatch;
1414 case XML_CDATA_SECTION_NODE:
1415 list = style->textMatch;
1417 case XML_COMMENT_NODE:
1418 list = style->commentMatch;
1420 case XML_ENTITY_REF_NODE:
1421 case XML_ENTITY_NODE:
1422 case XML_DOCUMENT_TYPE_NODE:
1423 case XML_DOCUMENT_FRAG_NODE:
1424 case XML_NOTATION_NODE:
1426 case XML_ELEMENT_DECL:
1427 case XML_ATTRIBUTE_DECL:
1428 case XML_ENTITY_DECL:
1429 case XML_NAMESPACE_DECL:
1430 case XML_XINCLUDE_START:
1431 case XML_XINCLUDE_END:
1437 while ((list != NULL) &&
1438 ((ret == NULL) || (list->priority > ret->priority))) {
1439 if (xsltTestCompMatch(ctxt, list, node)) {
1440 ret = list->template;
1449 * Cycle on next stylesheet import.
1451 style = xsltNextImport(style);
1458 * xsltFreeTemplateHashes:
1459 * @style: an XSLT stylesheet
1461 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1464 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1465 if (style->templatesHash != NULL)
1466 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1467 (xmlHashDeallocator) xsltFreeCompMatchList);
1468 if (style->rootMatch != NULL)
1469 xsltFreeCompMatchList(style->rootMatch);
1470 if (style->elemMatch != NULL)
1471 xsltFreeCompMatchList(style->elemMatch);
1472 if (style->attrMatch != NULL)
1473 xsltFreeCompMatchList(style->attrMatch);
1474 if (style->parentMatch != NULL)
1475 xsltFreeCompMatchList(style->parentMatch);
1476 if (style->textMatch != NULL)
1477 xsltFreeCompMatchList(style->textMatch);
1478 if (style->piMatch != NULL)
1479 xsltFreeCompMatchList(style->piMatch);
1480 if (style->commentMatch != NULL)
1481 xsltFreeCompMatchList(style->commentMatch);