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"
36 #ifdef WITH_XSLT_DEBUG
37 #define WITH_XSLT_DEBUG_PATTERN
63 typedef struct _xsltStepState xsltStepState;
64 typedef xsltStepState *xsltStepStatePtr;
65 struct _xsltStepState {
70 typedef struct _xsltStepStates xsltStepStates;
71 typedef xsltStepStates *xsltStepStatesPtr;
72 struct _xsltStepStates {
75 xsltStepStatePtr states;
78 typedef struct _xsltStepOp xsltStepOp;
79 typedef xsltStepOp *xsltStepOpPtr;
85 xmlXPathCompExprPtr comp;
87 * Optimisations for count
94 struct _xsltCompMatch {
95 struct _xsltCompMatch *next; /* siblings in the name hash */
96 float priority; /* the priority */
97 const xmlChar *pattern; /* the pattern */
98 const xmlChar *mode; /* the mode */
99 const xmlChar *modeURI; /* the mode URI */
100 xsltTemplatePtr template; /* the associated template */
102 /* TODO fix the statically allocated size steps[] */
105 xmlNsPtr *nsList; /* the namespaces in scope */
106 int nsNr; /* the number of namespaces in scope */
107 xsltStepOp steps[40]; /* ops for computation */
110 typedef struct _xsltParserContext xsltParserContext;
111 typedef xsltParserContext *xsltParserContextPtr;
112 struct _xsltParserContext {
113 xsltStylesheetPtr style; /* the stylesheet */
114 xsltTransformContextPtr ctxt; /* the transformation or NULL */
115 const xmlChar *cur; /* the current char being parsed */
116 const xmlChar *base; /* the full expression */
117 xmlDocPtr doc; /* the source document */
118 xmlNodePtr elem; /* the source element */
119 int error; /* error code */
120 xsltCompMatchPtr comp; /* the result */
123 /************************************************************************
127 ************************************************************************/
132 * Create a new XSLT CompMatch
134 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
136 static xsltCompMatchPtr
137 xsltNewCompMatch(void) {
138 xsltCompMatchPtr cur;
140 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
142 xsltTransformError(NULL, NULL, NULL,
143 "xsltNewCompMatch : malloc failed\n");
146 memset(cur, 0, sizeof(xsltCompMatch));
155 * @comp: an XSLT comp
157 * Free up the memory allocated by @comp
160 xsltFreeCompMatch(xsltCompMatchPtr comp) {
166 if (comp->pattern != NULL)
167 xmlFree((xmlChar *)comp->pattern);
168 if (comp->nsList != NULL)
169 xmlFree(comp->nsList);
170 for (i = 0;i < comp->nbStep;i++) {
171 op = &comp->steps[i];
172 if (op->value != NULL)
174 if (op->value2 != NULL)
176 if (op->value3 != NULL)
178 if (op->comp != NULL)
179 xmlXPathFreeCompExpr(op->comp);
181 memset(comp, -1, sizeof(xsltCompMatch));
186 * xsltFreeCompMatchList:
187 * @comp: an XSLT comp list
189 * Free up the memory allocated by all the elements of @comp
192 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
193 xsltCompMatchPtr cur;
195 while (comp != NULL) {
198 xsltFreeCompMatch(cur);
203 * xsltNormalizeCompSteps:
204 * @payload: pointer to template hash table entry
205 * @data: pointer to the stylesheet
206 * @name: template match name
208 * This is a hashtable scanner function to normalize the compiled
209 * steps of an imported stylesheet.
211 void xsltNormalizeCompSteps(void *payload,
212 void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
213 xsltCompMatchPtr comp = payload;
214 xsltStylesheetPtr style = data;
217 for (ix = 0; ix < comp->nbStep; ix++) {
218 comp->steps[ix].previousExtra += style->extrasNr;
219 comp->steps[ix].indexExtra += style->extrasNr;
220 comp->steps[ix].lenExtra += style->extrasNr;
225 * xsltNewParserContext:
226 * @style: the stylesheet
227 * @ctxt: the transformation context, if done at run-time
229 * Create a new XSLT ParserContext
231 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
233 static xsltParserContextPtr
234 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
235 xsltParserContextPtr cur;
237 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
239 xsltTransformError(NULL, NULL, NULL,
240 "xsltNewParserContext : malloc failed\n");
243 memset(cur, 0, sizeof(xsltParserContext));
250 * xsltFreeParserContext:
251 * @ctxt: an XSLT parser context
253 * Free up the memory allocated by @ctxt
256 xsltFreeParserContext(xsltParserContextPtr ctxt) {
259 memset(ctxt, -1, sizeof(xsltParserContext));
265 * @comp: the compiled match expression
267 * @value: the first value
268 * @value2: the second value
270 * Add an step to an XSLT Compiled Match
272 * Returns -1 in case of failure, 0 otherwise.
275 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
276 xsltOp op, xmlChar * value, xmlChar * value2)
278 if (comp->nbStep >= 40) {
279 xsltTransformError(NULL, NULL, NULL,
280 "xsltCompMatchAdd: overflow\n");
283 comp->steps[comp->nbStep].op = op;
284 comp->steps[comp->nbStep].value = value;
285 comp->steps[comp->nbStep].value2 = value2;
286 if (ctxt->ctxt != NULL) {
287 comp->steps[comp->nbStep].previousExtra =
288 xsltAllocateExtraCtxt(ctxt->ctxt);
289 comp->steps[comp->nbStep].indexExtra =
290 xsltAllocateExtraCtxt(ctxt->ctxt);
291 comp->steps[comp->nbStep].lenExtra =
292 xsltAllocateExtraCtxt(ctxt->ctxt);
294 comp->steps[comp->nbStep].previousExtra =
295 xsltAllocateExtra(ctxt->style);
296 comp->steps[comp->nbStep].indexExtra =
297 xsltAllocateExtra(ctxt->style);
298 comp->steps[comp->nbStep].lenExtra =
299 xsltAllocateExtra(ctxt->style);
301 if (op == XSLT_OP_PREDICATE) {
302 comp->steps[comp->nbStep].comp = xsltXPathCompile(ctxt->style, value);
309 * xsltSwapTopCompMatch:
310 * @comp: the compiled match expression
312 * reverse the two top steps.
315 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
317 int j = comp->nbStep - 1;
320 register xmlChar *tmp;
322 register xmlXPathCompExprPtr expr;
324 tmp = comp->steps[i].value;
325 comp->steps[i].value = comp->steps[j].value;
326 comp->steps[j].value = tmp;
327 tmp = comp->steps[i].value2;
328 comp->steps[i].value2 = comp->steps[j].value2;
329 comp->steps[j].value2 = tmp;
330 op = comp->steps[i].op;
331 comp->steps[i].op = comp->steps[j].op;
332 comp->steps[j].op = op;
333 expr = comp->steps[i].comp;
334 comp->steps[i].comp = comp->steps[j].comp;
335 comp->steps[j].comp = expr;
340 * xsltReverseCompMatch:
341 * @comp: the compiled match expression
343 * reverse all the stack of expressions
346 xsltReverseCompMatch(xsltCompMatchPtr comp) {
348 int j = comp->nbStep - 1;
351 register xmlChar *tmp;
353 register xmlXPathCompExprPtr expr;
354 tmp = comp->steps[i].value;
355 comp->steps[i].value = comp->steps[j].value;
356 comp->steps[j].value = tmp;
357 tmp = comp->steps[i].value2;
358 comp->steps[i].value2 = comp->steps[j].value2;
359 comp->steps[j].value2 = tmp;
360 op = comp->steps[i].op;
361 comp->steps[i].op = comp->steps[j].op;
362 comp->steps[j].op = op;
363 expr = comp->steps[i].comp;
364 comp->steps[i].comp = comp->steps[j].comp;
365 comp->steps[j].comp = expr;
369 comp->steps[comp->nbStep++].op = XSLT_OP_END;
372 /************************************************************************
374 * The interpreter for the precompiled patterns *
376 ************************************************************************/
379 xsltPatPushState(xsltStepStates *states, int step, xmlNodePtr node) {
380 if ((states->states == NULL) || (states->maxstates <= 0)) {
381 states->maxstates = 4;
382 states->nbstates = 0;
383 states->states = xmlMalloc(4 * sizeof(xsltStepState));
385 else if (states->maxstates <= states->nbstates) {
388 tmp = (xsltStepStatePtr) xmlRealloc(states->states,
389 2 * states->maxstates * sizeof(xsltStepState));
392 states->states = tmp;
393 states->maxstates *= 2;
395 states->states[states->nbstates].step = step;
396 states->states[states->nbstates++].node = node;
398 fprintf(stderr, "Push: %d, %s\n", step, node->name);
405 * @ctxt: a XSLT process context
406 * @comp: the precompiled pattern
408 * @mode: the mode name or NULL
409 * @modeURI: the mode URI or NULL
411 * Test whether the node matches the pattern
413 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
416 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
417 xmlNodePtr node, const xmlChar *mode,
418 const xmlChar *modeURI) {
420 xsltStepOpPtr step, sel = NULL;
421 xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
423 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
424 xsltTransformError(ctxt, NULL, node,
425 "xsltTestCompMatch: null arg\n");
429 if (comp->mode == NULL)
432 * both mode strings must be interned on the stylesheet dictionary
434 if (comp->mode != mode)
437 if (comp->mode != NULL)
440 if (modeURI != NULL) {
441 if (comp->modeURI == NULL)
444 * both modeURI strings must be interned on the stylesheet dictionary
446 if (comp->modeURI != modeURI)
449 if (comp->modeURI != NULL)
454 for (;i < comp->nbStep;i++) {
455 step = &comp->steps[i];
456 if (step->op != XSLT_OP_PREDICATE)
462 if ((node->type == XML_DOCUMENT_NODE) ||
463 #ifdef LIBXML_DOCB_ENABLED
464 (node->type == XML_DOCB_DOCUMENT_NODE) ||
466 (node->type == XML_HTML_DOCUMENT_NODE))
468 if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
472 if (node->type != XML_ELEMENT_NODE)
474 if (step->value == NULL)
476 if (step->value[0] != node->name[0])
478 if (!xmlStrEqual(step->value, node->name))
482 if (node->ns == NULL) {
483 if (step->value2 != NULL)
485 } else if (node->ns->href != NULL) {
486 if (step->value2 == NULL)
488 if (!xmlStrEqual(step->value2, node->ns->href))
492 case XSLT_OP_CHILD: {
495 if ((node->type != XML_ELEMENT_NODE) &&
496 (node->type != XML_DOCUMENT_NODE) &&
497 #ifdef LIBXML_DOCB_ENABLED
498 (node->type != XML_DOCB_DOCUMENT_NODE) &&
500 (node->type != XML_HTML_DOCUMENT_NODE))
503 lst = node->children;
505 if (step->value != NULL) {
506 while (lst != NULL) {
507 if ((lst->type == XML_ELEMENT_NODE) &&
508 (step->value[0] == lst->name[0]) &&
509 (xmlStrEqual(step->value, lst->name)))
519 if (node->type != XML_ATTRIBUTE_NODE)
521 if (step->value != NULL) {
522 if (step->value[0] != node->name[0])
524 if (!xmlStrEqual(step->value, node->name))
528 if (node->ns == NULL) {
529 if (step->value2 != NULL)
531 } else if (step->value2 != NULL) {
532 if (!xmlStrEqual(step->value2, node->ns->href))
537 if ((node->type == XML_DOCUMENT_NODE) ||
538 (node->type == XML_HTML_DOCUMENT_NODE) ||
539 #ifdef LIBXML_DOCB_ENABLED
540 (node->type == XML_DOCB_DOCUMENT_NODE) ||
542 (node->type == XML_NAMESPACE_DECL))
547 if (step->value == NULL)
549 if (step->value[0] != node->name[0])
551 if (!xmlStrEqual(step->value, node->name))
554 if (node->ns == NULL) {
555 if (step->value2 != NULL)
557 } else if (node->ns->href != NULL) {
558 if (step->value2 == NULL)
560 if (!xmlStrEqual(step->value2, node->ns->href))
564 case XSLT_OP_ANCESTOR:
565 /* TODO: implement coalescing of ANCESTOR/NODE ops */
566 if (step->value == NULL) {
567 step = &comp->steps[i+1];
568 if (step->op == XSLT_OP_ROOT)
570 /* added NS, ID and KEY as a result of bug 168208 */
571 if ((step->op != XSLT_OP_ELEM) &&
572 (step->op != XSLT_OP_ALL) &&
573 (step->op != XSLT_OP_NS) &&
574 (step->op != XSLT_OP_ID) &&
575 (step->op != XSLT_OP_KEY))
580 if ((node->type == XML_DOCUMENT_NODE) ||
581 (node->type == XML_HTML_DOCUMENT_NODE) ||
582 #ifdef LIBXML_DOCB_ENABLED
583 (node->type == XML_DOCB_DOCUMENT_NODE) ||
585 (node->type == XML_NAMESPACE_DECL))
588 if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
589 xsltPatPushState(&states, i, node);
593 if (step->value == NULL) {
594 xsltPatPushState(&states, i - 1, node);
597 while (node != NULL) {
600 if ((node->type == XML_ELEMENT_NODE) &&
601 (step->value[0] == node->name[0]) &&
602 (xmlStrEqual(step->value, node->name))) {
604 if (node->ns == NULL) {
605 if (step->value2 == NULL)
607 } else if (node->ns->href != NULL) {
608 if ((step->value2 != NULL) &&
609 (xmlStrEqual(step->value2, node->ns->href)))
617 xsltPatPushState(&states, i - 1, node);
620 /* TODO Handle IDs decently, must be done differently */
623 if (node->type != XML_ELEMENT_NODE)
626 id = xmlGetID(node->doc, step->value);
627 if ((id == NULL) || (id->parent != node))
635 list = xsltGetKey(ctxt, step->value,
636 step->value3, step->value2);
639 for (indx = 0;indx < list->nodeNr;indx++)
640 if (list->nodeTab[indx] == node)
642 if (indx >= list->nodeNr)
647 if (node->type != XML_ELEMENT_NODE)
649 if (node->ns == NULL) {
650 if (step->value != NULL)
652 } else if (node->ns->href != NULL) {
653 if (step->value == NULL)
655 if (!xmlStrEqual(step->value, node->ns->href))
660 if (node->type != XML_ELEMENT_NODE)
663 case XSLT_OP_PREDICATE: {
667 int pos = 0, len = 0;
672 (doc->name != NULL) &&
673 (doc->name[0] == ' ') &&
674 (xmlStrEqual(BAD_CAST doc->name,
675 BAD_CAST " fake node libxslt")))
680 * The simple existing predicate code cannot handle
681 * properly cascaded predicates. If in this situation
682 * compute directly the full node list once and check
683 * if the node is in the result list.
685 if (comp->steps[i + 1].op == XSLT_OP_PREDICATE) {
687 xmlXPathObjectPtr list;
691 prevdoc = (xmlDocPtr)
692 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
693 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
694 list = (xmlXPathObjectPtr)
695 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
697 if ((list == NULL) || (prevdoc != doc)) {
699 xmlXPathObjectPtr newlist;
700 xmlNodePtr parent = node->parent;
704 if (comp->pattern[0] == '/')
705 query = xmlStrdup(comp->pattern);
707 query = xmlStrdup((const xmlChar *)"//");
708 query = xmlStrcat(query, comp->pattern);
710 oldnode = ctxt->xpathCtxt->node;
711 olddoc = ctxt->xpathCtxt->doc;
712 ctxt->xpathCtxt->node = node;
713 ctxt->xpathCtxt->doc = doc;
714 newlist = xmlXPathEval(query, ctxt->xpathCtxt);
715 ctxt->xpathCtxt->node = oldnode;
716 ctxt->xpathCtxt->doc = olddoc;
720 if (newlist->type != XPATH_NODESET) {
721 xmlXPathFreeObject(newlist);
726 if ((parent == NULL) || (node->doc == NULL) || isRVT)
731 xmlXPathFreeObject(list);
734 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
736 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
738 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
740 XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
741 (xmlFreeFunc) xmlXPathFreeObject;
745 if ((list->nodesetval == NULL) ||
746 (list->nodesetval->nodeNr <= 0)) {
748 xmlXPathFreeObject(list);
751 /* TODO: store the index and use it for the scan */
753 for (j = 0;j < list->nodesetval->nodeNr;j++) {
754 if (list->nodesetval->nodeTab[j] == node) {
756 xmlXPathFreeObject(list);
763 xmlXPathFreeObject(list);
767 * Depending on the last selection, one may need to
768 * recompute contextSize and proximityPosition.
770 * TODO: make this thread safe !
772 oldCS = ctxt->xpathCtxt->contextSize;
773 oldCP = ctxt->xpathCtxt->proximityPosition;
775 (sel->op == XSLT_OP_ELEM) &&
776 (sel->value != NULL) &&
777 (node->type == XML_ELEMENT_NODE) &&
778 (node->parent != NULL)) {
782 previous = (xmlNodePtr)
783 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
784 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
785 if ((previous != NULL) &&
786 (previous->parent == node->parent)) {
788 * just walk back to adjust the index
791 xmlNodePtr sibling = node;
793 while (sibling != NULL) {
794 if (sibling == previous)
796 if ((previous->type == XML_ELEMENT_NODE) &&
797 (previous->name != NULL) &&
798 (sibling->name != NULL) &&
799 (previous->name[0] == sibling->name[0]) &&
800 (xmlStrEqual(previous->name, sibling->name))) {
801 if ((sel->value2 == NULL) ||
802 ((sibling->ns != NULL) &&
803 (xmlStrEqual(sel->value2,
804 sibling->ns->href))))
807 sibling = sibling->prev;
809 if (sibling == NULL) {
810 /* hum going backward in document order ... */
813 while (sibling != NULL) {
814 if (sibling == previous)
816 if ((sel->value2 == NULL) ||
817 ((sibling->ns != NULL) &&
818 (xmlStrEqual(sel->value2,
819 sibling->ns->href))))
821 sibling = sibling->next;
824 if (sibling != NULL) {
827 * If the node is in a Value Tree we need to
828 * save len, but cannot cache the node!
829 * (bugs 153137 and 158840)
831 if (node->doc != NULL) {
832 len = XSLT_RUNTIME_EXTRA(ctxt,
833 sel->lenExtra, ival);
835 XSLT_RUNTIME_EXTRA(ctxt,
836 sel->previousExtra, ptr) = node;
837 XSLT_RUNTIME_EXTRA(ctxt,
838 sel->indexExtra, ival) = pos;
846 * recompute the index
848 xmlNodePtr siblings = node->parent->children;
849 xmlNodePtr parent = node->parent;
851 while (siblings != NULL) {
852 if (siblings->type == XML_ELEMENT_NODE) {
853 if (siblings == node) {
856 } else if ((node->name != NULL) &&
857 (siblings->name != NULL) &&
858 (node->name[0] == siblings->name[0]) &&
859 (xmlStrEqual(node->name, siblings->name))) {
860 if ((sel->value2 == NULL) ||
861 ((siblings->ns != NULL) &&
862 (xmlStrEqual(sel->value2,
863 siblings->ns->href))))
867 siblings = siblings->next;
869 if ((parent == NULL) || (node->doc == NULL))
872 while (parent->parent != NULL)
873 parent = parent->parent;
874 if (((parent->type != XML_DOCUMENT_NODE) &&
875 (parent->type != XML_HTML_DOCUMENT_NODE)) ||
876 (parent != (xmlNodePtr) node->doc))
881 ctxt->xpathCtxt->contextSize = len;
882 ctxt->xpathCtxt->proximityPosition = pos;
884 * If the node is in a Value Tree we cannot
887 if ((!isRVT) && (node->doc != NULL) &&
889 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
891 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
893 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
897 } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
898 (node->type == XML_ELEMENT_NODE)) {
902 previous = (xmlNodePtr)
903 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
904 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
905 if ((previous != NULL) &&
906 (previous->parent == node->parent)) {
908 * just walk back to adjust the index
911 xmlNodePtr sibling = node;
913 while (sibling != NULL) {
914 if (sibling == previous)
916 if (sibling->type == XML_ELEMENT_NODE)
918 sibling = sibling->prev;
920 if (sibling == NULL) {
921 /* hum going backward in document order ... */
924 while (sibling != NULL) {
925 if (sibling == previous)
927 if (sibling->type == XML_ELEMENT_NODE)
929 sibling = sibling->next;
932 if (sibling != NULL) {
935 * If the node is in a Value Tree we cannot
938 if ((node->doc != NULL) && !isRVT) {
939 len = XSLT_RUNTIME_EXTRA(ctxt,
940 sel->lenExtra, ival);
941 XSLT_RUNTIME_EXTRA(ctxt,
942 sel->previousExtra, ptr) = node;
943 XSLT_RUNTIME_EXTRA(ctxt,
944 sel->indexExtra, ival) = pos;
950 * recompute the index
952 xmlNodePtr siblings = node->parent->children;
953 xmlNodePtr parent = node->parent;
955 while (siblings != NULL) {
956 if (siblings->type == XML_ELEMENT_NODE) {
958 if (siblings == node) {
962 siblings = siblings->next;
964 if ((parent == NULL) || (node->doc == NULL))
967 while (parent->parent != NULL)
968 parent = parent->parent;
969 if (((parent->type != XML_DOCUMENT_NODE) &&
970 (parent->type != XML_HTML_DOCUMENT_NODE)) ||
971 (parent != (xmlNodePtr) node->doc))
976 ctxt->xpathCtxt->contextSize = len;
977 ctxt->xpathCtxt->proximityPosition = pos;
979 * If the node is in a Value Tree we cannot
982 if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
983 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
985 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
987 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
992 oldNode = ctxt->node;
995 if (step->value == NULL)
997 if (step->comp == NULL)
1000 if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
1005 ctxt->xpathCtxt->contextSize = oldCS;
1006 ctxt->xpathCtxt->proximityPosition = oldCP;
1008 ctxt->node = oldNode;
1012 ctxt->xpathCtxt->contextSize = oldCS;
1013 ctxt->xpathCtxt->proximityPosition = oldCP;
1015 ctxt->node = oldNode;
1019 if (node->type != XML_PI_NODE)
1021 if (step->value != NULL) {
1022 if (!xmlStrEqual(step->value, node->name))
1026 case XSLT_OP_COMMENT:
1027 if (node->type != XML_COMMENT_NODE)
1031 if ((node->type != XML_TEXT_NODE) &&
1032 (node->type != XML_CDATA_SECTION_NODE))
1036 switch (node->type) {
1037 case XML_ELEMENT_NODE:
1038 case XML_CDATA_SECTION_NODE:
1040 case XML_COMMENT_NODE:
1050 if (states.states != NULL) {
1051 /* Free the rollback states */
1052 xmlFree(states.states);
1056 /* got an error try to rollback */
1057 if (states.states == NULL)
1059 if (states.nbstates <= 0) {
1060 xmlFree(states.states);
1064 i = states.states[states.nbstates].step;
1065 node = states.states[states.nbstates].node;
1067 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
1073 * xsltTestCompMatchList:
1074 * @ctxt: a XSLT process context
1076 * @comp: the precompiled pattern list
1078 * Test whether the node matches one of the patterns in the list
1080 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1083 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
1084 xsltCompMatchPtr comp) {
1087 if ((ctxt == NULL) || (node == NULL))
1089 while (comp != NULL) {
1090 ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1098 /************************************************************************
1100 * Dedicated parser for templates *
1102 ************************************************************************/
1104 #define CUR (*ctxt->cur)
1105 #define SKIP(val) ctxt->cur += (val)
1106 #define NXT(val) ctxt->cur[(val)]
1107 #define CUR_PTR ctxt->cur
1109 #define SKIP_BLANKS \
1110 while (IS_BLANK_CH(CUR)) NEXT
1112 #define CURRENT (*ctxt->cur)
1113 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1116 #define PUSH(op, val, val2) \
1117 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
1120 xsltSwapTopCompMatch(ctxt->comp);
1122 #define XSLT_ERROR(X) \
1123 { xsltError(ctxt, __FILE__, __LINE__, X); \
1124 ctxt->error = (X); return; }
1126 #define XSLT_ERROR0(X) \
1127 { xsltError(ctxt, __FILE__, __LINE__, X); \
1128 ctxt->error = (X); return(0); }
1132 * @ctxt: the XPath Parser context
1134 * Parse an XPath Litteral:
1136 * [29] Literal ::= '"' [^"]* '"'
1139 * Returns the Literal parsed or NULL
1143 xsltScanLiteral(xsltParserContextPtr ctxt) {
1144 const xmlChar *q, *cur;
1145 xmlChar *ret = NULL;
1152 val = xmlStringCurrentChar(NULL, cur, &len);
1153 while ((IS_CHAR(val)) && (val != '"')) {
1155 val = xmlStringCurrentChar(NULL, cur, &len);
1157 if (!IS_CHAR(val)) {
1161 ret = xmlStrndup(q, cur - q);
1165 } else if (CUR == '\'') {
1168 val = xmlStringCurrentChar(NULL, cur, &len);
1169 while ((IS_CHAR(val)) && (val != '\'')) {
1171 val = xmlStringCurrentChar(NULL, cur, &len);
1173 if (!IS_CHAR(val)) {
1177 ret = xmlStrndup(q, cur - q);
1182 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1191 * @ctxt: the XPath Parser context
1193 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
1194 * CombiningChar | Extender
1196 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
1198 * [6] Names ::= Name (S Name)*
1200 * Returns the Name parsed or NULL
1204 xsltScanName(xsltParserContextPtr ctxt) {
1205 const xmlChar *q, *cur;
1206 xmlChar *ret = NULL;
1212 val = xmlStringCurrentChar(NULL, cur, &len);
1213 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
1216 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1217 (val == '.') || (val == '-') ||
1219 (IS_COMBINING(val)) ||
1220 (IS_EXTENDER(val))) {
1222 val = xmlStringCurrentChar(NULL, cur, &len);
1224 ret = xmlStrndup(q, cur - q);
1231 * @ctxt: the XPath Parser context
1233 * Parses a non qualified name
1235 * Returns the Name parsed or NULL
1239 xsltScanNCName(xsltParserContextPtr ctxt) {
1240 const xmlChar *q, *cur;
1241 xmlChar *ret = NULL;
1247 val = xmlStringCurrentChar(NULL, cur, &len);
1248 if (!IS_LETTER(val) && (val != '_'))
1251 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1252 (val == '.') || (val == '-') ||
1254 (IS_COMBINING(val)) ||
1255 (IS_EXTENDER(val))) {
1257 val = xmlStringCurrentChar(NULL, cur, &len);
1259 ret = xmlStrndup(q, cur - q);
1266 * @ctxt: the XPath Parser context
1267 * @prefix: the place to store the prefix
1269 * Parse a qualified name
1271 * Returns the Name parsed or NULL
1275 xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) {
1276 xmlChar *ret = NULL;
1279 ret = xsltScanNCName(ctxt);
1283 ret = xsltScanNCName(ctxt);
1289 * xsltCompileIdKeyPattern:
1290 * @ctxt: the compilation context
1291 * @name: a preparsed name
1292 * @aid: whether id/key are allowed there
1294 * Compile the XSLT LocationIdKeyPattern
1295 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1296 * | 'key' '(' Literal ',' Literal ')'
1298 * also handle NodeType and PI from:
1300 * [7] NodeTest ::= NameTest
1301 * | NodeType '(' ')'
1302 * | 'processing-instruction' '(' Literal ')'
1305 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
1306 xmlChar *lit = NULL;
1307 xmlChar *lit2 = NULL;
1310 xsltTransformError(NULL, NULL, NULL,
1311 "xsltCompileIdKeyPattern : ( expected\n");
1315 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1318 lit = xsltScanLiteral(ctxt);
1323 xsltTransformError(NULL, NULL, NULL,
1324 "xsltCompileIdKeyPattern : ) expected\n");
1329 PUSH(XSLT_OP_ID, lit, NULL);
1330 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1333 lit = xsltScanLiteral(ctxt);
1338 xsltTransformError(NULL, NULL, NULL,
1339 "xsltCompileIdKeyPattern : , expected\n");
1345 lit2 = xsltScanLiteral(ctxt);
1350 xsltTransformError(NULL, NULL, NULL,
1351 "xsltCompileIdKeyPattern : ) expected\n");
1356 /* TODO: support namespace in keys */
1357 PUSH(XSLT_OP_KEY, lit, lit2);
1358 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1362 lit = xsltScanLiteral(ctxt);
1367 xsltTransformError(NULL, NULL, NULL,
1368 "xsltCompileIdKeyPattern : ) expected\n");
1374 PUSH(XSLT_OP_PI, lit, NULL);
1375 } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1379 xsltTransformError(NULL, NULL, NULL,
1380 "xsltCompileIdKeyPattern : ) expected\n");
1385 PUSH(XSLT_OP_TEXT, NULL, NULL);
1386 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1390 xsltTransformError(NULL, NULL, NULL,
1391 "xsltCompileIdKeyPattern : ) expected\n");
1396 PUSH(XSLT_OP_COMMENT, NULL, NULL);
1397 } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1401 xsltTransformError(NULL, NULL, NULL,
1402 "xsltCompileIdKeyPattern : ) expected\n");
1407 PUSH(XSLT_OP_NODE, NULL, NULL);
1409 xsltTransformError(NULL, NULL, NULL,
1410 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1414 xsltTransformError(NULL, NULL, NULL,
1415 "xsltCompileIdKeyPattern : node type\n");
1425 * xsltCompileStepPattern:
1426 * @ctxt: the compilation context
1427 * @token: a posible precompiled name
1429 * Compile the XSLT StepPattern and generates a precompiled
1430 * form suitable for fast matching.
1432 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1433 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1434 * | ('child' | 'attribute') '::'
1436 * [7] NodeTest ::= NameTest
1437 * | NodeType '(' ')'
1438 * | 'processing-instruction' '(' Literal ')'
1439 * [8] Predicate ::= '[' PredicateExpr ']'
1440 * [9] PredicateExpr ::= Expr
1441 * [13] AbbreviatedAxisSpecifier ::= '@'?
1442 * [37] NameTest ::= '*' | NCName ':' '*' | QName
1446 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1447 xmlChar *name = NULL;
1448 const xmlChar *URI = NULL;
1449 xmlChar *URL = NULL;
1453 if ((token == NULL) && (CUR == '@')) {
1454 xmlChar *prefix = NULL;
1459 PUSH(XSLT_OP_ATTR, NULL, NULL);
1460 goto parse_predicate;
1462 token = xsltScanQName(ctxt, &prefix);
1463 if (prefix != NULL) {
1466 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1468 xsltTransformError(NULL, NULL, NULL,
1469 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1472 URL = xmlStrdup(ns->href);
1476 if (token == NULL) {
1479 PUSH(XSLT_OP_ATTR, NULL, URL);
1482 xsltTransformError(NULL, NULL, NULL,
1483 "xsltCompileStepPattern : Name expected\n");
1487 PUSH(XSLT_OP_ATTR, token, URL);
1488 goto parse_predicate;
1491 token = xsltScanName(ctxt);
1492 if (token == NULL) {
1495 PUSH(XSLT_OP_ALL, token, NULL);
1496 goto parse_predicate;
1498 xsltTransformError(NULL, NULL, NULL,
1499 "xsltCompileStepPattern : Name expected\n");
1508 xsltCompileIdKeyPattern(ctxt, token, 0);
1511 } else if (CUR == ':') {
1514 xmlChar *prefix = token;
1518 * This is a namespace match
1520 token = xsltScanName(ctxt);
1521 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1523 xsltTransformError(NULL, NULL, NULL,
1524 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1529 URL = xmlStrdup(ns->href);
1532 if (token == NULL) {
1535 PUSH(XSLT_OP_NS, URL, NULL);
1537 xsltTransformError(NULL, NULL, NULL,
1538 "xsltCompileStepPattern : Name expected\n");
1543 PUSH(XSLT_OP_ELEM, token, URL);
1547 if (xmlStrEqual(token, (const xmlChar *) "child")) {
1549 token = xsltScanName(ctxt);
1550 if (token == NULL) {
1553 PUSH(XSLT_OP_ALL, token, NULL);
1554 goto parse_predicate;
1556 xsltTransformError(NULL, NULL, NULL,
1557 "xsltCompileStepPattern : QName expected\n");
1562 URI = xsltGetQNameURI(ctxt->elem, &token);
1563 if (token == NULL) {
1567 name = xmlStrdup(token);
1569 URL = xmlStrdup(URI);
1571 PUSH(XSLT_OP_CHILD, name, URL);
1572 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1574 token = xsltScanName(ctxt);
1575 if (token == NULL) {
1576 xsltTransformError(NULL, NULL, NULL,
1577 "xsltCompileStepPattern : QName expected\n");
1581 URI = xsltGetQNameURI(ctxt->elem, &token);
1582 if (token == NULL) {
1586 name = xmlStrdup(token);
1588 URL = xmlStrdup(URI);
1590 PUSH(XSLT_OP_ATTR, name, URL);
1592 xsltTransformError(NULL, NULL, NULL,
1593 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1599 } else if (CUR == '*') {
1601 PUSH(XSLT_OP_ALL, token, NULL);
1603 URI = xsltGetQNameURI(ctxt->elem, &token);
1604 if (token == NULL) {
1609 URL = xmlStrdup(URI);
1610 PUSH(XSLT_OP_ELEM, token, URL);
1615 while (CUR == '[') {
1617 xmlChar *ret = NULL;
1623 /* Skip over nested predicates */
1626 else if (CUR == ']') {
1630 } else if (CUR == '"') {
1632 while ((CUR != 0) && (CUR != '"'))
1634 } else if (CUR == '\'') {
1636 while ((CUR != 0) && (CUR != '\''))
1642 xsltTransformError(NULL, NULL, NULL,
1643 "xsltCompileStepPattern : ']' expected\n");
1647 ret = xmlStrndup(q, CUR_PTR - q);
1648 PUSH(XSLT_OP_PREDICATE, ret, NULL);
1649 /* push the predicate lower than local test */
1663 * xsltCompileRelativePathPattern:
1664 * @comp: the compilation context
1665 * @token: a posible precompiled name
1667 * Compile the XSLT RelativePathPattern and generates a precompiled
1668 * form suitable for fast matching.
1670 * [4] RelativePathPattern ::= StepPattern
1671 * | RelativePathPattern '/' StepPattern
1672 * | RelativePathPattern '//' StepPattern
1675 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1676 xsltCompileStepPattern(ctxt, token);
1680 while ((CUR != 0) && (CUR != '|')) {
1681 if ((CUR == '/') && (NXT(1) == '/')) {
1682 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1686 xsltCompileStepPattern(ctxt, NULL);
1687 } else if (CUR == '/') {
1688 PUSH(XSLT_OP_PARENT, NULL, NULL);
1691 if ((CUR != 0) && (CUR != '|')) {
1692 xsltCompileRelativePathPattern(ctxt, NULL);
1706 * xsltCompileLocationPathPattern:
1707 * @ctxt: the compilation context
1709 * Compile the XSLT LocationPathPattern and generates a precompiled
1710 * form suitable for fast matching.
1712 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1713 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1714 * | '//'? RelativePathPattern
1717 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
1719 if ((CUR == '/') && (NXT(1) == '/')) {
1721 * since we reverse the query
1722 * a leading // can be safely ignored
1726 ctxt->comp->priority = 0.5; /* '//' means not 0 priority */
1727 xsltCompileRelativePathPattern(ctxt, NULL);
1728 } else if (CUR == '/') {
1730 * We need to find root as the parent
1734 PUSH(XSLT_OP_ROOT, NULL, NULL);
1735 if ((CUR != 0) && (CUR != '|')) {
1736 PUSH(XSLT_OP_PARENT, NULL, NULL);
1737 xsltCompileRelativePathPattern(ctxt, NULL);
1739 } else if (CUR == '*') {
1740 xsltCompileRelativePathPattern(ctxt, NULL);
1741 } else if (CUR == '@') {
1742 xsltCompileRelativePathPattern(ctxt, NULL);
1745 name = xsltScanName(ctxt);
1747 xsltTransformError(NULL, NULL, NULL,
1748 "xsltCompileLocationPathPattern : Name expected\n");
1753 if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1754 xsltCompileIdKeyPattern(ctxt, name, 1);
1755 if ((CUR == '/') && (NXT(1) == '/')) {
1756 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1760 xsltCompileRelativePathPattern(ctxt, NULL);
1761 } else if (CUR == '/') {
1762 PUSH(XSLT_OP_PARENT, NULL, NULL);
1765 xsltCompileRelativePathPattern(ctxt, NULL);
1769 xsltCompileRelativePathPattern(ctxt, name);
1776 * xsltCompilePattern:
1777 * @pattern: an XSLT pattern
1778 * @doc: the containing document
1779 * @node: the containing element
1780 * @style: the stylesheet
1781 * @runtime: the transformation context, if done at run-time
1783 * Compile the XSLT pattern and generates a list of precompiled form suitable
1784 * for fast matching.
1786 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1788 * Returns the generated pattern list or NULL in case of failure
1792 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
1793 xmlNodePtr node, xsltStylesheetPtr style,
1794 xsltTransformContextPtr runtime) {
1795 xsltParserContextPtr ctxt = NULL;
1796 xsltCompMatchPtr element, first = NULL, previous = NULL;
1797 int current, start, end, level, j;
1799 if (pattern == NULL) {
1800 xsltTransformError(NULL, NULL, node,
1801 "xsltCompilePattern : NULL pattern\n");
1805 ctxt = xsltNewParserContext(style, runtime);
1811 while (pattern[current] != 0) {
1813 while (IS_BLANK_CH(pattern[current]))
1817 while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1818 if (pattern[end] == '[')
1820 else if (pattern[end] == ']')
1822 else if (pattern[end] == '\'') {
1824 while ((pattern[end] != 0) && (pattern[end] != '\''))
1826 } else if (pattern[end] == '"') {
1828 while ((pattern[end] != 0) && (pattern[end] != '"'))
1833 if (current == end) {
1834 xsltTransformError(NULL, NULL, node,
1835 "xsltCompilePattern : NULL pattern\n");
1838 element = xsltNewCompMatch();
1839 if (element == NULL) {
1844 else if (previous != NULL)
1845 previous->next = element;
1848 ctxt->comp = element;
1849 ctxt->base = xmlStrndup(&pattern[start], end - start);
1850 if (ctxt->base == NULL)
1852 ctxt->cur = &(ctxt->base)[current - start];
1853 element->pattern = ctxt->base;
1854 element->nsList = xmlGetNsList(doc, node);
1856 if (element->nsList != NULL) {
1857 while (element->nsList[j] != NULL)
1863 #ifdef WITH_XSLT_DEBUG_PATTERN
1864 xsltGenericDebug(xsltGenericDebugContext,
1865 "xsltCompilePattern : parsing '%s'\n",
1869 Preset default priority to be zero.
1870 This may be changed by xsltCompileLocationPathPattern.
1872 element->priority = 0;
1873 xsltCompileLocationPathPattern(ctxt);
1875 xsltTransformError(NULL, style, node,
1876 "xsltCompilePattern : failed to compile '%s'\n",
1878 if (style != NULL) style->errors++;
1883 * Reverse for faster interpretation.
1885 xsltReverseCompMatch(element);
1888 * Set-up the priority
1890 if (element->priority == 0) { /* if not yet determined */
1891 if (((element->steps[0].op == XSLT_OP_ELEM) ||
1892 (element->steps[0].op == XSLT_OP_ATTR) ||
1893 (element->steps[0].op == XSLT_OP_PI)) &&
1894 (element->steps[0].value != NULL) &&
1895 (element->steps[1].op == XSLT_OP_END)) {
1896 ; /* previously preset */
1897 } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1898 (element->steps[0].value2 != NULL) &&
1899 (element->steps[1].op == XSLT_OP_END)) {
1900 element->priority = -0.25;
1901 } else if ((element->steps[0].op == XSLT_OP_NS) &&
1902 (element->steps[0].value != NULL) &&
1903 (element->steps[1].op == XSLT_OP_END)) {
1904 element->priority = -0.25;
1905 } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1906 (element->steps[0].value == NULL) &&
1907 (element->steps[0].value2 == NULL) &&
1908 (element->steps[1].op == XSLT_OP_END)) {
1909 element->priority = -0.5;
1910 } else if (((element->steps[0].op == XSLT_OP_PI) ||
1911 (element->steps[0].op == XSLT_OP_TEXT) ||
1912 (element->steps[0].op == XSLT_OP_ALL) ||
1913 (element->steps[0].op == XSLT_OP_NODE) ||
1914 (element->steps[0].op == XSLT_OP_COMMENT)) &&
1915 (element->steps[1].op == XSLT_OP_END)) {
1916 element->priority = -0.5;
1918 element->priority = 0.5;
1921 #ifdef WITH_XSLT_DEBUG_PATTERN
1922 xsltGenericDebug(xsltGenericDebugContext,
1923 "xsltCompilePattern : parsed %s, default priority %f\n",
1924 element->pattern, element->priority);
1926 if (pattern[end] == '|')
1931 xsltTransformError(NULL, style, node,
1932 "xsltCompilePattern : NULL pattern\n");
1933 if (style != NULL) style->errors++;
1937 xsltFreeParserContext(ctxt);
1942 xsltFreeParserContext(ctxt);
1944 xsltFreeCompMatchList(first);
1948 /************************************************************************
1950 * Module interfaces *
1952 ************************************************************************/
1956 * @style: an XSLT stylesheet
1957 * @cur: an XSLT template
1958 * @mode: the mode name or NULL
1959 * @modeURI: the mode URI or NULL
1961 * Register the XSLT pattern associated to @cur
1963 * Returns -1 in case of error, 0 otherwise
1966 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
1967 const xmlChar *mode, const xmlChar *modeURI) {
1968 xsltCompMatchPtr pat, list, *top = NULL, next;
1969 const xmlChar *name = NULL;
1970 float priority; /* the priority */
1972 if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
1975 priority = cur->priority;
1976 pat = xsltCompilePattern(cur->match, style->doc, cur->elem, style, NULL);
1982 pat->template = cur;
1984 pat->mode = xmlDictLookup(style->dict, mode, -1);
1985 if (modeURI != NULL)
1986 pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
1987 if (priority != XSLT_PAT_NO_PRIORITY)
1988 pat->priority = priority;
1991 * insert it in the hash table list corresponding to its lookup name
1993 switch (pat->steps[0].op) {
1995 if (pat->steps[0].value != NULL)
1996 name = pat->steps[0].value;
1998 top = (xsltCompMatchPtr *) &(style->attrMatch);
2001 case XSLT_OP_PARENT:
2002 case XSLT_OP_ANCESTOR:
2003 top = (xsltCompMatchPtr *) &(style->elemMatch);
2006 top = (xsltCompMatchPtr *) &(style->rootMatch);
2009 top = (xsltCompMatchPtr *) &(style->keyMatch);
2012 /* TODO optimize ID !!! */
2015 top = (xsltCompMatchPtr *) &(style->elemMatch);
2018 case XSLT_OP_PREDICATE:
2019 xsltTransformError(NULL, style, NULL,
2020 "xsltAddTemplate: invalid compiled pattern\n");
2021 xsltFreeCompMatch(pat);
2024 * TODO: some flags at the top level about type based patterns
2025 * would be faster than inclusion in the hash table.
2028 if (pat->steps[0].value != NULL)
2029 name = pat->steps[0].value;
2031 top = (xsltCompMatchPtr *) &(style->piMatch);
2033 case XSLT_OP_COMMENT:
2034 top = (xsltCompMatchPtr *) &(style->commentMatch);
2037 top = (xsltCompMatchPtr *) &(style->textMatch);
2041 if (pat->steps[0].value != NULL)
2042 name = pat->steps[0].value;
2044 top = (xsltCompMatchPtr *) &(style->elemMatch);
2048 if (style->templatesHash == NULL) {
2049 style->templatesHash = xmlHashCreate(1024);
2050 if (style->templatesHash == NULL) {
2051 xsltFreeCompMatch(pat);
2054 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
2056 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
2057 name, mode, modeURI);
2059 xmlHashAddEntry3(style->templatesHash, name,
2060 mode, modeURI, pat);
2063 * Note '<=' since one must choose among the matching
2064 * template rules that are left, the one that occurs
2065 * last in the stylesheet
2067 if (list->priority <= pat->priority) {
2069 xmlHashUpdateEntry3(style->templatesHash, name,
2070 mode, modeURI, pat, NULL);
2072 while (list->next != NULL) {
2073 if (list->next->priority <= pat->priority)
2077 pat->next = list->next;
2082 } else if (top != NULL) {
2087 } else if (list->priority <= pat->priority) {
2091 while (list->next != NULL) {
2092 if (list->next->priority <= pat->priority)
2096 pat->next = list->next;
2100 xsltTransformError(NULL, style, NULL,
2101 "xsltAddTemplate: invalid compiled pattern\n");
2102 xsltFreeCompMatch(pat);
2105 #ifdef WITH_XSLT_DEBUG_PATTERN
2107 xsltGenericDebug(xsltGenericDebugContext,
2108 "added pattern : '%s' mode '%s' priority %f\n",
2109 pat->pattern, pat->mode, pat->priority);
2111 xsltGenericDebug(xsltGenericDebugContext,
2112 "added pattern : '%s' priority %f\n",
2113 pat->pattern, pat->priority);
2123 * @ctxt: a XSLT process context
2124 * @node: the node being processed
2125 * @style: the current style
2127 * Finds the template applying to this node, if @style is non-NULL
2128 * it means one needs to look for the next imported template in scope.
2130 * Returns the xsltTemplatePtr or NULL if not found
2133 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2134 xsltStylesheetPtr style) {
2135 xsltStylesheetPtr curstyle;
2136 xsltTemplatePtr ret = NULL;
2137 const xmlChar *name = NULL;
2138 xsltCompMatchPtr list = NULL;
2142 if ((ctxt == NULL) || (node == NULL))
2145 if (style == NULL) {
2146 curstyle = ctxt->style;
2148 curstyle = xsltNextImport(style);
2151 while ((curstyle != NULL) && (curstyle != style)) {
2152 priority = XSLT_PAT_NO_PRIORITY;
2153 /* TODO : handle IDs/keys here ! */
2154 if (curstyle->templatesHash != NULL) {
2156 * Use the top name as selector
2158 switch (node->type) {
2159 case XML_ELEMENT_NODE:
2160 if (node->name[0] == ' ')
2162 case XML_ATTRIBUTE_NODE:
2166 case XML_DOCUMENT_NODE:
2167 case XML_HTML_DOCUMENT_NODE:
2169 case XML_CDATA_SECTION_NODE:
2170 case XML_COMMENT_NODE:
2171 case XML_ENTITY_REF_NODE:
2172 case XML_ENTITY_NODE:
2173 case XML_DOCUMENT_TYPE_NODE:
2174 case XML_DOCUMENT_FRAG_NODE:
2175 case XML_NOTATION_NODE:
2177 case XML_ELEMENT_DECL:
2178 case XML_ATTRIBUTE_DECL:
2179 case XML_ENTITY_DECL:
2180 case XML_NAMESPACE_DECL:
2181 case XML_XINCLUDE_START:
2182 case XML_XINCLUDE_END:
2191 * find the list of applicable expressions based on the name
2193 list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2194 name, ctxt->mode, ctxt->modeURI);
2197 while (list != NULL) {
2198 if (xsltTestCompMatch(ctxt, list, node,
2199 ctxt->mode, ctxt->modeURI)) {
2200 ret = list->template;
2201 priority = list->priority;
2209 * find alternate generic matches
2211 switch (node->type) {
2212 case XML_ELEMENT_NODE:
2213 if (node->name[0] == ' ')
2214 list = curstyle->rootMatch;
2216 list = curstyle->elemMatch;
2217 if (node->psvi != NULL) keyed = 1;
2219 case XML_ATTRIBUTE_NODE: {
2222 list = curstyle->attrMatch;
2223 attr = (xmlAttrPtr) node;
2224 if (attr->psvi != NULL) keyed = 1;
2228 list = curstyle->piMatch;
2229 if (node->psvi != NULL) keyed = 1;
2231 case XML_DOCUMENT_NODE:
2232 case XML_HTML_DOCUMENT_NODE: {
2235 list = curstyle->rootMatch;
2236 doc = (xmlDocPtr) node;
2237 if (doc->psvi != NULL) keyed = 1;
2241 case XML_CDATA_SECTION_NODE:
2242 list = curstyle->textMatch;
2243 if (node->psvi != NULL) keyed = 1;
2245 case XML_COMMENT_NODE:
2246 list = curstyle->commentMatch;
2247 if (node->psvi != NULL) keyed = 1;
2249 case XML_ENTITY_REF_NODE:
2250 case XML_ENTITY_NODE:
2251 case XML_DOCUMENT_TYPE_NODE:
2252 case XML_DOCUMENT_FRAG_NODE:
2253 case XML_NOTATION_NODE:
2255 case XML_ELEMENT_DECL:
2256 case XML_ATTRIBUTE_DECL:
2257 case XML_ENTITY_DECL:
2258 case XML_NAMESPACE_DECL:
2259 case XML_XINCLUDE_START:
2260 case XML_XINCLUDE_END:
2265 while ((list != NULL) &&
2266 ((ret == NULL) || (list->priority > priority))) {
2267 if (xsltTestCompMatch(ctxt, list, node,
2268 ctxt->mode, ctxt->modeURI)) {
2269 ret = list->template;
2270 priority = list->priority;
2276 * Some of the tests for elements can also apply to documents
2278 if ((node->type == XML_DOCUMENT_NODE) ||
2279 (node->type == XML_HTML_DOCUMENT_NODE) ||
2280 (node->type == XML_TEXT_NODE)) {
2281 list = curstyle->elemMatch;
2282 while ((list != NULL) &&
2283 ((ret == NULL) || (list->priority > priority))) {
2284 if (xsltTestCompMatch(ctxt, list, node,
2285 ctxt->mode, ctxt->modeURI)) {
2286 ret = list->template;
2287 priority = list->priority;
2292 } else if ((node->type == XML_PI_NODE) ||
2293 (node->type == XML_COMMENT_NODE)) {
2294 list = curstyle->elemMatch;
2295 while ((list != NULL) &&
2296 ((ret == NULL) || (list->priority > priority))) {
2297 if (xsltTestCompMatch(ctxt, list, node,
2298 ctxt->mode, ctxt->modeURI)) {
2299 ret = list->template;
2300 priority = list->priority;
2308 list = curstyle->keyMatch;
2309 while ((list != NULL) &&
2310 ((ret == NULL) || (list->priority > priority))) {
2311 if (xsltTestCompMatch(ctxt, list, node,
2312 ctxt->mode, ctxt->modeURI)) {
2313 ret = list->template;
2314 priority = list->priority;
2324 * Cycle on next curstylesheet import.
2326 curstyle = xsltNextImport(curstyle);
2332 * xsltCleanupTemplates:
2333 * @style: an XSLT stylesheet
2335 * Cleanup the state of the templates used by the stylesheet and
2336 * the ones it imports.
2339 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2343 * xsltFreeTemplateHashes:
2344 * @style: an XSLT stylesheet
2346 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2349 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2350 if (style->templatesHash != NULL)
2351 xmlHashFree((xmlHashTablePtr) style->templatesHash,
2352 (xmlHashDeallocator) xsltFreeCompMatchList);
2353 if (style->rootMatch != NULL)
2354 xsltFreeCompMatchList(style->rootMatch);
2355 if (style->keyMatch != NULL)
2356 xsltFreeCompMatchList(style->keyMatch);
2357 if (style->elemMatch != NULL)
2358 xsltFreeCompMatchList(style->elemMatch);
2359 if (style->attrMatch != NULL)
2360 xsltFreeCompMatchList(style->attrMatch);
2361 if (style->parentMatch != NULL)
2362 xsltFreeCompMatchList(style->parentMatch);
2363 if (style->textMatch != NULL)
2364 xsltFreeCompMatchList(style->textMatch);
2365 if (style->piMatch != NULL)
2366 xsltFreeCompMatchList(style->piMatch);
2367 if (style->commentMatch != NULL)
2368 xsltFreeCompMatchList(style->commentMatch);