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"
29 /* #define DEBUG_PARSING */
55 typedef struct _xsltStepOp xsltStepOp;
56 typedef xsltStepOp *xsltStepOpPtr;
63 typedef struct _xsltCompMatch xsltCompMatch;
64 typedef xsltCompMatch *xsltCompMatchPtr;
65 struct _xsltCompMatch {
66 struct _xsltCompMatch *next; /* siblings in the name hash */
67 float priority; /* the priority */
68 const xmlChar *mode; /* the mode */
69 const xmlChar *modeURI; /* the mode URI */
70 xsltTemplatePtr template; /* the associated template */
72 /* TODO fix the statically allocated size steps[] */
75 xsltStepOp steps[20]; /* ops for computation */
78 typedef struct _xsltParserContext xsltParserContext;
79 typedef xsltParserContext *xsltParserContextPtr;
80 struct _xsltParserContext {
81 const xmlChar *cur; /* the current char being parsed */
82 const xmlChar *base; /* the full expression */
83 int error; /* error code */
84 xsltCompMatchPtr comp; /* the result */
87 /************************************************************************
91 ************************************************************************/
95 * @mode: the mode name or NULL
96 * @modeURI: the mode URI or NULL
98 * Create a new XSLT CompMatch
100 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
103 xsltNewCompMatch(const xmlChar *mode, const xmlChar *modeURI) {
104 xsltCompMatchPtr cur;
106 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
108 xsltGenericError(xsltGenericErrorContext,
109 "xsltNewCompMatch : malloc failed\n");
112 memset(cur, 0, sizeof(xsltCompMatch));
114 cur->mode = xmlStrdup(mode);
115 cur->modeURI = xmlStrdup(modeURI);
121 * @comp: an XSLT comp
123 * Free up the memory allocated by @comp
126 xsltFreeCompMatch(xsltCompMatchPtr comp) {
132 if (comp->mode != NULL)
133 xmlFree((xmlChar *)comp->mode);
134 if (comp->modeURI != NULL)
135 xmlFree((xmlChar *)comp->modeURI);
136 for (i = 0;i < comp->nbStep;i++) {
137 op = &comp->steps[i];
138 if (op->value != NULL)
140 if (op->value2 != NULL)
143 memset(comp, -1, sizeof(xsltCompMatch));
148 * xsltFreeCompMatchList:
149 * @comp: an XSLT comp list
151 * Free up the memory allocated by all the elements of @comp
154 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
155 xsltCompMatchPtr cur;
157 while (comp != NULL) {
160 xsltFreeCompMatch(cur);
165 * xsltNewParserContext:
167 * Create a new XSLT ParserContext
169 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
172 xsltNewParserContext(void) {
173 xsltParserContextPtr cur;
175 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
177 xsltGenericError(xsltGenericErrorContext,
178 "xsltNewParserContext : malloc failed\n");
181 memset(cur, 0, sizeof(xsltParserContext));
186 * xsltFreeParserContext:
187 * @ctxt: an XSLT parser context
189 * Free up the memory allocated by @ctxt
192 xsltFreeParserContext(xsltParserContextPtr ctxt) {
195 memset(ctxt, -1, sizeof(xsltParserContext));
201 * @comp: the compiled match expression
203 * @value: the first value
204 * @value2: the second value
206 * Add an step to an XSLT Compiled Match
208 * Returns -1 in case of failure, 0 otherwise.
211 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
213 if (comp->nbStep >= 20) {
214 xsltGenericError(xsltGenericErrorContext,
215 "xsltCompMatchAddOp: overflow\n");
218 comp->steps[comp->nbStep].op = op;
219 comp->steps[comp->nbStep].value = value;
220 comp->steps[comp->nbStep].value2 = value2;
226 * xsltSwapTopCompMatch:
227 * @comp: the compiled match expression
229 * reverse the two top steps.
232 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
234 int j = comp->nbStep - 1;
237 register xmlChar *tmp;
240 tmp = comp->steps[i].value;
241 comp->steps[i].value = comp->steps[j].value;
242 comp->steps[j].value = tmp;
243 tmp = comp->steps[i].value2;
244 comp->steps[i].value2 = comp->steps[j].value2;
245 comp->steps[j].value2 = tmp;
246 op = comp->steps[i].op;
247 comp->steps[i].op = comp->steps[j].op;
248 comp->steps[j].op = op;
253 * xsltReverseCompMatch:
254 * @comp: the compiled match expression
256 * reverse all the stack of expressions
259 xsltReverseCompMatch(xsltCompMatchPtr comp) {
261 int j = comp->nbStep - 1;
264 register xmlChar *tmp;
266 tmp = comp->steps[i].value;
267 comp->steps[i].value = comp->steps[j].value;
268 comp->steps[j].value = tmp;
269 tmp = comp->steps[i].value2;
270 comp->steps[i].value2 = comp->steps[j].value2;
271 comp->steps[j].value2 = tmp;
272 op = comp->steps[i].op;
273 comp->steps[i].op = comp->steps[j].op;
274 comp->steps[j].op = op;
278 comp->steps[comp->nbStep++].op = XSLT_OP_END;
281 /************************************************************************
283 * The interpreter for the precompiled patterns *
285 ************************************************************************/
289 * @ctxt: a XSLT process context
290 * @comp: the precompiled pattern
292 * @mode: the mode name or NULL
293 * @modeURI: the mode URI or NULL
295 * Test wether the node matches the pattern
297 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
300 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
301 xmlNodePtr node, const xmlChar *mode,
302 const xmlChar *modeURI) {
304 xsltStepOpPtr step, select = NULL;
306 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
307 xsltGenericError(xsltGenericErrorContext,
308 "xsltTestCompMatch: null arg\n");
312 if (comp->mode == NULL)
314 if ((comp->mode != mode) && (!xmlStrEqual(comp->mode, mode)))
317 if (comp->mode != NULL)
320 if (modeURI != NULL) {
321 if (comp->modeURI == NULL)
323 if ((comp->modeURI != modeURI) &&
324 (!xmlStrEqual(comp->modeURI, modeURI)))
327 if (comp->modeURI != NULL)
330 for (i = 0;i < comp->nbStep;i++) {
331 step = &comp->steps[i];
332 if (step->op != XSLT_OP_PREDICATE)
338 if ((node->type != XML_DOCUMENT_NODE) &&
339 (node->type != XML_HTML_DOCUMENT_NODE))
343 if (node->type != XML_ELEMENT_NODE)
345 if (step->value == NULL)
347 if (!xmlStrEqual(step->value, node->name))
351 if (node->ns == NULL) {
352 if (step->value2 != NULL)
354 } else if (node->ns->href != NULL) {
355 if (step->value2 == NULL)
357 if (!xmlStrEqual(step->value2, node->ns->href))
362 TODO /* Handle OP_CHILD */
365 if (node->type != XML_ATTRIBUTE_NODE)
367 if (step->value == NULL)
369 if (!xmlStrEqual(step->value, node->name))
373 if (node->ns == NULL) {
374 if (step->value2 != NULL)
376 } else if (node->ns->href != NULL) {
377 if (step->value2 == NULL)
379 if (!xmlStrEqual(step->value2, node->ns->href))
387 if (step->value == NULL)
389 if (!xmlStrEqual(step->value, node->name))
392 if (node->ns == NULL) {
393 if (step->value2 != NULL)
395 } else if (node->ns->href != NULL) {
396 if (step->value2 == NULL)
398 if (!xmlStrEqual(step->value2, node->ns->href))
402 case XSLT_OP_ANCESTOR:
403 /* TODO: implement coalescing of ANCESTOR/NODE ops */
404 if (step->value == NULL) {
406 step = &comp->steps[i];
407 if (step->op == XSLT_OP_ROOT)
409 if (step->op != XSLT_OP_ELEM)
411 if (step->value == NULL)
417 while (node != NULL) {
420 if (xmlStrEqual(step->value, node->name)) {
422 if (node->ns == NULL) {
423 if (step->value2 == NULL)
425 } else if (node->ns->href != NULL) {
426 if ((step->value2 != NULL) &&
427 (xmlStrEqual(step->value2, node->ns->href)))
437 /* TODO Handle IDs decently, must be done differently */
440 id = xmlGetID(node->doc, step->value);
441 if ((id == NULL) || (id->parent != node))
446 TODO /* Handle Keys, might be done differently */
450 if (node->ns == NULL) {
451 if (step->value != NULL)
453 } else if (node->ns->href != NULL) {
454 if (step->value == NULL)
456 if (!xmlStrEqual(step->value, node->ns->href))
461 switch (node->type) {
462 case XML_DOCUMENT_NODE:
463 case XML_HTML_DOCUMENT_NODE:
464 case XML_ELEMENT_NODE:
470 case XSLT_OP_PREDICATE: {
473 int pos = 0, len = 0;
475 * Depending on the last selection, one may need to
476 * recompute contextSize and proximityPosition.
478 oldCS = ctxt->xpathCtxt->contextSize;
479 oldCP = ctxt->xpathCtxt->proximityPosition;
480 if ((select != NULL) &&
481 (select->op == XSLT_OP_ELEM) &&
482 (select->value != NULL) &&
483 (node->type == XML_ELEMENT_NODE) &&
484 (node->parent != NULL)) {
486 /* TODO: cache those informations ?!? */
487 xmlNodePtr siblings = node->parent->children;
489 while (siblings != NULL) {
490 if (siblings->type == XML_ELEMENT_NODE) {
491 if (siblings == node) {
494 } else if (xmlStrEqual(node->name,
499 siblings = siblings->next;
502 ctxt->xpathCtxt->contextSize = len;
503 ctxt->xpathCtxt->proximityPosition = pos;
506 oldNode = ctxt->node;
509 if ((step->value == NULL) ||
510 (!xsltEvalXPathPredicate(ctxt, step->value))) {
512 ctxt->xpathCtxt->contextSize = oldCS;
513 ctxt->xpathCtxt->proximityPosition = oldCP;
515 ctxt->node = oldNode;
519 ctxt->xpathCtxt->contextSize = oldCS;
520 ctxt->xpathCtxt->proximityPosition = oldCP;
522 ctxt->node = oldNode;
526 if (node->type != XML_PI_NODE)
528 if (step->value != NULL) {
529 if (!xmlStrEqual(step->value, node->name))
533 case XSLT_OP_COMMENT:
534 if (node->type != XML_COMMENT_NODE)
538 if ((node->type != XML_TEXT_NODE) &&
539 (node->type != XML_CDATA_SECTION_NODE))
543 switch (node->type) {
544 case XML_DOCUMENT_NODE:
545 case XML_HTML_DOCUMENT_NODE:
546 case XML_ELEMENT_NODE:
547 case XML_CDATA_SECTION_NODE:
549 case XML_COMMENT_NODE:
551 case XML_ATTRIBUTE_NODE:
562 /************************************************************************
564 * Dedicated parser for templates *
566 ************************************************************************/
568 #define CUR (*ctxt->cur)
569 #define SKIP(val) ctxt->cur += (val)
570 #define NXT(val) ctxt->cur[(val)]
571 #define CUR_PTR ctxt->cur
573 #define SKIP_BLANKS \
574 while (IS_BLANK(CUR)) NEXT
576 #define CURRENT (*ctxt->cur)
577 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
580 #define PUSH(op, val, val2) \
581 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
584 xsltSwapTopCompMatch(ctxt->comp);
586 #define XSLT_ERROR(X) \
587 { xsltError(ctxt, __FILE__, __LINE__, X); \
588 ctxt->error = (X); return; }
590 #define XSLT_ERROR0(X) \
591 { xsltError(ctxt, __FILE__, __LINE__, X); \
592 ctxt->error = (X); return(0); }
596 * @ctxt: the XPath Parser context
598 * Parse an XPath Litteral:
600 * [29] Literal ::= '"' [^"]* '"'
603 * Returns the Literal parsed or NULL
607 xsltScanLiteral(xsltParserContextPtr ctxt) {
615 while ((IS_CHAR(CUR)) && (CUR != '"'))
618 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
622 ret = xmlStrndup(q, CUR_PTR - q);
625 } else if (CUR == '\'') {
628 while ((IS_CHAR(CUR)) && (CUR != '\''))
631 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
635 ret = xmlStrndup(q, CUR_PTR - q);
639 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
648 * @ctxt: the XPath Parser context
650 * Trickery: parse an XML name but without consuming the input flow
651 * Needed to avoid insanity in the parser state.
653 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
654 * CombiningChar | Extender
656 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
658 * [6] Names ::= Name (S Name)*
660 * Returns the Name parsed or NULL
664 xsltScanName(xsltParserContextPtr ctxt) {
665 xmlChar buf[XML_MAX_NAMELEN];
669 if (!IS_LETTER(CUR) && (CUR != '_') &&
674 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
675 (NXT(len) == '.') || (NXT(len) == '-') ||
676 (NXT(len) == '_') || (NXT(len) == ':') ||
677 (IS_COMBINING(NXT(len))) ||
678 (IS_EXTENDER(NXT(len)))) {
681 if (len >= XML_MAX_NAMELEN) {
682 xmlGenericError(xmlGenericErrorContext,
683 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
684 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
685 (NXT(len) == '.') || (NXT(len) == '-') ||
686 (NXT(len) == '_') || (NXT(len) == ':') ||
687 (IS_COMBINING(NXT(len))) ||
688 (IS_EXTENDER(NXT(len))))
694 return(xmlStrndup(buf, len));
697 * xsltCompileIdKeyPattern:
698 * @comp: the compilation context
699 * @name: a preparsed name
700 * @aid: whether id/key are allowed there
702 * Compile the XSLT LocationIdKeyPattern
703 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
704 * | 'key' '(' Literal ',' Literal ')'
706 * also handle NodeType and PI from:
708 * [7] NodeTest ::= NameTest
710 * | 'processing-instruction' '(' Literal ')'
713 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
715 xmlChar *lit2 = NULL;
718 xsltGenericError(xsltGenericErrorContext,
719 "xsltCompileIdKeyPattern : ( expected\n");
723 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
726 lit = xsltScanLiteral(ctxt);
731 xsltGenericError(xsltGenericErrorContext,
732 "xsltCompileIdKeyPattern : ) expected\n");
737 PUSH(XSLT_OP_ID, lit, NULL);
738 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
741 lit = xsltScanLiteral(ctxt);
746 xsltGenericError(xsltGenericErrorContext,
747 "xsltCompileIdKeyPattern : , expected\n");
753 lit2 = xsltScanLiteral(ctxt);
758 xsltGenericError(xsltGenericErrorContext,
759 "xsltCompileIdKeyPattern : ) expected\n");
764 PUSH(XSLT_OP_KEY, lit, lit2);
765 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
769 lit = xsltScanLiteral(ctxt);
774 xsltGenericError(xsltGenericErrorContext,
775 "xsltCompileIdKeyPattern : ) expected\n");
781 PUSH(XSLT_OP_PI, lit, NULL);
782 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
786 xsltGenericError(xsltGenericErrorContext,
787 "xsltCompileIdKeyPattern : ) expected\n");
792 PUSH(XSLT_OP_TEXT, NULL, NULL);
793 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
797 xsltGenericError(xsltGenericErrorContext,
798 "xsltCompileIdKeyPattern : ) expected\n");
803 PUSH(XSLT_OP_COMMENT, NULL, NULL);
804 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
808 xsltGenericError(xsltGenericErrorContext,
809 "xsltCompileIdKeyPattern : ) expected\n");
814 PUSH(XSLT_OP_NODE, NULL, NULL);
816 xsltGenericError(xsltGenericErrorContext,
817 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
821 xsltGenericError(xsltGenericErrorContext,
822 "xsltCompileIdKeyPattern : node type\n");
832 * xsltCompileStepPattern:
833 * @comp: the compilation context
834 * @token: a posible precompiled name
836 * Compile the XSLT StepPattern and generates a precompiled
837 * form suitable for fast matching.
839 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
840 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
841 * | ('child' | 'attribute') '::'
843 * [7] NodeTest ::= NameTest
845 * | 'processing-instruction' '(' Literal ')'
846 * [8] Predicate ::= '[' PredicateExpr ']'
847 * [9] PredicateExpr ::= Expr
848 * [13] AbbreviatedAxisSpecifier ::= '@'?
849 * [37] NameTest ::= '*' | NCName ':' '*' | QName
853 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
854 xmlChar *name = NULL;
857 if ((token == NULL) && (CUR == '@')) {
861 PUSH(XSLT_OP_ATTR, NULL, NULL);
864 token = xsltScanName(ctxt);
866 xsltGenericError(xsltGenericErrorContext,
867 "xsltCompileStepPattern : Name expected\n");
871 PUSH(XSLT_OP_ATTR, token, NULL);
875 token = xsltScanName(ctxt);
879 PUSH(XSLT_OP_ALL, token, NULL);
880 goto parse_predicate;
882 xsltGenericError(xsltGenericErrorContext,
883 "xsltCompileStepPattern : Name expected\n");
890 xsltCompileIdKeyPattern(ctxt, token, 0);
893 } else if (CUR == ':') {
896 xsltGenericError(xsltGenericErrorContext,
897 "xsltCompileStepPattern : sequence '::' expected\n");
902 if (xmlStrEqual(token, (const xmlChar *) "child")) {
903 /* TODO: handle namespace */
904 name = xsltScanName(ctxt);
906 xsltGenericError(xsltGenericErrorContext,
907 "xsltCompileStepPattern : QName expected\n");
911 PUSH(XSLT_OP_CHILD, name, NULL);
912 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
913 /* TODO: handle namespace */
914 name = xsltScanName(ctxt);
916 xsltGenericError(xsltGenericErrorContext,
917 "xsltCompileStepPattern : QName expected\n");
921 PUSH(XSLT_OP_ATTR, name, NULL);
923 xsltGenericError(xsltGenericErrorContext,
924 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
929 } else if (CUR == '*') {
931 PUSH(XSLT_OP_ALL, token, NULL);
933 /* TODO: handle namespace */
934 PUSH(XSLT_OP_ELEM, token, NULL);
944 /* TODO: avoid breaking in strings ... */
945 while ((IS_CHAR(CUR)) && (CUR != ']'))
948 xsltGenericError(xsltGenericErrorContext,
949 "xsltCompileStepPattern : ']' expected\n");
953 ret = xmlStrndup(q, CUR_PTR - q);
954 PUSH(XSLT_OP_PREDICATE, ret, NULL);
955 /* push the predicate lower than local test */
968 * xsltCompileRelativePathPattern:
969 * @comp: the compilation context
970 * @token: a posible precompiled name
972 * Compile the XSLT RelativePathPattern and generates a precompiled
973 * form suitable for fast matching.
975 * [4] RelativePathPattern ::= StepPattern
976 * | RelativePathPattern '/' StepPattern
977 * | RelativePathPattern '//' StepPattern
980 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
981 xsltCompileStepPattern(ctxt, token);
985 while ((CUR != 0) && (CUR != '|')) {
986 if ((CUR == '/') && (NXT(1) == '/')) {
987 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
991 xsltCompileStepPattern(ctxt, NULL);
992 } else if (CUR == '/') {
993 PUSH(XSLT_OP_PARENT, NULL, NULL);
996 if ((CUR != 0) || (CUR == '|')) {
997 xsltCompileRelativePathPattern(ctxt, NULL);
1011 * xsltCompileLocationPathPattern:
1012 * @comp: the compilation context
1014 * Compile the XSLT LocationPathPattern and generates a precompiled
1015 * form suitable for fast matching.
1017 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1018 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1019 * | '//'? RelativePathPattern
1022 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
1024 if ((CUR == '/') && (NXT(1) == '/')) {
1026 * since we reverse the query
1027 * a leading // can be safely ignored
1031 xsltCompileRelativePathPattern(ctxt, NULL);
1032 } else if (CUR == '/') {
1034 * We need to find root as the parent
1038 PUSH(XSLT_OP_ROOT, NULL, NULL);
1039 if ((CUR != 0) || (CUR == '|')) {
1040 PUSH(XSLT_OP_PARENT, NULL, NULL);
1041 xsltCompileRelativePathPattern(ctxt, NULL);
1043 } else if (CUR == '*') {
1044 xsltCompileRelativePathPattern(ctxt, NULL);
1045 } else if (CUR == '@') {
1046 xsltCompileRelativePathPattern(ctxt, NULL);
1049 name = xsltScanName(ctxt);
1051 xsltGenericError(xsltGenericErrorContext,
1052 "xsltCompileLocationPathPattern : Name expected\n");
1058 xsltCompileIdKeyPattern(ctxt, name, 1);
1059 if ((CUR == '/') && (NXT(1) == '/')) {
1060 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1064 xsltCompileRelativePathPattern(ctxt, NULL);
1065 } else if (CUR == '/') {
1066 PUSH(XSLT_OP_PARENT, NULL, NULL);
1069 xsltCompileRelativePathPattern(ctxt, NULL);
1073 xsltCompileRelativePathPattern(ctxt, name);
1080 * xsltCompilePattern:
1081 * @pattern an XSLT pattern
1082 * @mode: the mode name or NULL
1083 * @modeURI: the mode URI or NULL
1085 * Compile the XSLT pattern and generates a precompiled form suitable
1086 * for fast matching.
1087 * Note that the splitting as union of patterns is expected to be handled
1090 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1092 * Returns the generated xsltCompMatchPtr or NULL in case of failure
1096 xsltCompilePattern(const xmlChar *pattern, const xmlChar *mode,
1097 const xmlChar *modeURI) {
1098 xsltParserContextPtr ctxt;
1099 xsltCompMatchPtr ret;
1102 if (pattern == NULL) {
1103 xsltGenericError(xsltGenericErrorContext,
1104 "xsltCompilePattern : NULL pattern\n");
1108 #ifdef DEBUG_PARSING
1109 xsltGenericDebug(xsltGenericDebugContext,
1110 "xsltCompilePattern : parsing '%s'\n", pattern);
1114 while (IS_BLANK(*cur)) cur++;
1116 xsltGenericError(xsltGenericErrorContext,
1117 "xsltCompilePattern : NULL pattern\n");
1120 ctxt = xsltNewParserContext();
1123 ret = xsltNewCompMatch(mode, modeURI);
1125 xsltFreeParserContext(ctxt);
1130 ctxt->base = pattern;
1132 xsltCompileLocationPathPattern(ctxt);
1137 * Reverse for faster interpretation.
1139 xsltReverseCompMatch(ret);
1142 * Set-up the priority
1144 if (((ret->steps[0].op == XSLT_OP_ELEM) ||
1145 (ret->steps[0].op == XSLT_OP_ATTR)) &&
1146 (ret->steps[0].value != NULL) &&
1147 (ret->steps[1].op == XSLT_OP_END)) {
1149 } else if ((ret->steps[0].op == XSLT_OP_PI) &&
1150 (ret->steps[0].value != NULL) &&
1151 (ret->steps[1].op == XSLT_OP_END)) {
1153 } else if ((ret->steps[0].op == XSLT_OP_NS) &&
1154 (ret->steps[0].value != NULL) &&
1155 (ret->steps[1].op == XSLT_OP_END)) {
1156 ret->priority = -0.25;
1157 } else if (((ret->steps[0].op == XSLT_OP_PI) ||
1158 (ret->steps[0].op == XSLT_OP_TEXT) ||
1159 (ret->steps[0].op == XSLT_OP_NODE) ||
1160 (ret->steps[0].op == XSLT_OP_COMMENT)) &&
1161 (ret->steps[1].op == XSLT_OP_END)) {
1162 ret->priority = -0.5;
1164 ret->priority = 0.5;
1167 xsltFreeParserContext(ctxt);
1171 xsltFreeParserContext(ctxt);
1172 xsltFreeCompMatch(ret);
1178 /************************************************************************
1180 * Module interfaces *
1182 ************************************************************************/
1186 * @style: an XSLT stylesheet
1187 * @cur: an XSLT template
1188 * @mode: the mode name or NULL
1189 * @modeURI: the mode URI or NULL
1191 * Register the XSLT pattern associated to @cur
1193 * Returns -1 in case of error, 0 otherwise
1196 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
1197 const xmlChar *mode, const xmlChar *modeURI) {
1198 xsltCompMatchPtr pat, list, *top = NULL;
1199 const xmlChar *name = NULL;
1200 xmlChar *p, *pattern, tmp;
1202 if ((style == NULL) || (cur == NULL))
1213 * get a compiled form of the pattern
1216 while ((*p != 0) && (*p != '|')) {
1217 /* TODO: handle string escaping "a | b" in patterns ... */
1223 pat = xsltCompilePattern(pattern, mode, modeURI);
1229 pat->template = cur;
1230 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1231 pat->priority = cur->priority;
1234 * insert it in the hash table list corresponding to its lookup name
1236 switch (pat->steps[0].op) {
1238 if (pat->steps[0].value != NULL)
1239 name = pat->steps[0].value;
1241 top = (xsltCompMatchPtr *) &(style->attrMatch);
1245 case XSLT_OP_PARENT:
1246 case XSLT_OP_ANCESTOR:
1248 name = pat->steps[0].value;
1251 top = (xsltCompMatchPtr *) &(style->rootMatch);
1255 /* TODO optimize ID/KEY !!! */
1257 top = (xsltCompMatchPtr *) &(style->elemMatch);
1260 case XSLT_OP_PREDICATE:
1261 xsltGenericError(xsltGenericErrorContext,
1262 "xsltAddTemplate: invalid compiled pattern\n");
1263 xsltFreeCompMatch(pat);
1266 * TODO: some flags at the top level about type based patterns
1267 * would be faster than inclusion in the hash table.
1270 if (pat->steps[0].value != NULL)
1271 name = pat->steps[0].value;
1273 top = (xsltCompMatchPtr *) &(style->piMatch);
1275 case XSLT_OP_COMMENT:
1276 top = (xsltCompMatchPtr *) &(style->commentMatch);
1279 top = (xsltCompMatchPtr *) &(style->textMatch);
1282 if (pat->steps[0].value != NULL)
1283 name = pat->steps[0].value;
1285 top = (xsltCompMatchPtr *) &(style->elemMatch);
1290 if (style->templatesHash == NULL) {
1291 style->templatesHash = xmlHashCreate(1024);
1292 if (style->templatesHash == NULL) {
1293 xsltFreeCompMatch(pat);
1296 #ifdef DEBUG_PARSING
1297 xsltGenericDebug(xsltGenericDebugContext,
1298 "xsltAddTemplate: created template hash\n");
1300 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
1301 #ifdef DEBUG_PARSING
1302 xsltGenericDebug(xsltGenericDebugContext,
1303 "xsltAddTemplate: added new hash %s\n", name);
1306 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1307 name, mode, modeURI);
1309 xmlHashAddEntry3(style->templatesHash, name,
1310 mode, modeURI, pat);
1311 #ifdef DEBUG_PARSING
1312 xsltGenericDebug(xsltGenericDebugContext,
1313 "xsltAddTemplate: added new hash %s\n", name);
1317 * Note '<=' since one must choose among the matching
1318 * template rules that are left, the one that occurs
1319 * last in the stylesheet
1321 if (list->priority <= pat->priority) {
1323 xmlHashUpdateEntry3(style->templatesHash, name,
1324 mode, modeURI, pat, NULL);
1325 #ifdef DEBUG_PARSING
1326 xsltGenericDebug(xsltGenericDebugContext,
1327 "xsltAddTemplate: added head hash for %s\n", name);
1330 while (list->next != NULL) {
1331 if (list->next->priority <= pat->priority)
1335 pat->next = list->next;
1340 } else if (top != NULL) {
1345 } else if (list->priority <= pat->priority) {
1349 while (list->next != NULL) {
1350 if (list->next->priority <= pat->priority)
1354 pat->next = list->next;
1358 xsltGenericError(xsltGenericErrorContext,
1359 "xsltAddTemplate: invalid compiled pattern\n");
1360 xsltFreeCompMatch(pat);
1370 * @ctxt: a XSLT process context
1371 * @mode: the mode name or NULL
1373 * Finds the template applying to this node
1375 * Returns the xsltTemplatePtr or NULL if not found
1378 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1379 xsltStylesheetPtr style;
1380 xsltTemplatePtr ret = NULL;
1381 const xmlChar *name = NULL;
1382 xsltCompMatchPtr list = NULL;
1384 if ((ctxt == NULL) || (node == NULL))
1387 style = ctxt->style;
1388 while (style != NULL) {
1389 /* TODO : handle IDs/keys here ! */
1390 if (style->templatesHash != NULL) {
1392 * Use the top name as selector
1394 switch (node->type) {
1395 case XML_ELEMENT_NODE:
1396 case XML_ATTRIBUTE_NODE:
1400 case XML_DOCUMENT_NODE:
1401 case XML_HTML_DOCUMENT_NODE:
1403 case XML_CDATA_SECTION_NODE:
1404 case XML_COMMENT_NODE:
1405 case XML_ENTITY_REF_NODE:
1406 case XML_ENTITY_NODE:
1407 case XML_DOCUMENT_TYPE_NODE:
1408 case XML_DOCUMENT_FRAG_NODE:
1409 case XML_NOTATION_NODE:
1411 case XML_ELEMENT_DECL:
1412 case XML_ATTRIBUTE_DECL:
1413 case XML_ENTITY_DECL:
1414 case XML_NAMESPACE_DECL:
1415 case XML_XINCLUDE_START:
1416 case XML_XINCLUDE_END:
1425 * find the list of appliable expressions based on the name
1427 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1428 name, ctxt->mode, ctxt->modeURI);
1430 while (list != NULL) {
1431 if (xsltTestCompMatch(ctxt, list, node,
1432 ctxt->mode, ctxt->modeURI)) {
1433 ret = list->template;
1441 * find alternate generic matches
1443 switch (node->type) {
1444 case XML_ELEMENT_NODE:
1445 list = style->elemMatch;
1447 case XML_ATTRIBUTE_NODE:
1448 list = style->attrMatch;
1451 list = style->piMatch;
1453 case XML_DOCUMENT_NODE:
1454 case XML_HTML_DOCUMENT_NODE:
1455 list = style->rootMatch;
1458 case XML_CDATA_SECTION_NODE:
1459 list = style->textMatch;
1461 case XML_COMMENT_NODE:
1462 list = style->commentMatch;
1464 case XML_ENTITY_REF_NODE:
1465 case XML_ENTITY_NODE:
1466 case XML_DOCUMENT_TYPE_NODE:
1467 case XML_DOCUMENT_FRAG_NODE:
1468 case XML_NOTATION_NODE:
1470 case XML_ELEMENT_DECL:
1471 case XML_ATTRIBUTE_DECL:
1472 case XML_ENTITY_DECL:
1473 case XML_NAMESPACE_DECL:
1474 case XML_XINCLUDE_START:
1475 case XML_XINCLUDE_END:
1481 while ((list != NULL) &&
1482 ((ret == NULL) || (list->priority > ret->priority))) {
1483 if (xsltTestCompMatch(ctxt, list, node,
1484 ctxt->mode, ctxt->modeURI)) {
1485 ret = list->template;
1494 * Cycle on next stylesheet import.
1496 style = xsltNextImport(style);
1503 * xsltFreeTemplateHashes:
1504 * @style: an XSLT stylesheet
1506 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1509 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1510 if (style->templatesHash != NULL)
1511 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1512 (xmlHashDeallocator) xsltFreeCompMatchList);
1513 if (style->rootMatch != NULL)
1514 xsltFreeCompMatchList(style->rootMatch);
1515 if (style->elemMatch != NULL)
1516 xsltFreeCompMatchList(style->elemMatch);
1517 if (style->attrMatch != NULL)
1518 xsltFreeCompMatchList(style->attrMatch);
1519 if (style->parentMatch != NULL)
1520 xsltFreeCompMatchList(style->parentMatch);
1521 if (style->textMatch != NULL)
1522 xsltFreeCompMatchList(style->textMatch);
1523 if (style->piMatch != NULL)
1524 xsltFreeCompMatchList(style->piMatch);
1525 if (style->commentMatch != NULL)
1526 xsltFreeCompMatchList(style->commentMatch);