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>
28 #include <libxml/xpath.h>
30 #include "xsltInternals.h"
31 #include "xsltutils.h"
33 #include "templates.h"
36 #include "documents.h"
38 #ifdef WITH_XSLT_DEBUG
39 #define WITH_XSLT_DEBUG_PATTERN
69 typedef struct _xsltStepState xsltStepState
;
70 typedef xsltStepState
*xsltStepStatePtr
;
71 struct _xsltStepState
{
76 typedef struct _xsltStepStates xsltStepStates
;
77 typedef xsltStepStates
*xsltStepStatesPtr
;
78 struct _xsltStepStates
{
81 xsltStepStatePtr states
;
84 typedef struct _xsltStepOp xsltStepOp
;
85 typedef xsltStepOp
*xsltStepOpPtr
;
91 xmlXPathCompExprPtr comp
;
93 * Optimisations for count
100 struct _xsltCompMatch
{
101 struct _xsltCompMatch
*next
; /* siblings in the name hash */
102 float priority
; /* the priority */
103 const xmlChar
*pattern
; /* the pattern */
104 const xmlChar
*mode
; /* the mode */
105 const xmlChar
*modeURI
; /* the mode URI */
106 xsltTemplatePtr
template; /* the associated template */
109 /* TODO fix the statically allocated size steps[] */
112 xmlNsPtr
*nsList
; /* the namespaces in scope */
113 int nsNr
; /* the number of namespaces in scope */
114 xsltStepOpPtr steps
; /* ops for computation */
117 typedef struct _xsltParserContext xsltParserContext
;
118 typedef xsltParserContext
*xsltParserContextPtr
;
119 struct _xsltParserContext
{
120 xsltStylesheetPtr style
; /* the stylesheet */
121 xsltTransformContextPtr ctxt
; /* the transformation or NULL */
122 const xmlChar
*cur
; /* the current char being parsed */
123 const xmlChar
*base
; /* the full expression */
124 xmlDocPtr doc
; /* the source document */
125 xmlNodePtr elem
; /* the source element */
126 int error
; /* error code */
127 xsltCompMatchPtr comp
; /* the result */
130 /************************************************************************
134 ************************************************************************/
139 * Create a new XSLT CompMatch
141 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
143 static xsltCompMatchPtr
144 xsltNewCompMatch(void) {
145 xsltCompMatchPtr cur
;
147 cur
= (xsltCompMatchPtr
) xmlMalloc(sizeof(xsltCompMatch
));
149 xsltTransformError(NULL
, NULL
, NULL
,
150 "xsltNewCompMatch : out of memory error\n");
153 memset(cur
, 0, sizeof(xsltCompMatch
));
156 cur
-> steps
= (xsltStepOpPtr
) xmlMalloc(sizeof(xsltStepOp
) *
158 if (cur
->steps
== NULL
) {
159 xsltTransformError(NULL
, NULL
, NULL
,
160 "xsltNewCompMatch : out of memory error\n");
172 * @comp: an XSLT comp
174 * Free up the memory allocated by @comp
177 xsltFreeCompMatch(xsltCompMatchPtr comp
) {
183 if (comp
->pattern
!= NULL
)
184 xmlFree((xmlChar
*)comp
->pattern
);
185 if (comp
->nsList
!= NULL
)
186 xmlFree(comp
->nsList
);
187 for (i
= 0;i
< comp
->nbStep
;i
++) {
188 op
= &comp
->steps
[i
];
189 if (op
->value
!= NULL
)
191 if (op
->value2
!= NULL
)
193 if (op
->value3
!= NULL
)
195 if (op
->comp
!= NULL
)
196 xmlXPathFreeCompExpr(op
->comp
);
198 xmlFree(comp
->steps
);
199 memset(comp
, -1, sizeof(xsltCompMatch
));
204 * xsltFreeCompMatchList:
205 * @comp: an XSLT comp list
207 * Free up the memory allocated by all the elements of @comp
210 xsltFreeCompMatchList(xsltCompMatchPtr comp
) {
211 xsltCompMatchPtr cur
;
213 while (comp
!= NULL
) {
216 xsltFreeCompMatch(cur
);
221 * xsltNormalizeCompSteps:
222 * @payload: pointer to template hash table entry
223 * @data: pointer to the stylesheet
224 * @name: template match name
226 * This is a hashtable scanner function to normalize the compiled
227 * steps of an imported stylesheet.
229 void xsltNormalizeCompSteps(void *payload
,
230 void *data
, const xmlChar
*name ATTRIBUTE_UNUSED
) {
231 xsltCompMatchPtr comp
= payload
;
232 xsltStylesheetPtr style
= data
;
235 for (ix
= 0; ix
< comp
->nbStep
; ix
++) {
236 comp
->steps
[ix
].previousExtra
+= style
->extrasNr
;
237 comp
->steps
[ix
].indexExtra
+= style
->extrasNr
;
238 comp
->steps
[ix
].lenExtra
+= style
->extrasNr
;
243 * xsltNewParserContext:
244 * @style: the stylesheet
245 * @ctxt: the transformation context, if done at run-time
247 * Create a new XSLT ParserContext
249 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
251 static xsltParserContextPtr
252 xsltNewParserContext(xsltStylesheetPtr style
, xsltTransformContextPtr ctxt
) {
253 xsltParserContextPtr cur
;
255 cur
= (xsltParserContextPtr
) xmlMalloc(sizeof(xsltParserContext
));
257 xsltTransformError(NULL
, NULL
, NULL
,
258 "xsltNewParserContext : malloc failed\n");
261 memset(cur
, 0, sizeof(xsltParserContext
));
268 * xsltFreeParserContext:
269 * @ctxt: an XSLT parser context
271 * Free up the memory allocated by @ctxt
274 xsltFreeParserContext(xsltParserContextPtr ctxt
) {
277 memset(ctxt
, -1, sizeof(xsltParserContext
));
283 * @comp: the compiled match expression
285 * @value: the first value
286 * @value2: the second value
287 * @novar: flag to set XML_XPATH_NOVAR
289 * Add an step to an XSLT Compiled Match
291 * Returns -1 in case of failure, 0 otherwise.
294 xsltCompMatchAdd(xsltParserContextPtr ctxt
, xsltCompMatchPtr comp
,
295 xsltOp op
, xmlChar
* value
, xmlChar
* value2
, int novar
)
297 if (comp
->nbStep
>= comp
->maxStep
) {
300 tmp
= (xsltStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
303 xsltGenericError(xsltGenericErrorContext
,
304 "xsltCompMatchAdd: memory re-allocation failure.\n");
305 if (ctxt
->style
!= NULL
)
306 ctxt
->style
->errors
++;
316 comp
->steps
[comp
->nbStep
].op
= op
;
317 comp
->steps
[comp
->nbStep
].value
= value
;
318 comp
->steps
[comp
->nbStep
].value2
= value2
;
319 comp
->steps
[comp
->nbStep
].value3
= NULL
;
320 comp
->steps
[comp
->nbStep
].comp
= NULL
;
321 if (ctxt
->ctxt
!= NULL
) {
322 comp
->steps
[comp
->nbStep
].previousExtra
=
323 xsltAllocateExtraCtxt(ctxt
->ctxt
);
324 comp
->steps
[comp
->nbStep
].indexExtra
=
325 xsltAllocateExtraCtxt(ctxt
->ctxt
);
326 comp
->steps
[comp
->nbStep
].lenExtra
=
327 xsltAllocateExtraCtxt(ctxt
->ctxt
);
329 comp
->steps
[comp
->nbStep
].previousExtra
=
330 xsltAllocateExtra(ctxt
->style
);
331 comp
->steps
[comp
->nbStep
].indexExtra
=
332 xsltAllocateExtra(ctxt
->style
);
333 comp
->steps
[comp
->nbStep
].lenExtra
=
334 xsltAllocateExtra(ctxt
->style
);
336 if (op
== XSLT_OP_PREDICATE
) {
337 xmlXPathContextPtr xctxt
;
339 if (ctxt
->style
!= NULL
)
340 xctxt
= xmlXPathNewContext(ctxt
->style
->doc
);
342 xctxt
= xmlXPathNewContext(NULL
);
343 #ifdef XML_XPATH_NOVAR
345 xctxt
->flags
= XML_XPATH_NOVAR
;
347 if (ctxt
->style
!= NULL
)
348 xctxt
->dict
= ctxt
->style
->dict
;
349 comp
->steps
[comp
->nbStep
].comp
= xmlXPathCtxtCompile(xctxt
, value
);
350 xmlXPathFreeContext(xctxt
);
351 if (comp
->steps
[comp
->nbStep
].comp
== NULL
) {
352 xsltTransformError(NULL
, ctxt
->style
, ctxt
->elem
,
353 "Failed to compile predicate\n");
354 if (ctxt
->style
!= NULL
)
355 ctxt
->style
->errors
++;
363 * xsltSwapTopCompMatch:
364 * @comp: the compiled match expression
366 * reverse the two top steps.
369 xsltSwapTopCompMatch(xsltCompMatchPtr comp
) {
371 int j
= comp
->nbStep
- 1;
374 register xmlChar
*tmp
;
376 register xmlXPathCompExprPtr expr
;
379 tmp
= comp
->steps
[i
].value
;
380 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
381 comp
->steps
[j
].value
= tmp
;
382 tmp
= comp
->steps
[i
].value2
;
383 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
384 comp
->steps
[j
].value2
= tmp
;
385 tmp
= comp
->steps
[i
].value3
;
386 comp
->steps
[i
].value3
= comp
->steps
[j
].value3
;
387 comp
->steps
[j
].value3
= tmp
;
388 op
= comp
->steps
[i
].op
;
389 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
390 comp
->steps
[j
].op
= op
;
391 expr
= comp
->steps
[i
].comp
;
392 comp
->steps
[i
].comp
= comp
->steps
[j
].comp
;
393 comp
->steps
[j
].comp
= expr
;
394 t
= comp
->steps
[i
].previousExtra
;
395 comp
->steps
[i
].previousExtra
= comp
->steps
[j
].previousExtra
;
396 comp
->steps
[j
].previousExtra
= t
;
397 t
= comp
->steps
[i
].indexExtra
;
398 comp
->steps
[i
].indexExtra
= comp
->steps
[j
].indexExtra
;
399 comp
->steps
[j
].indexExtra
= t
;
400 t
= comp
->steps
[i
].lenExtra
;
401 comp
->steps
[i
].lenExtra
= comp
->steps
[j
].lenExtra
;
402 comp
->steps
[j
].lenExtra
= t
;
407 * xsltReverseCompMatch:
408 * @ctxt: the parser context
409 * @comp: the compiled match expression
411 * reverse all the stack of expressions
414 xsltReverseCompMatch(xsltParserContextPtr ctxt
, xsltCompMatchPtr comp
) {
416 int j
= comp
->nbStep
- 1;
419 register xmlChar
*tmp
;
421 register xmlXPathCompExprPtr expr
;
424 tmp
= comp
->steps
[i
].value
;
425 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
426 comp
->steps
[j
].value
= tmp
;
427 tmp
= comp
->steps
[i
].value2
;
428 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
429 comp
->steps
[j
].value2
= tmp
;
430 tmp
= comp
->steps
[i
].value3
;
431 comp
->steps
[i
].value3
= comp
->steps
[j
].value3
;
432 comp
->steps
[j
].value3
= tmp
;
433 op
= comp
->steps
[i
].op
;
434 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
435 comp
->steps
[j
].op
= op
;
436 expr
= comp
->steps
[i
].comp
;
437 comp
->steps
[i
].comp
= comp
->steps
[j
].comp
;
438 comp
->steps
[j
].comp
= expr
;
439 t
= comp
->steps
[i
].previousExtra
;
440 comp
->steps
[i
].previousExtra
= comp
->steps
[j
].previousExtra
;
441 comp
->steps
[j
].previousExtra
= t
;
442 t
= comp
->steps
[i
].indexExtra
;
443 comp
->steps
[i
].indexExtra
= comp
->steps
[j
].indexExtra
;
444 comp
->steps
[j
].indexExtra
= t
;
445 t
= comp
->steps
[i
].lenExtra
;
446 comp
->steps
[i
].lenExtra
= comp
->steps
[j
].lenExtra
;
447 comp
->steps
[j
].lenExtra
= t
;
451 xsltCompMatchAdd(ctxt
, comp
, XSLT_OP_END
, NULL
, NULL
, 0);
454 * detect consecutive XSLT_OP_PREDICATE indicating a direct
455 * matching should be done.
457 for (i
= 0;i
< comp
->nbStep
- 1;i
++) {
458 if ((comp
->steps
[i
].op
== XSLT_OP_PREDICATE
) &&
459 (comp
->steps
[i
+ 1].op
== XSLT_OP_PREDICATE
)) {
462 if (comp
->pattern
[0] != '/') {
465 query
= xmlStrdup((const xmlChar
*)"//");
466 query
= xmlStrcat(query
, comp
->pattern
);
468 xmlFree((xmlChar
*) comp
->pattern
);
469 comp
->pattern
= query
;
476 /************************************************************************
478 * The interpreter for the precompiled patterns *
480 ************************************************************************/
483 xsltPatPushState(xsltTransformContextPtr ctxt
, xsltStepStates
*states
,
484 int step
, xmlNodePtr node
) {
485 if ((states
->states
== NULL
) || (states
->maxstates
<= 0)) {
486 states
->maxstates
= 4;
487 states
->nbstates
= 0;
488 states
->states
= xmlMalloc(4 * sizeof(xsltStepState
));
490 else if (states
->maxstates
<= states
->nbstates
) {
493 tmp
= (xsltStepStatePtr
) xmlRealloc(states
->states
,
494 2 * states
->maxstates
* sizeof(xsltStepState
));
496 xsltGenericError(xsltGenericErrorContext
,
497 "xsltPatPushState: memory re-allocation failure.\n");
498 ctxt
->state
= XSLT_STATE_STOPPED
;
501 states
->states
= tmp
;
502 states
->maxstates
*= 2;
504 states
->states
[states
->nbstates
].step
= step
;
505 states
->states
[states
->nbstates
++].node
= node
;
507 fprintf(stderr
, "Push: %d, %s\n", step
, node
->name
);
513 * xsltTestCompMatchDirect:
514 * @ctxt: a XSLT process context
515 * @comp: the precompiled pattern
517 * @nsList: the namespaces in scope
518 * @nsNr: the number of namespaces in scope
520 * Test whether the node matches the pattern, do a direct evalutation
521 * and not a step by step evaluation.
523 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
526 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
527 xmlNodePtr node
, xmlNsPtr
*nsList
, int nsNr
) {
528 xsltStepOpPtr sel
= NULL
;
531 xmlXPathObjectPtr list
;
537 if (XSLT_IS_RES_TREE_FRAG(doc
))
541 sel
= &comp
->steps
[0]; /* store extra in first step arbitrarily */
543 prevdoc
= (xmlDocPtr
)
544 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
545 ix
= XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
);
546 list
= (xmlXPathObjectPtr
)
547 XSLT_RUNTIME_EXTRA_LST(ctxt
, sel
->lenExtra
);
549 if ((list
== NULL
) || (prevdoc
!= doc
)) {
550 xmlXPathObjectPtr newlist
;
551 xmlNodePtr parent
= node
->parent
;
554 int oldNsNr
, oldContextSize
, oldProximityPosition
;
555 xmlNsPtr
*oldNamespaces
;
557 oldnode
= ctxt
->xpathCtxt
->node
;
558 olddoc
= ctxt
->xpathCtxt
->doc
;
559 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
560 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
561 oldContextSize
= ctxt
->xpathCtxt
->contextSize
;
562 oldProximityPosition
= ctxt
->xpathCtxt
->proximityPosition
;
563 ctxt
->xpathCtxt
->node
= node
;
564 ctxt
->xpathCtxt
->doc
= doc
;
565 ctxt
->xpathCtxt
->namespaces
= nsList
;
566 ctxt
->xpathCtxt
->nsNr
= nsNr
;
567 newlist
= xmlXPathEval(comp
->pattern
, ctxt
->xpathCtxt
);
568 ctxt
->xpathCtxt
->node
= oldnode
;
569 ctxt
->xpathCtxt
->doc
= olddoc
;
570 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
571 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
572 ctxt
->xpathCtxt
->contextSize
= oldContextSize
;
573 ctxt
->xpathCtxt
->proximityPosition
= oldProximityPosition
;
576 if (newlist
->type
!= XPATH_NODESET
) {
577 xmlXPathFreeObject(newlist
);
582 if ((parent
== NULL
) || (node
->doc
== NULL
) || isRVT
)
587 xmlXPathFreeObject(list
);
590 XSLT_RUNTIME_EXTRA_LST(ctxt
, sel
->lenExtra
) =
592 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
594 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
596 XSLT_RUNTIME_EXTRA_FREE(ctxt
, sel
->lenExtra
) =
597 (xmlFreeFunc
) xmlXPathFreeObject
;
601 if ((list
->nodesetval
== NULL
) ||
602 (list
->nodesetval
->nodeNr
<= 0)) {
604 xmlXPathFreeObject(list
);
607 /* TODO: store the index and use it for the scan */
609 for (j
= 0;j
< list
->nodesetval
->nodeNr
;j
++) {
610 if (list
->nodesetval
->nodeTab
[j
] == node
) {
612 xmlXPathFreeObject(list
);
619 xmlXPathFreeObject(list
);
625 * @ctxt: a XSLT process context
626 * @comp: the precompiled pattern
628 * @mode: the mode name or NULL
629 * @modeURI: the mode URI or NULL
631 * Test whether the node matches the pattern
633 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
636 xsltTestCompMatch(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
637 xmlNodePtr node
, const xmlChar
*mode
,
638 const xmlChar
*modeURI
) {
640 xsltStepOpPtr step
, sel
= NULL
;
641 xsltStepStates states
= {0, 0, NULL
}; /* // may require backtrack */
643 if ((comp
== NULL
) || (node
== NULL
) || (ctxt
== NULL
)) {
644 xsltTransformError(ctxt
, NULL
, node
,
645 "xsltTestCompMatch: null arg\n");
649 if (comp
->mode
== NULL
)
652 * both mode strings must be interned on the stylesheet dictionary
654 if (comp
->mode
!= mode
)
657 if (comp
->mode
!= NULL
)
660 if (modeURI
!= NULL
) {
661 if (comp
->modeURI
== NULL
)
664 * both modeURI strings must be interned on the stylesheet dictionary
666 if (comp
->modeURI
!= modeURI
)
669 if (comp
->modeURI
!= NULL
)
675 for (;i
< comp
->nbStep
;i
++) {
676 step
= &comp
->steps
[i
];
677 if (step
->op
!= XSLT_OP_PREDICATE
)
683 if ((node
->type
== XML_DOCUMENT_NODE
) ||
684 #ifdef LIBXML_DOCB_ENABLED
685 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
687 (node
->type
== XML_HTML_DOCUMENT_NODE
))
689 if ((node
->type
== XML_ELEMENT_NODE
) && (node
->name
[0] == ' '))
693 if (node
->type
!= XML_ELEMENT_NODE
)
695 if (step
->value
== NULL
)
697 if (step
->value
[0] != node
->name
[0])
699 if (!xmlStrEqual(step
->value
, node
->name
))
703 if (node
->ns
== NULL
) {
704 if (step
->value2
!= NULL
)
706 } else if (node
->ns
->href
!= NULL
) {
707 if (step
->value2
== NULL
)
709 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
714 if (node
->type
!= XML_ATTRIBUTE_NODE
)
716 if (step
->value
!= NULL
) {
717 if (step
->value
[0] != node
->name
[0])
719 if (!xmlStrEqual(step
->value
, node
->name
))
723 if (node
->ns
== NULL
) {
724 if (step
->value2
!= NULL
)
726 } else if (step
->value2
!= NULL
) {
727 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
732 if ((node
->type
== XML_DOCUMENT_NODE
) ||
733 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
734 #ifdef LIBXML_DOCB_ENABLED
735 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
737 (node
->type
== XML_NAMESPACE_DECL
))
742 if (step
->value
== NULL
)
744 if (step
->value
[0] != node
->name
[0])
746 if (!xmlStrEqual(step
->value
, node
->name
))
749 if (node
->ns
== NULL
) {
750 if (step
->value2
!= NULL
)
752 } else if (node
->ns
->href
!= NULL
) {
753 if (step
->value2
== NULL
)
755 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
759 case XSLT_OP_ANCESTOR
:
760 /* TODO: implement coalescing of ANCESTOR/NODE ops */
761 if (step
->value
== NULL
) {
762 step
= &comp
->steps
[i
+1];
763 if (step
->op
== XSLT_OP_ROOT
)
765 /* added NS, ID and KEY as a result of bug 168208 */
766 if ((step
->op
!= XSLT_OP_ELEM
) &&
767 (step
->op
!= XSLT_OP_ALL
) &&
768 (step
->op
!= XSLT_OP_NS
) &&
769 (step
->op
!= XSLT_OP_ID
) &&
770 (step
->op
!= XSLT_OP_KEY
))
775 if ((node
->type
== XML_DOCUMENT_NODE
) ||
776 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
777 #ifdef LIBXML_DOCB_ENABLED
778 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
780 (node
->type
== XML_NAMESPACE_DECL
))
783 if ((step
->op
!= XSLT_OP_ELEM
) && step
->op
!= XSLT_OP_ALL
) {
784 xsltPatPushState(ctxt
, &states
, i
, node
);
788 if (step
->value
== NULL
) {
789 xsltPatPushState(ctxt
, &states
, i
- 1, node
);
792 while (node
!= NULL
) {
793 if ((node
->type
== XML_ELEMENT_NODE
) &&
794 (step
->value
[0] == node
->name
[0]) &&
795 (xmlStrEqual(step
->value
, node
->name
))) {
797 if (node
->ns
== NULL
) {
798 if (step
->value2
== NULL
)
800 } else if (node
->ns
->href
!= NULL
) {
801 if ((step
->value2
!= NULL
) &&
802 (xmlStrEqual(step
->value2
, node
->ns
->href
)))
810 xsltPatPushState(ctxt
, &states
, i
- 1, node
);
813 /* TODO Handle IDs decently, must be done differently */
816 if (node
->type
!= XML_ELEMENT_NODE
)
819 id
= xmlGetID(node
->doc
, step
->value
);
820 if ((id
== NULL
) || (id
->parent
!= node
))
828 list
= xsltGetKey(ctxt
, step
->value
,
829 step
->value3
, step
->value2
);
832 for (indx
= 0;indx
< list
->nodeNr
;indx
++)
833 if (list
->nodeTab
[indx
] == node
)
835 if (indx
>= list
->nodeNr
)
840 if (node
->type
!= XML_ELEMENT_NODE
)
842 if (node
->ns
== NULL
) {
843 if (step
->value
!= NULL
)
845 } else if (node
->ns
->href
!= NULL
) {
846 if (step
->value
== NULL
)
848 if (!xmlStrEqual(step
->value
, node
->ns
->href
))
853 if (node
->type
!= XML_ELEMENT_NODE
)
856 case XSLT_OP_PREDICATE
: {
860 int pos
= 0, len
= 0;
864 * when there is cascading XSLT_OP_PREDICATE, then use a
865 * direct computation approach. It's not done directly
866 * at the beginning of the routine to filter out as much
867 * as possible this costly computation.
870 if (states
.states
!= NULL
) {
871 /* Free the rollback states */
872 xmlFree(states
.states
);
874 return(xsltTestCompMatchDirect(ctxt
, comp
, node
,
875 comp
->nsList
, comp
->nsNr
));
879 if (XSLT_IS_RES_TREE_FRAG(doc
))
885 * Depending on the last selection, one may need to
886 * recompute contextSize and proximityPosition.
888 oldCS
= ctxt
->xpathCtxt
->contextSize
;
889 oldCP
= ctxt
->xpathCtxt
->proximityPosition
;
891 (sel
->op
== XSLT_OP_ELEM
) &&
892 (sel
->value
!= NULL
) &&
893 (node
->type
== XML_ELEMENT_NODE
) &&
894 (node
->parent
!= NULL
)) {
898 previous
= (xmlNodePtr
)
899 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
900 if ((previous
!= NULL
) &&
901 (previous
->parent
== node
->parent
)) {
903 * just walk back to adjust the index
906 xmlNodePtr sibling
= node
;
908 while (sibling
!= NULL
) {
909 if (sibling
== previous
)
911 if ((sibling
->type
== XML_ELEMENT_NODE
) &&
912 (previous
->name
!= NULL
) &&
913 (sibling
->name
!= NULL
) &&
914 (previous
->name
[0] == sibling
->name
[0]) &&
915 (xmlStrEqual(previous
->name
, sibling
->name
)))
917 if ((sel
->value2
== NULL
) ||
918 ((sibling
->ns
!= NULL
) &&
919 (xmlStrEqual(sel
->value2
,
920 sibling
->ns
->href
))))
923 sibling
= sibling
->prev
;
925 if (sibling
== NULL
) {
926 /* hum going backward in document order ... */
929 while (sibling
!= NULL
) {
930 if (sibling
== previous
)
932 if ((sibling
->type
== XML_ELEMENT_NODE
) &&
933 (previous
->name
!= NULL
) &&
934 (sibling
->name
!= NULL
) &&
935 (previous
->name
[0] == sibling
->name
[0]) &&
936 (xmlStrEqual(previous
->name
, sibling
->name
)))
938 if ((sel
->value2
== NULL
) ||
939 ((sibling
->ns
!= NULL
) &&
940 (xmlStrEqual(sel
->value2
,
941 sibling
->ns
->href
))))
946 sibling
= sibling
->next
;
949 if (sibling
!= NULL
) {
950 pos
= XSLT_RUNTIME_EXTRA(ctxt
,
951 sel
->indexExtra
, ival
) + indx
;
953 * If the node is in a Value Tree we need to
954 * save len, but cannot cache the node!
955 * (bugs 153137 and 158840)
957 if (node
->doc
!= NULL
) {
958 len
= XSLT_RUNTIME_EXTRA(ctxt
,
959 sel
->lenExtra
, ival
);
961 XSLT_RUNTIME_EXTRA(ctxt
,
962 sel
->previousExtra
, ptr
) = node
;
963 XSLT_RUNTIME_EXTRA(ctxt
,
964 sel
->indexExtra
, ival
) = pos
;
971 * recompute the index
973 xmlNodePtr parent
= node
->parent
;
974 xmlNodePtr siblings
= NULL
;
976 if (parent
) siblings
= parent
->children
;
978 while (siblings
!= NULL
) {
979 if (siblings
->type
== XML_ELEMENT_NODE
) {
980 if (siblings
== node
) {
983 } else if ((node
->name
!= NULL
) &&
984 (siblings
->name
!= NULL
) &&
985 (node
->name
[0] == siblings
->name
[0]) &&
986 (xmlStrEqual(node
->name
, siblings
->name
))) {
987 if ((sel
->value2
== NULL
) ||
988 ((siblings
->ns
!= NULL
) &&
989 (xmlStrEqual(sel
->value2
,
990 siblings
->ns
->href
))))
994 siblings
= siblings
->next
;
996 if ((parent
== NULL
) || (node
->doc
== NULL
))
999 while (parent
->parent
!= NULL
)
1000 parent
= parent
->parent
;
1001 if (((parent
->type
!= XML_DOCUMENT_NODE
) &&
1002 (parent
->type
!= XML_HTML_DOCUMENT_NODE
)) ||
1003 (parent
!= (xmlNodePtr
) node
->doc
))
1008 ctxt
->xpathCtxt
->contextSize
= len
;
1009 ctxt
->xpathCtxt
->proximityPosition
= pos
;
1011 * If the node is in a Value Tree we cannot
1014 if ((!isRVT
) && (node
->doc
!= NULL
) &&
1016 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
1018 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
1020 XSLT_RUNTIME_EXTRA(ctxt
, sel
->lenExtra
, ival
) =
1024 } else if ((sel
!= NULL
) && (sel
->op
== XSLT_OP_ALL
) &&
1025 (node
->type
== XML_ELEMENT_NODE
)) {
1026 xmlNodePtr previous
;
1029 previous
= (xmlNodePtr
)
1030 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
1031 if ((previous
!= NULL
) &&
1032 (previous
->parent
== node
->parent
)) {
1034 * just walk back to adjust the index
1037 xmlNodePtr sibling
= node
;
1039 while (sibling
!= NULL
) {
1040 if (sibling
== previous
)
1042 if (sibling
->type
== XML_ELEMENT_NODE
)
1044 sibling
= sibling
->prev
;
1046 if (sibling
== NULL
) {
1047 /* hum going backward in document order ... */
1050 while (sibling
!= NULL
) {
1051 if (sibling
== previous
)
1053 if (sibling
->type
== XML_ELEMENT_NODE
)
1055 sibling
= sibling
->next
;
1058 if (sibling
!= NULL
) {
1059 pos
= XSLT_RUNTIME_EXTRA(ctxt
,
1060 sel
->indexExtra
, ival
) + indx
;
1062 * If the node is in a Value Tree we cannot
1065 if ((node
->doc
!= NULL
) && !isRVT
) {
1066 len
= XSLT_RUNTIME_EXTRA(ctxt
,
1067 sel
->lenExtra
, ival
);
1068 XSLT_RUNTIME_EXTRA(ctxt
,
1069 sel
->previousExtra
, ptr
) = node
;
1070 XSLT_RUNTIME_EXTRA(ctxt
,
1071 sel
->indexExtra
, ival
) = pos
;
1077 * recompute the index
1079 xmlNodePtr parent
= node
->parent
;
1080 xmlNodePtr siblings
= NULL
;
1082 if (parent
) siblings
= parent
->children
;
1084 while (siblings
!= NULL
) {
1085 if (siblings
->type
== XML_ELEMENT_NODE
) {
1087 if (siblings
== node
) {
1091 siblings
= siblings
->next
;
1093 if ((parent
== NULL
) || (node
->doc
== NULL
))
1096 while (parent
->parent
!= NULL
)
1097 parent
= parent
->parent
;
1098 if (((parent
->type
!= XML_DOCUMENT_NODE
) &&
1099 (parent
->type
!= XML_HTML_DOCUMENT_NODE
)) ||
1100 (parent
!= (xmlNodePtr
) node
->doc
))
1105 ctxt
->xpathCtxt
->contextSize
= len
;
1106 ctxt
->xpathCtxt
->proximityPosition
= pos
;
1108 * If the node is in a Value Tree we cannot
1111 if ((node
->doc
!= NULL
) && (nocache
== 0) && !isRVT
) {
1112 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
1114 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
1116 XSLT_RUNTIME_EXTRA(ctxt
, sel
->lenExtra
, ival
) =
1121 oldNode
= ctxt
->node
;
1124 if (step
->value
== NULL
)
1126 if (step
->comp
== NULL
)
1129 if (!xsltEvalXPathPredicate(ctxt
, step
->comp
, comp
->nsList
,
1134 ctxt
->xpathCtxt
->contextSize
= oldCS
;
1135 ctxt
->xpathCtxt
->proximityPosition
= oldCP
;
1137 ctxt
->node
= oldNode
;
1141 ctxt
->xpathCtxt
->contextSize
= oldCS
;
1142 ctxt
->xpathCtxt
->proximityPosition
= oldCP
;
1144 ctxt
->node
= oldNode
;
1148 if (node
->type
!= XML_PI_NODE
)
1150 if (step
->value
!= NULL
) {
1151 if (!xmlStrEqual(step
->value
, node
->name
))
1155 case XSLT_OP_COMMENT
:
1156 if (node
->type
!= XML_COMMENT_NODE
)
1160 if ((node
->type
!= XML_TEXT_NODE
) &&
1161 (node
->type
!= XML_CDATA_SECTION_NODE
))
1165 switch (node
->type
) {
1166 case XML_ELEMENT_NODE
:
1167 case XML_CDATA_SECTION_NODE
:
1169 case XML_COMMENT_NODE
:
1179 if (states
.states
!= NULL
) {
1180 /* Free the rollback states */
1181 xmlFree(states
.states
);
1185 /* got an error try to rollback */
1186 if (states
.states
== NULL
)
1188 if (states
.nbstates
<= 0) {
1189 xmlFree(states
.states
);
1193 i
= states
.states
[states
.nbstates
].step
;
1194 node
= states
.states
[states
.nbstates
].node
;
1196 fprintf(stderr
, "Pop: %d, %s\n", i
, node
->name
);
1202 * xsltTestCompMatchList:
1203 * @ctxt: a XSLT process context
1205 * @comp: the precompiled pattern list
1207 * Test whether the node matches one of the patterns in the list
1209 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1212 xsltTestCompMatchList(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1213 xsltCompMatchPtr comp
) {
1216 if ((ctxt
== NULL
) || (node
== NULL
))
1218 while (comp
!= NULL
) {
1219 ret
= xsltTestCompMatch(ctxt
, comp
, node
, NULL
, NULL
);
1227 /************************************************************************
1229 * Dedicated parser for templates *
1231 ************************************************************************/
1233 #define CUR (*ctxt->cur)
1234 #define SKIP(val) ctxt->cur += (val)
1235 #define NXT(val) ctxt->cur[(val)]
1236 #define CUR_PTR ctxt->cur
1238 #define SKIP_BLANKS \
1239 while (IS_BLANK_CH(CUR)) NEXT
1241 #define CURRENT (*ctxt->cur)
1242 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1245 #define PUSH(op, val, val2, novar) \
1246 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1249 xsltSwapTopCompMatch(ctxt->comp);
1251 #define XSLT_ERROR(X) \
1252 { xsltError(ctxt, __FILE__, __LINE__, X); \
1253 ctxt->error = (X); return; }
1255 #define XSLT_ERROR0(X) \
1256 { xsltError(ctxt, __FILE__, __LINE__, X); \
1257 ctxt->error = (X); return(0); }
1261 * @ctxt: the XPath Parser context
1263 * Parse an XPath Litteral:
1265 * [29] Literal ::= '"' [^"]* '"'
1268 * Returns the Literal parsed or NULL
1272 xsltScanLiteral(xsltParserContextPtr ctxt
) {
1273 const xmlChar
*q
, *cur
;
1274 xmlChar
*ret
= NULL
;
1281 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1282 while ((IS_CHAR(val
)) && (val
!= '"')) {
1284 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1286 if (!IS_CHAR(val
)) {
1290 ret
= xmlStrndup(q
, cur
- q
);
1294 } else if (CUR
== '\'') {
1297 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1298 while ((IS_CHAR(val
)) && (val
!= '\'')) {
1300 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1302 if (!IS_CHAR(val
)) {
1306 ret
= xmlStrndup(q
, cur
- q
);
1311 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1320 * @ctxt: the XPath Parser context
1322 * Parses a non qualified name
1324 * Returns the Name parsed or NULL
1328 xsltScanNCName(xsltParserContextPtr ctxt
) {
1329 const xmlChar
*q
, *cur
;
1330 xmlChar
*ret
= NULL
;
1336 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1337 if (!IS_LETTER(val
) && (val
!= '_'))
1340 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
1341 (val
== '.') || (val
== '-') ||
1343 (IS_COMBINING(val
)) ||
1344 (IS_EXTENDER(val
))) {
1346 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1348 ret
= xmlStrndup(q
, cur
- q
);
1354 * xsltCompileIdKeyPattern:
1355 * @ctxt: the compilation context
1356 * @name: a preparsed name
1357 * @aid: whether id/key are allowed there
1358 * @novar: flag to prohibit xslt var
1360 * Compile the XSLT LocationIdKeyPattern
1361 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1362 * | 'key' '(' Literal ',' Literal ')'
1364 * also handle NodeType and PI from:
1366 * [7] NodeTest ::= NameTest
1367 * | NodeType '(' ')'
1368 * | 'processing-instruction' '(' Literal ')'
1371 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt
, xmlChar
*name
,
1372 int aid
, int novar
, xsltAxis axis
) {
1373 xmlChar
*lit
= NULL
;
1374 xmlChar
*lit2
= NULL
;
1377 xsltTransformError(NULL
, NULL
, NULL
,
1378 "xsltCompileIdKeyPattern : ( expected\n");
1382 if ((aid
) && (xmlStrEqual(name
, (const xmlChar
*)"id"))) {
1384 xsltTransformError(NULL
, NULL
, NULL
,
1385 "xsltCompileIdKeyPattern : NodeTest expected\n");
1391 lit
= xsltScanLiteral(ctxt
);
1393 xsltTransformError(NULL
, NULL
, NULL
,
1394 "xsltCompileIdKeyPattern : Literal expected\n");
1399 xsltTransformError(NULL
, NULL
, NULL
,
1400 "xsltCompileIdKeyPattern : ) expected\n");
1406 PUSH(XSLT_OP_ID
, lit
, NULL
, novar
);
1408 } else if ((aid
) && (xmlStrEqual(name
, (const xmlChar
*)"key"))) {
1410 xsltTransformError(NULL
, NULL
, NULL
,
1411 "xsltCompileIdKeyPattern : NodeTest expected\n");
1417 lit
= xsltScanLiteral(ctxt
);
1419 xsltTransformError(NULL
, NULL
, NULL
,
1420 "xsltCompileIdKeyPattern : Literal expected\n");
1425 xsltTransformError(NULL
, NULL
, NULL
,
1426 "xsltCompileIdKeyPattern : , expected\n");
1432 lit2
= xsltScanLiteral(ctxt
);
1434 xsltTransformError(NULL
, NULL
, NULL
,
1435 "xsltCompileIdKeyPattern : Literal expected\n");
1441 xsltTransformError(NULL
, NULL
, NULL
,
1442 "xsltCompileIdKeyPattern : ) expected\n");
1449 /* URGENT TODO: support namespace in keys */
1450 PUSH(XSLT_OP_KEY
, lit
, lit2
, novar
);
1453 } else if (xmlStrEqual(name
, (const xmlChar
*)"processing-instruction")) {
1457 lit
= xsltScanLiteral(ctxt
);
1459 xsltTransformError(NULL
, NULL
, NULL
,
1460 "xsltCompileIdKeyPattern : Literal expected\n");
1465 xsltTransformError(NULL
, NULL
, NULL
,
1466 "xsltCompileIdKeyPattern : ) expected\n");
1472 PUSH(XSLT_OP_PI
, lit
, NULL
, novar
);
1474 } else if (xmlStrEqual(name
, (const xmlChar
*)"text")) {
1478 xsltTransformError(NULL
, NULL
, NULL
,
1479 "xsltCompileIdKeyPattern : ) expected\n");
1484 PUSH(XSLT_OP_TEXT
, NULL
, NULL
, novar
);
1485 } else if (xmlStrEqual(name
, (const xmlChar
*)"comment")) {
1489 xsltTransformError(NULL
, NULL
, NULL
,
1490 "xsltCompileIdKeyPattern : ) expected\n");
1495 PUSH(XSLT_OP_COMMENT
, NULL
, NULL
, novar
);
1496 } else if (xmlStrEqual(name
, (const xmlChar
*)"node")) {
1500 xsltTransformError(NULL
, NULL
, NULL
,
1501 "xsltCompileIdKeyPattern : ) expected\n");
1506 if (axis
== AXIS_ATTRIBUTE
) {
1507 PUSH(XSLT_OP_ATTR
, NULL
, NULL
, novar
);
1510 PUSH(XSLT_OP_NODE
, NULL
, NULL
, novar
);
1513 xsltTransformError(NULL
, NULL
, NULL
,
1514 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1518 xsltTransformError(NULL
, NULL
, NULL
,
1519 "xsltCompileIdKeyPattern : node type\n");
1528 * xsltCompileStepPattern:
1529 * @ctxt: the compilation context
1530 * @token: a posible precompiled name
1531 * @novar: flag to prohibit xslt variables from pattern
1533 * Compile the XSLT StepPattern and generates a precompiled
1534 * form suitable for fast matching.
1536 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1537 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1538 * | ('child' | 'attribute') '::'
1540 * [7] NodeTest ::= NameTest
1541 * | NodeType '(' ')'
1542 * | 'processing-instruction' '(' Literal ')'
1543 * [8] Predicate ::= '[' PredicateExpr ']'
1544 * [9] PredicateExpr ::= Expr
1545 * [13] AbbreviatedAxisSpecifier ::= '@'?
1546 * [37] NameTest ::= '*' | NCName ':' '*' | QName
1550 xsltCompileStepPattern(xsltParserContextPtr ctxt
, xmlChar
*token
, int novar
) {
1551 xmlChar
*name
= NULL
;
1552 const xmlChar
*URI
= NULL
;
1553 xmlChar
*URL
= NULL
;
1558 if ((token
== NULL
) && (CUR
== '@')) {
1560 axis
= AXIS_ATTRIBUTE
;
1564 token
= xsltScanNCName(ctxt
);
1565 if (token
== NULL
) {
1568 if (axis
== AXIS_ATTRIBUTE
) {
1569 PUSH(XSLT_OP_ATTR
, NULL
, NULL
, novar
);
1572 PUSH(XSLT_OP_ALL
, NULL
, NULL
, novar
);
1574 goto parse_predicate
;
1576 xsltTransformError(NULL
, NULL
, NULL
,
1577 "xsltCompileStepPattern : Name expected\n");
1586 xsltCompileIdKeyPattern(ctxt
, token
, 0, novar
, axis
);
1591 } else if (CUR
== ':') {
1594 xmlChar
*prefix
= token
;
1598 * This is a namespace match
1600 token
= xsltScanNCName(ctxt
);
1601 ns
= xmlSearchNs(ctxt
->doc
, ctxt
->elem
, prefix
);
1603 xsltTransformError(NULL
, NULL
, NULL
,
1604 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1611 URL
= xmlStrdup(ns
->href
);
1615 if (token
== NULL
) {
1618 if (axis
== AXIS_ATTRIBUTE
) {
1619 PUSH(XSLT_OP_ATTR
, NULL
, URL
, novar
);
1623 PUSH(XSLT_OP_NS
, URL
, NULL
, novar
);
1627 xsltTransformError(NULL
, NULL
, NULL
,
1628 "xsltCompileStepPattern : Name expected\n");
1633 if (axis
== AXIS_ATTRIBUTE
) {
1634 PUSH(XSLT_OP_ATTR
, token
, URL
, novar
);
1639 PUSH(XSLT_OP_ELEM
, token
, URL
, novar
);
1646 xsltTransformError(NULL
, NULL
, NULL
,
1647 "xsltCompileStepPattern : NodeTest expected\n");
1652 if (xmlStrEqual(token
, (const xmlChar
*) "child")) {
1654 } else if (xmlStrEqual(token
, (const xmlChar
*) "attribute")) {
1655 axis
= AXIS_ATTRIBUTE
;
1657 xsltTransformError(NULL
, NULL
, NULL
,
1658 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1665 token
= xsltScanNCName(ctxt
);
1666 goto parse_node_test
;
1669 URI
= xsltGetQNameURI(ctxt
->elem
, &token
);
1670 if (token
== NULL
) {
1675 URL
= xmlStrdup(URI
);
1676 if (axis
== AXIS_ATTRIBUTE
) {
1677 PUSH(XSLT_OP_ATTR
, token
, URL
, novar
);
1682 PUSH(XSLT_OP_ELEM
, token
, URL
, novar
);
1690 while (CUR
== '[') {
1692 xmlChar
*ret
= NULL
;
1698 /* Skip over nested predicates */
1701 else if (CUR
== ']') {
1705 } else if (CUR
== '"') {
1707 while ((CUR
!= 0) && (CUR
!= '"'))
1709 } else if (CUR
== '\'') {
1711 while ((CUR
!= 0) && (CUR
!= '\''))
1717 xsltTransformError(NULL
, NULL
, NULL
,
1718 "xsltCompileStepPattern : ']' expected\n");
1722 ret
= xmlStrndup(q
, CUR_PTR
- q
);
1723 PUSH(XSLT_OP_PREDICATE
, ret
, NULL
, novar
);
1725 /* push the predicate lower than local test */
1739 * xsltCompileRelativePathPattern:
1740 * @comp: the compilation context
1741 * @token: a posible precompiled name
1742 * @novar: flag to prohibit xslt variables
1744 * Compile the XSLT RelativePathPattern and generates a precompiled
1745 * form suitable for fast matching.
1747 * [4] RelativePathPattern ::= StepPattern
1748 * | RelativePathPattern '/' StepPattern
1749 * | RelativePathPattern '//' StepPattern
1752 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt
, xmlChar
*token
, int novar
) {
1753 xsltCompileStepPattern(ctxt
, token
, novar
);
1757 while ((CUR
!= 0) && (CUR
!= '|')) {
1758 if ((CUR
== '/') && (NXT(1) == '/')) {
1759 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1763 xsltCompileStepPattern(ctxt
, NULL
, novar
);
1764 } else if (CUR
== '/') {
1765 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1768 if ((CUR
!= 0) && (CUR
!= '|')) {
1769 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1783 * xsltCompileLocationPathPattern:
1784 * @ctxt: the compilation context
1785 * @novar: flag to prohibit xslt variables
1787 * Compile the XSLT LocationPathPattern and generates a precompiled
1788 * form suitable for fast matching.
1790 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1791 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1792 * | '//'? RelativePathPattern
1795 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt
, int novar
) {
1797 if ((CUR
== '/') && (NXT(1) == '/')) {
1799 * since we reverse the query
1800 * a leading // can be safely ignored
1804 ctxt
->comp
->priority
= 0.5; /* '//' means not 0 priority */
1805 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1806 } else if (CUR
== '/') {
1808 * We need to find root as the parent
1812 PUSH(XSLT_OP_ROOT
, NULL
, NULL
, novar
);
1813 if ((CUR
!= 0) && (CUR
!= '|')) {
1814 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1815 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1817 } else if (CUR
== '*') {
1818 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1819 } else if (CUR
== '@') {
1820 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1823 name
= xsltScanNCName(ctxt
);
1825 xsltTransformError(NULL
, NULL
, NULL
,
1826 "xsltCompileLocationPathPattern : Name expected\n");
1831 if ((CUR
== '(') && !xmlXPathIsNodeType(name
)) {
1832 xsltCompileIdKeyPattern(ctxt
, name
, 1, novar
, 0);
1835 if ((CUR
== '/') && (NXT(1) == '/')) {
1836 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1840 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1841 } else if (CUR
== '/') {
1842 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1845 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1849 xsltCompileRelativePathPattern(ctxt
, name
, novar
);
1856 * xsltCompilePatternInternal:
1857 * @pattern: an XSLT pattern
1858 * @doc: the containing document
1859 * @node: the containing element
1860 * @style: the stylesheet
1861 * @runtime: the transformation context, if done at run-time
1862 * @novar: flag to prohibit xslt variables
1864 * Compile the XSLT pattern and generates a list of precompiled form suitable
1865 * for fast matching.
1867 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1869 * Returns the generated pattern list or NULL in case of failure
1872 static xsltCompMatchPtr
1873 xsltCompilePatternInternal(const xmlChar
*pattern
, xmlDocPtr doc
,
1874 xmlNodePtr node
, xsltStylesheetPtr style
,
1875 xsltTransformContextPtr runtime
, int novar
) {
1876 xsltParserContextPtr ctxt
= NULL
;
1877 xsltCompMatchPtr element
, first
= NULL
, previous
= NULL
;
1878 int current
, start
, end
, level
, j
;
1880 if (pattern
== NULL
) {
1881 xsltTransformError(NULL
, NULL
, node
,
1882 "xsltCompilePattern : NULL pattern\n");
1886 ctxt
= xsltNewParserContext(style
, runtime
);
1892 while (pattern
[current
] != 0) {
1894 while (IS_BLANK_CH(pattern
[current
]))
1898 while ((pattern
[end
] != 0) && ((pattern
[end
] != '|') || (level
!= 0))) {
1899 if (pattern
[end
] == '[')
1901 else if (pattern
[end
] == ']')
1903 else if (pattern
[end
] == '\'') {
1905 while ((pattern
[end
] != 0) && (pattern
[end
] != '\''))
1907 } else if (pattern
[end
] == '"') {
1909 while ((pattern
[end
] != 0) && (pattern
[end
] != '"'))
1912 if (pattern
[end
] == 0)
1916 if (current
== end
) {
1917 xsltTransformError(NULL
, NULL
, node
,
1918 "xsltCompilePattern : NULL pattern\n");
1921 element
= xsltNewCompMatch();
1922 if (element
== NULL
) {
1927 else if (previous
!= NULL
)
1928 previous
->next
= element
;
1931 ctxt
->comp
= element
;
1932 ctxt
->base
= xmlStrndup(&pattern
[start
], end
- start
);
1933 if (ctxt
->base
== NULL
)
1935 ctxt
->cur
= &(ctxt
->base
)[current
- start
];
1936 element
->pattern
= ctxt
->base
;
1937 element
->nsList
= xmlGetNsList(doc
, node
);
1939 if (element
->nsList
!= NULL
) {
1940 while (element
->nsList
[j
] != NULL
)
1946 #ifdef WITH_XSLT_DEBUG_PATTERN
1947 xsltGenericDebug(xsltGenericDebugContext
,
1948 "xsltCompilePattern : parsing '%s'\n",
1952 Preset default priority to be zero.
1953 This may be changed by xsltCompileLocationPathPattern.
1955 element
->priority
= 0;
1956 xsltCompileLocationPathPattern(ctxt
, novar
);
1958 xsltTransformError(NULL
, style
, node
,
1959 "xsltCompilePattern : failed to compile '%s'\n",
1961 if (style
!= NULL
) style
->errors
++;
1966 * Reverse for faster interpretation.
1968 xsltReverseCompMatch(ctxt
, element
);
1971 * Set-up the priority
1973 if (element
->priority
== 0) { /* if not yet determined */
1974 if (((element
->steps
[0].op
== XSLT_OP_ELEM
) ||
1975 (element
->steps
[0].op
== XSLT_OP_ATTR
) ||
1976 (element
->steps
[0].op
== XSLT_OP_PI
)) &&
1977 (element
->steps
[0].value
!= NULL
) &&
1978 (element
->steps
[1].op
== XSLT_OP_END
)) {
1979 ; /* previously preset */
1980 } else if ((element
->steps
[0].op
== XSLT_OP_ATTR
) &&
1981 (element
->steps
[0].value2
!= NULL
) &&
1982 (element
->steps
[1].op
== XSLT_OP_END
)) {
1983 element
->priority
= -0.25;
1984 } else if ((element
->steps
[0].op
== XSLT_OP_NS
) &&
1985 (element
->steps
[0].value
!= NULL
) &&
1986 (element
->steps
[1].op
== XSLT_OP_END
)) {
1987 element
->priority
= -0.25;
1988 } else if ((element
->steps
[0].op
== XSLT_OP_ATTR
) &&
1989 (element
->steps
[0].value
== NULL
) &&
1990 (element
->steps
[0].value2
== NULL
) &&
1991 (element
->steps
[1].op
== XSLT_OP_END
)) {
1992 element
->priority
= -0.5;
1993 } else if (((element
->steps
[0].op
== XSLT_OP_PI
) ||
1994 (element
->steps
[0].op
== XSLT_OP_TEXT
) ||
1995 (element
->steps
[0].op
== XSLT_OP_ALL
) ||
1996 (element
->steps
[0].op
== XSLT_OP_NODE
) ||
1997 (element
->steps
[0].op
== XSLT_OP_COMMENT
)) &&
1998 (element
->steps
[1].op
== XSLT_OP_END
)) {
1999 element
->priority
= -0.5;
2001 element
->priority
= 0.5;
2004 #ifdef WITH_XSLT_DEBUG_PATTERN
2005 xsltGenericDebug(xsltGenericDebugContext
,
2006 "xsltCompilePattern : parsed %s, default priority %f\n",
2007 element
->pattern
, element
->priority
);
2009 if (pattern
[end
] == '|')
2014 xsltTransformError(NULL
, style
, node
,
2015 "xsltCompilePattern : NULL pattern\n");
2016 if (style
!= NULL
) style
->errors
++;
2020 xsltFreeParserContext(ctxt
);
2025 xsltFreeParserContext(ctxt
);
2027 xsltFreeCompMatchList(first
);
2032 * xsltCompilePattern:
2033 * @pattern: an XSLT pattern
2034 * @doc: the containing document
2035 * @node: the containing element
2036 * @style: the stylesheet
2037 * @runtime: the transformation context, if done at run-time
2039 * Compile the XSLT pattern and generates a list of precompiled form suitable
2040 * for fast matching.
2042 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2044 * Returns the generated pattern list or NULL in case of failure
2048 xsltCompilePattern(const xmlChar
*pattern
, xmlDocPtr doc
,
2049 xmlNodePtr node
, xsltStylesheetPtr style
,
2050 xsltTransformContextPtr runtime
) {
2051 return (xsltCompilePatternInternal(pattern
, doc
, node
, style
, runtime
, 0));
2054 /************************************************************************
2056 * Module interfaces *
2058 ************************************************************************/
2062 * @style: an XSLT stylesheet
2063 * @cur: an XSLT template
2064 * @mode: the mode name or NULL
2065 * @modeURI: the mode URI or NULL
2067 * Register the XSLT pattern associated to @cur
2069 * Returns -1 in case of error, 0 otherwise
2072 xsltAddTemplate(xsltStylesheetPtr style
, xsltTemplatePtr cur
,
2073 const xmlChar
*mode
, const xmlChar
*modeURI
) {
2074 xsltCompMatchPtr pat
, list
, next
;
2076 * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2077 * avoids gcc 'type-punned pointer' warning.
2080 const xmlChar
*name
= NULL
;
2081 float priority
; /* the priority */
2083 if ((style
== NULL
) || (cur
== NULL
) || (cur
->match
== NULL
))
2086 priority
= cur
->priority
;
2087 pat
= xsltCompilePatternInternal(cur
->match
, style
->doc
, cur
->elem
,
2096 pat
->template = cur
;
2098 pat
->mode
= xmlDictLookup(style
->dict
, mode
, -1);
2099 if (modeURI
!= NULL
)
2100 pat
->modeURI
= xmlDictLookup(style
->dict
, modeURI
, -1);
2101 if (priority
!= XSLT_PAT_NO_PRIORITY
)
2102 pat
->priority
= priority
;
2105 * insert it in the hash table list corresponding to its lookup name
2107 switch (pat
->steps
[0].op
) {
2109 if (pat
->steps
[0].value
!= NULL
)
2110 name
= pat
->steps
[0].value
;
2112 top
= &(style
->attrMatch
);
2114 case XSLT_OP_PARENT
:
2115 case XSLT_OP_ANCESTOR
:
2116 top
= &(style
->elemMatch
);
2119 top
= &(style
->rootMatch
);
2122 top
= &(style
->keyMatch
);
2125 /* TODO optimize ID !!! */
2128 top
= &(style
->elemMatch
);
2131 case XSLT_OP_PREDICATE
:
2132 xsltTransformError(NULL
, style
, NULL
,
2133 "xsltAddTemplate: invalid compiled pattern\n");
2134 xsltFreeCompMatch(pat
);
2137 * TODO: some flags at the top level about type based patterns
2138 * would be faster than inclusion in the hash table.
2141 if (pat
->steps
[0].value
!= NULL
)
2142 name
= pat
->steps
[0].value
;
2144 top
= &(style
->piMatch
);
2146 case XSLT_OP_COMMENT
:
2147 top
= &(style
->commentMatch
);
2150 top
= &(style
->textMatch
);
2154 if (pat
->steps
[0].value
!= NULL
)
2155 name
= pat
->steps
[0].value
;
2157 top
= &(style
->elemMatch
);
2161 if (style
->templatesHash
== NULL
) {
2162 style
->templatesHash
= xmlHashCreate(1024);
2163 if (style
->templatesHash
== NULL
) {
2164 xsltFreeCompMatch(pat
);
2167 xmlHashAddEntry3(style
->templatesHash
, name
, mode
, modeURI
, pat
);
2169 list
= (xsltCompMatchPtr
) xmlHashLookup3(style
->templatesHash
,
2170 name
, mode
, modeURI
);
2172 xmlHashAddEntry3(style
->templatesHash
, name
,
2173 mode
, modeURI
, pat
);
2176 * Note '<=' since one must choose among the matching
2177 * template rules that are left, the one that occurs
2178 * last in the stylesheet
2180 if (list
->priority
<= pat
->priority
) {
2182 xmlHashUpdateEntry3(style
->templatesHash
, name
,
2183 mode
, modeURI
, pat
, NULL
);
2185 while (list
->next
!= NULL
) {
2186 if (list
->next
->priority
<= pat
->priority
)
2190 pat
->next
= list
->next
;
2195 } else if (top
!= NULL
) {
2200 } else if (list
->priority
<= pat
->priority
) {
2204 while (list
->next
!= NULL
) {
2205 if (list
->next
->priority
<= pat
->priority
)
2209 pat
->next
= list
->next
;
2213 xsltTransformError(NULL
, style
, NULL
,
2214 "xsltAddTemplate: invalid compiled pattern\n");
2215 xsltFreeCompMatch(pat
);
2218 #ifdef WITH_XSLT_DEBUG_PATTERN
2220 xsltGenericDebug(xsltGenericDebugContext
,
2221 "added pattern : '%s' mode '%s' priority %f\n",
2222 pat
->pattern
, pat
->mode
, pat
->priority
);
2224 xsltGenericDebug(xsltGenericDebugContext
,
2225 "added pattern : '%s' priority %f\n",
2226 pat
->pattern
, pat
->priority
);
2235 xsltComputeAllKeys(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
)
2237 if ((ctxt
== NULL
) || (contextNode
== NULL
)) {
2238 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2239 "Internal error in xsltComputeAllKeys(): "
2240 "Bad arguments.\n");
2244 if (ctxt
->document
== NULL
) {
2246 * The document info will only be NULL if we have a RTF.
2248 if (contextNode
->doc
->_private
!= NULL
)
2249 goto doc_info_mismatch
;
2251 * On-demand creation of the document info (needed for keys).
2253 ctxt
->document
= xsltNewDocument(ctxt
, contextNode
->doc
);
2254 if (ctxt
->document
== NULL
)
2257 return xsltInitAllDocKeys(ctxt
);
2260 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2261 "Internal error in xsltComputeAllKeys(): "
2262 "The context's document info doesn't match the "
2263 "document info of the current result tree.\n");
2264 ctxt
->state
= XSLT_STATE_STOPPED
;
2270 * @ctxt: a XSLT process context
2271 * @node: the node being processed
2272 * @style: the current style
2274 * Finds the template applying to this node, if @style is non-NULL
2275 * it means one needs to look for the next imported template in scope.
2277 * Returns the xsltTemplatePtr or NULL if not found
2280 xsltGetTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
2281 xsltStylesheetPtr style
)
2283 xsltStylesheetPtr curstyle
;
2284 xsltTemplatePtr ret
= NULL
;
2285 const xmlChar
*name
= NULL
;
2286 xsltCompMatchPtr list
= NULL
;
2290 if ((ctxt
== NULL
) || (node
== NULL
))
2293 if (style
== NULL
) {
2294 curstyle
= ctxt
->style
;
2296 curstyle
= xsltNextImport(style
);
2299 while ((curstyle
!= NULL
) && (curstyle
!= style
)) {
2300 priority
= XSLT_PAT_NO_PRIORITY
;
2301 /* TODO : handle IDs/keys here ! */
2302 if (curstyle
->templatesHash
!= NULL
) {
2304 * Use the top name as selector
2306 switch (node
->type
) {
2307 case XML_ELEMENT_NODE
:
2308 if (node
->name
[0] == ' ')
2310 case XML_ATTRIBUTE_NODE
:
2314 case XML_DOCUMENT_NODE
:
2315 case XML_HTML_DOCUMENT_NODE
:
2317 case XML_CDATA_SECTION_NODE
:
2318 case XML_COMMENT_NODE
:
2319 case XML_ENTITY_REF_NODE
:
2320 case XML_ENTITY_NODE
:
2321 case XML_DOCUMENT_TYPE_NODE
:
2322 case XML_DOCUMENT_FRAG_NODE
:
2323 case XML_NOTATION_NODE
:
2325 case XML_ELEMENT_DECL
:
2326 case XML_ATTRIBUTE_DECL
:
2327 case XML_ENTITY_DECL
:
2328 case XML_NAMESPACE_DECL
:
2329 case XML_XINCLUDE_START
:
2330 case XML_XINCLUDE_END
:
2339 * find the list of applicable expressions based on the name
2341 list
= (xsltCompMatchPtr
) xmlHashLookup3(curstyle
->templatesHash
,
2342 name
, ctxt
->mode
, ctxt
->modeURI
);
2345 while (list
!= NULL
) {
2346 if (xsltTestCompMatch(ctxt
, list
, node
,
2347 ctxt
->mode
, ctxt
->modeURI
)) {
2348 ret
= list
->template;
2349 priority
= list
->priority
;
2357 * find alternate generic matches
2359 switch (node
->type
) {
2360 case XML_ELEMENT_NODE
:
2361 if (node
->name
[0] == ' ')
2362 list
= curstyle
->rootMatch
;
2364 list
= curstyle
->elemMatch
;
2365 if (node
->psvi
!= NULL
) keyed
= 1;
2367 case XML_ATTRIBUTE_NODE
: {
2370 list
= curstyle
->attrMatch
;
2371 attr
= (xmlAttrPtr
) node
;
2372 if (attr
->psvi
!= NULL
) keyed
= 1;
2376 list
= curstyle
->piMatch
;
2377 if (node
->psvi
!= NULL
) keyed
= 1;
2379 case XML_DOCUMENT_NODE
:
2380 case XML_HTML_DOCUMENT_NODE
: {
2383 list
= curstyle
->rootMatch
;
2384 doc
= (xmlDocPtr
) node
;
2385 if (doc
->psvi
!= NULL
) keyed
= 1;
2389 case XML_CDATA_SECTION_NODE
:
2390 list
= curstyle
->textMatch
;
2391 if (node
->psvi
!= NULL
) keyed
= 1;
2393 case XML_COMMENT_NODE
:
2394 list
= curstyle
->commentMatch
;
2395 if (node
->psvi
!= NULL
) keyed
= 1;
2397 case XML_ENTITY_REF_NODE
:
2398 case XML_ENTITY_NODE
:
2399 case XML_DOCUMENT_TYPE_NODE
:
2400 case XML_DOCUMENT_FRAG_NODE
:
2401 case XML_NOTATION_NODE
:
2403 case XML_ELEMENT_DECL
:
2404 case XML_ATTRIBUTE_DECL
:
2405 case XML_ENTITY_DECL
:
2406 case XML_NAMESPACE_DECL
:
2407 case XML_XINCLUDE_START
:
2408 case XML_XINCLUDE_END
:
2413 while ((list
!= NULL
) &&
2414 ((ret
== NULL
) || (list
->priority
> priority
))) {
2415 if (xsltTestCompMatch(ctxt
, list
, node
,
2416 ctxt
->mode
, ctxt
->modeURI
)) {
2417 ret
= list
->template;
2418 priority
= list
->priority
;
2424 * Some of the tests for elements can also apply to documents
2426 if ((node
->type
== XML_DOCUMENT_NODE
) ||
2427 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
2428 (node
->type
== XML_TEXT_NODE
)) {
2429 list
= curstyle
->elemMatch
;
2430 while ((list
!= NULL
) &&
2431 ((ret
== NULL
) || (list
->priority
> priority
))) {
2432 if (xsltTestCompMatch(ctxt
, list
, node
,
2433 ctxt
->mode
, ctxt
->modeURI
)) {
2434 ret
= list
->template;
2435 priority
= list
->priority
;
2440 } else if ((node
->type
== XML_PI_NODE
) ||
2441 (node
->type
== XML_COMMENT_NODE
)) {
2442 list
= curstyle
->elemMatch
;
2443 while ((list
!= NULL
) &&
2444 ((ret
== NULL
) || (list
->priority
> priority
))) {
2445 if (xsltTestCompMatch(ctxt
, list
, node
,
2446 ctxt
->mode
, ctxt
->modeURI
)) {
2447 ret
= list
->template;
2448 priority
= list
->priority
;
2457 list
= curstyle
->keyMatch
;
2458 while ((list
!= NULL
) &&
2459 ((ret
== NULL
) || (list
->priority
> priority
))) {
2460 if (xsltTestCompMatch(ctxt
, list
, node
,
2461 ctxt
->mode
, ctxt
->modeURI
)) {
2462 ret
= list
->template;
2463 priority
= list
->priority
;
2469 else if (ctxt
->hasTemplKeyPatterns
&&
2470 ((ctxt
->document
== NULL
) ||
2471 (ctxt
->document
->nbKeysComputed
< ctxt
->nbKeys
)))
2474 * Compute all remaining keys for this document.
2476 * REVISIT TODO: I think this could be further optimized.
2478 if (xsltComputeAllKeys(ctxt
, node
) == -1)
2481 switch (node
->type
) {
2482 case XML_ELEMENT_NODE
:
2483 if (node
->psvi
!= NULL
) keyed
= 1;
2485 case XML_ATTRIBUTE_NODE
:
2486 if (((xmlAttrPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2489 case XML_CDATA_SECTION_NODE
:
2490 case XML_COMMENT_NODE
:
2492 if (node
->psvi
!= NULL
) keyed
= 1;
2494 case XML_DOCUMENT_NODE
:
2495 case XML_HTML_DOCUMENT_NODE
:
2496 if (((xmlDocPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2508 * Cycle on next curstylesheet import.
2510 curstyle
= xsltNextImport(curstyle
);
2518 * xsltCleanupTemplates:
2519 * @style: an XSLT stylesheet
2521 * Cleanup the state of the templates used by the stylesheet and
2522 * the ones it imports.
2525 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED
) {
2529 * xsltFreeTemplateHashes:
2530 * @style: an XSLT stylesheet
2532 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2535 xsltFreeTemplateHashes(xsltStylesheetPtr style
) {
2536 if (style
->templatesHash
!= NULL
)
2537 xmlHashFree((xmlHashTablePtr
) style
->templatesHash
,
2538 (xmlHashDeallocator
) xsltFreeCompMatchList
);
2539 if (style
->rootMatch
!= NULL
)
2540 xsltFreeCompMatchList(style
->rootMatch
);
2541 if (style
->keyMatch
!= NULL
)
2542 xsltFreeCompMatchList(style
->keyMatch
);
2543 if (style
->elemMatch
!= NULL
)
2544 xsltFreeCompMatchList(style
->elemMatch
);
2545 if (style
->attrMatch
!= NULL
)
2546 xsltFreeCompMatchList(style
->attrMatch
);
2547 if (style
->parentMatch
!= NULL
)
2548 xsltFreeCompMatchList(style
->parentMatch
);
2549 if (style
->textMatch
!= NULL
)
2550 xsltFreeCompMatchList(style
->textMatch
);
2551 if (style
->piMatch
!= NULL
)
2552 xsltFreeCompMatchList(style
->piMatch
);
2553 if (style
->commentMatch
!= NULL
)
2554 xsltFreeCompMatchList(style
->commentMatch
);