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.
13 * TODO: handle pathological cases like *[*[@a="b"]]
14 * TODO: detect [number] at compilation, optimize accordingly
22 #include <libxml/xmlmemory.h>
23 #include <libxml/tree.h>
24 #include <libxml/valid.h>
25 #include <libxml/hash.h>
26 #include <libxml/xmlerror.h>
27 #include <libxml/parserInternals.h>
29 #include "xsltInternals.h"
30 #include "xsltutils.h"
32 #include "templates.h"
35 #include "documents.h"
37 #ifdef WITH_XSLT_DEBUG
38 #define WITH_XSLT_DEBUG_PATTERN
68 typedef struct _xsltStepState xsltStepState;
69 typedef xsltStepState *xsltStepStatePtr;
70 struct _xsltStepState {
75 typedef struct _xsltStepStates xsltStepStates;
76 typedef xsltStepStates *xsltStepStatesPtr;
77 struct _xsltStepStates {
80 xsltStepStatePtr states;
83 typedef struct _xsltStepOp xsltStepOp;
84 typedef xsltStepOp *xsltStepOpPtr;
90 xmlXPathCompExprPtr comp;
92 * Optimisations for count
99 struct _xsltCompMatch {
100 struct _xsltCompMatch *next; /* siblings in the name hash */
101 float priority; /* the priority */
102 const xmlChar *pattern; /* the pattern */
103 const xmlChar *mode; /* the mode */
104 const xmlChar *modeURI; /* the mode URI */
105 xsltTemplatePtr template; /* the associated template */
108 /* TODO fix the statically allocated size steps[] */
111 xmlNsPtr *nsList; /* the namespaces in scope */
112 int nsNr; /* the number of namespaces in scope */
113 xsltStepOpPtr steps; /* ops for computation */
116 typedef struct _xsltParserContext xsltParserContext;
117 typedef xsltParserContext *xsltParserContextPtr;
118 struct _xsltParserContext {
119 xsltStylesheetPtr style; /* the stylesheet */
120 xsltTransformContextPtr ctxt; /* the transformation or NULL */
121 const xmlChar *cur; /* the current char being parsed */
122 const xmlChar *base; /* the full expression */
123 xmlDocPtr doc; /* the source document */
124 xmlNodePtr elem; /* the source element */
125 int error; /* error code */
126 xsltCompMatchPtr comp; /* the result */
129 /************************************************************************
133 ************************************************************************/
138 * Create a new XSLT CompMatch
140 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
142 static xsltCompMatchPtr
143 xsltNewCompMatch(void) {
144 xsltCompMatchPtr cur;
146 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
148 xsltTransformError(NULL, NULL, NULL,
149 "xsltNewCompMatch : out of memory error\n");
152 memset(cur, 0, sizeof(xsltCompMatch));
155 cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
157 if (cur->steps == NULL) {
158 xsltTransformError(NULL, NULL, NULL,
159 "xsltNewCompMatch : out of memory error\n");
171 * @comp: an XSLT comp
173 * Free up the memory allocated by @comp
176 xsltFreeCompMatch(xsltCompMatchPtr comp) {
182 if (comp->pattern != NULL)
183 xmlFree((xmlChar *)comp->pattern);
184 if (comp->nsList != NULL)
185 xmlFree(comp->nsList);
186 for (i = 0;i < comp->nbStep;i++) {
187 op = &comp->steps[i];
188 if (op->value != NULL)
190 if (op->value2 != NULL)
192 if (op->value3 != NULL)
194 if (op->comp != NULL)
195 xmlXPathFreeCompExpr(op->comp);
197 xmlFree(comp->steps);
198 memset(comp, -1, sizeof(xsltCompMatch));
203 * xsltFreeCompMatchList:
204 * @comp: an XSLT comp list
206 * Free up the memory allocated by all the elements of @comp
209 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
210 xsltCompMatchPtr cur;
212 while (comp != NULL) {
215 xsltFreeCompMatch(cur);
220 * xsltNormalizeCompSteps:
221 * @payload: pointer to template hash table entry
222 * @data: pointer to the stylesheet
223 * @name: template match name
225 * This is a hashtable scanner function to normalize the compiled
226 * steps of an imported stylesheet.
228 void xsltNormalizeCompSteps(void *payload,
229 void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
230 xsltCompMatchPtr comp = payload;
231 xsltStylesheetPtr style = data;
234 for (ix = 0; ix < comp->nbStep; ix++) {
235 comp->steps[ix].previousExtra += style->extrasNr;
236 comp->steps[ix].indexExtra += style->extrasNr;
237 comp->steps[ix].lenExtra += style->extrasNr;
242 * xsltNewParserContext:
243 * @style: the stylesheet
244 * @ctxt: the transformation context, if done at run-time
246 * Create a new XSLT ParserContext
248 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
250 static xsltParserContextPtr
251 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
252 xsltParserContextPtr cur;
254 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
256 xsltTransformError(NULL, NULL, NULL,
257 "xsltNewParserContext : malloc failed\n");
260 memset(cur, 0, sizeof(xsltParserContext));
267 * xsltFreeParserContext:
268 * @ctxt: an XSLT parser context
270 * Free up the memory allocated by @ctxt
273 xsltFreeParserContext(xsltParserContextPtr ctxt) {
276 memset(ctxt, -1, sizeof(xsltParserContext));
282 * @comp: the compiled match expression
284 * @value: the first value
285 * @value2: the second value
286 * @novar: flag to set XML_XPATH_NOVAR
288 * Add an step to an XSLT Compiled Match
290 * Returns -1 in case of failure, 0 otherwise.
293 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
294 xsltOp op, xmlChar * value, xmlChar * value2, int novar)
296 if (comp->nbStep >= comp->maxStep) {
299 tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
302 xsltGenericError(xsltGenericErrorContext,
303 "xsltCompMatchAdd: memory re-allocation failure.\n");
304 if (ctxt->style != NULL)
305 ctxt->style->errors++;
311 comp->steps[comp->nbStep].op = op;
312 comp->steps[comp->nbStep].value = value;
313 comp->steps[comp->nbStep].value2 = value2;
314 comp->steps[comp->nbStep].value3 = NULL;
315 comp->steps[comp->nbStep].comp = NULL;
316 if (ctxt->ctxt != NULL) {
317 comp->steps[comp->nbStep].previousExtra =
318 xsltAllocateExtraCtxt(ctxt->ctxt);
319 comp->steps[comp->nbStep].indexExtra =
320 xsltAllocateExtraCtxt(ctxt->ctxt);
321 comp->steps[comp->nbStep].lenExtra =
322 xsltAllocateExtraCtxt(ctxt->ctxt);
324 comp->steps[comp->nbStep].previousExtra =
325 xsltAllocateExtra(ctxt->style);
326 comp->steps[comp->nbStep].indexExtra =
327 xsltAllocateExtra(ctxt->style);
328 comp->steps[comp->nbStep].lenExtra =
329 xsltAllocateExtra(ctxt->style);
331 if (op == XSLT_OP_PREDICATE) {
332 xmlXPathContextPtr xctxt;
334 if (ctxt->style != NULL)
335 xctxt = xmlXPathNewContext(ctxt->style->doc);
337 xctxt = xmlXPathNewContext(NULL);
338 #ifdef XML_XPATH_NOVAR
340 xctxt->flags = XML_XPATH_NOVAR;
342 if (ctxt->style != NULL)
343 xctxt->dict = ctxt->style->dict;
344 comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
345 xmlXPathFreeContext(xctxt);
346 if (comp->steps[comp->nbStep].comp == NULL) {
347 xsltTransformError(NULL, ctxt->style, ctxt->elem,
348 "Failed to compile predicate\n");
349 if (ctxt->style != NULL)
350 ctxt->style->errors++;
358 * xsltSwapTopCompMatch:
359 * @comp: the compiled match expression
361 * reverse the two top steps.
364 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
366 int j = comp->nbStep - 1;
369 register xmlChar *tmp;
371 register xmlXPathCompExprPtr expr;
374 tmp = comp->steps[i].value;
375 comp->steps[i].value = comp->steps[j].value;
376 comp->steps[j].value = tmp;
377 tmp = comp->steps[i].value2;
378 comp->steps[i].value2 = comp->steps[j].value2;
379 comp->steps[j].value2 = tmp;
380 tmp = comp->steps[i].value3;
381 comp->steps[i].value3 = comp->steps[j].value3;
382 comp->steps[j].value3 = tmp;
383 op = comp->steps[i].op;
384 comp->steps[i].op = comp->steps[j].op;
385 comp->steps[j].op = op;
386 expr = comp->steps[i].comp;
387 comp->steps[i].comp = comp->steps[j].comp;
388 comp->steps[j].comp = expr;
389 t = comp->steps[i].previousExtra;
390 comp->steps[i].previousExtra = comp->steps[j].previousExtra;
391 comp->steps[j].previousExtra = t;
392 t = comp->steps[i].indexExtra;
393 comp->steps[i].indexExtra = comp->steps[j].indexExtra;
394 comp->steps[j].indexExtra = t;
395 t = comp->steps[i].lenExtra;
396 comp->steps[i].lenExtra = comp->steps[j].lenExtra;
397 comp->steps[j].lenExtra = t;
402 * xsltReverseCompMatch:
403 * @ctxt: the parser context
404 * @comp: the compiled match expression
406 * reverse all the stack of expressions
409 xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
411 int j = comp->nbStep - 1;
414 register xmlChar *tmp;
416 register xmlXPathCompExprPtr expr;
419 tmp = comp->steps[i].value;
420 comp->steps[i].value = comp->steps[j].value;
421 comp->steps[j].value = tmp;
422 tmp = comp->steps[i].value2;
423 comp->steps[i].value2 = comp->steps[j].value2;
424 comp->steps[j].value2 = tmp;
425 tmp = comp->steps[i].value3;
426 comp->steps[i].value3 = comp->steps[j].value3;
427 comp->steps[j].value3 = tmp;
428 op = comp->steps[i].op;
429 comp->steps[i].op = comp->steps[j].op;
430 comp->steps[j].op = op;
431 expr = comp->steps[i].comp;
432 comp->steps[i].comp = comp->steps[j].comp;
433 comp->steps[j].comp = expr;
434 t = comp->steps[i].previousExtra;
435 comp->steps[i].previousExtra = comp->steps[j].previousExtra;
436 comp->steps[j].previousExtra = t;
437 t = comp->steps[i].indexExtra;
438 comp->steps[i].indexExtra = comp->steps[j].indexExtra;
439 comp->steps[j].indexExtra = t;
440 t = comp->steps[i].lenExtra;
441 comp->steps[i].lenExtra = comp->steps[j].lenExtra;
442 comp->steps[j].lenExtra = t;
446 xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0);
449 * detect consecutive XSLT_OP_PREDICATE indicating a direct
450 * matching should be done.
452 for (i = 0;i < comp->nbStep - 1;i++) {
453 if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
454 (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
457 if (comp->pattern[0] != '/') {
460 query = xmlStrdup((const xmlChar *)"//");
461 query = xmlStrcat(query, comp->pattern);
463 xmlFree((xmlChar *) comp->pattern);
464 comp->pattern = query;
471 /************************************************************************
473 * The interpreter for the precompiled patterns *
475 ************************************************************************/
478 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
479 int step, xmlNodePtr node) {
480 if ((states->states == NULL) || (states->maxstates <= 0)) {
481 states->maxstates = 4;
482 states->nbstates = 0;
483 states->states = xmlMalloc(4 * sizeof(xsltStepState));
485 else if (states->maxstates <= states->nbstates) {
488 tmp = (xsltStepStatePtr) xmlRealloc(states->states,
489 2 * states->maxstates * sizeof(xsltStepState));
491 xsltGenericError(xsltGenericErrorContext,
492 "xsltPatPushState: memory re-allocation failure.\n");
493 ctxt->state = XSLT_STATE_STOPPED;
496 states->states = tmp;
497 states->maxstates *= 2;
499 states->states[states->nbstates].step = step;
500 states->states[states->nbstates++].node = node;
502 fprintf(stderr, "Push: %d, %s\n", step, node->name);
508 * xsltTestCompMatchDirect:
509 * @ctxt: a XSLT process context
510 * @comp: the precompiled pattern
512 * @nsList: the namespaces in scope
513 * @nsNr: the number of namespaces in scope
515 * Test whether the node matches the pattern, do a direct evalutation
516 * and not a step by step evaluation.
518 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
521 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
522 xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
523 xsltStepOpPtr sel = NULL;
526 xmlXPathObjectPtr list;
532 if (XSLT_IS_RES_TREE_FRAG(doc))
536 sel = &comp->steps[0]; /* store extra in first step arbitrarily */
538 prevdoc = (xmlDocPtr)
539 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
540 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
541 list = (xmlXPathObjectPtr)
542 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
544 if ((list == NULL) || (prevdoc != doc)) {
545 xmlXPathObjectPtr newlist;
546 xmlNodePtr parent = node->parent;
550 xmlNsPtr *oldNamespaces;
552 oldnode = ctxt->xpathCtxt->node;
553 olddoc = ctxt->xpathCtxt->doc;
554 oldNsNr = ctxt->xpathCtxt->nsNr;
555 oldNamespaces = ctxt->xpathCtxt->namespaces;
556 ctxt->xpathCtxt->node = node;
557 ctxt->xpathCtxt->doc = doc;
558 ctxt->xpathCtxt->namespaces = nsList;
559 ctxt->xpathCtxt->nsNr = nsNr;
560 newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
561 ctxt->xpathCtxt->node = oldnode;
562 ctxt->xpathCtxt->doc = olddoc;
563 ctxt->xpathCtxt->namespaces = oldNamespaces;
564 ctxt->xpathCtxt->nsNr = oldNsNr;
567 if (newlist->type != XPATH_NODESET) {
568 xmlXPathFreeObject(newlist);
573 if ((parent == NULL) || (node->doc == NULL) || isRVT)
578 xmlXPathFreeObject(list);
581 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
583 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
585 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
587 XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
588 (xmlFreeFunc) xmlXPathFreeObject;
592 if ((list->nodesetval == NULL) ||
593 (list->nodesetval->nodeNr <= 0)) {
595 xmlXPathFreeObject(list);
598 /* TODO: store the index and use it for the scan */
600 for (j = 0;j < list->nodesetval->nodeNr;j++) {
601 if (list->nodesetval->nodeTab[j] == node) {
603 xmlXPathFreeObject(list);
610 xmlXPathFreeObject(list);
616 * @ctxt: a XSLT process context
617 * @comp: the precompiled pattern
619 * @mode: the mode name or NULL
620 * @modeURI: the mode URI or NULL
622 * Test whether the node matches the pattern
624 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
627 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
628 xmlNodePtr node, const xmlChar *mode,
629 const xmlChar *modeURI) {
631 xsltStepOpPtr step, sel = NULL;
632 xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
634 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
635 xsltTransformError(ctxt, NULL, node,
636 "xsltTestCompMatch: null arg\n");
640 if (comp->mode == NULL)
643 * both mode strings must be interned on the stylesheet dictionary
645 if (comp->mode != mode)
648 if (comp->mode != NULL)
651 if (modeURI != NULL) {
652 if (comp->modeURI == NULL)
655 * both modeURI strings must be interned on the stylesheet dictionary
657 if (comp->modeURI != modeURI)
660 if (comp->modeURI != NULL)
666 for (;i < comp->nbStep;i++) {
667 step = &comp->steps[i];
668 if (step->op != XSLT_OP_PREDICATE)
674 if ((node->type == XML_DOCUMENT_NODE) ||
675 #ifdef LIBXML_DOCB_ENABLED
676 (node->type == XML_DOCB_DOCUMENT_NODE) ||
678 (node->type == XML_HTML_DOCUMENT_NODE))
680 if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
684 if (node->type != XML_ELEMENT_NODE)
686 if (step->value == NULL)
688 if (step->value[0] != node->name[0])
690 if (!xmlStrEqual(step->value, node->name))
694 if (node->ns == NULL) {
695 if (step->value2 != NULL)
697 } else if (node->ns->href != NULL) {
698 if (step->value2 == NULL)
700 if (!xmlStrEqual(step->value2, node->ns->href))
705 if (node->type != XML_ATTRIBUTE_NODE)
707 if (step->value != NULL) {
708 if (step->value[0] != node->name[0])
710 if (!xmlStrEqual(step->value, node->name))
714 if (node->ns == NULL) {
715 if (step->value2 != NULL)
717 } else if (step->value2 != NULL) {
718 if (!xmlStrEqual(step->value2, node->ns->href))
723 if ((node->type == XML_DOCUMENT_NODE) ||
724 (node->type == XML_HTML_DOCUMENT_NODE) ||
725 #ifdef LIBXML_DOCB_ENABLED
726 (node->type == XML_DOCB_DOCUMENT_NODE) ||
728 (node->type == XML_NAMESPACE_DECL))
733 if (step->value == NULL)
735 if (step->value[0] != node->name[0])
737 if (!xmlStrEqual(step->value, node->name))
740 if (node->ns == NULL) {
741 if (step->value2 != NULL)
743 } else if (node->ns->href != NULL) {
744 if (step->value2 == NULL)
746 if (!xmlStrEqual(step->value2, node->ns->href))
750 case XSLT_OP_ANCESTOR:
751 /* TODO: implement coalescing of ANCESTOR/NODE ops */
752 if (step->value == NULL) {
753 step = &comp->steps[i+1];
754 if (step->op == XSLT_OP_ROOT)
756 /* added NS, ID and KEY as a result of bug 168208 */
757 if ((step->op != XSLT_OP_ELEM) &&
758 (step->op != XSLT_OP_ALL) &&
759 (step->op != XSLT_OP_NS) &&
760 (step->op != XSLT_OP_ID) &&
761 (step->op != XSLT_OP_KEY))
766 if ((node->type == XML_DOCUMENT_NODE) ||
767 (node->type == XML_HTML_DOCUMENT_NODE) ||
768 #ifdef LIBXML_DOCB_ENABLED
769 (node->type == XML_DOCB_DOCUMENT_NODE) ||
771 (node->type == XML_NAMESPACE_DECL))
774 if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
775 xsltPatPushState(ctxt, &states, i, node);
779 if (step->value == NULL) {
780 xsltPatPushState(ctxt, &states, i - 1, node);
783 while (node != NULL) {
784 if ((node->type == XML_ELEMENT_NODE) &&
785 (step->value[0] == node->name[0]) &&
786 (xmlStrEqual(step->value, node->name))) {
788 if (node->ns == NULL) {
789 if (step->value2 == NULL)
791 } else if (node->ns->href != NULL) {
792 if ((step->value2 != NULL) &&
793 (xmlStrEqual(step->value2, node->ns->href)))
801 xsltPatPushState(ctxt, &states, i - 1, node);
804 /* TODO Handle IDs decently, must be done differently */
807 if (node->type != XML_ELEMENT_NODE)
810 id = xmlGetID(node->doc, step->value);
811 if ((id == NULL) || (id->parent != node))
819 list = xsltGetKey(ctxt, step->value,
820 step->value3, step->value2);
823 for (indx = 0;indx < list->nodeNr;indx++)
824 if (list->nodeTab[indx] == node)
826 if (indx >= list->nodeNr)
831 if (node->type != XML_ELEMENT_NODE)
833 if (node->ns == NULL) {
834 if (step->value != NULL)
836 } else if (node->ns->href != NULL) {
837 if (step->value == NULL)
839 if (!xmlStrEqual(step->value, node->ns->href))
844 if (node->type != XML_ELEMENT_NODE)
847 case XSLT_OP_PREDICATE: {
851 int pos = 0, len = 0;
855 * when there is cascading XSLT_OP_PREDICATE, then use a
856 * direct computation approach. It's not done directly
857 * at the beginning of the routine to filter out as much
858 * as possible this costly computation.
861 if (states.states != NULL) {
862 /* Free the rollback states */
863 xmlFree(states.states);
865 return(xsltTestCompMatchDirect(ctxt, comp, node,
866 comp->nsList, comp->nsNr));
870 if (XSLT_IS_RES_TREE_FRAG(doc))
876 * Depending on the last selection, one may need to
877 * recompute contextSize and proximityPosition.
879 oldCS = ctxt->xpathCtxt->contextSize;
880 oldCP = ctxt->xpathCtxt->proximityPosition;
882 (sel->op == XSLT_OP_ELEM) &&
883 (sel->value != NULL) &&
884 (node->type == XML_ELEMENT_NODE) &&
885 (node->parent != NULL)) {
889 previous = (xmlNodePtr)
890 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
891 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
892 if ((previous != NULL) &&
893 (previous->parent == node->parent)) {
895 * just walk back to adjust the index
898 xmlNodePtr sibling = node;
900 while (sibling != NULL) {
901 if (sibling == previous)
903 if ((previous->type == XML_ELEMENT_NODE) &&
904 (previous->name != NULL) &&
905 (sibling->name != NULL) &&
906 (previous->name[0] == sibling->name[0]) &&
907 (xmlStrEqual(previous->name, sibling->name)))
909 if ((sel->value2 == NULL) ||
910 ((sibling->ns != NULL) &&
911 (xmlStrEqual(sel->value2,
912 sibling->ns->href))))
915 sibling = sibling->prev;
917 if (sibling == NULL) {
918 /* hum going backward in document order ... */
921 while (sibling != NULL) {
922 if (sibling == previous)
924 if ((previous->type == XML_ELEMENT_NODE) &&
925 (previous->name != NULL) &&
926 (sibling->name != NULL) &&
927 (previous->name[0] == sibling->name[0]) &&
928 (xmlStrEqual(previous->name, sibling->name)))
930 if ((sel->value2 == NULL) ||
931 ((sibling->ns != NULL) &&
932 (xmlStrEqual(sel->value2,
933 sibling->ns->href))))
938 sibling = sibling->next;
941 if (sibling != NULL) {
944 * If the node is in a Value Tree we need to
945 * save len, but cannot cache the node!
946 * (bugs 153137 and 158840)
948 if (node->doc != NULL) {
949 len = XSLT_RUNTIME_EXTRA(ctxt,
950 sel->lenExtra, ival);
952 XSLT_RUNTIME_EXTRA(ctxt,
953 sel->previousExtra, ptr) = node;
954 XSLT_RUNTIME_EXTRA(ctxt,
955 sel->indexExtra, ival) = pos;
963 * recompute the index
965 xmlNodePtr parent = node->parent;
966 xmlNodePtr siblings = NULL;
968 if (parent) siblings = parent->children;
970 while (siblings != NULL) {
971 if (siblings->type == XML_ELEMENT_NODE) {
972 if (siblings == node) {
975 } else if ((node->name != NULL) &&
976 (siblings->name != NULL) &&
977 (node->name[0] == siblings->name[0]) &&
978 (xmlStrEqual(node->name, siblings->name))) {
979 if ((sel->value2 == NULL) ||
980 ((siblings->ns != NULL) &&
981 (xmlStrEqual(sel->value2,
982 siblings->ns->href))))
986 siblings = siblings->next;
988 if ((parent == NULL) || (node->doc == NULL))
991 while (parent->parent != NULL)
992 parent = parent->parent;
993 if (((parent->type != XML_DOCUMENT_NODE) &&
994 (parent->type != XML_HTML_DOCUMENT_NODE)) ||
995 (parent != (xmlNodePtr) node->doc))
1000 ctxt->xpathCtxt->contextSize = len;
1001 ctxt->xpathCtxt->proximityPosition = pos;
1003 * If the node is in a Value Tree we cannot
1006 if ((!isRVT) && (node->doc != NULL) &&
1008 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
1010 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
1012 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
1016 } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
1017 (node->type == XML_ELEMENT_NODE)) {
1018 xmlNodePtr previous;
1019 int ix, nocache = 0;
1021 previous = (xmlNodePtr)
1022 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
1023 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
1024 if ((previous != NULL) &&
1025 (previous->parent == node->parent)) {
1027 * just walk back to adjust the index
1030 xmlNodePtr sibling = node;
1032 while (sibling != NULL) {
1033 if (sibling == previous)
1035 if (sibling->type == XML_ELEMENT_NODE)
1037 sibling = sibling->prev;
1039 if (sibling == NULL) {
1040 /* hum going backward in document order ... */
1043 while (sibling != NULL) {
1044 if (sibling == previous)
1046 if (sibling->type == XML_ELEMENT_NODE)
1048 sibling = sibling->next;
1051 if (sibling != NULL) {
1054 * If the node is in a Value Tree we cannot
1057 if ((node->doc != NULL) && !isRVT) {
1058 len = XSLT_RUNTIME_EXTRA(ctxt,
1059 sel->lenExtra, ival);
1060 XSLT_RUNTIME_EXTRA(ctxt,
1061 sel->previousExtra, ptr) = node;
1062 XSLT_RUNTIME_EXTRA(ctxt,
1063 sel->indexExtra, ival) = pos;
1069 * recompute the index
1071 xmlNodePtr parent = node->parent;
1072 xmlNodePtr siblings = NULL;
1074 if (parent) siblings = parent->children;
1076 while (siblings != NULL) {
1077 if (siblings->type == XML_ELEMENT_NODE) {
1079 if (siblings == node) {
1083 siblings = siblings->next;
1085 if ((parent == NULL) || (node->doc == NULL))
1088 while (parent->parent != NULL)
1089 parent = parent->parent;
1090 if (((parent->type != XML_DOCUMENT_NODE) &&
1091 (parent->type != XML_HTML_DOCUMENT_NODE)) ||
1092 (parent != (xmlNodePtr) node->doc))
1097 ctxt->xpathCtxt->contextSize = len;
1098 ctxt->xpathCtxt->proximityPosition = pos;
1100 * If the node is in a Value Tree we cannot
1103 if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
1104 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
1106 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
1108 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
1113 oldNode = ctxt->node;
1116 if (step->value == NULL)
1118 if (step->comp == NULL)
1121 if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
1126 ctxt->xpathCtxt->contextSize = oldCS;
1127 ctxt->xpathCtxt->proximityPosition = oldCP;
1129 ctxt->node = oldNode;
1133 ctxt->xpathCtxt->contextSize = oldCS;
1134 ctxt->xpathCtxt->proximityPosition = oldCP;
1136 ctxt->node = oldNode;
1140 if (node->type != XML_PI_NODE)
1142 if (step->value != NULL) {
1143 if (!xmlStrEqual(step->value, node->name))
1147 case XSLT_OP_COMMENT:
1148 if (node->type != XML_COMMENT_NODE)
1152 if ((node->type != XML_TEXT_NODE) &&
1153 (node->type != XML_CDATA_SECTION_NODE))
1157 switch (node->type) {
1158 case XML_ELEMENT_NODE:
1159 case XML_CDATA_SECTION_NODE:
1161 case XML_COMMENT_NODE:
1171 if (states.states != NULL) {
1172 /* Free the rollback states */
1173 xmlFree(states.states);
1177 /* got an error try to rollback */
1178 if (states.states == NULL)
1180 if (states.nbstates <= 0) {
1181 xmlFree(states.states);
1185 i = states.states[states.nbstates].step;
1186 node = states.states[states.nbstates].node;
1188 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
1194 * xsltTestCompMatchList:
1195 * @ctxt: a XSLT process context
1197 * @comp: the precompiled pattern list
1199 * Test whether the node matches one of the patterns in the list
1201 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1204 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
1205 xsltCompMatchPtr comp) {
1208 if ((ctxt == NULL) || (node == NULL))
1210 while (comp != NULL) {
1211 ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1219 /************************************************************************
1221 * Dedicated parser for templates *
1223 ************************************************************************/
1225 #define CUR (*ctxt->cur)
1226 #define SKIP(val) ctxt->cur += (val)
1227 #define NXT(val) ctxt->cur[(val)]
1228 #define CUR_PTR ctxt->cur
1230 #define SKIP_BLANKS \
1231 while (IS_BLANK_CH(CUR)) NEXT
1233 #define CURRENT (*ctxt->cur)
1234 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1237 #define PUSH(op, val, val2, novar) \
1238 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1241 xsltSwapTopCompMatch(ctxt->comp);
1243 #define XSLT_ERROR(X) \
1244 { xsltError(ctxt, __FILE__, __LINE__, X); \
1245 ctxt->error = (X); return; }
1247 #define XSLT_ERROR0(X) \
1248 { xsltError(ctxt, __FILE__, __LINE__, X); \
1249 ctxt->error = (X); return(0); }
1253 * @ctxt: the XPath Parser context
1255 * Parse an XPath Litteral:
1257 * [29] Literal ::= '"' [^"]* '"'
1260 * Returns the Literal parsed or NULL
1264 xsltScanLiteral(xsltParserContextPtr ctxt) {
1265 const xmlChar *q, *cur;
1266 xmlChar *ret = NULL;
1273 val = xmlStringCurrentChar(NULL, cur, &len);
1274 while ((IS_CHAR(val)) && (val != '"')) {
1276 val = xmlStringCurrentChar(NULL, cur, &len);
1278 if (!IS_CHAR(val)) {
1282 ret = xmlStrndup(q, cur - q);
1286 } else if (CUR == '\'') {
1289 val = xmlStringCurrentChar(NULL, cur, &len);
1290 while ((IS_CHAR(val)) && (val != '\'')) {
1292 val = xmlStringCurrentChar(NULL, cur, &len);
1294 if (!IS_CHAR(val)) {
1298 ret = xmlStrndup(q, cur - q);
1303 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1312 * @ctxt: the XPath Parser context
1314 * Parses a non qualified name
1316 * Returns the Name parsed or NULL
1320 xsltScanNCName(xsltParserContextPtr ctxt) {
1321 const xmlChar *q, *cur;
1322 xmlChar *ret = NULL;
1328 val = xmlStringCurrentChar(NULL, cur, &len);
1329 if (!IS_LETTER(val) && (val != '_'))
1332 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1333 (val == '.') || (val == '-') ||
1335 (IS_COMBINING(val)) ||
1336 (IS_EXTENDER(val))) {
1338 val = xmlStringCurrentChar(NULL, cur, &len);
1340 ret = xmlStrndup(q, cur - q);
1346 * xsltCompileIdKeyPattern:
1347 * @ctxt: the compilation context
1348 * @name: a preparsed name
1349 * @aid: whether id/key are allowed there
1350 * @novar: flag to prohibit xslt var
1352 * Compile the XSLT LocationIdKeyPattern
1353 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1354 * | 'key' '(' Literal ',' Literal ')'
1356 * also handle NodeType and PI from:
1358 * [7] NodeTest ::= NameTest
1359 * | NodeType '(' ')'
1360 * | 'processing-instruction' '(' Literal ')'
1363 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
1364 int aid, int novar, xsltAxis axis) {
1365 xmlChar *lit = NULL;
1366 xmlChar *lit2 = NULL;
1369 xsltTransformError(NULL, NULL, NULL,
1370 "xsltCompileIdKeyPattern : ( expected\n");
1374 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1376 xsltTransformError(NULL, NULL, NULL,
1377 "xsltCompileIdKeyPattern : NodeTest expected\n");
1383 lit = xsltScanLiteral(ctxt);
1388 xsltTransformError(NULL, NULL, NULL,
1389 "xsltCompileIdKeyPattern : ) expected\n");
1394 PUSH(XSLT_OP_ID, lit, NULL, novar);
1395 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1397 xsltTransformError(NULL, NULL, NULL,
1398 "xsltCompileIdKeyPattern : NodeTest expected\n");
1404 lit = xsltScanLiteral(ctxt);
1409 xsltTransformError(NULL, NULL, NULL,
1410 "xsltCompileIdKeyPattern : , expected\n");
1416 lit2 = xsltScanLiteral(ctxt);
1421 xsltTransformError(NULL, NULL, NULL,
1422 "xsltCompileIdKeyPattern : ) expected\n");
1427 /* URGENT TODO: support namespace in keys */
1428 PUSH(XSLT_OP_KEY, lit, lit2, novar);
1429 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1433 lit = xsltScanLiteral(ctxt);
1438 xsltTransformError(NULL, NULL, NULL,
1439 "xsltCompileIdKeyPattern : ) expected\n");
1445 PUSH(XSLT_OP_PI, lit, NULL, novar);
1446 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1450 xsltTransformError(NULL, NULL, NULL,
1451 "xsltCompileIdKeyPattern : ) expected\n");
1456 PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
1457 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1461 xsltTransformError(NULL, NULL, NULL,
1462 "xsltCompileIdKeyPattern : ) expected\n");
1467 PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
1468 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1472 xsltTransformError(NULL, NULL, NULL,
1473 "xsltCompileIdKeyPattern : ) expected\n");
1478 if (axis == AXIS_ATTRIBUTE) {
1479 PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1482 PUSH(XSLT_OP_NODE, NULL, NULL, novar);
1485 xsltTransformError(NULL, NULL, NULL,
1486 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1490 xsltTransformError(NULL, NULL, NULL,
1491 "xsltCompileIdKeyPattern : node type\n");
1501 * xsltCompileStepPattern:
1502 * @ctxt: the compilation context
1503 * @token: a posible precompiled name
1504 * @novar: flag to prohibit xslt variables from pattern
1506 * Compile the XSLT StepPattern and generates a precompiled
1507 * form suitable for fast matching.
1509 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1510 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1511 * | ('child' | 'attribute') '::'
1513 * [7] NodeTest ::= NameTest
1514 * | NodeType '(' ')'
1515 * | 'processing-instruction' '(' Literal ')'
1516 * [8] Predicate ::= '[' PredicateExpr ']'
1517 * [9] PredicateExpr ::= Expr
1518 * [13] AbbreviatedAxisSpecifier ::= '@'?
1519 * [37] NameTest ::= '*' | NCName ':' '*' | QName
1523 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1524 xmlChar *name = NULL;
1525 const xmlChar *URI = NULL;
1526 xmlChar *URL = NULL;
1531 if ((token == NULL) && (CUR == '@')) {
1533 axis = AXIS_ATTRIBUTE;
1537 token = xsltScanNCName(ctxt);
1538 if (token == NULL) {
1541 if (axis == AXIS_ATTRIBUTE) {
1542 PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1545 PUSH(XSLT_OP_ALL, NULL, NULL, novar);
1547 goto parse_predicate;
1549 xsltTransformError(NULL, NULL, NULL,
1550 "xsltCompileStepPattern : Name expected\n");
1559 xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
1562 } else if (CUR == ':') {
1565 xmlChar *prefix = token;
1569 * This is a namespace match
1571 token = xsltScanNCName(ctxt);
1572 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1574 xsltTransformError(NULL, NULL, NULL,
1575 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1581 URL = xmlStrdup(ns->href);
1584 if (token == NULL) {
1587 if (axis == AXIS_ATTRIBUTE) {
1588 PUSH(XSLT_OP_ATTR, NULL, URL, novar);
1591 PUSH(XSLT_OP_NS, URL, NULL, novar);
1594 xsltTransformError(NULL, NULL, NULL,
1595 "xsltCompileStepPattern : Name expected\n");
1600 if (axis == AXIS_ATTRIBUTE) {
1601 PUSH(XSLT_OP_ATTR, token, URL, novar);
1604 PUSH(XSLT_OP_ELEM, token, URL, novar);
1609 xsltTransformError(NULL, NULL, NULL,
1610 "xsltCompileStepPattern : NodeTest expected\n");
1615 if (xmlStrEqual(token, (const xmlChar *) "child")) {
1617 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1618 axis = AXIS_ATTRIBUTE;
1620 xsltTransformError(NULL, NULL, NULL,
1621 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1627 token = xsltScanNCName(ctxt);
1628 goto parse_node_test;
1631 URI = xsltGetQNameURI(ctxt->elem, &token);
1632 if (token == NULL) {
1637 URL = xmlStrdup(URI);
1638 if (axis == AXIS_ATTRIBUTE) {
1639 PUSH(XSLT_OP_ATTR, token, URL, novar);
1642 PUSH(XSLT_OP_ELEM, token, URL, novar);
1648 while (CUR == '[') {
1650 xmlChar *ret = NULL;
1656 /* Skip over nested predicates */
1659 else if (CUR == ']') {
1663 } else if (CUR == '"') {
1665 while ((CUR != 0) && (CUR != '"'))
1667 } else if (CUR == '\'') {
1669 while ((CUR != 0) && (CUR != '\''))
1675 xsltTransformError(NULL, NULL, NULL,
1676 "xsltCompileStepPattern : ']' expected\n");
1680 ret = xmlStrndup(q, CUR_PTR - q);
1681 PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
1682 /* push the predicate lower than local test */
1696 * xsltCompileRelativePathPattern:
1697 * @comp: the compilation context
1698 * @token: a posible precompiled name
1699 * @novar: flag to prohibit xslt variables
1701 * Compile the XSLT RelativePathPattern and generates a precompiled
1702 * form suitable for fast matching.
1704 * [4] RelativePathPattern ::= StepPattern
1705 * | RelativePathPattern '/' StepPattern
1706 * | RelativePathPattern '//' StepPattern
1709 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1710 xsltCompileStepPattern(ctxt, token, novar);
1714 while ((CUR != 0) && (CUR != '|')) {
1715 if ((CUR == '/') && (NXT(1) == '/')) {
1716 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1720 xsltCompileStepPattern(ctxt, NULL, novar);
1721 } else if (CUR == '/') {
1722 PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1725 if ((CUR != 0) && (CUR != '|')) {
1726 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1740 * xsltCompileLocationPathPattern:
1741 * @ctxt: the compilation context
1742 * @novar: flag to prohibit xslt variables
1744 * Compile the XSLT LocationPathPattern and generates a precompiled
1745 * form suitable for fast matching.
1747 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1748 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1749 * | '//'? RelativePathPattern
1752 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
1754 if ((CUR == '/') && (NXT(1) == '/')) {
1756 * since we reverse the query
1757 * a leading // can be safely ignored
1761 ctxt->comp->priority = 0.5; /* '//' means not 0 priority */
1762 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1763 } else if (CUR == '/') {
1765 * We need to find root as the parent
1769 PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
1770 if ((CUR != 0) && (CUR != '|')) {
1771 PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1772 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1774 } else if (CUR == '*') {
1775 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1776 } else if (CUR == '@') {
1777 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1780 name = xsltScanNCName(ctxt);
1782 xsltTransformError(NULL, NULL, NULL,
1783 "xsltCompileLocationPathPattern : Name expected\n");
1788 if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1789 xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
1792 if ((CUR == '/') && (NXT(1) == '/')) {
1793 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1797 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1798 } else if (CUR == '/') {
1799 PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1802 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1806 xsltCompileRelativePathPattern(ctxt, name, novar);
1813 * xsltCompilePatternInternal:
1814 * @pattern: an XSLT pattern
1815 * @doc: the containing document
1816 * @node: the containing element
1817 * @style: the stylesheet
1818 * @runtime: the transformation context, if done at run-time
1819 * @novar: flag to prohibit xslt variables
1821 * Compile the XSLT pattern and generates a list of precompiled form suitable
1822 * for fast matching.
1824 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1826 * Returns the generated pattern list or NULL in case of failure
1829 static xsltCompMatchPtr
1830 xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
1831 xmlNodePtr node, xsltStylesheetPtr style,
1832 xsltTransformContextPtr runtime, int novar) {
1833 xsltParserContextPtr ctxt = NULL;
1834 xsltCompMatchPtr element, first = NULL, previous = NULL;
1835 int current, start, end, level, j;
1837 if (pattern == NULL) {
1838 xsltTransformError(NULL, NULL, node,
1839 "xsltCompilePattern : NULL pattern\n");
1843 ctxt = xsltNewParserContext(style, runtime);
1849 while (pattern[current] != 0) {
1851 while (IS_BLANK_CH(pattern[current]))
1855 while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1856 if (pattern[end] == '[')
1858 else if (pattern[end] == ']')
1860 else if (pattern[end] == '\'') {
1862 while ((pattern[end] != 0) && (pattern[end] != '\''))
1864 } else if (pattern[end] == '"') {
1866 while ((pattern[end] != 0) && (pattern[end] != '"'))
1869 if (pattern[end] == 0)
1873 if (current == end) {
1874 xsltTransformError(NULL, NULL, node,
1875 "xsltCompilePattern : NULL pattern\n");
1878 element = xsltNewCompMatch();
1879 if (element == NULL) {
1884 else if (previous != NULL)
1885 previous->next = element;
1888 ctxt->comp = element;
1889 ctxt->base = xmlStrndup(&pattern[start], end - start);
1890 if (ctxt->base == NULL)
1892 ctxt->cur = &(ctxt->base)[current - start];
1893 element->pattern = ctxt->base;
1894 element->nsList = xmlGetNsList(doc, node);
1896 if (element->nsList != NULL) {
1897 while (element->nsList[j] != NULL)
1903 #ifdef WITH_XSLT_DEBUG_PATTERN
1904 xsltGenericDebug(xsltGenericDebugContext,
1905 "xsltCompilePattern : parsing '%s'\n",
1909 Preset default priority to be zero.
1910 This may be changed by xsltCompileLocationPathPattern.
1912 element->priority = 0;
1913 xsltCompileLocationPathPattern(ctxt, novar);
1915 xsltTransformError(NULL, style, node,
1916 "xsltCompilePattern : failed to compile '%s'\n",
1918 if (style != NULL) style->errors++;
1923 * Reverse for faster interpretation.
1925 xsltReverseCompMatch(ctxt, element);
1928 * Set-up the priority
1930 if (element->priority == 0) { /* if not yet determined */
1931 if (((element->steps[0].op == XSLT_OP_ELEM) ||
1932 (element->steps[0].op == XSLT_OP_ATTR) ||
1933 (element->steps[0].op == XSLT_OP_PI)) &&
1934 (element->steps[0].value != NULL) &&
1935 (element->steps[1].op == XSLT_OP_END)) {
1936 ; /* previously preset */
1937 } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1938 (element->steps[0].value2 != NULL) &&
1939 (element->steps[1].op == XSLT_OP_END)) {
1940 element->priority = -0.25;
1941 } else if ((element->steps[0].op == XSLT_OP_NS) &&
1942 (element->steps[0].value != NULL) &&
1943 (element->steps[1].op == XSLT_OP_END)) {
1944 element->priority = -0.25;
1945 } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1946 (element->steps[0].value == NULL) &&
1947 (element->steps[0].value2 == NULL) &&
1948 (element->steps[1].op == XSLT_OP_END)) {
1949 element->priority = -0.5;
1950 } else if (((element->steps[0].op == XSLT_OP_PI) ||
1951 (element->steps[0].op == XSLT_OP_TEXT) ||
1952 (element->steps[0].op == XSLT_OP_ALL) ||
1953 (element->steps[0].op == XSLT_OP_NODE) ||
1954 (element->steps[0].op == XSLT_OP_COMMENT)) &&
1955 (element->steps[1].op == XSLT_OP_END)) {
1956 element->priority = -0.5;
1958 element->priority = 0.5;
1961 #ifdef WITH_XSLT_DEBUG_PATTERN
1962 xsltGenericDebug(xsltGenericDebugContext,
1963 "xsltCompilePattern : parsed %s, default priority %f\n",
1964 element->pattern, element->priority);
1966 if (pattern[end] == '|')
1971 xsltTransformError(NULL, style, node,
1972 "xsltCompilePattern : NULL pattern\n");
1973 if (style != NULL) style->errors++;
1977 xsltFreeParserContext(ctxt);
1982 xsltFreeParserContext(ctxt);
1984 xsltFreeCompMatchList(first);
1989 * xsltCompilePattern:
1990 * @pattern: an XSLT pattern
1991 * @doc: the containing document
1992 * @node: the containing element
1993 * @style: the stylesheet
1994 * @runtime: the transformation context, if done at run-time
1996 * Compile the XSLT pattern and generates a list of precompiled form suitable
1997 * for fast matching.
1999 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2001 * Returns the generated pattern list or NULL in case of failure
2005 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
2006 xmlNodePtr node, xsltStylesheetPtr style,
2007 xsltTransformContextPtr runtime) {
2008 return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
2011 /************************************************************************
2013 * Module interfaces *
2015 ************************************************************************/
2019 * @style: an XSLT stylesheet
2020 * @cur: an XSLT template
2021 * @mode: the mode name or NULL
2022 * @modeURI: the mode URI or NULL
2024 * Register the XSLT pattern associated to @cur
2026 * Returns -1 in case of error, 0 otherwise
2029 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
2030 const xmlChar *mode, const xmlChar *modeURI) {
2031 xsltCompMatchPtr pat, list, next;
2033 * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2034 * avoids gcc 'type-punned pointer' warning.
2037 const xmlChar *name = NULL;
2038 float priority; /* the priority */
2040 if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
2043 priority = cur->priority;
2044 pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
2053 pat->template = cur;
2055 pat->mode = xmlDictLookup(style->dict, mode, -1);
2056 if (modeURI != NULL)
2057 pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
2058 if (priority != XSLT_PAT_NO_PRIORITY)
2059 pat->priority = priority;
2062 * insert it in the hash table list corresponding to its lookup name
2064 switch (pat->steps[0].op) {
2066 if (pat->steps[0].value != NULL)
2067 name = pat->steps[0].value;
2069 top = &(style->attrMatch);
2071 case XSLT_OP_PARENT:
2072 case XSLT_OP_ANCESTOR:
2073 top = &(style->elemMatch);
2076 top = &(style->rootMatch);
2079 top = &(style->keyMatch);
2082 /* TODO optimize ID !!! */
2085 top = &(style->elemMatch);
2088 case XSLT_OP_PREDICATE:
2089 xsltTransformError(NULL, style, NULL,
2090 "xsltAddTemplate: invalid compiled pattern\n");
2091 xsltFreeCompMatch(pat);
2094 * TODO: some flags at the top level about type based patterns
2095 * would be faster than inclusion in the hash table.
2098 if (pat->steps[0].value != NULL)
2099 name = pat->steps[0].value;
2101 top = &(style->piMatch);
2103 case XSLT_OP_COMMENT:
2104 top = &(style->commentMatch);
2107 top = &(style->textMatch);
2111 if (pat->steps[0].value != NULL)
2112 name = pat->steps[0].value;
2114 top = &(style->elemMatch);
2118 if (style->templatesHash == NULL) {
2119 style->templatesHash = xmlHashCreate(1024);
2120 if (style->templatesHash == NULL) {
2121 xsltFreeCompMatch(pat);
2124 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
2126 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
2127 name, mode, modeURI);
2129 xmlHashAddEntry3(style->templatesHash, name,
2130 mode, modeURI, pat);
2133 * Note '<=' since one must choose among the matching
2134 * template rules that are left, the one that occurs
2135 * last in the stylesheet
2137 if (list->priority <= pat->priority) {
2139 xmlHashUpdateEntry3(style->templatesHash, name,
2140 mode, modeURI, pat, NULL);
2142 while (list->next != NULL) {
2143 if (list->next->priority <= pat->priority)
2147 pat->next = list->next;
2152 } else if (top != NULL) {
2157 } else if (list->priority <= pat->priority) {
2161 while (list->next != NULL) {
2162 if (list->next->priority <= pat->priority)
2166 pat->next = list->next;
2170 xsltTransformError(NULL, style, NULL,
2171 "xsltAddTemplate: invalid compiled pattern\n");
2172 xsltFreeCompMatch(pat);
2175 #ifdef WITH_XSLT_DEBUG_PATTERN
2177 xsltGenericDebug(xsltGenericDebugContext,
2178 "added pattern : '%s' mode '%s' priority %f\n",
2179 pat->pattern, pat->mode, pat->priority);
2181 xsltGenericDebug(xsltGenericDebugContext,
2182 "added pattern : '%s' priority %f\n",
2183 pat->pattern, pat->priority);
2192 xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
2194 if ((ctxt == NULL) || (contextNode == NULL)) {
2195 xsltTransformError(ctxt, NULL, ctxt->inst,
2196 "Internal error in xsltComputeAllKeys(): "
2197 "Bad arguments.\n");
2201 if (ctxt->document == NULL) {
2203 * The document info will only be NULL if we have a RTF.
2205 if (contextNode->doc->_private != NULL)
2206 goto doc_info_mismatch;
2208 * On-demand creation of the document info (needed for keys).
2210 ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
2211 if (ctxt->document == NULL)
2214 return xsltInitAllDocKeys(ctxt);
2217 xsltTransformError(ctxt, NULL, ctxt->inst,
2218 "Internal error in xsltComputeAllKeys(): "
2219 "The context's document info doesn't match the "
2220 "document info of the current result tree.\n");
2221 ctxt->state = XSLT_STATE_STOPPED;
2227 * @ctxt: a XSLT process context
2228 * @node: the node being processed
2229 * @style: the current style
2231 * Finds the template applying to this node, if @style is non-NULL
2232 * it means one needs to look for the next imported template in scope.
2234 * Returns the xsltTemplatePtr or NULL if not found
2237 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2238 xsltStylesheetPtr style)
2240 xsltStylesheetPtr curstyle;
2241 xsltTemplatePtr ret = NULL;
2242 const xmlChar *name = NULL;
2243 xsltCompMatchPtr list = NULL;
2247 if ((ctxt == NULL) || (node == NULL))
2250 if (style == NULL) {
2251 curstyle = ctxt->style;
2253 curstyle = xsltNextImport(style);
2256 while ((curstyle != NULL) && (curstyle != style)) {
2257 priority = XSLT_PAT_NO_PRIORITY;
2258 /* TODO : handle IDs/keys here ! */
2259 if (curstyle->templatesHash != NULL) {
2261 * Use the top name as selector
2263 switch (node->type) {
2264 case XML_ELEMENT_NODE:
2265 if (node->name[0] == ' ')
2267 case XML_ATTRIBUTE_NODE:
2271 case XML_DOCUMENT_NODE:
2272 case XML_HTML_DOCUMENT_NODE:
2274 case XML_CDATA_SECTION_NODE:
2275 case XML_COMMENT_NODE:
2276 case XML_ENTITY_REF_NODE:
2277 case XML_ENTITY_NODE:
2278 case XML_DOCUMENT_TYPE_NODE:
2279 case XML_DOCUMENT_FRAG_NODE:
2280 case XML_NOTATION_NODE:
2282 case XML_ELEMENT_DECL:
2283 case XML_ATTRIBUTE_DECL:
2284 case XML_ENTITY_DECL:
2285 case XML_NAMESPACE_DECL:
2286 case XML_XINCLUDE_START:
2287 case XML_XINCLUDE_END:
2296 * find the list of applicable expressions based on the name
2298 list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2299 name, ctxt->mode, ctxt->modeURI);
2302 while (list != NULL) {
2303 if (xsltTestCompMatch(ctxt, list, node,
2304 ctxt->mode, ctxt->modeURI)) {
2305 ret = list->template;
2306 priority = list->priority;
2314 * find alternate generic matches
2316 switch (node->type) {
2317 case XML_ELEMENT_NODE:
2318 if (node->name[0] == ' ')
2319 list = curstyle->rootMatch;
2321 list = curstyle->elemMatch;
2322 if (node->psvi != NULL) keyed = 1;
2324 case XML_ATTRIBUTE_NODE: {
2327 list = curstyle->attrMatch;
2328 attr = (xmlAttrPtr) node;
2329 if (attr->psvi != NULL) keyed = 1;
2333 list = curstyle->piMatch;
2334 if (node->psvi != NULL) keyed = 1;
2336 case XML_DOCUMENT_NODE:
2337 case XML_HTML_DOCUMENT_NODE: {
2340 list = curstyle->rootMatch;
2341 doc = (xmlDocPtr) node;
2342 if (doc->psvi != NULL) keyed = 1;
2346 case XML_CDATA_SECTION_NODE:
2347 list = curstyle->textMatch;
2348 if (node->psvi != NULL) keyed = 1;
2350 case XML_COMMENT_NODE:
2351 list = curstyle->commentMatch;
2352 if (node->psvi != NULL) keyed = 1;
2354 case XML_ENTITY_REF_NODE:
2355 case XML_ENTITY_NODE:
2356 case XML_DOCUMENT_TYPE_NODE:
2357 case XML_DOCUMENT_FRAG_NODE:
2358 case XML_NOTATION_NODE:
2360 case XML_ELEMENT_DECL:
2361 case XML_ATTRIBUTE_DECL:
2362 case XML_ENTITY_DECL:
2363 case XML_NAMESPACE_DECL:
2364 case XML_XINCLUDE_START:
2365 case XML_XINCLUDE_END:
2370 while ((list != NULL) &&
2371 ((ret == NULL) || (list->priority > priority))) {
2372 if (xsltTestCompMatch(ctxt, list, node,
2373 ctxt->mode, ctxt->modeURI)) {
2374 ret = list->template;
2375 priority = list->priority;
2381 * Some of the tests for elements can also apply to documents
2383 if ((node->type == XML_DOCUMENT_NODE) ||
2384 (node->type == XML_HTML_DOCUMENT_NODE) ||
2385 (node->type == XML_TEXT_NODE)) {
2386 list = curstyle->elemMatch;
2387 while ((list != NULL) &&
2388 ((ret == NULL) || (list->priority > priority))) {
2389 if (xsltTestCompMatch(ctxt, list, node,
2390 ctxt->mode, ctxt->modeURI)) {
2391 ret = list->template;
2392 priority = list->priority;
2397 } else if ((node->type == XML_PI_NODE) ||
2398 (node->type == XML_COMMENT_NODE)) {
2399 list = curstyle->elemMatch;
2400 while ((list != NULL) &&
2401 ((ret == NULL) || (list->priority > priority))) {
2402 if (xsltTestCompMatch(ctxt, list, node,
2403 ctxt->mode, ctxt->modeURI)) {
2404 ret = list->template;
2405 priority = list->priority;
2414 list = curstyle->keyMatch;
2415 while ((list != NULL) &&
2416 ((ret == NULL) || (list->priority > priority))) {
2417 if (xsltTestCompMatch(ctxt, list, node,
2418 ctxt->mode, ctxt->modeURI)) {
2419 ret = list->template;
2420 priority = list->priority;
2426 else if (ctxt->hasTemplKeyPatterns &&
2427 ((ctxt->document == NULL) ||
2428 (ctxt->document->nbKeysComputed < ctxt->nbKeys)))
2431 * Compute all remaining keys for this document.
2433 * REVISIT TODO: I think this could be further optimized.
2435 if (xsltComputeAllKeys(ctxt, node) == -1)
2438 switch (node->type) {
2439 case XML_ELEMENT_NODE:
2440 if (node->psvi != NULL) keyed = 1;
2442 case XML_ATTRIBUTE_NODE:
2443 if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
2446 case XML_CDATA_SECTION_NODE:
2447 case XML_COMMENT_NODE:
2449 if (node->psvi != NULL) keyed = 1;
2451 case XML_DOCUMENT_NODE:
2452 case XML_HTML_DOCUMENT_NODE:
2453 if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
2465 * Cycle on next curstylesheet import.
2467 curstyle = xsltNextImport(curstyle);
2475 * xsltCleanupTemplates:
2476 * @style: an XSLT stylesheet
2478 * Cleanup the state of the templates used by the stylesheet and
2479 * the ones it imports.
2482 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2486 * xsltFreeTemplateHashes:
2487 * @style: an XSLT stylesheet
2489 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2492 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2493 if (style->templatesHash != NULL)
2494 xmlHashFree((xmlHashTablePtr) style->templatesHash,
2495 (xmlHashDeallocator) xsltFreeCompMatchList);
2496 if (style->rootMatch != NULL)
2497 xsltFreeCompMatchList(style->rootMatch);
2498 if (style->keyMatch != NULL)
2499 xsltFreeCompMatchList(style->keyMatch);
2500 if (style->elemMatch != NULL)
2501 xsltFreeCompMatchList(style->elemMatch);
2502 if (style->attrMatch != NULL)
2503 xsltFreeCompMatchList(style->attrMatch);
2504 if (style->parentMatch != NULL)
2505 xsltFreeCompMatchList(style->parentMatch);
2506 if (style->textMatch != NULL)
2507 xsltFreeCompMatchList(style->textMatch);
2508 if (style->piMatch != NULL)
2509 xsltFreeCompMatchList(style->piMatch);
2510 if (style->commentMatch != NULL)
2511 xsltFreeCompMatchList(style->commentMatch);