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);
1790 if ((CUR
== '/') && (NXT(1) == '/')) {
1791 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1795 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1796 } else if (CUR
== '/') {
1797 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1800 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1804 xsltCompileRelativePathPattern(ctxt
, name
, novar
);
1811 * xsltCompilePatternInternal:
1812 * @pattern: an XSLT pattern
1813 * @doc: the containing document
1814 * @node: the containing element
1815 * @style: the stylesheet
1816 * @runtime: the transformation context, if done at run-time
1817 * @novar: flag to prohibit xslt variables
1819 * Compile the XSLT pattern and generates a list of precompiled form suitable
1820 * for fast matching.
1822 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1824 * Returns the generated pattern list or NULL in case of failure
1827 static xsltCompMatchPtr
1828 xsltCompilePatternInternal(const xmlChar
*pattern
, xmlDocPtr doc
,
1829 xmlNodePtr node
, xsltStylesheetPtr style
,
1830 xsltTransformContextPtr runtime
, int novar
) {
1831 xsltParserContextPtr ctxt
= NULL
;
1832 xsltCompMatchPtr element
, first
= NULL
, previous
= NULL
;
1833 int current
, start
, end
, level
, j
;
1835 if (pattern
== NULL
) {
1836 xsltTransformError(NULL
, NULL
, node
,
1837 "xsltCompilePattern : NULL pattern\n");
1841 ctxt
= xsltNewParserContext(style
, runtime
);
1847 while (pattern
[current
] != 0) {
1849 while (IS_BLANK_CH(pattern
[current
]))
1853 while ((pattern
[end
] != 0) && ((pattern
[end
] != '|') || (level
!= 0))) {
1854 if (pattern
[end
] == '[')
1856 else if (pattern
[end
] == ']')
1858 else if (pattern
[end
] == '\'') {
1860 while ((pattern
[end
] != 0) && (pattern
[end
] != '\''))
1862 } else if (pattern
[end
] == '"') {
1864 while ((pattern
[end
] != 0) && (pattern
[end
] != '"'))
1869 if (current
== end
) {
1870 xsltTransformError(NULL
, NULL
, node
,
1871 "xsltCompilePattern : NULL pattern\n");
1874 element
= xsltNewCompMatch();
1875 if (element
== NULL
) {
1880 else if (previous
!= NULL
)
1881 previous
->next
= element
;
1884 ctxt
->comp
= element
;
1885 ctxt
->base
= xmlStrndup(&pattern
[start
], end
- start
);
1886 if (ctxt
->base
== NULL
)
1888 ctxt
->cur
= &(ctxt
->base
)[current
- start
];
1889 element
->pattern
= ctxt
->base
;
1890 element
->nsList
= xmlGetNsList(doc
, node
);
1892 if (element
->nsList
!= NULL
) {
1893 while (element
->nsList
[j
] != NULL
)
1899 #ifdef WITH_XSLT_DEBUG_PATTERN
1900 xsltGenericDebug(xsltGenericDebugContext
,
1901 "xsltCompilePattern : parsing '%s'\n",
1905 Preset default priority to be zero.
1906 This may be changed by xsltCompileLocationPathPattern.
1908 element
->priority
= 0;
1909 xsltCompileLocationPathPattern(ctxt
, novar
);
1911 xsltTransformError(NULL
, style
, node
,
1912 "xsltCompilePattern : failed to compile '%s'\n",
1914 if (style
!= NULL
) style
->errors
++;
1919 * Reverse for faster interpretation.
1921 xsltReverseCompMatch(ctxt
, element
);
1924 * Set-up the priority
1926 if (element
->priority
== 0) { /* if not yet determined */
1927 if (((element
->steps
[0].op
== XSLT_OP_ELEM
) ||
1928 (element
->steps
[0].op
== XSLT_OP_ATTR
) ||
1929 (element
->steps
[0].op
== XSLT_OP_PI
)) &&
1930 (element
->steps
[0].value
!= NULL
) &&
1931 (element
->steps
[1].op
== XSLT_OP_END
)) {
1932 ; /* previously preset */
1933 } else if ((element
->steps
[0].op
== XSLT_OP_ATTR
) &&
1934 (element
->steps
[0].value2
!= NULL
) &&
1935 (element
->steps
[1].op
== XSLT_OP_END
)) {
1936 element
->priority
= -0.25;
1937 } else if ((element
->steps
[0].op
== XSLT_OP_NS
) &&
1938 (element
->steps
[0].value
!= NULL
) &&
1939 (element
->steps
[1].op
== XSLT_OP_END
)) {
1940 element
->priority
= -0.25;
1941 } else if ((element
->steps
[0].op
== XSLT_OP_ATTR
) &&
1942 (element
->steps
[0].value
== NULL
) &&
1943 (element
->steps
[0].value2
== NULL
) &&
1944 (element
->steps
[1].op
== XSLT_OP_END
)) {
1945 element
->priority
= -0.5;
1946 } else if (((element
->steps
[0].op
== XSLT_OP_PI
) ||
1947 (element
->steps
[0].op
== XSLT_OP_TEXT
) ||
1948 (element
->steps
[0].op
== XSLT_OP_ALL
) ||
1949 (element
->steps
[0].op
== XSLT_OP_NODE
) ||
1950 (element
->steps
[0].op
== XSLT_OP_COMMENT
)) &&
1951 (element
->steps
[1].op
== XSLT_OP_END
)) {
1952 element
->priority
= -0.5;
1954 element
->priority
= 0.5;
1957 #ifdef WITH_XSLT_DEBUG_PATTERN
1958 xsltGenericDebug(xsltGenericDebugContext
,
1959 "xsltCompilePattern : parsed %s, default priority %f\n",
1960 element
->pattern
, element
->priority
);
1962 if (pattern
[end
] == '|')
1967 xsltTransformError(NULL
, style
, node
,
1968 "xsltCompilePattern : NULL pattern\n");
1969 if (style
!= NULL
) style
->errors
++;
1973 xsltFreeParserContext(ctxt
);
1978 xsltFreeParserContext(ctxt
);
1980 xsltFreeCompMatchList(first
);
1985 * xsltCompilePattern:
1986 * @pattern: an XSLT pattern
1987 * @doc: the containing document
1988 * @node: the containing element
1989 * @style: the stylesheet
1990 * @runtime: the transformation context, if done at run-time
1992 * Compile the XSLT pattern and generates a list of precompiled form suitable
1993 * for fast matching.
1995 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1997 * Returns the generated pattern list or NULL in case of failure
2001 xsltCompilePattern(const xmlChar
*pattern
, xmlDocPtr doc
,
2002 xmlNodePtr node
, xsltStylesheetPtr style
,
2003 xsltTransformContextPtr runtime
) {
2004 return (xsltCompilePatternInternal(pattern
, doc
, node
, style
, runtime
, 0));
2007 /************************************************************************
2009 * Module interfaces *
2011 ************************************************************************/
2015 * @style: an XSLT stylesheet
2016 * @cur: an XSLT template
2017 * @mode: the mode name or NULL
2018 * @modeURI: the mode URI or NULL
2020 * Register the XSLT pattern associated to @cur
2022 * Returns -1 in case of error, 0 otherwise
2025 xsltAddTemplate(xsltStylesheetPtr style
, xsltTemplatePtr cur
,
2026 const xmlChar
*mode
, const xmlChar
*modeURI
) {
2027 xsltCompMatchPtr pat
, list
, next
;
2029 * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2030 * avoids gcc 'type-punned pointer' warning.
2033 const xmlChar
*name
= NULL
;
2034 float priority
; /* the priority */
2036 if ((style
== NULL
) || (cur
== NULL
) || (cur
->match
== NULL
))
2039 priority
= cur
->priority
;
2040 pat
= xsltCompilePatternInternal(cur
->match
, style
->doc
, cur
->elem
,
2049 pat
->template = cur
;
2051 pat
->mode
= xmlDictLookup(style
->dict
, mode
, -1);
2052 if (modeURI
!= NULL
)
2053 pat
->modeURI
= xmlDictLookup(style
->dict
, modeURI
, -1);
2054 if (priority
!= XSLT_PAT_NO_PRIORITY
)
2055 pat
->priority
= priority
;
2058 * insert it in the hash table list corresponding to its lookup name
2060 switch (pat
->steps
[0].op
) {
2062 if (pat
->steps
[0].value
!= NULL
)
2063 name
= pat
->steps
[0].value
;
2065 top
= &(style
->attrMatch
);
2067 case XSLT_OP_PARENT
:
2068 case XSLT_OP_ANCESTOR
:
2069 top
= &(style
->elemMatch
);
2072 top
= &(style
->rootMatch
);
2075 top
= &(style
->keyMatch
);
2078 /* TODO optimize ID !!! */
2081 top
= &(style
->elemMatch
);
2084 case XSLT_OP_PREDICATE
:
2085 xsltTransformError(NULL
, style
, NULL
,
2086 "xsltAddTemplate: invalid compiled pattern\n");
2087 xsltFreeCompMatch(pat
);
2090 * TODO: some flags at the top level about type based patterns
2091 * would be faster than inclusion in the hash table.
2094 if (pat
->steps
[0].value
!= NULL
)
2095 name
= pat
->steps
[0].value
;
2097 top
= &(style
->piMatch
);
2099 case XSLT_OP_COMMENT
:
2100 top
= &(style
->commentMatch
);
2103 top
= &(style
->textMatch
);
2107 if (pat
->steps
[0].value
!= NULL
)
2108 name
= pat
->steps
[0].value
;
2110 top
= &(style
->elemMatch
);
2114 if (style
->templatesHash
== NULL
) {
2115 style
->templatesHash
= xmlHashCreate(1024);
2116 if (style
->templatesHash
== NULL
) {
2117 xsltFreeCompMatch(pat
);
2120 xmlHashAddEntry3(style
->templatesHash
, name
, mode
, modeURI
, pat
);
2122 list
= (xsltCompMatchPtr
) xmlHashLookup3(style
->templatesHash
,
2123 name
, mode
, modeURI
);
2125 xmlHashAddEntry3(style
->templatesHash
, name
,
2126 mode
, modeURI
, pat
);
2129 * Note '<=' since one must choose among the matching
2130 * template rules that are left, the one that occurs
2131 * last in the stylesheet
2133 if (list
->priority
<= pat
->priority
) {
2135 xmlHashUpdateEntry3(style
->templatesHash
, name
,
2136 mode
, modeURI
, pat
, NULL
);
2138 while (list
->next
!= NULL
) {
2139 if (list
->next
->priority
<= pat
->priority
)
2143 pat
->next
= list
->next
;
2148 } else if (top
!= NULL
) {
2153 } else if (list
->priority
<= pat
->priority
) {
2157 while (list
->next
!= NULL
) {
2158 if (list
->next
->priority
<= pat
->priority
)
2162 pat
->next
= list
->next
;
2166 xsltTransformError(NULL
, style
, NULL
,
2167 "xsltAddTemplate: invalid compiled pattern\n");
2168 xsltFreeCompMatch(pat
);
2171 #ifdef WITH_XSLT_DEBUG_PATTERN
2173 xsltGenericDebug(xsltGenericDebugContext
,
2174 "added pattern : '%s' mode '%s' priority %f\n",
2175 pat
->pattern
, pat
->mode
, pat
->priority
);
2177 xsltGenericDebug(xsltGenericDebugContext
,
2178 "added pattern : '%s' priority %f\n",
2179 pat
->pattern
, pat
->priority
);
2188 xsltComputeAllKeys(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
)
2190 if ((ctxt
== NULL
) || (contextNode
== NULL
)) {
2191 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2192 "Internal error in xsltComputeAllKeys(): "
2193 "Bad arguments.\n");
2197 if (ctxt
->document
== NULL
) {
2199 * The document info will only be NULL if we have a RTF.
2201 if (contextNode
->doc
->_private
!= NULL
)
2202 goto doc_info_mismatch
;
2204 * On-demand creation of the document info (needed for keys).
2206 ctxt
->document
= xsltNewDocument(ctxt
, contextNode
->doc
);
2207 if (ctxt
->document
== NULL
)
2210 return xsltInitAllDocKeys(ctxt
);
2213 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2214 "Internal error in xsltComputeAllKeys(): "
2215 "The context's document info doesn't match the "
2216 "document info of the current result tree.\n");
2217 ctxt
->state
= XSLT_STATE_STOPPED
;
2223 * @ctxt: a XSLT process context
2224 * @node: the node being processed
2225 * @style: the current style
2227 * Finds the template applying to this node, if @style is non-NULL
2228 * it means one needs to look for the next imported template in scope.
2230 * Returns the xsltTemplatePtr or NULL if not found
2233 xsltGetTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
2234 xsltStylesheetPtr style
)
2236 xsltStylesheetPtr curstyle
;
2237 xsltTemplatePtr ret
= NULL
;
2238 const xmlChar
*name
= NULL
;
2239 xsltCompMatchPtr list
= NULL
;
2243 if ((ctxt
== NULL
) || (node
== NULL
))
2246 if (style
== NULL
) {
2247 curstyle
= ctxt
->style
;
2249 curstyle
= xsltNextImport(style
);
2252 while ((curstyle
!= NULL
) && (curstyle
!= style
)) {
2253 priority
= XSLT_PAT_NO_PRIORITY
;
2254 /* TODO : handle IDs/keys here ! */
2255 if (curstyle
->templatesHash
!= NULL
) {
2257 * Use the top name as selector
2259 switch (node
->type
) {
2260 case XML_ELEMENT_NODE
:
2261 if (node
->name
[0] == ' ')
2263 case XML_ATTRIBUTE_NODE
:
2267 case XML_DOCUMENT_NODE
:
2268 case XML_HTML_DOCUMENT_NODE
:
2270 case XML_CDATA_SECTION_NODE
:
2271 case XML_COMMENT_NODE
:
2272 case XML_ENTITY_REF_NODE
:
2273 case XML_ENTITY_NODE
:
2274 case XML_DOCUMENT_TYPE_NODE
:
2275 case XML_DOCUMENT_FRAG_NODE
:
2276 case XML_NOTATION_NODE
:
2278 case XML_ELEMENT_DECL
:
2279 case XML_ATTRIBUTE_DECL
:
2280 case XML_ENTITY_DECL
:
2281 case XML_NAMESPACE_DECL
:
2282 case XML_XINCLUDE_START
:
2283 case XML_XINCLUDE_END
:
2292 * find the list of applicable expressions based on the name
2294 list
= (xsltCompMatchPtr
) xmlHashLookup3(curstyle
->templatesHash
,
2295 name
, ctxt
->mode
, ctxt
->modeURI
);
2298 while (list
!= NULL
) {
2299 if (xsltTestCompMatch(ctxt
, list
, node
,
2300 ctxt
->mode
, ctxt
->modeURI
)) {
2301 ret
= list
->template;
2302 priority
= list
->priority
;
2310 * find alternate generic matches
2312 switch (node
->type
) {
2313 case XML_ELEMENT_NODE
:
2314 if (node
->name
[0] == ' ')
2315 list
= curstyle
->rootMatch
;
2317 list
= curstyle
->elemMatch
;
2318 if (node
->psvi
!= NULL
) keyed
= 1;
2320 case XML_ATTRIBUTE_NODE
: {
2323 list
= curstyle
->attrMatch
;
2324 attr
= (xmlAttrPtr
) node
;
2325 if (attr
->psvi
!= NULL
) keyed
= 1;
2329 list
= curstyle
->piMatch
;
2330 if (node
->psvi
!= NULL
) keyed
= 1;
2332 case XML_DOCUMENT_NODE
:
2333 case XML_HTML_DOCUMENT_NODE
: {
2336 list
= curstyle
->rootMatch
;
2337 doc
= (xmlDocPtr
) node
;
2338 if (doc
->psvi
!= NULL
) keyed
= 1;
2342 case XML_CDATA_SECTION_NODE
:
2343 list
= curstyle
->textMatch
;
2344 if (node
->psvi
!= NULL
) keyed
= 1;
2346 case XML_COMMENT_NODE
:
2347 list
= curstyle
->commentMatch
;
2348 if (node
->psvi
!= NULL
) keyed
= 1;
2350 case XML_ENTITY_REF_NODE
:
2351 case XML_ENTITY_NODE
:
2352 case XML_DOCUMENT_TYPE_NODE
:
2353 case XML_DOCUMENT_FRAG_NODE
:
2354 case XML_NOTATION_NODE
:
2356 case XML_ELEMENT_DECL
:
2357 case XML_ATTRIBUTE_DECL
:
2358 case XML_ENTITY_DECL
:
2359 case XML_NAMESPACE_DECL
:
2360 case XML_XINCLUDE_START
:
2361 case XML_XINCLUDE_END
:
2366 while ((list
!= NULL
) &&
2367 ((ret
== NULL
) || (list
->priority
> priority
))) {
2368 if (xsltTestCompMatch(ctxt
, list
, node
,
2369 ctxt
->mode
, ctxt
->modeURI
)) {
2370 ret
= list
->template;
2371 priority
= list
->priority
;
2377 * Some of the tests for elements can also apply to documents
2379 if ((node
->type
== XML_DOCUMENT_NODE
) ||
2380 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
2381 (node
->type
== XML_TEXT_NODE
)) {
2382 list
= curstyle
->elemMatch
;
2383 while ((list
!= NULL
) &&
2384 ((ret
== NULL
) || (list
->priority
> priority
))) {
2385 if (xsltTestCompMatch(ctxt
, list
, node
,
2386 ctxt
->mode
, ctxt
->modeURI
)) {
2387 ret
= list
->template;
2388 priority
= list
->priority
;
2393 } else if ((node
->type
== XML_PI_NODE
) ||
2394 (node
->type
== XML_COMMENT_NODE
)) {
2395 list
= curstyle
->elemMatch
;
2396 while ((list
!= NULL
) &&
2397 ((ret
== NULL
) || (list
->priority
> priority
))) {
2398 if (xsltTestCompMatch(ctxt
, list
, node
,
2399 ctxt
->mode
, ctxt
->modeURI
)) {
2400 ret
= list
->template;
2401 priority
= list
->priority
;
2410 list
= curstyle
->keyMatch
;
2411 while ((list
!= NULL
) &&
2412 ((ret
== NULL
) || (list
->priority
> priority
))) {
2413 if (xsltTestCompMatch(ctxt
, list
, node
,
2414 ctxt
->mode
, ctxt
->modeURI
)) {
2415 ret
= list
->template;
2416 priority
= list
->priority
;
2422 else if (ctxt
->hasTemplKeyPatterns
&&
2423 ((ctxt
->document
== NULL
) ||
2424 (ctxt
->document
->nbKeysComputed
< ctxt
->nbKeys
)))
2427 * Compute all remaining keys for this document.
2429 * REVISIT TODO: I think this could be further optimized.
2431 if (xsltComputeAllKeys(ctxt
, node
) == -1)
2434 switch (node
->type
) {
2435 case XML_ELEMENT_NODE
:
2436 if (node
->psvi
!= NULL
) keyed
= 1;
2438 case XML_ATTRIBUTE_NODE
:
2439 if (((xmlAttrPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2442 case XML_CDATA_SECTION_NODE
:
2443 case XML_COMMENT_NODE
:
2445 if (node
->psvi
!= NULL
) keyed
= 1;
2447 case XML_DOCUMENT_NODE
:
2448 case XML_HTML_DOCUMENT_NODE
:
2449 if (((xmlDocPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2461 * Cycle on next curstylesheet import.
2463 curstyle
= xsltNextImport(curstyle
);
2471 * xsltCleanupTemplates:
2472 * @style: an XSLT stylesheet
2474 * Cleanup the state of the templates used by the stylesheet and
2475 * the ones it imports.
2478 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED
) {
2482 * xsltFreeTemplateHashes:
2483 * @style: an XSLT stylesheet
2485 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2488 xsltFreeTemplateHashes(xsltStylesheetPtr style
) {
2489 if (style
->templatesHash
!= NULL
)
2490 xmlHashFree((xmlHashTablePtr
) style
->templatesHash
,
2491 (xmlHashDeallocator
) xsltFreeCompMatchList
);
2492 if (style
->rootMatch
!= NULL
)
2493 xsltFreeCompMatchList(style
->rootMatch
);
2494 if (style
->keyMatch
!= NULL
)
2495 xsltFreeCompMatchList(style
->keyMatch
);
2496 if (style
->elemMatch
!= NULL
)
2497 xsltFreeCompMatchList(style
->elemMatch
);
2498 if (style
->attrMatch
!= NULL
)
2499 xsltFreeCompMatchList(style
->attrMatch
);
2500 if (style
->parentMatch
!= NULL
)
2501 xsltFreeCompMatchList(style
->parentMatch
);
2502 if (style
->textMatch
!= NULL
)
2503 xsltFreeCompMatchList(style
->textMatch
);
2504 if (style
->piMatch
!= NULL
)
2505 xsltFreeCompMatchList(style
->piMatch
);
2506 if (style
->commentMatch
!= NULL
)
2507 xsltFreeCompMatchList(style
->commentMatch
);