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 typedef struct _xsltCompMatch xsltCompMatch;
66 typedef xsltCompMatch *xsltCompMatchPtr;
67 struct _xsltCompMatch {
68 struct _xsltCompMatch *next; /* siblings in the name hash */
69 float priority; /* the priority */
70 const xmlChar *mode; /* the mode */
71 const xmlChar *modeURI; /* the mode URI */
72 xsltTemplatePtr template; /* the associated template */
74 /* TODO fix the statically allocated size steps[] */
77 xsltStepOp steps[20]; /* ops for computation */
80 typedef struct _xsltParserContext xsltParserContext;
81 typedef xsltParserContext *xsltParserContextPtr;
82 struct _xsltParserContext {
83 const xmlChar *cur; /* the current char being parsed */
84 const xmlChar *base; /* the full expression */
85 int error; /* error code */
86 xsltCompMatchPtr comp; /* the result */
89 /************************************************************************
93 ************************************************************************/
97 * @mode: the mode name or NULL
98 * @modeURI: the mode URI or NULL
100 * Create a new XSLT CompMatch
102 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
105 xsltNewCompMatch(const xmlChar *mode, const xmlChar *modeURI) {
106 xsltCompMatchPtr cur;
108 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
110 xsltGenericError(xsltGenericErrorContext,
111 "xsltNewCompMatch : malloc failed\n");
114 memset(cur, 0, sizeof(xsltCompMatch));
116 cur->mode = xmlStrdup(mode);
117 cur->modeURI = xmlStrdup(modeURI);
123 * @comp: an XSLT comp
125 * Free up the memory allocated by @comp
128 xsltFreeCompMatch(xsltCompMatchPtr comp) {
134 if (comp->mode != NULL)
135 xmlFree((xmlChar *)comp->mode);
136 if (comp->modeURI != NULL)
137 xmlFree((xmlChar *)comp->modeURI);
138 for (i = 0;i < comp->nbStep;i++) {
139 op = &comp->steps[i];
140 if (op->value != NULL)
142 if (op->value2 != NULL)
144 if (op->value3 != NULL)
147 memset(comp, -1, sizeof(xsltCompMatch));
152 * xsltFreeCompMatchList:
153 * @comp: an XSLT comp list
155 * Free up the memory allocated by all the elements of @comp
158 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
159 xsltCompMatchPtr cur;
161 while (comp != NULL) {
164 xsltFreeCompMatch(cur);
169 * xsltNewParserContext:
171 * Create a new XSLT ParserContext
173 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
176 xsltNewParserContext(void) {
177 xsltParserContextPtr cur;
179 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
181 xsltGenericError(xsltGenericErrorContext,
182 "xsltNewParserContext : malloc failed\n");
185 memset(cur, 0, sizeof(xsltParserContext));
190 * xsltFreeParserContext:
191 * @ctxt: an XSLT parser context
193 * Free up the memory allocated by @ctxt
196 xsltFreeParserContext(xsltParserContextPtr ctxt) {
199 memset(ctxt, -1, sizeof(xsltParserContext));
205 * @comp: the compiled match expression
207 * @value: the first value
208 * @value2: the second value
210 * Add an step to an XSLT Compiled Match
212 * Returns -1 in case of failure, 0 otherwise.
215 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
217 if (comp->nbStep >= 20) {
218 xsltGenericError(xsltGenericErrorContext,
219 "xsltCompMatchAddOp: overflow\n");
222 comp->steps[comp->nbStep].op = op;
223 comp->steps[comp->nbStep].value = value;
224 comp->steps[comp->nbStep].value2 = value2;
230 * xsltSwapTopCompMatch:
231 * @comp: the compiled match expression
233 * reverse the two top steps.
236 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
238 int j = comp->nbStep - 1;
241 register xmlChar *tmp;
244 tmp = comp->steps[i].value;
245 comp->steps[i].value = comp->steps[j].value;
246 comp->steps[j].value = tmp;
247 tmp = comp->steps[i].value2;
248 comp->steps[i].value2 = comp->steps[j].value2;
249 comp->steps[j].value2 = tmp;
250 op = comp->steps[i].op;
251 comp->steps[i].op = comp->steps[j].op;
252 comp->steps[j].op = op;
257 * xsltReverseCompMatch:
258 * @comp: the compiled match expression
260 * reverse all the stack of expressions
263 xsltReverseCompMatch(xsltCompMatchPtr comp) {
265 int j = comp->nbStep - 1;
268 register xmlChar *tmp;
270 tmp = comp->steps[i].value;
271 comp->steps[i].value = comp->steps[j].value;
272 comp->steps[j].value = tmp;
273 tmp = comp->steps[i].value2;
274 comp->steps[i].value2 = comp->steps[j].value2;
275 comp->steps[j].value2 = tmp;
276 op = comp->steps[i].op;
277 comp->steps[i].op = comp->steps[j].op;
278 comp->steps[j].op = op;
282 comp->steps[comp->nbStep++].op = XSLT_OP_END;
285 /************************************************************************
287 * The interpreter for the precompiled patterns *
289 ************************************************************************/
293 * @ctxt: a XSLT process context
294 * @comp: the precompiled pattern
296 * @mode: the mode name or NULL
297 * @modeURI: the mode URI or NULL
299 * Test wether the node matches the pattern
301 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
304 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
305 xmlNodePtr node, const xmlChar *mode,
306 const xmlChar *modeURI) {
308 xsltStepOpPtr step, select = NULL;
310 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
311 xsltGenericError(xsltGenericErrorContext,
312 "xsltTestCompMatch: null arg\n");
316 if (comp->mode == NULL)
318 if ((comp->mode != mode) && (!xmlStrEqual(comp->mode, mode)))
321 if (comp->mode != NULL)
324 if (modeURI != NULL) {
325 if (comp->modeURI == NULL)
327 if ((comp->modeURI != modeURI) &&
328 (!xmlStrEqual(comp->modeURI, modeURI)))
331 if (comp->modeURI != NULL)
334 for (i = 0;i < comp->nbStep;i++) {
335 step = &comp->steps[i];
336 if (step->op != XSLT_OP_PREDICATE)
342 if ((node->type != XML_DOCUMENT_NODE) &&
343 (node->type != XML_HTML_DOCUMENT_NODE))
347 if (node->type != XML_ELEMENT_NODE)
349 if (step->value == NULL)
351 if (!xmlStrEqual(step->value, node->name))
355 if (node->ns == NULL) {
356 if (step->value2 != NULL)
358 } else if (node->ns->href != NULL) {
359 if (step->value2 == NULL)
361 if (!xmlStrEqual(step->value2, node->ns->href))
366 TODO /* Handle OP_CHILD */
369 if (node->type != XML_ATTRIBUTE_NODE)
371 if (step->value == NULL)
373 if (!xmlStrEqual(step->value, node->name))
377 if (node->ns == NULL) {
378 if (step->value2 != NULL)
380 } else if (node->ns->href != NULL) {
381 if (step->value2 == NULL)
383 if (!xmlStrEqual(step->value2, node->ns->href))
391 if (step->value == NULL)
393 if (!xmlStrEqual(step->value, node->name))
396 if (node->ns == NULL) {
397 if (step->value2 != NULL)
399 } else if (node->ns->href != NULL) {
400 if (step->value2 == NULL)
402 if (!xmlStrEqual(step->value2, node->ns->href))
406 case XSLT_OP_ANCESTOR:
407 /* TODO: implement coalescing of ANCESTOR/NODE ops */
408 if (step->value == NULL) {
410 step = &comp->steps[i];
411 if (step->op == XSLT_OP_ROOT)
413 if (step->op != XSLT_OP_ELEM)
415 if (step->value == NULL)
421 while (node != NULL) {
424 if (xmlStrEqual(step->value, node->name)) {
426 if (node->ns == NULL) {
427 if (step->value2 == NULL)
429 } else if (node->ns->href != NULL) {
430 if ((step->value2 != NULL) &&
431 (xmlStrEqual(step->value2, node->ns->href)))
441 /* TODO Handle IDs decently, must be done differently */
444 id = xmlGetID(node->doc, step->value);
445 if ((id == NULL) || (id->parent != node))
453 list = xsltGetKey(ctxt, step->value,
454 step->value3, step->value2);
457 for (i = 0;i < list->nodeNr;i++)
458 if (list->nodeTab[i] == node)
460 if (i >= list->nodeNr)
466 if (node->ns == NULL) {
467 if (step->value != NULL)
469 } else if (node->ns->href != NULL) {
470 if (step->value == NULL)
472 if (!xmlStrEqual(step->value, node->ns->href))
477 switch (node->type) {
478 case XML_DOCUMENT_NODE:
479 case XML_HTML_DOCUMENT_NODE:
480 case XML_ELEMENT_NODE:
486 case XSLT_OP_PREDICATE: {
489 int pos = 0, len = 0;
491 * Depending on the last selection, one may need to
492 * recompute contextSize and proximityPosition.
494 oldCS = ctxt->xpathCtxt->contextSize;
495 oldCP = ctxt->xpathCtxt->proximityPosition;
496 if ((select != NULL) &&
497 (select->op == XSLT_OP_ELEM) &&
498 (select->value != NULL) &&
499 (node->type == XML_ELEMENT_NODE) &&
500 (node->parent != NULL)) {
502 /* TODO: cache those informations ?!? */
503 xmlNodePtr siblings = node->parent->children;
505 while (siblings != NULL) {
506 if (siblings->type == XML_ELEMENT_NODE) {
507 if (siblings == node) {
510 } else if (xmlStrEqual(node->name,
515 siblings = siblings->next;
518 ctxt->xpathCtxt->contextSize = len;
519 ctxt->xpathCtxt->proximityPosition = pos;
522 oldNode = ctxt->node;
525 if ((step->value == NULL) ||
526 (!xsltEvalXPathPredicate(ctxt, step->value))) {
528 ctxt->xpathCtxt->contextSize = oldCS;
529 ctxt->xpathCtxt->proximityPosition = oldCP;
531 ctxt->node = oldNode;
535 ctxt->xpathCtxt->contextSize = oldCS;
536 ctxt->xpathCtxt->proximityPosition = oldCP;
538 ctxt->node = oldNode;
542 if (node->type != XML_PI_NODE)
544 if (step->value != NULL) {
545 if (!xmlStrEqual(step->value, node->name))
549 case XSLT_OP_COMMENT:
550 if (node->type != XML_COMMENT_NODE)
554 if ((node->type != XML_TEXT_NODE) &&
555 (node->type != XML_CDATA_SECTION_NODE))
559 switch (node->type) {
560 case XML_DOCUMENT_NODE:
561 case XML_HTML_DOCUMENT_NODE:
562 case XML_ELEMENT_NODE:
563 case XML_CDATA_SECTION_NODE:
565 case XML_COMMENT_NODE:
567 case XML_ATTRIBUTE_NODE:
578 /************************************************************************
580 * Dedicated parser for templates *
582 ************************************************************************/
584 #define CUR (*ctxt->cur)
585 #define SKIP(val) ctxt->cur += (val)
586 #define NXT(val) ctxt->cur[(val)]
587 #define CUR_PTR ctxt->cur
589 #define SKIP_BLANKS \
590 while (IS_BLANK(CUR)) NEXT
592 #define CURRENT (*ctxt->cur)
593 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
596 #define PUSH(op, val, val2) \
597 if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
600 xsltSwapTopCompMatch(ctxt->comp);
602 #define XSLT_ERROR(X) \
603 { xsltError(ctxt, __FILE__, __LINE__, X); \
604 ctxt->error = (X); return; }
606 #define XSLT_ERROR0(X) \
607 { xsltError(ctxt, __FILE__, __LINE__, X); \
608 ctxt->error = (X); return(0); }
612 * @ctxt: the XPath Parser context
614 * Parse an XPath Litteral:
616 * [29] Literal ::= '"' [^"]* '"'
619 * Returns the Literal parsed or NULL
623 xsltScanLiteral(xsltParserContextPtr ctxt) {
631 while ((IS_CHAR(CUR)) && (CUR != '"'))
634 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
638 ret = xmlStrndup(q, CUR_PTR - q);
641 } else if (CUR == '\'') {
644 while ((IS_CHAR(CUR)) && (CUR != '\''))
647 /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
651 ret = xmlStrndup(q, CUR_PTR - q);
655 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
664 * @ctxt: the XPath Parser context
666 * Trickery: parse an XML name but without consuming the input flow
667 * Needed to avoid insanity in the parser state.
669 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
670 * CombiningChar | Extender
672 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
674 * [6] Names ::= Name (S Name)*
676 * Returns the Name parsed or NULL
680 xsltScanName(xsltParserContextPtr ctxt) {
681 xmlChar buf[XML_MAX_NAMELEN];
685 if (!IS_LETTER(CUR) && (CUR != '_') &&
690 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
691 (NXT(len) == '.') || (NXT(len) == '-') ||
692 (NXT(len) == '_') || (NXT(len) == ':') ||
693 (IS_COMBINING(NXT(len))) ||
694 (IS_EXTENDER(NXT(len)))) {
697 if (len >= XML_MAX_NAMELEN) {
698 xmlGenericError(xmlGenericErrorContext,
699 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
700 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
701 (NXT(len) == '.') || (NXT(len) == '-') ||
702 (NXT(len) == '_') || (NXT(len) == ':') ||
703 (IS_COMBINING(NXT(len))) ||
704 (IS_EXTENDER(NXT(len))))
710 return(xmlStrndup(buf, len));
713 * xsltCompileIdKeyPattern:
714 * @comp: the compilation context
715 * @name: a preparsed name
716 * @aid: whether id/key are allowed there
718 * Compile the XSLT LocationIdKeyPattern
719 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
720 * | 'key' '(' Literal ',' Literal ')'
722 * also handle NodeType and PI from:
724 * [7] NodeTest ::= NameTest
726 * | 'processing-instruction' '(' Literal ')'
729 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
731 xmlChar *lit2 = NULL;
734 xsltGenericError(xsltGenericErrorContext,
735 "xsltCompileIdKeyPattern : ( expected\n");
739 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
742 lit = xsltScanLiteral(ctxt);
747 xsltGenericError(xsltGenericErrorContext,
748 "xsltCompileIdKeyPattern : ) expected\n");
753 PUSH(XSLT_OP_ID, lit, NULL);
754 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
757 lit = xsltScanLiteral(ctxt);
762 xsltGenericError(xsltGenericErrorContext,
763 "xsltCompileIdKeyPattern : , expected\n");
769 lit2 = xsltScanLiteral(ctxt);
774 xsltGenericError(xsltGenericErrorContext,
775 "xsltCompileIdKeyPattern : ) expected\n");
780 /* TODO: support namespace in keys */
781 PUSH(XSLT_OP_KEY, lit, lit2);
782 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
786 lit = xsltScanLiteral(ctxt);
791 xsltGenericError(xsltGenericErrorContext,
792 "xsltCompileIdKeyPattern : ) expected\n");
798 PUSH(XSLT_OP_PI, lit, NULL);
799 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
803 xsltGenericError(xsltGenericErrorContext,
804 "xsltCompileIdKeyPattern : ) expected\n");
809 PUSH(XSLT_OP_TEXT, NULL, NULL);
810 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
814 xsltGenericError(xsltGenericErrorContext,
815 "xsltCompileIdKeyPattern : ) expected\n");
820 PUSH(XSLT_OP_COMMENT, NULL, NULL);
821 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
825 xsltGenericError(xsltGenericErrorContext,
826 "xsltCompileIdKeyPattern : ) expected\n");
831 PUSH(XSLT_OP_NODE, NULL, NULL);
833 xsltGenericError(xsltGenericErrorContext,
834 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
838 xsltGenericError(xsltGenericErrorContext,
839 "xsltCompileIdKeyPattern : node type\n");
849 * xsltCompileStepPattern:
850 * @comp: the compilation context
851 * @token: a posible precompiled name
853 * Compile the XSLT StepPattern and generates a precompiled
854 * form suitable for fast matching.
856 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
857 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
858 * | ('child' | 'attribute') '::'
860 * [7] NodeTest ::= NameTest
862 * | 'processing-instruction' '(' Literal ')'
863 * [8] Predicate ::= '[' PredicateExpr ']'
864 * [9] PredicateExpr ::= Expr
865 * [13] AbbreviatedAxisSpecifier ::= '@'?
866 * [37] NameTest ::= '*' | NCName ':' '*' | QName
870 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
871 xmlChar *name = NULL;
874 if ((token == NULL) && (CUR == '@')) {
878 PUSH(XSLT_OP_ATTR, NULL, NULL);
881 token = xsltScanName(ctxt);
883 xsltGenericError(xsltGenericErrorContext,
884 "xsltCompileStepPattern : Name expected\n");
888 PUSH(XSLT_OP_ATTR, token, NULL);
892 token = xsltScanName(ctxt);
896 PUSH(XSLT_OP_ALL, token, NULL);
897 goto parse_predicate;
899 xsltGenericError(xsltGenericErrorContext,
900 "xsltCompileStepPattern : Name expected\n");
907 xsltCompileIdKeyPattern(ctxt, token, 0);
910 } else if (CUR == ':') {
913 xsltGenericError(xsltGenericErrorContext,
914 "xsltCompileStepPattern : sequence '::' expected\n");
919 if (xmlStrEqual(token, (const xmlChar *) "child")) {
920 /* TODO: handle namespace */
921 name = xsltScanName(ctxt);
923 xsltGenericError(xsltGenericErrorContext,
924 "xsltCompileStepPattern : QName expected\n");
928 PUSH(XSLT_OP_CHILD, name, NULL);
929 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
930 /* TODO: handle namespace */
931 name = xsltScanName(ctxt);
933 xsltGenericError(xsltGenericErrorContext,
934 "xsltCompileStepPattern : QName expected\n");
938 PUSH(XSLT_OP_ATTR, name, NULL);
940 xsltGenericError(xsltGenericErrorContext,
941 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
946 } else if (CUR == '*') {
948 PUSH(XSLT_OP_ALL, token, NULL);
950 /* TODO: handle namespace */
951 PUSH(XSLT_OP_ELEM, token, NULL);
961 /* TODO: avoid breaking in strings ... */
962 while ((IS_CHAR(CUR)) && (CUR != ']'))
965 xsltGenericError(xsltGenericErrorContext,
966 "xsltCompileStepPattern : ']' expected\n");
970 ret = xmlStrndup(q, CUR_PTR - q);
971 PUSH(XSLT_OP_PREDICATE, ret, NULL);
972 /* push the predicate lower than local test */
985 * xsltCompileRelativePathPattern:
986 * @comp: the compilation context
987 * @token: a posible precompiled name
989 * Compile the XSLT RelativePathPattern and generates a precompiled
990 * form suitable for fast matching.
992 * [4] RelativePathPattern ::= StepPattern
993 * | RelativePathPattern '/' StepPattern
994 * | RelativePathPattern '//' StepPattern
997 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
998 xsltCompileStepPattern(ctxt, token);
1002 while ((CUR != 0) && (CUR != '|')) {
1003 if ((CUR == '/') && (NXT(1) == '/')) {
1004 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1008 xsltCompileStepPattern(ctxt, NULL);
1009 } else if (CUR == '/') {
1010 PUSH(XSLT_OP_PARENT, NULL, NULL);
1013 if ((CUR != 0) || (CUR == '|')) {
1014 xsltCompileRelativePathPattern(ctxt, NULL);
1028 * xsltCompileLocationPathPattern:
1029 * @comp: the compilation context
1031 * Compile the XSLT LocationPathPattern and generates a precompiled
1032 * form suitable for fast matching.
1034 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1035 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1036 * | '//'? RelativePathPattern
1039 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
1041 if ((CUR == '/') && (NXT(1) == '/')) {
1043 * since we reverse the query
1044 * a leading // can be safely ignored
1048 xsltCompileRelativePathPattern(ctxt, NULL);
1049 } else if (CUR == '/') {
1051 * We need to find root as the parent
1055 PUSH(XSLT_OP_ROOT, NULL, NULL);
1056 if ((CUR != 0) || (CUR == '|')) {
1057 PUSH(XSLT_OP_PARENT, NULL, NULL);
1058 xsltCompileRelativePathPattern(ctxt, NULL);
1060 } else if (CUR == '*') {
1061 xsltCompileRelativePathPattern(ctxt, NULL);
1062 } else if (CUR == '@') {
1063 xsltCompileRelativePathPattern(ctxt, NULL);
1066 name = xsltScanName(ctxt);
1068 xsltGenericError(xsltGenericErrorContext,
1069 "xsltCompileLocationPathPattern : Name expected\n");
1075 xsltCompileIdKeyPattern(ctxt, name, 1);
1076 if ((CUR == '/') && (NXT(1) == '/')) {
1077 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1081 xsltCompileRelativePathPattern(ctxt, NULL);
1082 } else if (CUR == '/') {
1083 PUSH(XSLT_OP_PARENT, NULL, NULL);
1086 xsltCompileRelativePathPattern(ctxt, NULL);
1090 xsltCompileRelativePathPattern(ctxt, name);
1097 * xsltCompilePattern:
1098 * @pattern an XSLT pattern
1099 * @mode: the mode name or NULL
1100 * @modeURI: the mode URI or NULL
1102 * Compile the XSLT pattern and generates a precompiled form suitable
1103 * for fast matching.
1104 * Note that the splitting as union of patterns is expected to be handled
1107 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1109 * Returns the generated xsltCompMatchPtr or NULL in case of failure
1113 xsltCompilePattern(const xmlChar *pattern, const xmlChar *mode,
1114 const xmlChar *modeURI) {
1115 xsltParserContextPtr ctxt = NULL;
1116 xsltCompMatchPtr element, first = NULL, previous = NULL;
1117 int current, start, end;
1119 if (pattern == NULL) {
1120 xsltGenericError(xsltGenericErrorContext,
1121 "xsltCompilePattern : NULL pattern\n");
1125 #ifdef DEBUG_PARSING
1126 xsltGenericDebug(xsltGenericDebugContext,
1127 "xsltCompilePattern : parsing '%s'\n", pattern);
1130 ctxt = xsltNewParserContext();
1134 while (pattern[current] != 0) {
1136 while (IS_BLANK(pattern[current]))
1139 while ((pattern[end] != 0) && (pattern[end] != '|'))
1141 if (current == end) {
1142 xsltGenericError(xsltGenericErrorContext,
1143 "xsltCompilePattern : NULL pattern\n");
1146 element = xsltNewCompMatch(mode, modeURI);
1147 if (element == NULL) {
1152 else if (previous != NULL)
1153 previous->next = element;
1156 ctxt->comp = element;
1157 ctxt->base = xmlStrndup(&pattern[start], end - start);
1158 ctxt->cur = &(ctxt->base)[current - start];
1159 xsltCompileLocationPathPattern(ctxt);
1161 xmlFree((xmlChar *)ctxt->base);
1166 * Reverse for faster interpretation.
1168 xsltReverseCompMatch(element);
1171 * Set-up the priority
1173 if (((element->steps[0].op == XSLT_OP_ELEM) ||
1174 (element->steps[0].op == XSLT_OP_ATTR)) &&
1175 (element->steps[0].value != NULL) &&
1176 (element->steps[1].op == XSLT_OP_END)) {
1177 element->priority = 0;
1178 } else if ((element->steps[0].op == XSLT_OP_PI) &&
1179 (element->steps[0].value != NULL) &&
1180 (element->steps[1].op == XSLT_OP_END)) {
1181 element->priority = 0;
1182 } else if ((element->steps[0].op == XSLT_OP_NS) &&
1183 (element->steps[0].value != NULL) &&
1184 (element->steps[1].op == XSLT_OP_END)) {
1185 element->priority = -0.25;
1186 } else if (((element->steps[0].op == XSLT_OP_PI) ||
1187 (element->steps[0].op == XSLT_OP_TEXT) ||
1188 (element->steps[0].op == XSLT_OP_NODE) ||
1189 (element->steps[0].op == XSLT_OP_COMMENT)) &&
1190 (element->steps[1].op == XSLT_OP_END)) {
1191 element->priority = -0.5;
1193 element->priority = 0.5;
1195 if (pattern[end] == '|')
1200 xsltGenericError(xsltGenericErrorContext,
1201 "xsltCompilePattern : NULL pattern\n");
1205 xsltFreeParserContext(ctxt);
1210 xsltFreeParserContext(ctxt);
1212 xsltFreeCompMatchList(first);
1217 /************************************************************************
1219 * Module interfaces *
1221 ************************************************************************/
1225 * @style: an XSLT stylesheet
1226 * @cur: an XSLT template
1227 * @mode: the mode name or NULL
1228 * @modeURI: the mode URI or NULL
1230 * Register the XSLT pattern associated to @cur
1232 * Returns -1 in case of error, 0 otherwise
1235 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
1236 const xmlChar *mode, const xmlChar *modeURI) {
1237 xsltCompMatchPtr pat, list, *top = NULL, next;
1238 const xmlChar *name = NULL;
1240 if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
1243 pat = xsltCompilePattern(cur->match, mode, modeURI);
1248 pat->template = cur;
1249 if (cur->priority != XSLT_PAT_NO_PRIORITY)
1250 pat->priority = cur->priority;
1253 * insert it in the hash table list corresponding to its lookup name
1255 switch (pat->steps[0].op) {
1257 if (pat->steps[0].value != NULL)
1258 name = pat->steps[0].value;
1260 top = (xsltCompMatchPtr *) &(style->attrMatch);
1264 case XSLT_OP_PARENT:
1265 case XSLT_OP_ANCESTOR:
1267 name = pat->steps[0].value;
1270 top = (xsltCompMatchPtr *) &(style->rootMatch);
1273 top = (xsltCompMatchPtr *) &(style->keyMatch);
1276 /* TODO optimize ID !!! */
1278 top = (xsltCompMatchPtr *) &(style->elemMatch);
1281 case XSLT_OP_PREDICATE:
1282 xsltGenericError(xsltGenericErrorContext,
1283 "xsltAddTemplate: invalid compiled pattern\n");
1284 xsltFreeCompMatch(pat);
1287 * TODO: some flags at the top level about type based patterns
1288 * would be faster than inclusion in the hash table.
1291 if (pat->steps[0].value != NULL)
1292 name = pat->steps[0].value;
1294 top = (xsltCompMatchPtr *) &(style->piMatch);
1296 case XSLT_OP_COMMENT:
1297 top = (xsltCompMatchPtr *) &(style->commentMatch);
1300 top = (xsltCompMatchPtr *) &(style->textMatch);
1303 if (pat->steps[0].value != NULL)
1304 name = pat->steps[0].value;
1306 top = (xsltCompMatchPtr *) &(style->elemMatch);
1311 if (style->templatesHash == NULL) {
1312 style->templatesHash = xmlHashCreate(1024);
1313 if (style->templatesHash == NULL) {
1314 xsltFreeCompMatch(pat);
1317 #ifdef DEBUG_PARSING
1318 xsltGenericDebug(xsltGenericDebugContext,
1319 "xsltAddTemplate: created template hash\n");
1321 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
1322 #ifdef DEBUG_PARSING
1323 xsltGenericDebug(xsltGenericDebugContext,
1324 "xsltAddTemplate: added new hash %s\n", name);
1327 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1328 name, mode, modeURI);
1330 xmlHashAddEntry3(style->templatesHash, name,
1331 mode, modeURI, pat);
1332 #ifdef DEBUG_PARSING
1333 xsltGenericDebug(xsltGenericDebugContext,
1334 "xsltAddTemplate: added new hash %s\n", name);
1338 * Note '<=' since one must choose among the matching
1339 * template rules that are left, the one that occurs
1340 * last in the stylesheet
1342 if (list->priority <= pat->priority) {
1344 xmlHashUpdateEntry3(style->templatesHash, name,
1345 mode, modeURI, pat, NULL);
1346 #ifdef DEBUG_PARSING
1347 xsltGenericDebug(xsltGenericDebugContext,
1348 "xsltAddTemplate: added head hash for %s\n", name);
1351 while (list->next != NULL) {
1352 if (list->next->priority <= pat->priority)
1356 pat->next = list->next;
1361 } else if (top != NULL) {
1366 } else if (list->priority <= pat->priority) {
1370 while (list->next != NULL) {
1371 if (list->next->priority <= pat->priority)
1375 pat->next = list->next;
1379 xsltGenericError(xsltGenericErrorContext,
1380 "xsltAddTemplate: invalid compiled pattern\n");
1381 xsltFreeCompMatch(pat);
1391 * @ctxt: a XSLT process context
1392 * @mode: the mode name or NULL
1394 * Finds the template applying to this node
1396 * Returns the xsltTemplatePtr or NULL if not found
1399 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node) {
1400 xsltStylesheetPtr style;
1401 xsltTemplatePtr ret = NULL;
1402 const xmlChar *name = NULL;
1403 xsltCompMatchPtr list = NULL;
1405 if ((ctxt == NULL) || (node == NULL))
1408 style = ctxt->style;
1409 while (style != NULL) {
1410 /* TODO : handle IDs/keys here ! */
1411 if (style->templatesHash != NULL) {
1413 * Use the top name as selector
1415 switch (node->type) {
1416 case XML_ELEMENT_NODE:
1417 case XML_ATTRIBUTE_NODE:
1421 case XML_DOCUMENT_NODE:
1422 case XML_HTML_DOCUMENT_NODE:
1424 case XML_CDATA_SECTION_NODE:
1425 case XML_COMMENT_NODE:
1426 case XML_ENTITY_REF_NODE:
1427 case XML_ENTITY_NODE:
1428 case XML_DOCUMENT_TYPE_NODE:
1429 case XML_DOCUMENT_FRAG_NODE:
1430 case XML_NOTATION_NODE:
1432 case XML_ELEMENT_DECL:
1433 case XML_ATTRIBUTE_DECL:
1434 case XML_ENTITY_DECL:
1435 case XML_NAMESPACE_DECL:
1436 case XML_XINCLUDE_START:
1437 case XML_XINCLUDE_END:
1446 * find the list of appliable expressions based on the name
1448 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1449 name, ctxt->mode, ctxt->modeURI);
1451 while (list != NULL) {
1452 if (xsltTestCompMatch(ctxt, list, node,
1453 ctxt->mode, ctxt->modeURI)) {
1454 ret = list->template;
1462 * find alternate generic matches
1464 switch (node->type) {
1465 case XML_ELEMENT_NODE:
1466 list = style->elemMatch;
1468 case XML_ATTRIBUTE_NODE:
1469 list = style->attrMatch;
1472 list = style->piMatch;
1474 case XML_DOCUMENT_NODE:
1475 case XML_HTML_DOCUMENT_NODE:
1476 list = style->rootMatch;
1479 case XML_CDATA_SECTION_NODE:
1480 list = style->textMatch;
1482 case XML_COMMENT_NODE:
1483 list = style->commentMatch;
1485 case XML_ENTITY_REF_NODE:
1486 case XML_ENTITY_NODE:
1487 case XML_DOCUMENT_TYPE_NODE:
1488 case XML_DOCUMENT_FRAG_NODE:
1489 case XML_NOTATION_NODE:
1491 case XML_ELEMENT_DECL:
1492 case XML_ATTRIBUTE_DECL:
1493 case XML_ENTITY_DECL:
1494 case XML_NAMESPACE_DECL:
1495 case XML_XINCLUDE_START:
1496 case XML_XINCLUDE_END:
1502 while ((list != NULL) &&
1503 ((ret == NULL) || (list->priority > ret->priority))) {
1504 if (xsltTestCompMatch(ctxt, list, node,
1505 ctxt->mode, ctxt->modeURI)) {
1506 ret = list->template;
1511 if (node->_private != NULL) {
1512 list = style->keyMatch;
1513 while ((list != NULL) &&
1514 ((ret == NULL) || (list->priority > ret->priority))) {
1515 if (xsltTestCompMatch(ctxt, list, node,
1516 ctxt->mode, ctxt->modeURI)) {
1517 ret = list->template;
1528 * Cycle on next stylesheet import.
1530 style = xsltNextImport(style);
1537 * xsltFreeTemplateHashes:
1538 * @style: an XSLT stylesheet
1540 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1543 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1544 if (style->templatesHash != NULL)
1545 xmlHashFree((xmlHashTablePtr) style->templatesHash,
1546 (xmlHashDeallocator) xsltFreeCompMatchList);
1547 if (style->rootMatch != NULL)
1548 xsltFreeCompMatchList(style->rootMatch);
1549 if (style->keyMatch != NULL)
1550 xsltFreeCompMatchList(style->keyMatch);
1551 if (style->elemMatch != NULL)
1552 xsltFreeCompMatchList(style->elemMatch);
1553 if (style->attrMatch != NULL)
1554 xsltFreeCompMatchList(style->attrMatch);
1555 if (style->parentMatch != NULL)
1556 xsltFreeCompMatchList(style->parentMatch);
1557 if (style->textMatch != NULL)
1558 xsltFreeCompMatchList(style->textMatch);
1559 if (style->piMatch != NULL)
1560 xsltFreeCompMatchList(style->piMatch);
1561 if (style->commentMatch != NULL)
1562 xsltFreeCompMatchList(style->commentMatch);
1567 * @node: a node in the source tree
1568 * @pattern: an XSLT pattern
1570 * Determine if a node matches a pattern.
1573 xsltMatchPattern(xsltTransformContextPtr context,
1575 const xmlChar *pattern)
1578 xsltCompMatchPtr first, comp;
1580 if ((context != NULL) && (pattern != NULL)) {
1581 first = xsltCompilePattern(pattern, NULL, NULL);
1582 for (comp = first; comp != NULL; comp = comp->next) {
1583 match = xsltTestCompMatch(context, comp, node, NULL, NULL);
1588 xsltFreeCompMatchList(first);