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
19 #ifdef WITH_XSLT_DEBUG
20 #define WITH_XSLT_DEBUG_PATTERN
50 typedef struct _xsltStepState xsltStepState
;
51 typedef xsltStepState
*xsltStepStatePtr
;
52 struct _xsltStepState
{
57 typedef struct _xsltStepStates xsltStepStates
;
58 typedef xsltStepStates
*xsltStepStatesPtr
;
59 struct _xsltStepStates
{
62 xsltStepStatePtr states
;
65 typedef struct _xsltStepOp xsltStepOp
;
66 typedef xsltStepOp
*xsltStepOpPtr
;
72 xmlXPathCompExprPtr comp
;
74 * Optimisations for count
81 struct _xsltCompMatch
{
82 struct _xsltCompMatch
*next
; /* siblings in the name hash */
83 float priority
; /* the priority */
84 const xmlChar
*pattern
; /* the pattern */
85 const xmlChar
*mode
; /* the mode */
86 const xmlChar
*modeURI
; /* the mode URI */
87 xsltTemplatePtr
template; /* the associated template */
90 /* TODO fix the statically allocated size steps[] */
93 xmlNsPtr
*nsList
; /* the namespaces in scope */
94 int nsNr
; /* the number of namespaces in scope */
95 xsltStepOpPtr steps
; /* ops for computation */
98 typedef struct _xsltParserContext xsltParserContext
;
99 typedef xsltParserContext
*xsltParserContextPtr
;
100 struct _xsltParserContext
{
101 xsltStylesheetPtr style
; /* the stylesheet */
102 xsltTransformContextPtr ctxt
; /* the transformation or NULL */
103 const xmlChar
*cur
; /* the current char being parsed */
104 const xmlChar
*base
; /* the full expression */
105 xmlDocPtr doc
; /* the source document */
106 xmlNodePtr elem
; /* the source element */
107 int error
; /* error code */
108 xsltCompMatchPtr comp
; /* the result */
111 /************************************************************************
115 ************************************************************************/
120 * Create a new XSLT CompMatch
122 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
124 static xsltCompMatchPtr
125 xsltNewCompMatch(void) {
126 xsltCompMatchPtr cur
;
128 cur
= (xsltCompMatchPtr
) xmlMalloc(sizeof(xsltCompMatch
));
130 xsltTransformError(NULL
, NULL
, NULL
,
131 "xsltNewCompMatch : out of memory error\n");
134 memset(cur
, 0, sizeof(xsltCompMatch
));
137 cur
-> steps
= (xsltStepOpPtr
) xmlMalloc(sizeof(xsltStepOp
) *
139 if (cur
->steps
== NULL
) {
140 xsltTransformError(NULL
, NULL
, NULL
,
141 "xsltNewCompMatch : out of memory error\n");
153 * @comp: an XSLT comp
155 * Free up the memory allocated by @comp
158 xsltFreeCompMatch(xsltCompMatchPtr comp
) {
164 if (comp
->pattern
!= NULL
)
165 xmlFree((xmlChar
*)comp
->pattern
);
166 if (comp
->nsList
!= NULL
)
167 xmlFree(comp
->nsList
);
168 for (i
= 0;i
< comp
->nbStep
;i
++) {
169 op
= &comp
->steps
[i
];
170 if (op
->value
!= NULL
)
172 if (op
->value2
!= NULL
)
174 if (op
->value3
!= NULL
)
176 if (op
->comp
!= NULL
)
177 xmlXPathFreeCompExpr(op
->comp
);
179 xmlFree(comp
->steps
);
180 memset(comp
, -1, sizeof(xsltCompMatch
));
185 * xsltFreeCompMatchList:
186 * @comp: an XSLT comp list
188 * Free up the memory allocated by all the elements of @comp
191 xsltFreeCompMatchList(xsltCompMatchPtr comp
) {
192 xsltCompMatchPtr cur
;
194 while (comp
!= NULL
) {
197 xsltFreeCompMatch(cur
);
202 * xsltNormalizeCompSteps:
203 * @payload: pointer to template hash table entry
204 * @data: pointer to the stylesheet
205 * @name: template match name
207 * This is a hashtable scanner function to normalize the compiled
208 * steps of an imported stylesheet.
210 void xsltNormalizeCompSteps(void *payload
,
211 void *data
, const xmlChar
*name ATTRIBUTE_UNUSED
) {
212 xsltCompMatchPtr comp
= payload
;
213 xsltStylesheetPtr style
= data
;
216 for (ix
= 0; ix
< comp
->nbStep
; ix
++) {
217 comp
->steps
[ix
].previousExtra
+= style
->extrasNr
;
218 comp
->steps
[ix
].indexExtra
+= style
->extrasNr
;
219 comp
->steps
[ix
].lenExtra
+= style
->extrasNr
;
224 * xsltNewParserContext:
225 * @style: the stylesheet
226 * @ctxt: the transformation context, if done at run-time
228 * Create a new XSLT ParserContext
230 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
232 static xsltParserContextPtr
233 xsltNewParserContext(xsltStylesheetPtr style
, xsltTransformContextPtr ctxt
) {
234 xsltParserContextPtr cur
;
236 cur
= (xsltParserContextPtr
) xmlMalloc(sizeof(xsltParserContext
));
238 xsltTransformError(NULL
, NULL
, NULL
,
239 "xsltNewParserContext : malloc failed\n");
242 memset(cur
, 0, sizeof(xsltParserContext
));
249 * xsltFreeParserContext:
250 * @ctxt: an XSLT parser context
252 * Free up the memory allocated by @ctxt
255 xsltFreeParserContext(xsltParserContextPtr ctxt
) {
258 memset(ctxt
, -1, sizeof(xsltParserContext
));
264 * @comp: the compiled match expression
266 * @value: the first value
267 * @value2: the second value
268 * @novar: flag to set XML_XPATH_NOVAR
270 * Add an step to an XSLT Compiled Match
272 * Returns -1 in case of failure, 0 otherwise.
275 xsltCompMatchAdd(xsltParserContextPtr ctxt
, xsltCompMatchPtr comp
,
276 xsltOp op
, xmlChar
* value
, xmlChar
* value2
, int novar
)
278 if (comp
->nbStep
>= comp
->maxStep
) {
281 tmp
= (xsltStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
284 xsltGenericError(xsltGenericErrorContext
,
285 "xsltCompMatchAdd: memory re-allocation failure.\n");
286 if (ctxt
->style
!= NULL
)
287 ctxt
->style
->errors
++;
297 comp
->steps
[comp
->nbStep
].op
= op
;
298 comp
->steps
[comp
->nbStep
].value
= value
;
299 comp
->steps
[comp
->nbStep
].value2
= value2
;
300 comp
->steps
[comp
->nbStep
].value3
= NULL
;
301 comp
->steps
[comp
->nbStep
].comp
= NULL
;
302 if (ctxt
->ctxt
!= NULL
) {
303 comp
->steps
[comp
->nbStep
].previousExtra
=
304 xsltAllocateExtraCtxt(ctxt
->ctxt
);
305 comp
->steps
[comp
->nbStep
].indexExtra
=
306 xsltAllocateExtraCtxt(ctxt
->ctxt
);
307 comp
->steps
[comp
->nbStep
].lenExtra
=
308 xsltAllocateExtraCtxt(ctxt
->ctxt
);
310 comp
->steps
[comp
->nbStep
].previousExtra
=
311 xsltAllocateExtra(ctxt
->style
);
312 comp
->steps
[comp
->nbStep
].indexExtra
=
313 xsltAllocateExtra(ctxt
->style
);
314 comp
->steps
[comp
->nbStep
].lenExtra
=
315 xsltAllocateExtra(ctxt
->style
);
317 if (op
== XSLT_OP_PREDICATE
) {
318 xmlXPathContextPtr xctxt
;
320 if (ctxt
->style
!= NULL
)
321 xctxt
= xmlXPathNewContext(ctxt
->style
->doc
);
323 xctxt
= xmlXPathNewContext(NULL
);
324 #ifdef XML_XPATH_NOVAR
326 xctxt
->flags
= XML_XPATH_NOVAR
;
328 if (ctxt
->style
!= NULL
)
329 xctxt
->dict
= ctxt
->style
->dict
;
330 comp
->steps
[comp
->nbStep
].comp
= xmlXPathCtxtCompile(xctxt
, value
);
331 xmlXPathFreeContext(xctxt
);
332 if (comp
->steps
[comp
->nbStep
].comp
== NULL
) {
333 xsltTransformError(NULL
, ctxt
->style
, ctxt
->elem
,
334 "Failed to compile predicate\n");
335 if (ctxt
->style
!= NULL
)
336 ctxt
->style
->errors
++;
344 * xsltSwapTopCompMatch:
345 * @comp: the compiled match expression
347 * reverse the two top steps.
350 xsltSwapTopCompMatch(xsltCompMatchPtr comp
) {
352 int j
= comp
->nbStep
- 1;
355 register xmlChar
*tmp
;
357 register xmlXPathCompExprPtr expr
;
360 tmp
= comp
->steps
[i
].value
;
361 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
362 comp
->steps
[j
].value
= tmp
;
363 tmp
= comp
->steps
[i
].value2
;
364 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
365 comp
->steps
[j
].value2
= tmp
;
366 tmp
= comp
->steps
[i
].value3
;
367 comp
->steps
[i
].value3
= comp
->steps
[j
].value3
;
368 comp
->steps
[j
].value3
= tmp
;
369 op
= comp
->steps
[i
].op
;
370 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
371 comp
->steps
[j
].op
= op
;
372 expr
= comp
->steps
[i
].comp
;
373 comp
->steps
[i
].comp
= comp
->steps
[j
].comp
;
374 comp
->steps
[j
].comp
= expr
;
375 t
= comp
->steps
[i
].previousExtra
;
376 comp
->steps
[i
].previousExtra
= comp
->steps
[j
].previousExtra
;
377 comp
->steps
[j
].previousExtra
= t
;
378 t
= comp
->steps
[i
].indexExtra
;
379 comp
->steps
[i
].indexExtra
= comp
->steps
[j
].indexExtra
;
380 comp
->steps
[j
].indexExtra
= t
;
381 t
= comp
->steps
[i
].lenExtra
;
382 comp
->steps
[i
].lenExtra
= comp
->steps
[j
].lenExtra
;
383 comp
->steps
[j
].lenExtra
= t
;
388 * xsltReverseCompMatch:
389 * @ctxt: the parser context
390 * @comp: the compiled match expression
392 * reverse all the stack of expressions
395 xsltReverseCompMatch(xsltParserContextPtr ctxt
, xsltCompMatchPtr comp
) {
397 int j
= comp
->nbStep
- 1;
400 register xmlChar
*tmp
;
402 register xmlXPathCompExprPtr expr
;
405 tmp
= comp
->steps
[i
].value
;
406 comp
->steps
[i
].value
= comp
->steps
[j
].value
;
407 comp
->steps
[j
].value
= tmp
;
408 tmp
= comp
->steps
[i
].value2
;
409 comp
->steps
[i
].value2
= comp
->steps
[j
].value2
;
410 comp
->steps
[j
].value2
= tmp
;
411 tmp
= comp
->steps
[i
].value3
;
412 comp
->steps
[i
].value3
= comp
->steps
[j
].value3
;
413 comp
->steps
[j
].value3
= tmp
;
414 op
= comp
->steps
[i
].op
;
415 comp
->steps
[i
].op
= comp
->steps
[j
].op
;
416 comp
->steps
[j
].op
= op
;
417 expr
= comp
->steps
[i
].comp
;
418 comp
->steps
[i
].comp
= comp
->steps
[j
].comp
;
419 comp
->steps
[j
].comp
= expr
;
420 t
= comp
->steps
[i
].previousExtra
;
421 comp
->steps
[i
].previousExtra
= comp
->steps
[j
].previousExtra
;
422 comp
->steps
[j
].previousExtra
= t
;
423 t
= comp
->steps
[i
].indexExtra
;
424 comp
->steps
[i
].indexExtra
= comp
->steps
[j
].indexExtra
;
425 comp
->steps
[j
].indexExtra
= t
;
426 t
= comp
->steps
[i
].lenExtra
;
427 comp
->steps
[i
].lenExtra
= comp
->steps
[j
].lenExtra
;
428 comp
->steps
[j
].lenExtra
= t
;
432 xsltCompMatchAdd(ctxt
, comp
, XSLT_OP_END
, NULL
, NULL
, 0);
435 * detect consecutive XSLT_OP_PREDICATE indicating a direct
436 * matching should be done.
438 for (i
= 0;i
< comp
->nbStep
- 1;i
++) {
439 if ((comp
->steps
[i
].op
== XSLT_OP_PREDICATE
) &&
440 (comp
->steps
[i
+ 1].op
== XSLT_OP_PREDICATE
)) {
443 if (comp
->pattern
[0] != '/') {
446 query
= xmlStrdup((const xmlChar
*)"//");
447 query
= xmlStrcat(query
, comp
->pattern
);
449 xmlFree((xmlChar
*) comp
->pattern
);
450 comp
->pattern
= query
;
457 /************************************************************************
459 * The interpreter for the precompiled patterns *
461 ************************************************************************/
464 xsltPatPushState(xsltTransformContextPtr ctxt
, xsltStepStates
*states
,
465 int step
, xmlNodePtr node
) {
466 if ((states
->states
== NULL
) || (states
->maxstates
<= 0)) {
467 states
->maxstates
= 4;
468 states
->nbstates
= 0;
469 states
->states
= xmlMalloc(4 * sizeof(xsltStepState
));
471 else if (states
->maxstates
<= states
->nbstates
) {
474 tmp
= (xsltStepStatePtr
) xmlRealloc(states
->states
,
475 2 * states
->maxstates
* sizeof(xsltStepState
));
477 xsltGenericError(xsltGenericErrorContext
,
478 "xsltPatPushState: memory re-allocation failure.\n");
479 ctxt
->state
= XSLT_STATE_STOPPED
;
482 states
->states
= tmp
;
483 states
->maxstates
*= 2;
485 states
->states
[states
->nbstates
].step
= step
;
486 states
->states
[states
->nbstates
++].node
= node
;
488 fprintf(stderr
, "Push: %d, %s\n", step
, node
->name
);
494 * xsltTestCompMatchDirect:
495 * @ctxt: a XSLT process context
496 * @comp: the precompiled pattern
498 * @nsList: the namespaces in scope
499 * @nsNr: the number of namespaces in scope
501 * Test whether the node matches the pattern, do a direct evalutation
502 * and not a step by step evaluation.
504 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
507 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
508 xmlNodePtr node
, xmlNsPtr
*nsList
, int nsNr
) {
509 xsltStepOpPtr sel
= NULL
;
512 xmlXPathObjectPtr list
;
518 if (XSLT_IS_RES_TREE_FRAG(doc
))
522 sel
= &comp
->steps
[0]; /* store extra in first step arbitrarily */
524 prevdoc
= (xmlDocPtr
)
525 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
526 ix
= XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
);
527 list
= (xmlXPathObjectPtr
)
528 XSLT_RUNTIME_EXTRA_LST(ctxt
, sel
->lenExtra
);
530 if ((list
== NULL
) || (prevdoc
!= doc
)) {
531 xmlXPathObjectPtr newlist
;
532 xmlNodePtr parent
= node
->parent
;
535 int oldNsNr
, oldContextSize
, oldProximityPosition
;
536 xmlNsPtr
*oldNamespaces
;
538 oldnode
= ctxt
->xpathCtxt
->node
;
539 olddoc
= ctxt
->xpathCtxt
->doc
;
540 oldNsNr
= ctxt
->xpathCtxt
->nsNr
;
541 oldNamespaces
= ctxt
->xpathCtxt
->namespaces
;
542 oldContextSize
= ctxt
->xpathCtxt
->contextSize
;
543 oldProximityPosition
= ctxt
->xpathCtxt
->proximityPosition
;
544 ctxt
->xpathCtxt
->node
= node
;
545 ctxt
->xpathCtxt
->doc
= doc
;
546 ctxt
->xpathCtxt
->namespaces
= nsList
;
547 ctxt
->xpathCtxt
->nsNr
= nsNr
;
548 newlist
= xmlXPathEval(comp
->pattern
, ctxt
->xpathCtxt
);
549 ctxt
->xpathCtxt
->node
= oldnode
;
550 ctxt
->xpathCtxt
->doc
= olddoc
;
551 ctxt
->xpathCtxt
->namespaces
= oldNamespaces
;
552 ctxt
->xpathCtxt
->nsNr
= oldNsNr
;
553 ctxt
->xpathCtxt
->contextSize
= oldContextSize
;
554 ctxt
->xpathCtxt
->proximityPosition
= oldProximityPosition
;
557 if (newlist
->type
!= XPATH_NODESET
) {
558 xmlXPathFreeObject(newlist
);
563 if ((parent
== NULL
) || (node
->doc
== NULL
) || isRVT
)
568 xmlXPathFreeObject(list
);
571 XSLT_RUNTIME_EXTRA_LST(ctxt
, sel
->lenExtra
) =
573 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
575 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
577 XSLT_RUNTIME_EXTRA_FREE(ctxt
, sel
->lenExtra
) =
578 (xmlFreeFunc
) xmlXPathFreeObject
;
582 if ((list
->nodesetval
== NULL
) ||
583 (list
->nodesetval
->nodeNr
<= 0)) {
585 xmlXPathFreeObject(list
);
588 /* TODO: store the index and use it for the scan */
590 for (j
= 0;j
< list
->nodesetval
->nodeNr
;j
++) {
591 if (list
->nodesetval
->nodeTab
[j
] == node
) {
593 xmlXPathFreeObject(list
);
600 xmlXPathFreeObject(list
);
606 * @ctxt: a XSLT process context
607 * @comp: the precompiled pattern
609 * @mode: the mode name or NULL
610 * @modeURI: the mode URI or NULL
612 * Test whether the node matches the pattern
614 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
617 xsltTestCompMatch(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
618 xmlNodePtr node
, const xmlChar
*mode
,
619 const xmlChar
*modeURI
) {
621 xsltStepOpPtr step
, sel
= NULL
;
622 xsltStepStates states
= {0, 0, NULL
}; /* // may require backtrack */
624 if ((comp
== NULL
) || (node
== NULL
) || (ctxt
== NULL
)) {
625 xsltTransformError(ctxt
, NULL
, node
,
626 "xsltTestCompMatch: null arg\n");
630 if (comp
->mode
== NULL
)
633 * both mode strings must be interned on the stylesheet dictionary
635 if (comp
->mode
!= mode
)
638 if (comp
->mode
!= NULL
)
641 if (modeURI
!= NULL
) {
642 if (comp
->modeURI
== NULL
)
645 * both modeURI strings must be interned on the stylesheet dictionary
647 if (comp
->modeURI
!= modeURI
)
650 if (comp
->modeURI
!= NULL
)
656 for (;i
< comp
->nbStep
;i
++) {
657 step
= &comp
->steps
[i
];
658 if (step
->op
!= XSLT_OP_PREDICATE
)
664 if ((node
->type
== XML_DOCUMENT_NODE
) ||
665 #ifdef LIBXML_DOCB_ENABLED
666 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
668 (node
->type
== XML_HTML_DOCUMENT_NODE
))
670 if ((node
->type
== XML_ELEMENT_NODE
) && (node
->name
[0] == ' '))
674 if (node
->type
!= XML_ELEMENT_NODE
)
676 if (step
->value
== NULL
)
678 if (step
->value
[0] != node
->name
[0])
680 if (!xmlStrEqual(step
->value
, node
->name
))
684 if (node
->ns
== NULL
) {
685 if (step
->value2
!= NULL
)
687 } else if (node
->ns
->href
!= NULL
) {
688 if (step
->value2
== NULL
)
690 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
695 if (node
->type
!= XML_ATTRIBUTE_NODE
)
697 if (step
->value
!= NULL
) {
698 if (step
->value
[0] != node
->name
[0])
700 if (!xmlStrEqual(step
->value
, node
->name
))
704 if (node
->ns
== NULL
) {
705 if (step
->value2
!= NULL
)
707 } else if (step
->value2
!= NULL
) {
708 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
713 if ((node
->type
== XML_DOCUMENT_NODE
) ||
714 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
715 #ifdef LIBXML_DOCB_ENABLED
716 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
718 (node
->type
== XML_NAMESPACE_DECL
))
723 if (step
->value
== NULL
)
725 if (step
->value
[0] != node
->name
[0])
727 if (!xmlStrEqual(step
->value
, node
->name
))
730 if (node
->ns
== NULL
) {
731 if (step
->value2
!= NULL
)
733 } else if (node
->ns
->href
!= NULL
) {
734 if (step
->value2
== NULL
)
736 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
740 case XSLT_OP_ANCESTOR
:
741 /* TODO: implement coalescing of ANCESTOR/NODE ops */
742 if (step
->value
== NULL
) {
743 step
= &comp
->steps
[i
+1];
744 if (step
->op
== XSLT_OP_ROOT
)
746 /* added NS, ID and KEY as a result of bug 168208 */
747 if ((step
->op
!= XSLT_OP_ELEM
) &&
748 (step
->op
!= XSLT_OP_ALL
) &&
749 (step
->op
!= XSLT_OP_NS
) &&
750 (step
->op
!= XSLT_OP_ID
) &&
751 (step
->op
!= XSLT_OP_KEY
))
756 if ((node
->type
== XML_DOCUMENT_NODE
) ||
757 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
758 #ifdef LIBXML_DOCB_ENABLED
759 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
761 (node
->type
== XML_NAMESPACE_DECL
))
764 if ((step
->op
!= XSLT_OP_ELEM
) && step
->op
!= XSLT_OP_ALL
) {
765 xsltPatPushState(ctxt
, &states
, i
, node
);
769 if (step
->value
== NULL
) {
770 xsltPatPushState(ctxt
, &states
, i
- 1, node
);
773 while (node
!= NULL
) {
774 if ((node
->type
== XML_ELEMENT_NODE
) &&
775 (step
->value
[0] == node
->name
[0]) &&
776 (xmlStrEqual(step
->value
, node
->name
))) {
778 if (node
->ns
== NULL
) {
779 if (step
->value2
== NULL
)
781 } else if (node
->ns
->href
!= NULL
) {
782 if ((step
->value2
!= NULL
) &&
783 (xmlStrEqual(step
->value2
, node
->ns
->href
)))
791 xsltPatPushState(ctxt
, &states
, i
- 1, node
);
794 /* TODO Handle IDs decently, must be done differently */
797 if (node
->type
!= XML_ELEMENT_NODE
)
800 id
= xmlGetID(node
->doc
, step
->value
);
801 if ((id
== NULL
) || (id
->parent
!= node
))
809 list
= xsltGetKey(ctxt
, step
->value
,
810 step
->value3
, step
->value2
);
813 for (indx
= 0;indx
< list
->nodeNr
;indx
++)
814 if (list
->nodeTab
[indx
] == node
)
816 if (indx
>= list
->nodeNr
)
821 if (node
->type
!= XML_ELEMENT_NODE
)
823 if (node
->ns
== NULL
) {
824 if (step
->value
!= NULL
)
826 } else if (node
->ns
->href
!= NULL
) {
827 if (step
->value
== NULL
)
829 if (!xmlStrEqual(step
->value
, node
->ns
->href
))
834 if (node
->type
!= XML_ELEMENT_NODE
)
837 case XSLT_OP_PREDICATE
: {
841 int pos
= 0, len
= 0;
845 * when there is cascading XSLT_OP_PREDICATE, then use a
846 * direct computation approach. It's not done directly
847 * at the beginning of the routine to filter out as much
848 * as possible this costly computation.
851 if (states
.states
!= NULL
) {
852 /* Free the rollback states */
853 xmlFree(states
.states
);
855 return(xsltTestCompMatchDirect(ctxt
, comp
, node
,
856 comp
->nsList
, comp
->nsNr
));
860 if (XSLT_IS_RES_TREE_FRAG(doc
))
866 * Depending on the last selection, one may need to
867 * recompute contextSize and proximityPosition.
869 oldCS
= ctxt
->xpathCtxt
->contextSize
;
870 oldCP
= ctxt
->xpathCtxt
->proximityPosition
;
872 (sel
->op
== XSLT_OP_ELEM
) &&
873 (sel
->value
!= NULL
) &&
874 (node
->type
== XML_ELEMENT_NODE
) &&
875 (node
->parent
!= NULL
)) {
879 previous
= (xmlNodePtr
)
880 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
881 if ((previous
!= NULL
) &&
882 (previous
->parent
== node
->parent
)) {
884 * just walk back to adjust the index
887 xmlNodePtr sibling
= node
;
889 while (sibling
!= NULL
) {
890 if (sibling
== previous
)
892 if ((sibling
->type
== XML_ELEMENT_NODE
) &&
893 (previous
->name
!= NULL
) &&
894 (sibling
->name
!= NULL
) &&
895 (previous
->name
[0] == sibling
->name
[0]) &&
896 (xmlStrEqual(previous
->name
, sibling
->name
)))
898 if ((sel
->value2
== NULL
) ||
899 ((sibling
->ns
!= NULL
) &&
900 (xmlStrEqual(sel
->value2
,
901 sibling
->ns
->href
))))
904 sibling
= sibling
->prev
;
906 if (sibling
== NULL
) {
907 /* hum going backward in document order ... */
910 while (sibling
!= NULL
) {
911 if (sibling
== previous
)
913 if ((sibling
->type
== XML_ELEMENT_NODE
) &&
914 (previous
->name
!= NULL
) &&
915 (sibling
->name
!= NULL
) &&
916 (previous
->name
[0] == sibling
->name
[0]) &&
917 (xmlStrEqual(previous
->name
, sibling
->name
)))
919 if ((sel
->value2
== NULL
) ||
920 ((sibling
->ns
!= NULL
) &&
921 (xmlStrEqual(sel
->value2
,
922 sibling
->ns
->href
))))
927 sibling
= sibling
->next
;
930 if (sibling
!= NULL
) {
931 pos
= XSLT_RUNTIME_EXTRA(ctxt
,
932 sel
->indexExtra
, ival
) + indx
;
934 * If the node is in a Value Tree we need to
935 * save len, but cannot cache the node!
936 * (bugs 153137 and 158840)
938 if (node
->doc
!= NULL
) {
939 len
= XSLT_RUNTIME_EXTRA(ctxt
,
940 sel
->lenExtra
, ival
);
942 XSLT_RUNTIME_EXTRA(ctxt
,
943 sel
->previousExtra
, ptr
) = node
;
944 XSLT_RUNTIME_EXTRA(ctxt
,
945 sel
->indexExtra
, ival
) = pos
;
952 * recompute the index
954 xmlNodePtr parent
= node
->parent
;
955 xmlNodePtr siblings
= NULL
;
957 if (parent
) siblings
= parent
->children
;
959 while (siblings
!= NULL
) {
960 if (siblings
->type
== XML_ELEMENT_NODE
) {
961 if (siblings
== node
) {
964 } else if ((node
->name
!= NULL
) &&
965 (siblings
->name
!= NULL
) &&
966 (node
->name
[0] == siblings
->name
[0]) &&
967 (xmlStrEqual(node
->name
, siblings
->name
))) {
968 if ((sel
->value2
== NULL
) ||
969 ((siblings
->ns
!= NULL
) &&
970 (xmlStrEqual(sel
->value2
,
971 siblings
->ns
->href
))))
975 siblings
= siblings
->next
;
977 if ((parent
== NULL
) || (node
->doc
== NULL
))
980 while (parent
->parent
!= NULL
)
981 parent
= parent
->parent
;
982 if (((parent
->type
!= XML_DOCUMENT_NODE
) &&
983 (parent
->type
!= XML_HTML_DOCUMENT_NODE
)) ||
984 (parent
!= (xmlNodePtr
) node
->doc
))
989 ctxt
->xpathCtxt
->contextSize
= len
;
990 ctxt
->xpathCtxt
->proximityPosition
= pos
;
992 * If the node is in a Value Tree we cannot
995 if ((!isRVT
) && (node
->doc
!= NULL
) &&
997 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
999 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
1001 XSLT_RUNTIME_EXTRA(ctxt
, sel
->lenExtra
, ival
) =
1005 } else if ((sel
!= NULL
) && (sel
->op
== XSLT_OP_ALL
) &&
1006 (node
->type
== XML_ELEMENT_NODE
)) {
1007 xmlNodePtr previous
;
1010 previous
= (xmlNodePtr
)
1011 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
1012 if ((previous
!= NULL
) &&
1013 (previous
->parent
== node
->parent
)) {
1015 * just walk back to adjust the index
1018 xmlNodePtr sibling
= node
;
1020 while (sibling
!= NULL
) {
1021 if (sibling
== previous
)
1023 if (sibling
->type
== XML_ELEMENT_NODE
)
1025 sibling
= sibling
->prev
;
1027 if (sibling
== NULL
) {
1028 /* hum going backward in document order ... */
1031 while (sibling
!= NULL
) {
1032 if (sibling
== previous
)
1034 if (sibling
->type
== XML_ELEMENT_NODE
)
1036 sibling
= sibling
->next
;
1039 if (sibling
!= NULL
) {
1040 pos
= XSLT_RUNTIME_EXTRA(ctxt
,
1041 sel
->indexExtra
, ival
) + indx
;
1043 * If the node is in a Value Tree we cannot
1046 if ((node
->doc
!= NULL
) && !isRVT
) {
1047 len
= XSLT_RUNTIME_EXTRA(ctxt
,
1048 sel
->lenExtra
, ival
);
1049 XSLT_RUNTIME_EXTRA(ctxt
,
1050 sel
->previousExtra
, ptr
) = node
;
1051 XSLT_RUNTIME_EXTRA(ctxt
,
1052 sel
->indexExtra
, ival
) = pos
;
1058 * recompute the index
1060 xmlNodePtr parent
= node
->parent
;
1061 xmlNodePtr siblings
= NULL
;
1063 if (parent
) siblings
= parent
->children
;
1065 while (siblings
!= NULL
) {
1066 if (siblings
->type
== XML_ELEMENT_NODE
) {
1068 if (siblings
== node
) {
1072 siblings
= siblings
->next
;
1074 if ((parent
== NULL
) || (node
->doc
== NULL
))
1077 while (parent
->parent
!= NULL
)
1078 parent
= parent
->parent
;
1079 if (((parent
->type
!= XML_DOCUMENT_NODE
) &&
1080 (parent
->type
!= XML_HTML_DOCUMENT_NODE
)) ||
1081 (parent
!= (xmlNodePtr
) node
->doc
))
1086 ctxt
->xpathCtxt
->contextSize
= len
;
1087 ctxt
->xpathCtxt
->proximityPosition
= pos
;
1089 * If the node is in a Value Tree we cannot
1092 if ((node
->doc
!= NULL
) && (nocache
== 0) && !isRVT
) {
1093 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
1095 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
1097 XSLT_RUNTIME_EXTRA(ctxt
, sel
->lenExtra
, ival
) =
1102 oldNode
= ctxt
->node
;
1105 if (step
->value
== NULL
)
1107 if (step
->comp
== NULL
)
1110 if (!xsltEvalXPathPredicate(ctxt
, step
->comp
, comp
->nsList
,
1115 ctxt
->xpathCtxt
->contextSize
= oldCS
;
1116 ctxt
->xpathCtxt
->proximityPosition
= oldCP
;
1118 ctxt
->node
= oldNode
;
1122 ctxt
->xpathCtxt
->contextSize
= oldCS
;
1123 ctxt
->xpathCtxt
->proximityPosition
= oldCP
;
1125 ctxt
->node
= oldNode
;
1129 if (node
->type
!= XML_PI_NODE
)
1131 if (step
->value
!= NULL
) {
1132 if (!xmlStrEqual(step
->value
, node
->name
))
1136 case XSLT_OP_COMMENT
:
1137 if (node
->type
!= XML_COMMENT_NODE
)
1141 if ((node
->type
!= XML_TEXT_NODE
) &&
1142 (node
->type
!= XML_CDATA_SECTION_NODE
))
1146 switch (node
->type
) {
1147 case XML_ELEMENT_NODE
:
1148 case XML_CDATA_SECTION_NODE
:
1150 case XML_COMMENT_NODE
:
1160 if (states
.states
!= NULL
) {
1161 /* Free the rollback states */
1162 xmlFree(states
.states
);
1166 /* got an error try to rollback */
1167 if (states
.states
== NULL
)
1169 if (states
.nbstates
<= 0) {
1170 xmlFree(states
.states
);
1174 i
= states
.states
[states
.nbstates
].step
;
1175 node
= states
.states
[states
.nbstates
].node
;
1177 fprintf(stderr
, "Pop: %d, %s\n", i
, node
->name
);
1183 * xsltTestCompMatchList:
1184 * @ctxt: a XSLT process context
1186 * @comp: the precompiled pattern list
1188 * Test whether the node matches one of the patterns in the list
1190 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1193 xsltTestCompMatchList(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1194 xsltCompMatchPtr comp
) {
1197 if ((ctxt
== NULL
) || (node
== NULL
))
1199 while (comp
!= NULL
) {
1200 ret
= xsltTestCompMatch(ctxt
, comp
, node
, NULL
, NULL
);
1208 /************************************************************************
1210 * Dedicated parser for templates *
1212 ************************************************************************/
1214 #define CUR (*ctxt->cur)
1215 #define SKIP(val) ctxt->cur += (val)
1216 #define NXT(val) ctxt->cur[(val)]
1217 #define CUR_PTR ctxt->cur
1219 #define SKIP_BLANKS \
1220 while (IS_BLANK_CH(CUR)) NEXT
1222 #define CURRENT (*ctxt->cur)
1223 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1226 #define PUSH(op, val, val2, novar) \
1227 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1230 xsltSwapTopCompMatch(ctxt->comp);
1232 #define XSLT_ERROR(X) \
1233 { xsltError(ctxt, __FILE__, __LINE__, X); \
1234 ctxt->error = (X); return; }
1236 #define XSLT_ERROR0(X) \
1237 { xsltError(ctxt, __FILE__, __LINE__, X); \
1238 ctxt->error = (X); return(0); }
1242 * @ctxt: the XPath Parser context
1244 * Parse an XPath Litteral:
1246 * [29] Literal ::= '"' [^"]* '"'
1249 * Returns the Literal parsed or NULL
1253 xsltScanLiteral(xsltParserContextPtr ctxt
) {
1254 const xmlChar
*q
, *cur
;
1255 xmlChar
*ret
= NULL
;
1262 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1263 while ((IS_CHAR(val
)) && (val
!= '"')) {
1265 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1267 if (!IS_CHAR(val
)) {
1271 ret
= xmlStrndup(q
, cur
- q
);
1275 } else if (CUR
== '\'') {
1278 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1279 while ((IS_CHAR(val
)) && (val
!= '\'')) {
1281 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1283 if (!IS_CHAR(val
)) {
1287 ret
= xmlStrndup(q
, cur
- q
);
1292 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1301 * @ctxt: the XPath Parser context
1303 * Parses a non qualified name
1305 * Returns the Name parsed or NULL
1309 xsltScanNCName(xsltParserContextPtr ctxt
) {
1310 const xmlChar
*q
, *cur
;
1311 xmlChar
*ret
= NULL
;
1317 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1318 if (!IS_LETTER(val
) && (val
!= '_'))
1321 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
1322 (val
== '.') || (val
== '-') ||
1324 (IS_COMBINING(val
)) ||
1325 (IS_EXTENDER(val
))) {
1327 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1329 ret
= xmlStrndup(q
, cur
- q
);
1335 * xsltCompileIdKeyPattern:
1336 * @ctxt: the compilation context
1337 * @name: a preparsed name
1338 * @aid: whether id/key are allowed there
1339 * @novar: flag to prohibit xslt var
1341 * Compile the XSLT LocationIdKeyPattern
1342 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1343 * | 'key' '(' Literal ',' Literal ')'
1345 * also handle NodeType and PI from:
1347 * [7] NodeTest ::= NameTest
1348 * | NodeType '(' ')'
1349 * | 'processing-instruction' '(' Literal ')'
1352 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt
, xmlChar
*name
,
1353 int aid
, int novar
, xsltAxis axis
) {
1354 xmlChar
*lit
= NULL
;
1355 xmlChar
*lit2
= NULL
;
1358 xsltTransformError(NULL
, NULL
, NULL
,
1359 "xsltCompileIdKeyPattern : ( expected\n");
1363 if ((aid
) && (xmlStrEqual(name
, (const xmlChar
*)"id"))) {
1365 xsltTransformError(NULL
, NULL
, NULL
,
1366 "xsltCompileIdKeyPattern : NodeTest expected\n");
1372 lit
= xsltScanLiteral(ctxt
);
1374 xsltTransformError(NULL
, NULL
, NULL
,
1375 "xsltCompileIdKeyPattern : Literal expected\n");
1380 xsltTransformError(NULL
, NULL
, NULL
,
1381 "xsltCompileIdKeyPattern : ) expected\n");
1387 PUSH(XSLT_OP_ID
, lit
, NULL
, novar
);
1389 } else if ((aid
) && (xmlStrEqual(name
, (const xmlChar
*)"key"))) {
1391 xsltTransformError(NULL
, NULL
, NULL
,
1392 "xsltCompileIdKeyPattern : NodeTest expected\n");
1398 lit
= xsltScanLiteral(ctxt
);
1400 xsltTransformError(NULL
, NULL
, NULL
,
1401 "xsltCompileIdKeyPattern : Literal expected\n");
1406 xsltTransformError(NULL
, NULL
, NULL
,
1407 "xsltCompileIdKeyPattern : , expected\n");
1413 lit2
= xsltScanLiteral(ctxt
);
1415 xsltTransformError(NULL
, NULL
, NULL
,
1416 "xsltCompileIdKeyPattern : Literal expected\n");
1422 xsltTransformError(NULL
, NULL
, NULL
,
1423 "xsltCompileIdKeyPattern : ) expected\n");
1430 /* URGENT TODO: support namespace in keys */
1431 PUSH(XSLT_OP_KEY
, lit
, lit2
, novar
);
1434 } else if (xmlStrEqual(name
, (const xmlChar
*)"processing-instruction")) {
1438 lit
= xsltScanLiteral(ctxt
);
1440 xsltTransformError(NULL
, NULL
, NULL
,
1441 "xsltCompileIdKeyPattern : Literal expected\n");
1446 xsltTransformError(NULL
, NULL
, NULL
,
1447 "xsltCompileIdKeyPattern : ) expected\n");
1453 PUSH(XSLT_OP_PI
, lit
, NULL
, novar
);
1455 } else if (xmlStrEqual(name
, (const xmlChar
*)"text")) {
1459 xsltTransformError(NULL
, NULL
, NULL
,
1460 "xsltCompileIdKeyPattern : ) expected\n");
1465 PUSH(XSLT_OP_TEXT
, NULL
, NULL
, novar
);
1466 } else if (xmlStrEqual(name
, (const xmlChar
*)"comment")) {
1470 xsltTransformError(NULL
, NULL
, NULL
,
1471 "xsltCompileIdKeyPattern : ) expected\n");
1476 PUSH(XSLT_OP_COMMENT
, NULL
, NULL
, novar
);
1477 } else if (xmlStrEqual(name
, (const xmlChar
*)"node")) {
1481 xsltTransformError(NULL
, NULL
, NULL
,
1482 "xsltCompileIdKeyPattern : ) expected\n");
1487 if (axis
== AXIS_ATTRIBUTE
) {
1488 PUSH(XSLT_OP_ATTR
, NULL
, NULL
, novar
);
1491 PUSH(XSLT_OP_NODE
, NULL
, NULL
, novar
);
1494 xsltTransformError(NULL
, NULL
, NULL
,
1495 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1499 xsltTransformError(NULL
, NULL
, NULL
,
1500 "xsltCompileIdKeyPattern : node type\n");
1509 * xsltCompileStepPattern:
1510 * @ctxt: the compilation context
1511 * @token: a posible precompiled name
1512 * @novar: flag to prohibit xslt variables from pattern
1514 * Compile the XSLT StepPattern and generates a precompiled
1515 * form suitable for fast matching.
1517 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1518 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1519 * | ('child' | 'attribute') '::'
1521 * [7] NodeTest ::= NameTest
1522 * | NodeType '(' ')'
1523 * | 'processing-instruction' '(' Literal ')'
1524 * [8] Predicate ::= '[' PredicateExpr ']'
1525 * [9] PredicateExpr ::= Expr
1526 * [13] AbbreviatedAxisSpecifier ::= '@'?
1527 * [37] NameTest ::= '*' | NCName ':' '*' | QName
1531 xsltCompileStepPattern(xsltParserContextPtr ctxt
, xmlChar
*token
, int novar
) {
1532 xmlChar
*name
= NULL
;
1533 const xmlChar
*URI
= NULL
;
1534 xmlChar
*URL
= NULL
;
1539 if ((token
== NULL
) && (CUR
== '@')) {
1541 axis
= AXIS_ATTRIBUTE
;
1545 token
= xsltScanNCName(ctxt
);
1546 if (token
== NULL
) {
1549 if (axis
== AXIS_ATTRIBUTE
) {
1550 PUSH(XSLT_OP_ATTR
, NULL
, NULL
, novar
);
1553 PUSH(XSLT_OP_ALL
, NULL
, NULL
, novar
);
1555 goto parse_predicate
;
1557 xsltTransformError(NULL
, NULL
, NULL
,
1558 "xsltCompileStepPattern : Name expected\n");
1567 xsltCompileIdKeyPattern(ctxt
, token
, 0, novar
, axis
);
1572 } else if (CUR
== ':') {
1575 xmlChar
*prefix
= token
;
1579 * This is a namespace match
1581 token
= xsltScanNCName(ctxt
);
1582 ns
= xmlSearchNs(ctxt
->doc
, ctxt
->elem
, prefix
);
1584 xsltTransformError(NULL
, NULL
, NULL
,
1585 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1592 URL
= xmlStrdup(ns
->href
);
1596 if (token
== NULL
) {
1599 if (axis
== AXIS_ATTRIBUTE
) {
1600 PUSH(XSLT_OP_ATTR
, NULL
, URL
, novar
);
1604 PUSH(XSLT_OP_NS
, URL
, NULL
, novar
);
1608 xsltTransformError(NULL
, NULL
, NULL
,
1609 "xsltCompileStepPattern : Name expected\n");
1614 if (axis
== AXIS_ATTRIBUTE
) {
1615 PUSH(XSLT_OP_ATTR
, token
, URL
, novar
);
1620 PUSH(XSLT_OP_ELEM
, token
, URL
, novar
);
1627 xsltTransformError(NULL
, NULL
, NULL
,
1628 "xsltCompileStepPattern : NodeTest expected\n");
1633 if (xmlStrEqual(token
, (const xmlChar
*) "child")) {
1635 } else if (xmlStrEqual(token
, (const xmlChar
*) "attribute")) {
1636 axis
= AXIS_ATTRIBUTE
;
1638 xsltTransformError(NULL
, NULL
, NULL
,
1639 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1646 token
= xsltScanNCName(ctxt
);
1647 goto parse_node_test
;
1650 URI
= xsltGetQNameURI(ctxt
->elem
, &token
);
1651 if (token
== NULL
) {
1656 URL
= xmlStrdup(URI
);
1657 if (axis
== AXIS_ATTRIBUTE
) {
1658 PUSH(XSLT_OP_ATTR
, token
, URL
, novar
);
1663 PUSH(XSLT_OP_ELEM
, token
, URL
, novar
);
1671 while (CUR
== '[') {
1673 xmlChar
*ret
= NULL
;
1679 /* Skip over nested predicates */
1682 else if (CUR
== ']') {
1686 } else if (CUR
== '"') {
1688 while ((CUR
!= 0) && (CUR
!= '"'))
1690 } else if (CUR
== '\'') {
1692 while ((CUR
!= 0) && (CUR
!= '\''))
1698 xsltTransformError(NULL
, NULL
, NULL
,
1699 "xsltCompileStepPattern : ']' expected\n");
1703 ret
= xmlStrndup(q
, CUR_PTR
- q
);
1704 PUSH(XSLT_OP_PREDICATE
, ret
, NULL
, novar
);
1706 /* push the predicate lower than local test */
1720 * xsltCompileRelativePathPattern:
1721 * @comp: the compilation context
1722 * @token: a posible precompiled name
1723 * @novar: flag to prohibit xslt variables
1725 * Compile the XSLT RelativePathPattern and generates a precompiled
1726 * form suitable for fast matching.
1728 * [4] RelativePathPattern ::= StepPattern
1729 * | RelativePathPattern '/' StepPattern
1730 * | RelativePathPattern '//' StepPattern
1733 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt
, xmlChar
*token
, int novar
) {
1734 xsltCompileStepPattern(ctxt
, token
, novar
);
1738 while ((CUR
!= 0) && (CUR
!= '|')) {
1739 if ((CUR
== '/') && (NXT(1) == '/')) {
1740 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1744 xsltCompileStepPattern(ctxt
, NULL
, novar
);
1745 } else if (CUR
== '/') {
1746 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1749 if ((CUR
!= 0) && (CUR
!= '|')) {
1750 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1764 * xsltCompileLocationPathPattern:
1765 * @ctxt: the compilation context
1766 * @novar: flag to prohibit xslt variables
1768 * Compile the XSLT LocationPathPattern and generates a precompiled
1769 * form suitable for fast matching.
1771 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1772 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1773 * | '//'? RelativePathPattern
1776 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt
, int novar
) {
1778 if ((CUR
== '/') && (NXT(1) == '/')) {
1780 * since we reverse the query
1781 * a leading // can be safely ignored
1785 ctxt
->comp
->priority
= 0.5; /* '//' means not 0 priority */
1786 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1787 } else if (CUR
== '/') {
1789 * We need to find root as the parent
1793 PUSH(XSLT_OP_ROOT
, NULL
, NULL
, novar
);
1794 if ((CUR
!= 0) && (CUR
!= '|')) {
1795 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1796 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1798 } else if (CUR
== '*') {
1799 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1800 } else if (CUR
== '@') {
1801 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1804 name
= xsltScanNCName(ctxt
);
1806 xsltTransformError(NULL
, NULL
, NULL
,
1807 "xsltCompileLocationPathPattern : Name expected\n");
1812 if ((CUR
== '(') && !xmlXPathIsNodeType(name
)) {
1813 xsltCompileIdKeyPattern(ctxt
, name
, 1, novar
, 0);
1816 if ((CUR
== '/') && (NXT(1) == '/')) {
1817 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1821 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1822 } else if (CUR
== '/') {
1823 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1826 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1830 xsltCompileRelativePathPattern(ctxt
, name
, novar
);
1837 * xsltCompilePatternInternal:
1838 * @pattern: an XSLT pattern
1839 * @doc: the containing document
1840 * @node: the containing element
1841 * @style: the stylesheet
1842 * @runtime: the transformation context, if done at run-time
1843 * @novar: flag to prohibit xslt variables
1845 * Compile the XSLT pattern and generates a list of precompiled form suitable
1846 * for fast matching.
1848 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1850 * Returns the generated pattern list or NULL in case of failure
1853 static xsltCompMatchPtr
1854 xsltCompilePatternInternal(const xmlChar
*pattern
, xmlDocPtr doc
,
1855 xmlNodePtr node
, xsltStylesheetPtr style
,
1856 xsltTransformContextPtr runtime
, int novar
) {
1857 xsltParserContextPtr ctxt
= NULL
;
1858 xsltCompMatchPtr element
, first
= NULL
, previous
= NULL
;
1859 int current
, start
, end
, level
, j
;
1861 if (pattern
== NULL
) {
1862 xsltTransformError(NULL
, NULL
, node
,
1863 "xsltCompilePattern : NULL pattern\n");
1867 ctxt
= xsltNewParserContext(style
, runtime
);
1873 while (pattern
[current
] != 0) {
1875 while (IS_BLANK_CH(pattern
[current
]))
1879 while ((pattern
[end
] != 0) && ((pattern
[end
] != '|') || (level
!= 0))) {
1880 if (pattern
[end
] == '[')
1882 else if (pattern
[end
] == ']')
1884 else if (pattern
[end
] == '\'') {
1886 while ((pattern
[end
] != 0) && (pattern
[end
] != '\''))
1888 } else if (pattern
[end
] == '"') {
1890 while ((pattern
[end
] != 0) && (pattern
[end
] != '"'))
1893 if (pattern
[end
] == 0)
1897 if (current
== end
) {
1898 xsltTransformError(NULL
, NULL
, node
,
1899 "xsltCompilePattern : NULL pattern\n");
1902 element
= xsltNewCompMatch();
1903 if (element
== NULL
) {
1908 else if (previous
!= NULL
)
1909 previous
->next
= element
;
1912 ctxt
->comp
= element
;
1913 ctxt
->base
= xmlStrndup(&pattern
[start
], end
- start
);
1914 if (ctxt
->base
== NULL
)
1916 ctxt
->cur
= &(ctxt
->base
)[current
- start
];
1917 element
->pattern
= ctxt
->base
;
1918 element
->nsList
= xmlGetNsList(doc
, node
);
1920 if (element
->nsList
!= NULL
) {
1921 while (element
->nsList
[j
] != NULL
)
1927 #ifdef WITH_XSLT_DEBUG_PATTERN
1928 xsltGenericDebug(xsltGenericDebugContext
,
1929 "xsltCompilePattern : parsing '%s'\n",
1933 Preset default priority to be zero.
1934 This may be changed by xsltCompileLocationPathPattern.
1936 element
->priority
= 0;
1937 xsltCompileLocationPathPattern(ctxt
, novar
);
1939 xsltTransformError(NULL
, style
, node
,
1940 "xsltCompilePattern : failed to compile '%s'\n",
1942 if (style
!= NULL
) style
->errors
++;
1947 * Reverse for faster interpretation.
1949 xsltReverseCompMatch(ctxt
, element
);
1952 * Set-up the priority
1954 if (element
->priority
== 0) { /* if not yet determined */
1955 if (((element
->steps
[0].op
== XSLT_OP_ELEM
) ||
1956 (element
->steps
[0].op
== XSLT_OP_ATTR
) ||
1957 (element
->steps
[0].op
== XSLT_OP_PI
)) &&
1958 (element
->steps
[0].value
!= NULL
) &&
1959 (element
->steps
[1].op
== XSLT_OP_END
)) {
1960 ; /* previously preset */
1961 } else if ((element
->steps
[0].op
== XSLT_OP_ATTR
) &&
1962 (element
->steps
[0].value2
!= NULL
) &&
1963 (element
->steps
[1].op
== XSLT_OP_END
)) {
1964 element
->priority
= -0.25;
1965 } else if ((element
->steps
[0].op
== XSLT_OP_NS
) &&
1966 (element
->steps
[0].value
!= NULL
) &&
1967 (element
->steps
[1].op
== XSLT_OP_END
)) {
1968 element
->priority
= -0.25;
1969 } else if ((element
->steps
[0].op
== XSLT_OP_ATTR
) &&
1970 (element
->steps
[0].value
== NULL
) &&
1971 (element
->steps
[0].value2
== NULL
) &&
1972 (element
->steps
[1].op
== XSLT_OP_END
)) {
1973 element
->priority
= -0.5;
1974 } else if (((element
->steps
[0].op
== XSLT_OP_PI
) ||
1975 (element
->steps
[0].op
== XSLT_OP_TEXT
) ||
1976 (element
->steps
[0].op
== XSLT_OP_ALL
) ||
1977 (element
->steps
[0].op
== XSLT_OP_NODE
) ||
1978 (element
->steps
[0].op
== XSLT_OP_COMMENT
)) &&
1979 (element
->steps
[1].op
== XSLT_OP_END
)) {
1980 element
->priority
= -0.5;
1982 element
->priority
= 0.5;
1985 #ifdef WITH_XSLT_DEBUG_PATTERN
1986 xsltGenericDebug(xsltGenericDebugContext
,
1987 "xsltCompilePattern : parsed %s, default priority %f\n",
1988 element
->pattern
, element
->priority
);
1990 if (pattern
[end
] == '|')
1995 xsltTransformError(NULL
, style
, node
,
1996 "xsltCompilePattern : NULL pattern\n");
1997 if (style
!= NULL
) style
->errors
++;
2001 xsltFreeParserContext(ctxt
);
2006 xsltFreeParserContext(ctxt
);
2008 xsltFreeCompMatchList(first
);
2013 * xsltCompilePattern:
2014 * @pattern: an XSLT pattern
2015 * @doc: the containing document
2016 * @node: the containing element
2017 * @style: the stylesheet
2018 * @runtime: the transformation context, if done at run-time
2020 * Compile the XSLT pattern and generates a list of precompiled form suitable
2021 * for fast matching.
2023 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2025 * Returns the generated pattern list or NULL in case of failure
2029 xsltCompilePattern(const xmlChar
*pattern
, xmlDocPtr doc
,
2030 xmlNodePtr node
, xsltStylesheetPtr style
,
2031 xsltTransformContextPtr runtime
) {
2032 return (xsltCompilePatternInternal(pattern
, doc
, node
, style
, runtime
, 0));
2035 /************************************************************************
2037 * Module interfaces *
2039 ************************************************************************/
2043 * @style: an XSLT stylesheet
2044 * @cur: an XSLT template
2045 * @mode: the mode name or NULL
2046 * @modeURI: the mode URI or NULL
2048 * Register the XSLT pattern associated to @cur
2050 * Returns -1 in case of error, 0 otherwise
2053 xsltAddTemplate(xsltStylesheetPtr style
, xsltTemplatePtr cur
,
2054 const xmlChar
*mode
, const xmlChar
*modeURI
) {
2055 xsltCompMatchPtr pat
, list
, next
;
2057 * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2058 * avoids gcc 'type-punned pointer' warning.
2061 const xmlChar
*name
= NULL
;
2062 float priority
; /* the priority */
2064 if ((style
== NULL
) || (cur
== NULL
) || (cur
->match
== NULL
))
2067 priority
= cur
->priority
;
2068 pat
= xsltCompilePatternInternal(cur
->match
, style
->doc
, cur
->elem
,
2077 pat
->template = cur
;
2079 pat
->mode
= xmlDictLookup(style
->dict
, mode
, -1);
2080 if (modeURI
!= NULL
)
2081 pat
->modeURI
= xmlDictLookup(style
->dict
, modeURI
, -1);
2082 if (priority
!= XSLT_PAT_NO_PRIORITY
)
2083 pat
->priority
= priority
;
2086 * insert it in the hash table list corresponding to its lookup name
2088 switch (pat
->steps
[0].op
) {
2090 if (pat
->steps
[0].value
!= NULL
)
2091 name
= pat
->steps
[0].value
;
2093 top
= &(style
->attrMatch
);
2095 case XSLT_OP_PARENT
:
2096 case XSLT_OP_ANCESTOR
:
2097 top
= &(style
->elemMatch
);
2100 top
= &(style
->rootMatch
);
2103 top
= &(style
->keyMatch
);
2106 /* TODO optimize ID !!! */
2109 top
= &(style
->elemMatch
);
2112 case XSLT_OP_PREDICATE
:
2113 xsltTransformError(NULL
, style
, NULL
,
2114 "xsltAddTemplate: invalid compiled pattern\n");
2115 xsltFreeCompMatch(pat
);
2118 * TODO: some flags at the top level about type based patterns
2119 * would be faster than inclusion in the hash table.
2122 if (pat
->steps
[0].value
!= NULL
)
2123 name
= pat
->steps
[0].value
;
2125 top
= &(style
->piMatch
);
2127 case XSLT_OP_COMMENT
:
2128 top
= &(style
->commentMatch
);
2131 top
= &(style
->textMatch
);
2135 if (pat
->steps
[0].value
!= NULL
)
2136 name
= pat
->steps
[0].value
;
2138 top
= &(style
->elemMatch
);
2142 if (style
->templatesHash
== NULL
) {
2143 style
->templatesHash
= xmlHashCreate(1024);
2144 if (style
->templatesHash
== NULL
) {
2145 xsltFreeCompMatch(pat
);
2148 xmlHashAddEntry3(style
->templatesHash
, name
, mode
, modeURI
, pat
);
2150 list
= (xsltCompMatchPtr
) xmlHashLookup3(style
->templatesHash
,
2151 name
, mode
, modeURI
);
2153 xmlHashAddEntry3(style
->templatesHash
, name
,
2154 mode
, modeURI
, pat
);
2157 * Note '<=' since one must choose among the matching
2158 * template rules that are left, the one that occurs
2159 * last in the stylesheet
2161 if (list
->priority
<= pat
->priority
) {
2163 xmlHashUpdateEntry3(style
->templatesHash
, name
,
2164 mode
, modeURI
, pat
, NULL
);
2166 while (list
->next
!= NULL
) {
2167 if (list
->next
->priority
<= pat
->priority
)
2171 pat
->next
= list
->next
;
2176 } else if (top
!= NULL
) {
2181 } else if (list
->priority
<= pat
->priority
) {
2185 while (list
->next
!= NULL
) {
2186 if (list
->next
->priority
<= pat
->priority
)
2190 pat
->next
= list
->next
;
2194 xsltTransformError(NULL
, style
, NULL
,
2195 "xsltAddTemplate: invalid compiled pattern\n");
2196 xsltFreeCompMatch(pat
);
2199 #ifdef WITH_XSLT_DEBUG_PATTERN
2201 xsltGenericDebug(xsltGenericDebugContext
,
2202 "added pattern : '%s' mode '%s' priority %f\n",
2203 pat
->pattern
, pat
->mode
, pat
->priority
);
2205 xsltGenericDebug(xsltGenericDebugContext
,
2206 "added pattern : '%s' priority %f\n",
2207 pat
->pattern
, pat
->priority
);
2216 xsltComputeAllKeys(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
)
2218 if ((ctxt
== NULL
) || (contextNode
== NULL
)) {
2219 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2220 "Internal error in xsltComputeAllKeys(): "
2221 "Bad arguments.\n");
2225 if (ctxt
->document
== NULL
) {
2227 * The document info will only be NULL if we have a RTF.
2229 if (contextNode
->doc
->_private
!= NULL
)
2230 goto doc_info_mismatch
;
2232 * On-demand creation of the document info (needed for keys).
2234 ctxt
->document
= xsltNewDocument(ctxt
, contextNode
->doc
);
2235 if (ctxt
->document
== NULL
)
2238 return xsltInitAllDocKeys(ctxt
);
2241 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2242 "Internal error in xsltComputeAllKeys(): "
2243 "The context's document info doesn't match the "
2244 "document info of the current result tree.\n");
2245 ctxt
->state
= XSLT_STATE_STOPPED
;
2251 * @ctxt: a XSLT process context
2252 * @node: the node being processed
2253 * @style: the current style
2255 * Finds the template applying to this node, if @style is non-NULL
2256 * it means one needs to look for the next imported template in scope.
2258 * Returns the xsltTemplatePtr or NULL if not found
2261 xsltGetTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
2262 xsltStylesheetPtr style
)
2264 xsltStylesheetPtr curstyle
;
2265 xsltTemplatePtr ret
= NULL
;
2266 const xmlChar
*name
= NULL
;
2267 xsltCompMatchPtr list
= NULL
;
2271 if ((ctxt
== NULL
) || (node
== NULL
))
2274 if (style
== NULL
) {
2275 curstyle
= ctxt
->style
;
2277 curstyle
= xsltNextImport(style
);
2280 while ((curstyle
!= NULL
) && (curstyle
!= style
)) {
2281 priority
= XSLT_PAT_NO_PRIORITY
;
2282 /* TODO : handle IDs/keys here ! */
2283 if (curstyle
->templatesHash
!= NULL
) {
2285 * Use the top name as selector
2287 switch (node
->type
) {
2288 case XML_ELEMENT_NODE
:
2289 if (node
->name
[0] == ' ')
2291 case XML_ATTRIBUTE_NODE
:
2295 case XML_DOCUMENT_NODE
:
2296 case XML_HTML_DOCUMENT_NODE
:
2298 case XML_CDATA_SECTION_NODE
:
2299 case XML_COMMENT_NODE
:
2300 case XML_ENTITY_REF_NODE
:
2301 case XML_ENTITY_NODE
:
2302 case XML_DOCUMENT_TYPE_NODE
:
2303 case XML_DOCUMENT_FRAG_NODE
:
2304 case XML_NOTATION_NODE
:
2306 case XML_ELEMENT_DECL
:
2307 case XML_ATTRIBUTE_DECL
:
2308 case XML_ENTITY_DECL
:
2309 case XML_NAMESPACE_DECL
:
2310 case XML_XINCLUDE_START
:
2311 case XML_XINCLUDE_END
:
2320 * find the list of applicable expressions based on the name
2322 list
= (xsltCompMatchPtr
) xmlHashLookup3(curstyle
->templatesHash
,
2323 name
, ctxt
->mode
, ctxt
->modeURI
);
2326 while (list
!= NULL
) {
2327 if (xsltTestCompMatch(ctxt
, list
, node
,
2328 ctxt
->mode
, ctxt
->modeURI
)) {
2329 ret
= list
->template;
2330 priority
= list
->priority
;
2338 * find alternate generic matches
2340 switch (node
->type
) {
2341 case XML_ELEMENT_NODE
:
2342 if (node
->name
[0] == ' ')
2343 list
= curstyle
->rootMatch
;
2345 list
= curstyle
->elemMatch
;
2346 if (node
->psvi
!= NULL
) keyed
= 1;
2348 case XML_ATTRIBUTE_NODE
: {
2351 list
= curstyle
->attrMatch
;
2352 attr
= (xmlAttrPtr
) node
;
2353 if (attr
->psvi
!= NULL
) keyed
= 1;
2357 list
= curstyle
->piMatch
;
2358 if (node
->psvi
!= NULL
) keyed
= 1;
2360 case XML_DOCUMENT_NODE
:
2361 case XML_HTML_DOCUMENT_NODE
: {
2364 list
= curstyle
->rootMatch
;
2365 doc
= (xmlDocPtr
) node
;
2366 if (doc
->psvi
!= NULL
) keyed
= 1;
2370 case XML_CDATA_SECTION_NODE
:
2371 list
= curstyle
->textMatch
;
2372 if (node
->psvi
!= NULL
) keyed
= 1;
2374 case XML_COMMENT_NODE
:
2375 list
= curstyle
->commentMatch
;
2376 if (node
->psvi
!= NULL
) keyed
= 1;
2378 case XML_ENTITY_REF_NODE
:
2379 case XML_ENTITY_NODE
:
2380 case XML_DOCUMENT_TYPE_NODE
:
2381 case XML_DOCUMENT_FRAG_NODE
:
2382 case XML_NOTATION_NODE
:
2384 case XML_ELEMENT_DECL
:
2385 case XML_ATTRIBUTE_DECL
:
2386 case XML_ENTITY_DECL
:
2387 case XML_NAMESPACE_DECL
:
2388 case XML_XINCLUDE_START
:
2389 case XML_XINCLUDE_END
:
2394 while ((list
!= NULL
) &&
2395 ((ret
== NULL
) || (list
->priority
> priority
))) {
2396 if (xsltTestCompMatch(ctxt
, list
, node
,
2397 ctxt
->mode
, ctxt
->modeURI
)) {
2398 ret
= list
->template;
2399 priority
= list
->priority
;
2405 * Some of the tests for elements can also apply to documents
2407 if ((node
->type
== XML_DOCUMENT_NODE
) ||
2408 (node
->type
== XML_HTML_DOCUMENT_NODE
) ||
2409 (node
->type
== XML_TEXT_NODE
)) {
2410 list
= curstyle
->elemMatch
;
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
;
2421 } else if ((node
->type
== XML_PI_NODE
) ||
2422 (node
->type
== XML_COMMENT_NODE
)) {
2423 list
= curstyle
->elemMatch
;
2424 while ((list
!= NULL
) &&
2425 ((ret
== NULL
) || (list
->priority
> priority
))) {
2426 if (xsltTestCompMatch(ctxt
, list
, node
,
2427 ctxt
->mode
, ctxt
->modeURI
)) {
2428 ret
= list
->template;
2429 priority
= list
->priority
;
2438 list
= curstyle
->keyMatch
;
2439 while ((list
!= NULL
) &&
2440 ((ret
== NULL
) || (list
->priority
> priority
))) {
2441 if (xsltTestCompMatch(ctxt
, list
, node
,
2442 ctxt
->mode
, ctxt
->modeURI
)) {
2443 ret
= list
->template;
2444 priority
= list
->priority
;
2450 else if (ctxt
->hasTemplKeyPatterns
&&
2451 ((ctxt
->document
== NULL
) ||
2452 (ctxt
->document
->nbKeysComputed
< ctxt
->nbKeys
)))
2455 * Compute all remaining keys for this document.
2457 * REVISIT TODO: I think this could be further optimized.
2459 if (xsltComputeAllKeys(ctxt
, node
) == -1)
2462 switch (node
->type
) {
2463 case XML_ELEMENT_NODE
:
2464 if (node
->psvi
!= NULL
) keyed
= 1;
2466 case XML_ATTRIBUTE_NODE
:
2467 if (((xmlAttrPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2470 case XML_CDATA_SECTION_NODE
:
2471 case XML_COMMENT_NODE
:
2473 if (node
->psvi
!= NULL
) keyed
= 1;
2475 case XML_DOCUMENT_NODE
:
2476 case XML_HTML_DOCUMENT_NODE
:
2477 if (((xmlDocPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2489 * Cycle on next curstylesheet import.
2491 curstyle
= xsltNextImport(curstyle
);
2499 * xsltCleanupTemplates:
2500 * @style: an XSLT stylesheet
2502 * Cleanup the state of the templates used by the stylesheet and
2503 * the ones it imports.
2506 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED
) {
2510 * xsltFreeTemplateHashes:
2511 * @style: an XSLT stylesheet
2513 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2516 xsltFreeTemplateHashes(xsltStylesheetPtr style
) {
2517 if (style
->templatesHash
!= NULL
)
2518 xmlHashFree((xmlHashTablePtr
) style
->templatesHash
,
2519 (xmlHashDeallocator
) xsltFreeCompMatchList
);
2520 if (style
->rootMatch
!= NULL
)
2521 xsltFreeCompMatchList(style
->rootMatch
);
2522 if (style
->keyMatch
!= NULL
)
2523 xsltFreeCompMatchList(style
->keyMatch
);
2524 if (style
->elemMatch
!= NULL
)
2525 xsltFreeCompMatchList(style
->elemMatch
);
2526 if (style
->attrMatch
!= NULL
)
2527 xsltFreeCompMatchList(style
->attrMatch
);
2528 if (style
->parentMatch
!= NULL
)
2529 xsltFreeCompMatchList(style
->parentMatch
);
2530 if (style
->textMatch
!= NULL
)
2531 xsltFreeCompMatchList(style
->textMatch
);
2532 if (style
->piMatch
!= NULL
)
2533 xsltFreeCompMatchList(style
->piMatch
);
2534 if (style
->commentMatch
!= NULL
)
2535 xsltFreeCompMatchList(style
->commentMatch
);