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"
30 /* #define DEBUG_PARSING */
56 typedef struct _xsltStepOp xsltStepOp;
57 typedef xsltStepOp *xsltStepOpPtr;
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 ************************************************************************/
96 * Create a new XSLT CompMatch
98 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
101 xsltNewCompMatch(void) {
102 xsltCompMatchPtr cur;
104 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
106 xsltGenericError(xsltGenericErrorContext,
107 "xsltNewCompMatch : malloc failed\n");
110 memset(cur, 0, sizeof(xsltCompMatch));
117 * @comp: an XSLT comp
119 * Free up the memory allocated by @comp
122 xsltFreeCompMatch(xsltCompMatchPtr comp) {
128 if (comp->mode != NULL)
129 xmlFree((xmlChar *)comp->mode);
130 if (comp->modeURI != NULL)
131 xmlFree((xmlChar *)comp->modeURI);
132 for (i = 0;i < comp->nbStep;i++) {
133 op = &comp->steps[i];
134 if (op->value != NULL)
136 if (op->value2 != NULL)
138 if (op->value3 != NULL)
141 memset(comp, -1, sizeof(xsltCompMatch));
146 * xsltFreeCompMatchList:
147 * @comp: an XSLT comp list
149 * Free up the memory allocated by all the elements of @comp
152 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
153 xsltCompMatchPtr cur;
155 while (comp != NULL) {
158 xsltFreeCompMatch(cur);
163 * xsltNewParserContext:
165 * Create a new XSLT ParserContext
167 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
170 xsltNewParserContext(void) {
171 xsltParserContextPtr cur;
173 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
175 xsltGenericError(xsltGenericErrorContext,
176 "xsltNewParserContext : malloc failed\n");
179 memset(cur, 0, sizeof(xsltParserContext));
184 * xsltFreeParserContext:
185 * @ctxt: an XSLT parser context
187 * Free up the memory allocated by @ctxt
190 xsltFreeParserContext(xsltParserContextPtr ctxt) {
193 memset(ctxt, -1, sizeof(xsltParserContext));
199 * @comp: the compiled match expression
201 * @value: the first value
202 * @value2: the second value
204 * Add an step to an XSLT Compiled Match
206 * Returns -1 in case of failure, 0 otherwise.
209 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
211 if (comp->nbStep >= 20) {
212 xsltGenericError(xsltGenericErrorContext,
213 "xsltCompMatchAddOp: overflow\n");
216 comp->steps[comp->nbStep].op = op;
217 comp->steps[comp->nbStep].value = value;
218 comp->steps[comp->nbStep].value2 = value2;
224 * xsltSwapTopCompMatch:
225 * @comp: the compiled match expression
227 * reverse the two top steps.
230 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
232 int j = comp->nbStep - 1;
235 register xmlChar *tmp;
238 tmp = comp->steps[i].value;
239 comp->steps[i].value = comp->steps[j].value;
240 comp->steps[j].value = tmp;
241 tmp = comp->steps[i].value2;
242 comp->steps[i].value2 = comp->steps[j].value2;
243 comp->steps[j].value2 = tmp;
244 op = comp->steps[i].op;
245 comp->steps[i].op = comp->steps[j].op;
246 comp->steps[j].op = op;
251 * xsltReverseCompMatch:
252 * @comp: the compiled match expression
254 * reverse all the stack of expressions
257 xsltReverseCompMatch(xsltCompMatchPtr comp) {
259 int j = comp->nbStep - 1;
262 register xmlChar *tmp;
264 tmp = comp->steps[i].value;
265 comp->steps[i].value = comp->steps[j].value;
266 comp->steps[j].value = tmp;
267 tmp = comp->steps[i].value2;
268 comp->steps[i].value2 = comp->steps[j].value2;
269 comp->steps[j].value2 = tmp;
270 op = comp->steps[i].op;
271 comp->steps[i].op = comp->steps[j].op;
272 comp->steps[j].op = op;
276 comp->steps[comp->nbStep++].op = XSLT_OP_END;
279 /************************************************************************
281 * The interpreter for the precompiled patterns *
283 ************************************************************************/
287 * @ctxt: a XSLT process context
288 * @comp: the precompiled pattern
290 * @mode: the mode name or NULL
291 * @modeURI: the mode URI or NULL
293 * Test wether the node matches the pattern
295 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
298 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
299 xmlNodePtr node, const xmlChar *mode,
300 const xmlChar *modeURI) {
302 xsltStepOpPtr step, select = NULL;
304 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
305 xsltGenericError(xsltGenericErrorContext,
306 "xsltTestCompMatch: null arg\n");
310 if (comp->mode == NULL)
312 if ((comp->mode != mode) && (!xmlStrEqual(comp->mode, mode)))
315 if (comp->mode != NULL)
318 if (modeURI != NULL) {
319 if (comp->modeURI == NULL)
321 if ((comp->modeURI != modeURI) &&
322 (!xmlStrEqual(comp->modeURI, modeURI)))
325 if (comp->modeURI != NULL)
328 for (i = 0;i < comp->nbStep;i++) {
329 step = &comp->steps[i];
330 if (step->op != XSLT_OP_PREDICATE)
336 if ((node->type != XML_DOCUMENT_NODE) &&
337 (node->type != XML_HTML_DOCUMENT_NODE))
341 if (node->type != XML_ELEMENT_NODE)
343 if (step->value == NULL)
345 if (!xmlStrEqual(step->value, node->name))
349 if (node->ns == NULL) {
350 if (step->value2 != NULL)
352 } else if (node->ns->href != NULL) {
353 if (step->value2 == NULL)
355 if (!xmlStrEqual(step->value2, node->ns->href))
360 TODO /* Handle OP_CHILD */
363 if (node->type != XML_ATTRIBUTE_NODE)
365 if (step->value == NULL)
367 if (!xmlStrEqual(step->value, node->name))
371 if (node->ns == NULL) {
372 if (step->value2 != NULL)
374 } else if (node->ns->href != NULL) {
375 if (step->value2 == NULL)
377 if (!xmlStrEqual(step->value2, node->ns->href))
385 if (step->value == NULL)
387 if (!xmlStrEqual(step->value, node->name))
390 if (node->ns == NULL) {
391 if (step->value2 != NULL)
393 } else if (node->ns->href != NULL) {
394 if (step->value2 == NULL)
396 if (!xmlStrEqual(step->value2, node->ns->href))
400 case XSLT_OP_ANCESTOR:
401 /* TODO: implement coalescing of ANCESTOR/NODE ops */
402 if (step->value == NULL) {
404 step = &comp->steps[i];
405 if (step->op == XSLT_OP_ROOT)
407 if (step->op != XSLT_OP_ELEM)
409 if (step->value == NULL)
415 while (node != NULL) {
418 if (xmlStrEqual(step->value, node->name)) {
420 if (node->ns == NULL) {
421 if (step->value2 == NULL)
423 } else if (node->ns->href != NULL) {
424 if ((step->value2 != NULL) &&
425 (xmlStrEqual(step->value2, node->ns->href)))
435 /* TODO Handle IDs decently, must be done differently */
438 id = xmlGetID(node->doc, step->value);
439 if ((id == NULL) || (id->parent != node))
447 list = xsltGetKey(ctxt, step->value,
448 step->value3, step->value2);
451 for (i = 0;i < list->nodeNr;i++)
452 if (list->nodeTab[i] == node)
454 if (i >= list->nodeNr)
460 if (node->ns == NULL) {
461 if (step->value != NULL)
463 } else if (node->ns->href != NULL) {
464 if (step->value == NULL)
466 if (!xmlStrEqual(step->value, node->ns->href))
471 switch (node->type) {
472 case XML_DOCUMENT_NODE:
473 case XML_HTML_DOCUMENT_NODE:
474 case XML_ELEMENT_NODE:
480 case XSLT_OP_PREDICATE: {
483 int pos = 0, len = 0;
485 * Depending on the last selection, one may need to
486 * recompute contextSize and proximityPosition.
488 oldCS = ctxt->xpathCtxt->contextSize;
489 oldCP = ctxt->xpathCtxt->proximityPosition;
490 if ((select != NULL) &&
491 (select->op == XSLT_OP_ELEM) &&
492 (select->value != NULL) &&
493 (node->type == XML_ELEMENT_NODE) &&
494 (node->parent != NULL)) {
496 /* TODO: cache those informations ?!? */
497 xmlNodePtr siblings = node->parent->children;
499 while (siblings != NULL) {
500 if (siblings->type == XML_ELEMENT_NODE) {
501 if (siblings == node) {
504 } else if (xmlStrEqual(node->name,
509 siblings = siblings->next;
512 ctxt->xpathCtxt->contextSize = len;
513 ctxt->xpathCtxt->proximityPosition = pos;
516 oldNode = ctxt->node;
519 if ((step->value == NULL) ||
520 (!xsltEvalXPathPredicate(ctxt, step->value))) {
522 ctxt->xpathCtxt->contextSize = oldCS;
523 ctxt->xpathCtxt->proximityPosition = oldCP;
525 ctxt->node = oldNode;
529 ctxt->xpathCtxt->contextSize = oldCS;
530 ctxt->xpathCtxt->proximityPosition = oldCP;
532 ctxt->node = oldNode;
536 if (node->type != XML_PI_NODE)
538 if (step->value != NULL) {
539 if (!xmlStrEqual(step->value, node->name))
543 case XSLT_OP_COMMENT:
544 if (node->type != XML_COMMENT_NODE)
548 if ((node->type != XML_TEXT_NODE) &&
549 (node->type != XML_CDATA_SECTION_NODE))
553 switch (node->type) {
554 case XML_DOCUMENT_NODE:
555 case XML_HTML_DOCUMENT_NODE:
556 case XML_ELEMENT_NODE:
557 case XML_CDATA_SECTION_NODE:
559 case XML_COMMENT_NODE:
561 case XML_ATTRIBUTE_NODE:
573 * xsltTestCompMatchList:
574 * @ctxt: a XSLT process context
576 * @comp: the precompiled pattern list
578 * Test wether the node matches one of the patterns in the list
580 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
583 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
584 xsltCompMatchPtr comp) {
587 if ((ctxt == NULL) || (node == NULL))
589 while (comp != NULL) {
590 ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
598 /************************************************************************
600 * Dedicated parser for templates *
602 ************************************************************************/
604 #define CUR (*ctxt->cur)
605 #define SKIP(val) ctxt->cur += (val)
606 #define NXT(val) ctxt->cur[(val)]
607 #define CUR_PTR ctxt->cur
609 #define SKIP_BLANKS \
610 while (IS_BLANK(CUR)) NEXT
612 #define CURRENT (*ctxt->cur)
613 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
616 #define PUSH(op, val, val2) \
617 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
620 xsltSwapTopCompMatch(ctxt->comp);
622 #define XSLT_ERROR(X) \
623 { xsltError(ctxt, __FILE__, __LINE__, X); \
624 ctxt->error = (X); return; }
626 #define XSLT_ERROR0(X) \
627 { xsltError(ctxt, __FILE__, __LINE__, X); \
628 ctxt->error = (X); return(0); }
632 * @ctxt: the XPath Parser context
634 * Parse an XPath Litteral:
636 * [29] Literal ::= '"' [^"]* '"'
639 * Returns the Literal parsed or NULL
643 xsltScanLiteral(xsltParserContextPtr ctxt) {
651 while ((IS_CHAR(CUR)) && (CUR != '"'))
654 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
658 ret = xmlStrndup(q, CUR_PTR - q);
661 } else if (CUR == '\'') {
664 while ((IS_CHAR(CUR)) && (CUR != '\''))
667 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
671 ret = xmlStrndup(q, CUR_PTR - q);
675 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
684 * @ctxt: the XPath Parser context
686 * Trickery: parse an XML name but without consuming the input flow
687 * Needed to avoid insanity in the parser state.
689 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
690 * CombiningChar | Extender
692 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
694 * [6] Names ::= Name (S Name)*
696 * Returns the Name parsed or NULL
700 xsltScanName(xsltParserContextPtr ctxt) {
701 xmlChar buf[XML_MAX_NAMELEN];
705 if (!IS_LETTER(CUR) && (CUR != '_') &&
710 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
711 (NXT(len) == '.') || (NXT(len) == '-') ||
712 (NXT(len) == '_') || (NXT(len) == ':') ||
713 (IS_COMBINING(NXT(len))) ||
714 (IS_EXTENDER(NXT(len)))) {
717 if (len >= XML_MAX_NAMELEN) {
718 xmlGenericError(xmlGenericErrorContext,
719 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
720 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
721 (NXT(len) == '.') || (NXT(len) == '-') ||
722 (NXT(len) == '_') || (NXT(len) == ':') ||
723 (IS_COMBINING(NXT(len))) ||
724 (IS_EXTENDER(NXT(len))))
730 return(xmlStrndup(buf, len));
733 * xsltCompileIdKeyPattern:
734 * @comp: the compilation context
735 * @name: a preparsed name
736 * @aid: whether id/key are allowed there
738 * Compile the XSLT LocationIdKeyPattern
739 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
740 * | 'key' '(' Literal ',' Literal ')'
742 * also handle NodeType and PI from:
744 * [7] NodeTest ::= NameTest
746 * | 'processing-instruction' '(' Literal ')'
749 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
751 xmlChar *lit2 = NULL;
754 xsltGenericError(xsltGenericErrorContext,
755 "xsltCompileIdKeyPattern : ( expected\n");
759 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
762 lit = xsltScanLiteral(ctxt);
767 xsltGenericError(xsltGenericErrorContext,
768 "xsltCompileIdKeyPattern : ) expected\n");
773 PUSH(XSLT_OP_ID, lit, NULL);
774 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
777 lit = xsltScanLiteral(ctxt);
782 xsltGenericError(xsltGenericErrorContext,
783 "xsltCompileIdKeyPattern : , expected\n");
789 lit2 = xsltScanLiteral(ctxt);
794 xsltGenericError(xsltGenericErrorContext,
795 "xsltCompileIdKeyPattern : ) expected\n");
800 /* TODO: support namespace in keys */
801 PUSH(XSLT_OP_KEY, lit, lit2);
802 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
806 lit = xsltScanLiteral(ctxt);
811 xsltGenericError(xsltGenericErrorContext,
812 "xsltCompileIdKeyPattern : ) expected\n");
818 PUSH(XSLT_OP_PI, lit, NULL);
819 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
823 xsltGenericError(xsltGenericErrorContext,
824 "xsltCompileIdKeyPattern : ) expected\n");
829 PUSH(XSLT_OP_TEXT, NULL, NULL);
830 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
834 xsltGenericError(xsltGenericErrorContext,
835 "xsltCompileIdKeyPattern : ) expected\n");
840 PUSH(XSLT_OP_COMMENT, NULL, NULL);
841 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
845 xsltGenericError(xsltGenericErrorContext,
846 "xsltCompileIdKeyPattern : ) expected\n");
851 PUSH(XSLT_OP_NODE, NULL, NULL);
853 xsltGenericError(xsltGenericErrorContext,
854 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
858 xsltGenericError(xsltGenericErrorContext,
859 "xsltCompileIdKeyPattern : node type\n");
869 * xsltCompileStepPattern:
870 * @comp: the compilation context
871 * @token: a posible precompiled name
873 * Compile the XSLT StepPattern and generates a precompiled
874 * form suitable for fast matching.
876 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
877 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
878 * | ('child' | 'attribute') '::'
880 * [7] NodeTest ::= NameTest
882 * | 'processing-instruction' '(' Literal ')'
883 * [8] Predicate ::= '[' PredicateExpr ']'
884 * [9] PredicateExpr ::= Expr
885 * [13] AbbreviatedAxisSpecifier ::= '@'?
886 * [37] NameTest ::= '*' | NCName ':' '*' | QName
890 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
891 xmlChar *name = NULL;
894 if ((token == NULL) && (CUR == '@')) {
898 PUSH(XSLT_OP_ATTR, NULL, NULL);
901 token = xsltScanName(ctxt);
903 xsltGenericError(xsltGenericErrorContext,
904 "xsltCompileStepPattern : Name expected\n");
908 PUSH(XSLT_OP_ATTR, token, NULL);
912 token = xsltScanName(ctxt);
916 PUSH(XSLT_OP_ALL, token, NULL);
917 goto parse_predicate;
919 xsltGenericError(xsltGenericErrorContext,
920 "xsltCompileStepPattern : Name expected\n");
927 xsltCompileIdKeyPattern(ctxt, token, 0);
930 } else if (CUR == ':') {
933 xsltGenericError(xsltGenericErrorContext,
934 "xsltCompileStepPattern : sequence '::' expected\n");
939 if (xmlStrEqual(token, (const xmlChar *) "child")) {
940 /* TODO: handle namespace */
941 name = xsltScanName(ctxt);
943 xsltGenericError(xsltGenericErrorContext,
944 "xsltCompileStepPattern : QName expected\n");
948 PUSH(XSLT_OP_CHILD, name, NULL);
949 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
950 /* TODO: handle namespace */
951 name = xsltScanName(ctxt);
953 xsltGenericError(xsltGenericErrorContext,
954 "xsltCompileStepPattern : QName expected\n");
958 PUSH(XSLT_OP_ATTR, name, NULL);
960 xsltGenericError(xsltGenericErrorContext,
961 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
966 } else if (CUR == '*') {
968 PUSH(XSLT_OP_ALL, token, NULL);
970 /* TODO: handle namespace */
971 PUSH(XSLT_OP_ELEM, token, NULL);
981 /* TODO: avoid breaking in strings ... */
982 while ((IS_CHAR(CUR)) && (CUR != ']'))
985 xsltGenericError(xsltGenericErrorContext,
986 "xsltCompileStepPattern : ']' expected\n");
990 ret = xmlStrndup(q, CUR_PTR - q);
991 PUSH(XSLT_OP_PREDICATE, ret, NULL);
992 /* push the predicate lower than local test */
1005 * xsltCompileRelativePathPattern:
1006 * @comp: the compilation context
1007 * @token: a posible precompiled name
1009 * Compile the XSLT RelativePathPattern and generates a precompiled
1010 * form suitable for fast matching.
1012 * [4] RelativePathPattern ::= StepPattern
1013 * | RelativePathPattern '/' StepPattern
1014 * | RelativePathPattern '//' StepPattern
1017 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1018 xsltCompileStepPattern(ctxt, token);
1022 while ((CUR != 0) && (CUR != '|')) {
1023 if ((CUR == '/') && (NXT(1) == '/')) {
1024 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1028 xsltCompileStepPattern(ctxt, NULL);
1029 } else if (CUR == '/') {
1030 PUSH(XSLT_OP_PARENT, NULL, NULL);
1033 if ((CUR != 0) || (CUR == '|')) {
1034 xsltCompileRelativePathPattern(ctxt, NULL);
1048 * xsltCompileLocationPathPattern:
1049 * @comp: the compilation context
1051 * Compile the XSLT LocationPathPattern and generates a precompiled
1052 * form suitable for fast matching.
1054 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1055 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1056 * | '//'? RelativePathPattern
1059 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
1061 if ((CUR == '/') && (NXT(1) == '/')) {
1063 * since we reverse the query
1064 * a leading // can be safely ignored
1068 xsltCompileRelativePathPattern(ctxt, NULL);
1069 } else if (CUR == '/') {
1071 * We need to find root as the parent
1075 PUSH(XSLT_OP_ROOT, NULL, NULL);
1076 if ((CUR != 0) || (CUR == '|')) {
1077 PUSH(XSLT_OP_PARENT, NULL, NULL);
1078 xsltCompileRelativePathPattern(ctxt, NULL);
1080 } else if (CUR == '*') {
1081 xsltCompileRelativePathPattern(ctxt, NULL);
1082 } else if (CUR == '@') {
1083 xsltCompileRelativePathPattern(ctxt, NULL);
1086 name = xsltScanName(ctxt);
1088 xsltGenericError(xsltGenericErrorContext,
1089 "xsltCompileLocationPathPattern : Name expected\n");
1095 xsltCompileIdKeyPattern(ctxt, name, 1);
1096 if ((CUR == '/') && (NXT(1) == '/')) {
1097 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1101 xsltCompileRelativePathPattern(ctxt, NULL);
1102 } else if (CUR == '/') {
1103 PUSH(XSLT_OP_PARENT, NULL, NULL);
1106 xsltCompileRelativePathPattern(ctxt, NULL);
1110 xsltCompileRelativePathPattern(ctxt, name);
1117 * xsltCompilePattern:
1118 * @pattern an XSLT pattern
1120 * Compile the XSLT pattern and generates a list of precompiled form suitable
1121 * for fast matching.
1123 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1125 * Returns the generated pattern list or NULL in case of failure
1129 xsltCompilePattern(const xmlChar *pattern) {
1130 xsltParserContextPtr ctxt = NULL;
1131 xsltCompMatchPtr element, first = NULL, previous = NULL;
1132 int current, start, end;
1134 if (pattern == NULL) {
1135 xsltGenericError(xsltGenericErrorContext,
1136 "xsltCompilePattern : NULL pattern\n");
1140 #ifdef DEBUG_PARSING
1141 xsltGenericDebug(xsltGenericDebugContext,
1142 "xsltCompilePattern : parsing '%s'\n", pattern);
1145 ctxt = xsltNewParserContext();
1149 while (pattern[current] != 0) {
1151 while (IS_BLANK(pattern[current]))
1154 while ((pattern[end] != 0) && (pattern[end] != '|'))
1156 if (current == end) {
1157 xsltGenericError(xsltGenericErrorContext,
1158 "xsltCompilePattern : NULL pattern\n");
1161 element = xsltNewCompMatch();
1162 if (element == NULL) {
1167 else if (previous != NULL)
1168 previous->next = element;
1171 ctxt->comp = element;
1172 ctxt->base = xmlStrndup(&pattern[start], end - start);
1173 ctxt->cur = &(ctxt->base)[current - start];
1174 xsltCompileLocationPathPattern(ctxt);
1176 xmlFree((xmlChar *)ctxt->base);
1181 * Reverse for faster interpretation.
1183 xsltReverseCompMatch(element);
1186 * Set-up the priority
1188 if (((element->steps[0].op == XSLT_OP_ELEM) ||
1189 (element->steps[0].op == XSLT_OP_ATTR)) &&
1190 (element->steps[0].value != NULL) &&
1191 (element->steps[1].op == XSLT_OP_END)) {
1192 element->priority = 0;
1193 } else if ((element->steps[0].op == XSLT_OP_PI) &&
1194 (element->steps[0].value != NULL) &&
1195 (element->steps[1].op == XSLT_OP_END)) {
1196 element->priority = 0;
1197 } else if ((element->steps[0].op == XSLT_OP_NS) &&
1198 (element->steps[0].value != NULL) &&
1199 (element->steps[1].op == XSLT_OP_END)) {
1200 element->priority = -0.25;
1201 } else if (((element->steps[0].op == XSLT_OP_PI) ||
1202 (element->steps[0].op == XSLT_OP_TEXT) ||
1203 (element->steps[0].op == XSLT_OP_ALL) ||
1204 (element->steps[0].op == XSLT_OP_NODE) ||
1205 (element->steps[0].op == XSLT_OP_COMMENT)) &&
1206 (element->steps[1].op == XSLT_OP_END)) {
1207 element->priority = -0.5;
1209 element->priority = 0.5;
1211 if (pattern[end] == '|')
1216 xsltGenericError(xsltGenericErrorContext,
1217 "xsltCompilePattern : NULL pattern\n");
1221 xsltFreeParserContext(ctxt);
1226 xsltFreeParserContext(ctxt);
1228 xsltFreeCompMatchList(first);
1232 /************************************************************************
1234 * Module interfaces *
1236 ************************************************************************/
1240 * @style: an XSLT stylesheet
1241 * @cur: an XSLT template
1242 * @mode: the mode name or NULL
1243 * @modeURI: the mode URI or NULL
1245 * Register the XSLT pattern associated to @cur
1247 * Returns -1 in case of error, 0 otherwise
1250 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
1251 const xmlChar *mode, const xmlChar *modeURI) {
1252 xsltCompMatchPtr pat, list, *top = NULL, next;
1253 const xmlChar *name = NULL;
1255 if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
1258 pat = xsltCompilePattern(cur->match);
1263 pat->template = cur;
1265 pat->mode = xmlStrdup(mode);
1266 if (modeURI != NULL)
1267 pat->modeURI = xmlStrdup(modeURI);
1268 if (pat->priority != XSLT_PAT_NO_PRIORITY)
1269 cur->priority = pat->priority;
1271 pat->priority = cur->priority;
1274 * insert it in the hash table list corresponding to its lookup name
1276 switch (pat->steps[0].op) {
1278 if (pat->steps[0].value != NULL)
1279 name = pat->steps[0].value;
1281 top = (xsltCompMatchPtr *) &(style->attrMatch);
1285 case XSLT_OP_PARENT:
1286 case XSLT_OP_ANCESTOR:
1288 name = pat->steps[0].value;
1291 top = (xsltCompMatchPtr *) &(style->rootMatch);
1294 top = (xsltCompMatchPtr *) &(style->keyMatch);
1297 /* TODO optimize ID !!! */
1299 top = (xsltCompMatchPtr *) &(style->elemMatch);
1302 case XSLT_OP_PREDICATE:
1303 xsltGenericError(xsltGenericErrorContext,
1304 "xsltAddTemplate: invalid compiled pattern\n");
1305 xsltFreeCompMatch(pat);
1308 * TODO: some flags at the top level about type based patterns
1309 * would be faster than inclusion in the hash table.
1312 if (pat->steps[0].value != NULL)
1313 name = pat->steps[0].value;
1315 top = (xsltCompMatchPtr *) &(style->piMatch);
1317 case XSLT_OP_COMMENT:
1318 top = (xsltCompMatchPtr *) &(style->commentMatch);
1321 top = (xsltCompMatchPtr *) &(style->textMatch);
1324 if (pat->steps[0].value != NULL)
1325 name = pat->steps[0].value;
1327 top = (xsltCompMatchPtr *) &(style->elemMatch);
1332 if (style->templatesHash == NULL) {
1333 style->templatesHash = xmlHashCreate(1024);
1334 if (style->templatesHash == NULL) {
1335 xsltFreeCompMatch(pat);
1338 #ifdef DEBUG_PARSING
1339 xsltGenericDebug(xsltGenericDebugContext,
1340 "xsltAddTemplate: created template hash\n");
1342 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
1343 #ifdef DEBUG_PARSING
1344 xsltGenericDebug(xsltGenericDebugContext,
1345 "xsltAddTemplate: added new hash %s\n", name);
1348 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1349 name, mode, modeURI);
1351 xmlHashAddEntry3(style->templatesHash, name,
1352 mode, modeURI, pat);
1353 #ifdef DEBUG_PARSING
1354 xsltGenericDebug(xsltGenericDebugContext,
1355 "xsltAddTemplate: added new hash %s\n", name);
1359 * Note '<=' since one must choose among the matching
1360 * template rules that are left, the one that occurs
1361 * last in the stylesheet
1363 if (list->priority <= pat->priority) {
1365 xmlHashUpdateEntry3(style->templatesHash, name,
1366 mode, modeURI, pat, NULL);
1367 #ifdef DEBUG_PARSING
1368 xsltGenericDebug(xsltGenericDebugContext,
1369 "xsltAddTemplate: added head hash for %s\n", name);
1372 while (list->next != NULL) {
1373 if (list->next->priority <= pat->priority)
1377 pat->next = list->next;
1382 } else if (top != NULL) {
1387 } else if (list->priority <= pat->priority) {
1391 while (list->next != NULL) {
1392 if (list->next->priority <= pat->priority)
1396 pat->next = list->next;
1400 xsltGenericError(xsltGenericErrorContext,
1401 "xsltAddTemplate: invalid compiled pattern\n");
1402 xsltFreeCompMatch(pat);
1412 * @ctxt: a XSLT process context
1414 * @style: the current style
1416 * Finds the template applying to this node, if @style is non-NULL
1417 * it means one need to look for the next imported template in scope.
1419 * Returns the xsltTemplatePtr or NULL if not found
1422 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
1423 xsltStylesheetPtr style) {
1424 xsltStylesheetPtr curstyle;
1425 xsltTemplatePtr ret = NULL;
1426 const xmlChar *name = NULL;
1427 xsltCompMatchPtr list = NULL;
1429 if ((ctxt == NULL) || (node == NULL))
1432 if (style == NULL) {
1433 curstyle = ctxt->style;
1435 curstyle = xsltNextImport(style);
1438 while ((curstyle != NULL) && (curstyle != style)) {
1439 /* TODO : handle IDs/keys here ! */
1440 if (curstyle->templatesHash != NULL) {
1442 * Use the top name as selector
1444 switch (node->type) {
1445 case XML_ELEMENT_NODE:
1446 case XML_ATTRIBUTE_NODE:
1450 case XML_DOCUMENT_NODE:
1451 case XML_HTML_DOCUMENT_NODE:
1453 case XML_CDATA_SECTION_NODE:
1454 case XML_COMMENT_NODE:
1455 case XML_ENTITY_REF_NODE:
1456 case XML_ENTITY_NODE:
1457 case XML_DOCUMENT_TYPE_NODE:
1458 case XML_DOCUMENT_FRAG_NODE:
1459 case XML_NOTATION_NODE:
1461 case XML_ELEMENT_DECL:
1462 case XML_ATTRIBUTE_DECL:
1463 case XML_ENTITY_DECL:
1464 case XML_NAMESPACE_DECL:
1465 case XML_XINCLUDE_START:
1466 case XML_XINCLUDE_END:
1475 * find the list of appliable expressions based on the name
1477 list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
1478 name, ctxt->mode, ctxt->modeURI);
1480 while (list != NULL) {
1481 if (xsltTestCompMatch(ctxt, list, node,
1482 ctxt->mode, ctxt->modeURI)) {
1483 ret = list->template;
1491 * find alternate generic matches
1493 switch (node->type) {
1494 case XML_ELEMENT_NODE:
1495 list = curstyle->elemMatch;
1497 case XML_ATTRIBUTE_NODE:
1498 list = curstyle->attrMatch;
1501 list = curstyle->piMatch;
1503 case XML_DOCUMENT_NODE:
1504 case XML_HTML_DOCUMENT_NODE:
1505 list = curstyle->rootMatch;
1508 case XML_CDATA_SECTION_NODE:
1509 list = curstyle->textMatch;
1511 case XML_COMMENT_NODE:
1512 list = curstyle->commentMatch;
1514 case XML_ENTITY_REF_NODE:
1515 case XML_ENTITY_NODE:
1516 case XML_DOCUMENT_TYPE_NODE:
1517 case XML_DOCUMENT_FRAG_NODE:
1518 case XML_NOTATION_NODE:
1520 case XML_ELEMENT_DECL:
1521 case XML_ATTRIBUTE_DECL:
1522 case XML_ENTITY_DECL:
1523 case XML_NAMESPACE_DECL:
1524 case XML_XINCLUDE_START:
1525 case XML_XINCLUDE_END:
1531 while ((list != NULL) &&
1532 ((ret == NULL) || (list->priority > ret->priority))) {
1533 if (xsltTestCompMatch(ctxt, list, node,
1534 ctxt->mode, ctxt->modeURI)) {
1535 ret = list->template;
1540 if (node->_private != NULL) {
1541 list = curstyle->keyMatch;
1542 while ((list != NULL) &&
1543 ((ret == NULL) || (list->priority > ret->priority))) {
1544 if (xsltTestCompMatch(ctxt, list, node,
1545 ctxt->mode, ctxt->modeURI)) {
1546 ret = list->template;
1557 * Cycle on next curstylesheet import.
1559 curstyle = xsltNextImport(curstyle);
1566 * xsltFreeTemplateHashes:
1567 * @style: an XSLT stylesheet
1569 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1572 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1573 if (style->templatesHash != NULL)
1574 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1575 (xmlHashDeallocator) xsltFreeCompMatchList);
1576 if (style->rootMatch != NULL)
1577 xsltFreeCompMatchList(style->rootMatch);
1578 if (style->keyMatch != NULL)
1579 xsltFreeCompMatchList(style->keyMatch);
1580 if (style->elemMatch != NULL)
1581 xsltFreeCompMatchList(style->elemMatch);
1582 if (style->attrMatch != NULL)
1583 xsltFreeCompMatchList(style->attrMatch);
1584 if (style->parentMatch != NULL)
1585 xsltFreeCompMatchList(style->parentMatch);
1586 if (style->textMatch != NULL)
1587 xsltFreeCompMatchList(style->textMatch);
1588 if (style->piMatch != NULL)
1589 xsltFreeCompMatchList(style->piMatch);
1590 if (style->commentMatch != NULL)
1591 xsltFreeCompMatchList(style->commentMatch);
1596 * @node: a node in the source tree
1597 * @pattern: an XSLT pattern
1599 * Determine if a node matches a pattern.
1602 xsltMatchPattern(xsltTransformContextPtr context,
1604 const xmlChar *pattern)
1607 xsltCompMatchPtr first, comp;
1609 if ((context != NULL) && (pattern != NULL)) {
1610 first = xsltCompilePattern(pattern);
1611 for (comp = first; comp != NULL; comp = comp->next) {
1612 match = xsltTestCompMatch(context, comp, node, NULL, NULL);
1617 xsltFreeCompMatchList(first);