OLD | NEW |
| (Empty) |
1 /* | |
2 * pattern.c: Implemetation of the template match compilation and lookup | |
3 * | |
4 * Reference: | |
5 * http://www.w3.org/TR/1999/REC-xslt-19991116 | |
6 * | |
7 * See Copyright for the status of this software. | |
8 * | |
9 * daniel@veillard.com | |
10 */ | |
11 | |
12 /* | |
13 * TODO: handle pathological cases like *[*[@a="b"]] | |
14 * TODO: detect [number] at compilation, optimize accordingly | |
15 */ | |
16 | |
17 #define IN_LIBXSLT | |
18 #include "libxslt.h" | |
19 | |
20 #include <string.h> | |
21 | |
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> | |
29 #include "xslt.h" | |
30 #include "xsltInternals.h" | |
31 #include "xsltutils.h" | |
32 #include "imports.h" | |
33 #include "templates.h" | |
34 #include "keys.h" | |
35 #include "pattern.h" | |
36 #include "documents.h" | |
37 | |
38 #ifdef WITH_XSLT_DEBUG | |
39 #define WITH_XSLT_DEBUG_PATTERN | |
40 #endif | |
41 | |
42 /* | |
43 * Types are private: | |
44 */ | |
45 | |
46 typedef enum { | |
47 XSLT_OP_END=0, | |
48 XSLT_OP_ROOT, | |
49 XSLT_OP_ELEM, | |
50 XSLT_OP_ATTR, | |
51 XSLT_OP_PARENT, | |
52 XSLT_OP_ANCESTOR, | |
53 XSLT_OP_ID, | |
54 XSLT_OP_KEY, | |
55 XSLT_OP_NS, | |
56 XSLT_OP_ALL, | |
57 XSLT_OP_PI, | |
58 XSLT_OP_COMMENT, | |
59 XSLT_OP_TEXT, | |
60 XSLT_OP_NODE, | |
61 XSLT_OP_PREDICATE | |
62 } xsltOp; | |
63 | |
64 typedef enum { | |
65 AXIS_CHILD=1, | |
66 AXIS_ATTRIBUTE | |
67 } xsltAxis; | |
68 | |
69 typedef struct _xsltStepState xsltStepState; | |
70 typedef xsltStepState *xsltStepStatePtr; | |
71 struct _xsltStepState { | |
72 int step; | |
73 xmlNodePtr node; | |
74 }; | |
75 | |
76 typedef struct _xsltStepStates xsltStepStates; | |
77 typedef xsltStepStates *xsltStepStatesPtr; | |
78 struct _xsltStepStates { | |
79 int nbstates; | |
80 int maxstates; | |
81 xsltStepStatePtr states; | |
82 }; | |
83 | |
84 typedef struct _xsltStepOp xsltStepOp; | |
85 typedef xsltStepOp *xsltStepOpPtr; | |
86 struct _xsltStepOp { | |
87 xsltOp op; | |
88 xmlChar *value; | |
89 xmlChar *value2; | |
90 xmlChar *value3; | |
91 xmlXPathCompExprPtr comp; | |
92 /* | |
93 * Optimisations for count | |
94 */ | |
95 int previousExtra; | |
96 int indexExtra; | |
97 int lenExtra; | |
98 }; | |
99 | |
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 */ | |
107 xmlNodePtr node; /* the containing element */ | |
108 | |
109 int direct; | |
110 /* TODO fix the statically allocated size steps[] */ | |
111 int nbStep; | |
112 int maxStep; | |
113 xmlNsPtr *nsList; /* the namespaces in scope */ | |
114 int nsNr; /* the number of namespaces in scope */ | |
115 xsltStepOpPtr steps; /* ops for computation */ | |
116 }; | |
117 | |
118 typedef struct _xsltParserContext xsltParserContext; | |
119 typedef xsltParserContext *xsltParserContextPtr; | |
120 struct _xsltParserContext { | |
121 xsltStylesheetPtr style; /* the stylesheet */ | |
122 xsltTransformContextPtr ctxt; /* the transformation or NULL */ | |
123 const xmlChar *cur; /* the current char being parsed */ | |
124 const xmlChar *base; /* the full expression */ | |
125 xmlDocPtr doc; /* the source document */ | |
126 xmlNodePtr elem; /* the source element */ | |
127 int error; /* error code */ | |
128 xsltCompMatchPtr comp; /* the result */ | |
129 }; | |
130 | |
131 /************************************************************************ | |
132 * * | |
133 * Type functions * | |
134 * * | |
135 ************************************************************************/ | |
136 | |
137 /** | |
138 * xsltNewCompMatch: | |
139 * | |
140 * Create a new XSLT CompMatch | |
141 * | |
142 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error | |
143 */ | |
144 static xsltCompMatchPtr | |
145 xsltNewCompMatch(void) { | |
146 xsltCompMatchPtr cur; | |
147 | |
148 cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch)); | |
149 if (cur == NULL) { | |
150 xsltTransformError(NULL, NULL, NULL, | |
151 "xsltNewCompMatch : out of memory error\n"); | |
152 return(NULL); | |
153 } | |
154 memset(cur, 0, sizeof(xsltCompMatch)); | |
155 cur->maxStep = 10; | |
156 cur->nbStep = 0; | |
157 cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) * | |
158 cur->maxStep); | |
159 if (cur->steps == NULL) { | |
160 xsltTransformError(NULL, NULL, NULL, | |
161 "xsltNewCompMatch : out of memory error\n"); | |
162 xmlFree(cur); | |
163 return(NULL); | |
164 } | |
165 cur->nsNr = 0; | |
166 cur->nsList = NULL; | |
167 cur->direct = 0; | |
168 return(cur); | |
169 } | |
170 | |
171 /** | |
172 * xsltFreeCompMatch: | |
173 * @comp: an XSLT comp | |
174 * | |
175 * Free up the memory allocated by @comp | |
176 */ | |
177 static void | |
178 xsltFreeCompMatch(xsltCompMatchPtr comp) { | |
179 xsltStepOpPtr op; | |
180 int i; | |
181 | |
182 if (comp == NULL) | |
183 return; | |
184 if (comp->pattern != NULL) | |
185 xmlFree((xmlChar *)comp->pattern); | |
186 if (comp->nsList != NULL) | |
187 xmlFree(comp->nsList); | |
188 for (i = 0;i < comp->nbStep;i++) { | |
189 op = &comp->steps[i]; | |
190 if (op->value != NULL) | |
191 xmlFree(op->value); | |
192 if (op->value2 != NULL) | |
193 xmlFree(op->value2); | |
194 if (op->value3 != NULL) | |
195 xmlFree(op->value3); | |
196 if (op->comp != NULL) | |
197 xmlXPathFreeCompExpr(op->comp); | |
198 } | |
199 xmlFree(comp->steps); | |
200 memset(comp, -1, sizeof(xsltCompMatch)); | |
201 xmlFree(comp); | |
202 } | |
203 | |
204 /** | |
205 * xsltFreeCompMatchList: | |
206 * @comp: an XSLT comp list | |
207 * | |
208 * Free up the memory allocated by all the elements of @comp | |
209 */ | |
210 void | |
211 xsltFreeCompMatchList(xsltCompMatchPtr comp) { | |
212 xsltCompMatchPtr cur; | |
213 | |
214 while (comp != NULL) { | |
215 cur = comp; | |
216 comp = comp->next; | |
217 xsltFreeCompMatch(cur); | |
218 } | |
219 } | |
220 | |
221 /** | |
222 * xsltNormalizeCompSteps: | |
223 * @payload: pointer to template hash table entry | |
224 * @data: pointer to the stylesheet | |
225 * @name: template match name | |
226 * | |
227 * This is a hashtable scanner function to normalize the compiled | |
228 * steps of an imported stylesheet. | |
229 */ | |
230 void xsltNormalizeCompSteps(void *payload, | |
231 void *data, const xmlChar *name ATTRIBUTE_UNUSED) { | |
232 xsltCompMatchPtr comp = payload; | |
233 xsltStylesheetPtr style = data; | |
234 int ix; | |
235 | |
236 for (ix = 0; ix < comp->nbStep; ix++) { | |
237 comp->steps[ix].previousExtra += style->extrasNr; | |
238 comp->steps[ix].indexExtra += style->extrasNr; | |
239 comp->steps[ix].lenExtra += style->extrasNr; | |
240 } | |
241 } | |
242 | |
243 /** | |
244 * xsltNewParserContext: | |
245 * @style: the stylesheet | |
246 * @ctxt: the transformation context, if done at run-time | |
247 * | |
248 * Create a new XSLT ParserContext | |
249 * | |
250 * Returns the newly allocated xsltParserContextPtr or NULL in case of error | |
251 */ | |
252 static xsltParserContextPtr | |
253 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) { | |
254 xsltParserContextPtr cur; | |
255 | |
256 cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext)); | |
257 if (cur == NULL) { | |
258 xsltTransformError(NULL, NULL, NULL, | |
259 "xsltNewParserContext : malloc failed\n"); | |
260 return(NULL); | |
261 } | |
262 memset(cur, 0, sizeof(xsltParserContext)); | |
263 cur->style = style; | |
264 cur->ctxt = ctxt; | |
265 return(cur); | |
266 } | |
267 | |
268 /** | |
269 * xsltFreeParserContext: | |
270 * @ctxt: an XSLT parser context | |
271 * | |
272 * Free up the memory allocated by @ctxt | |
273 */ | |
274 static void | |
275 xsltFreeParserContext(xsltParserContextPtr ctxt) { | |
276 if (ctxt == NULL) | |
277 return; | |
278 memset(ctxt, -1, sizeof(xsltParserContext)); | |
279 xmlFree(ctxt); | |
280 } | |
281 | |
282 /** | |
283 * xsltCompMatchAdd: | |
284 * @comp: the compiled match expression | |
285 * @op: an op | |
286 * @value: the first value | |
287 * @value2: the second value | |
288 * @novar: flag to set XML_XPATH_NOVAR | |
289 * | |
290 * Add an step to an XSLT Compiled Match | |
291 * | |
292 * Returns -1 in case of failure, 0 otherwise. | |
293 */ | |
294 static int | |
295 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp, | |
296 xsltOp op, xmlChar * value, xmlChar * value2, int novar) | |
297 { | |
298 if (comp->nbStep >= comp->maxStep) { | |
299 xsltStepOpPtr tmp; | |
300 | |
301 tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 * | |
302 sizeof(xsltStepOp)); | |
303 if (tmp == NULL) { | |
304 xsltGenericError(xsltGenericErrorContext, | |
305 "xsltCompMatchAdd: memory re-allocation failure.\n"); | |
306 if (ctxt->style != NULL) | |
307 ctxt->style->errors++; | |
308 if (value) | |
309 xmlFree(value); | |
310 if (value2) | |
311 xmlFree(value2); | |
312 return (-1); | |
313 } | |
314 comp->maxStep *= 2; | |
315 comp->steps = tmp; | |
316 } | |
317 comp->steps[comp->nbStep].op = op; | |
318 comp->steps[comp->nbStep].value = value; | |
319 comp->steps[comp->nbStep].value2 = value2; | |
320 comp->steps[comp->nbStep].value3 = NULL; | |
321 comp->steps[comp->nbStep].comp = NULL; | |
322 if (ctxt->ctxt != NULL) { | |
323 comp->steps[comp->nbStep].previousExtra = | |
324 xsltAllocateExtraCtxt(ctxt->ctxt); | |
325 comp->steps[comp->nbStep].indexExtra = | |
326 xsltAllocateExtraCtxt(ctxt->ctxt); | |
327 comp->steps[comp->nbStep].lenExtra = | |
328 xsltAllocateExtraCtxt(ctxt->ctxt); | |
329 } else { | |
330 comp->steps[comp->nbStep].previousExtra = | |
331 xsltAllocateExtra(ctxt->style); | |
332 comp->steps[comp->nbStep].indexExtra = | |
333 xsltAllocateExtra(ctxt->style); | |
334 comp->steps[comp->nbStep].lenExtra = | |
335 xsltAllocateExtra(ctxt->style); | |
336 } | |
337 if (op == XSLT_OP_PREDICATE) { | |
338 xmlXPathContextPtr xctxt; | |
339 | |
340 if (ctxt->style != NULL) | |
341 xctxt = xmlXPathNewContext(ctxt->style->doc); | |
342 else | |
343 xctxt = xmlXPathNewContext(NULL); | |
344 #ifdef XML_XPATH_NOVAR | |
345 if (novar != 0) | |
346 xctxt->flags = XML_XPATH_NOVAR; | |
347 #endif | |
348 if (ctxt->style != NULL) | |
349 xctxt->dict = ctxt->style->dict; | |
350 comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value); | |
351 xmlXPathFreeContext(xctxt); | |
352 if (comp->steps[comp->nbStep].comp == NULL) { | |
353 xsltTransformError(NULL, ctxt->style, ctxt->elem, | |
354 "Failed to compile predicate\n"); | |
355 if (ctxt->style != NULL) | |
356 ctxt->style->errors++; | |
357 } | |
358 } | |
359 comp->nbStep++; | |
360 return (0); | |
361 } | |
362 | |
363 /** | |
364 * xsltSwapTopCompMatch: | |
365 * @comp: the compiled match expression | |
366 * | |
367 * reverse the two top steps. | |
368 */ | |
369 static void | |
370 xsltSwapTopCompMatch(xsltCompMatchPtr comp) { | |
371 int i; | |
372 int j = comp->nbStep - 1; | |
373 | |
374 if (j > 0) { | |
375 register xmlChar *tmp; | |
376 register xsltOp op; | |
377 register xmlXPathCompExprPtr expr; | |
378 register int t; | |
379 i = j - 1; | |
380 tmp = comp->steps[i].value; | |
381 comp->steps[i].value = comp->steps[j].value; | |
382 comp->steps[j].value = tmp; | |
383 tmp = comp->steps[i].value2; | |
384 comp->steps[i].value2 = comp->steps[j].value2; | |
385 comp->steps[j].value2 = tmp; | |
386 tmp = comp->steps[i].value3; | |
387 comp->steps[i].value3 = comp->steps[j].value3; | |
388 comp->steps[j].value3 = tmp; | |
389 op = comp->steps[i].op; | |
390 comp->steps[i].op = comp->steps[j].op; | |
391 comp->steps[j].op = op; | |
392 expr = comp->steps[i].comp; | |
393 comp->steps[i].comp = comp->steps[j].comp; | |
394 comp->steps[j].comp = expr; | |
395 t = comp->steps[i].previousExtra; | |
396 comp->steps[i].previousExtra = comp->steps[j].previousExtra; | |
397 comp->steps[j].previousExtra = t; | |
398 t = comp->steps[i].indexExtra; | |
399 comp->steps[i].indexExtra = comp->steps[j].indexExtra; | |
400 comp->steps[j].indexExtra = t; | |
401 t = comp->steps[i].lenExtra; | |
402 comp->steps[i].lenExtra = comp->steps[j].lenExtra; | |
403 comp->steps[j].lenExtra = t; | |
404 } | |
405 } | |
406 | |
407 /** | |
408 * xsltReverseCompMatch: | |
409 * @ctxt: the parser context | |
410 * @comp: the compiled match expression | |
411 * | |
412 * reverse all the stack of expressions | |
413 */ | |
414 static void | |
415 xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) { | |
416 int i = 0; | |
417 int j = comp->nbStep - 1; | |
418 | |
419 while (j > i) { | |
420 register xmlChar *tmp; | |
421 register xsltOp op; | |
422 register xmlXPathCompExprPtr expr; | |
423 register int t; | |
424 | |
425 tmp = comp->steps[i].value; | |
426 comp->steps[i].value = comp->steps[j].value; | |
427 comp->steps[j].value = tmp; | |
428 tmp = comp->steps[i].value2; | |
429 comp->steps[i].value2 = comp->steps[j].value2; | |
430 comp->steps[j].value2 = tmp; | |
431 tmp = comp->steps[i].value3; | |
432 comp->steps[i].value3 = comp->steps[j].value3; | |
433 comp->steps[j].value3 = tmp; | |
434 op = comp->steps[i].op; | |
435 comp->steps[i].op = comp->steps[j].op; | |
436 comp->steps[j].op = op; | |
437 expr = comp->steps[i].comp; | |
438 comp->steps[i].comp = comp->steps[j].comp; | |
439 comp->steps[j].comp = expr; | |
440 t = comp->steps[i].previousExtra; | |
441 comp->steps[i].previousExtra = comp->steps[j].previousExtra; | |
442 comp->steps[j].previousExtra = t; | |
443 t = comp->steps[i].indexExtra; | |
444 comp->steps[i].indexExtra = comp->steps[j].indexExtra; | |
445 comp->steps[j].indexExtra = t; | |
446 t = comp->steps[i].lenExtra; | |
447 comp->steps[i].lenExtra = comp->steps[j].lenExtra; | |
448 comp->steps[j].lenExtra = t; | |
449 j--; | |
450 i++; | |
451 } | |
452 xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0); | |
453 | |
454 /* | |
455 * Detect consecutive XSLT_OP_PREDICATE and predicates on ops which | |
456 * haven't been optimized yet indicating a direct matching should be done. | |
457 */ | |
458 for (i = 0;i < comp->nbStep - 1;i++) { | |
459 xsltOp op = comp->steps[i].op; | |
460 | |
461 if ((op != XSLT_OP_ELEM) && | |
462 (op != XSLT_OP_ALL) && | |
463 (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) { | |
464 | |
465 comp->direct = 1; | |
466 if (comp->pattern[0] != '/') { | |
467 xmlChar *query; | |
468 | |
469 query = xmlStrdup((const xmlChar *)"//"); | |
470 query = xmlStrcat(query, comp->pattern); | |
471 | |
472 xmlFree((xmlChar *) comp->pattern); | |
473 comp->pattern = query; | |
474 } | |
475 break; | |
476 } | |
477 } | |
478 } | |
479 | |
480 /************************************************************************ | |
481 * * | |
482 * The interpreter for the precompiled patterns * | |
483 * * | |
484 ************************************************************************/ | |
485 | |
486 static int | |
487 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states, | |
488 int step, xmlNodePtr node) { | |
489 if ((states->states == NULL) || (states->maxstates <= 0)) { | |
490 states->maxstates = 4; | |
491 states->nbstates = 0; | |
492 states->states = xmlMalloc(4 * sizeof(xsltStepState)); | |
493 } | |
494 else if (states->maxstates <= states->nbstates) { | |
495 xsltStepState *tmp; | |
496 | |
497 tmp = (xsltStepStatePtr) xmlRealloc(states->states, | |
498 2 * states->maxstates * sizeof(xsltStepState)); | |
499 if (tmp == NULL) { | |
500 xsltGenericError(xsltGenericErrorContext, | |
501 "xsltPatPushState: memory re-allocation failure.\n"); | |
502 ctxt->state = XSLT_STATE_STOPPED; | |
503 return(-1); | |
504 } | |
505 states->states = tmp; | |
506 states->maxstates *= 2; | |
507 } | |
508 states->states[states->nbstates].step = step; | |
509 states->states[states->nbstates++].node = node; | |
510 #if 0 | |
511 fprintf(stderr, "Push: %d, %s\n", step, node->name); | |
512 #endif | |
513 return(0); | |
514 } | |
515 | |
516 /** | |
517 * xsltTestCompMatchDirect: | |
518 * @ctxt: a XSLT process context | |
519 * @comp: the precompiled pattern | |
520 * @node: a node | |
521 * @nsList: the namespaces in scope | |
522 * @nsNr: the number of namespaces in scope | |
523 * | |
524 * Test whether the node matches the pattern, do a direct evalutation | |
525 * and not a step by step evaluation. | |
526 * | |
527 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
528 */ | |
529 static int | |
530 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, | |
531 xmlNodePtr node, xmlNsPtr *nsList, int nsNr) { | |
532 xsltStepOpPtr sel = NULL; | |
533 xmlDocPtr prevdoc; | |
534 xmlDocPtr doc; | |
535 xmlXPathObjectPtr list; | |
536 int ix, j; | |
537 int nocache = 0; | |
538 int isRVT; | |
539 | |
540 doc = node->doc; | |
541 if (XSLT_IS_RES_TREE_FRAG(doc)) | |
542 isRVT = 1; | |
543 else | |
544 isRVT = 0; | |
545 sel = &comp->steps[0]; /* store extra in first step arbitrarily */ | |
546 | |
547 prevdoc = (xmlDocPtr) | |
548 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); | |
549 ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival); | |
550 list = (xmlXPathObjectPtr) | |
551 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra); | |
552 | |
553 if ((list == NULL) || (prevdoc != doc)) { | |
554 xmlXPathObjectPtr newlist; | |
555 xmlNodePtr parent = node->parent; | |
556 xmlDocPtr olddoc; | |
557 xmlNodePtr oldnode; | |
558 int oldNsNr, oldContextSize, oldProximityPosition; | |
559 xmlNsPtr *oldNamespaces; | |
560 | |
561 oldnode = ctxt->xpathCtxt->node; | |
562 olddoc = ctxt->xpathCtxt->doc; | |
563 oldNsNr = ctxt->xpathCtxt->nsNr; | |
564 oldNamespaces = ctxt->xpathCtxt->namespaces; | |
565 oldContextSize = ctxt->xpathCtxt->contextSize; | |
566 oldProximityPosition = ctxt->xpathCtxt->proximityPosition; | |
567 ctxt->xpathCtxt->node = node; | |
568 ctxt->xpathCtxt->doc = doc; | |
569 ctxt->xpathCtxt->namespaces = nsList; | |
570 ctxt->xpathCtxt->nsNr = nsNr; | |
571 newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt); | |
572 ctxt->xpathCtxt->node = oldnode; | |
573 ctxt->xpathCtxt->doc = olddoc; | |
574 ctxt->xpathCtxt->namespaces = oldNamespaces; | |
575 ctxt->xpathCtxt->nsNr = oldNsNr; | |
576 ctxt->xpathCtxt->contextSize = oldContextSize; | |
577 ctxt->xpathCtxt->proximityPosition = oldProximityPosition; | |
578 if (newlist == NULL) | |
579 return(-1); | |
580 if (newlist->type != XPATH_NODESET) { | |
581 xmlXPathFreeObject(newlist); | |
582 return(-1); | |
583 } | |
584 ix = 0; | |
585 | |
586 if ((parent == NULL) || (node->doc == NULL) || isRVT) | |
587 nocache = 1; | |
588 | |
589 if (nocache == 0) { | |
590 if (list != NULL) | |
591 xmlXPathFreeObject(list); | |
592 list = newlist; | |
593 | |
594 XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) = | |
595 (void *) list; | |
596 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = | |
597 (void *) doc; | |
598 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = | |
599 0; | |
600 XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) = | |
601 (xmlFreeFunc) xmlXPathFreeObject; | |
602 } else | |
603 list = newlist; | |
604 } | |
605 if ((list->nodesetval == NULL) || | |
606 (list->nodesetval->nodeNr <= 0)) { | |
607 if (nocache == 1) | |
608 xmlXPathFreeObject(list); | |
609 return(0); | |
610 } | |
611 /* TODO: store the index and use it for the scan */ | |
612 if (ix == 0) { | |
613 for (j = 0;j < list->nodesetval->nodeNr;j++) { | |
614 if (list->nodesetval->nodeTab[j] == node) { | |
615 if (nocache == 1) | |
616 xmlXPathFreeObject(list); | |
617 return(1); | |
618 } | |
619 } | |
620 } else { | |
621 } | |
622 if (nocache == 1) | |
623 xmlXPathFreeObject(list); | |
624 return(0); | |
625 } | |
626 | |
627 /** | |
628 * xsltTestPredicateMatch: | |
629 * @ctxt: a XSLT process context | |
630 * @comp: the precompiled pattern | |
631 * @node: a node | |
632 * @step: the predicate step | |
633 * @sel: the previous step | |
634 * | |
635 * Test whether the node matches the predicate | |
636 * | |
637 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
638 */ | |
639 static int | |
640 xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, | |
641 xmlNodePtr node, xsltStepOpPtr step, | |
642 xsltStepOpPtr sel) { | |
643 xmlNodePtr oldNode; | |
644 xmlDocPtr doc; | |
645 int oldCS, oldCP; | |
646 int pos = 0, len = 0; | |
647 int isRVT; | |
648 int match; | |
649 | |
650 if (step->value == NULL) | |
651 return(0); | |
652 if (step->comp == NULL) | |
653 return(0); | |
654 | |
655 doc = node->doc; | |
656 if (XSLT_IS_RES_TREE_FRAG(doc)) | |
657 isRVT = 1; | |
658 else | |
659 isRVT = 0; | |
660 | |
661 /* | |
662 * Recompute contextSize and proximityPosition. | |
663 * | |
664 * TODO: Make this work for additional ops. Currently, only XSLT_OP_ELEM | |
665 * and XSLT_OP_ALL are supported. | |
666 */ | |
667 oldCS = ctxt->xpathCtxt->contextSize; | |
668 oldCP = ctxt->xpathCtxt->proximityPosition; | |
669 if ((sel != NULL) && | |
670 (sel->op == XSLT_OP_ELEM) && | |
671 (sel->value != NULL) && | |
672 (node->type == XML_ELEMENT_NODE) && | |
673 (node->parent != NULL)) { | |
674 xmlNodePtr previous; | |
675 int nocache = 0; | |
676 | |
677 previous = (xmlNodePtr) | |
678 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); | |
679 if ((previous != NULL) && | |
680 (previous->parent == node->parent)) { | |
681 /* | |
682 * just walk back to adjust the index | |
683 */ | |
684 int indx = 0; | |
685 xmlNodePtr sibling = node; | |
686 | |
687 while (sibling != NULL) { | |
688 if (sibling == previous) | |
689 break; | |
690 if ((sibling->type == XML_ELEMENT_NODE) && | |
691 (previous->name != NULL) && | |
692 (sibling->name != NULL) && | |
693 (previous->name[0] == sibling->name[0]) && | |
694 (xmlStrEqual(previous->name, sibling->name))) | |
695 { | |
696 if ((sel->value2 == NULL) || | |
697 ((sibling->ns != NULL) && | |
698 (xmlStrEqual(sel->value2, sibling->ns->href)))) | |
699 indx++; | |
700 } | |
701 sibling = sibling->prev; | |
702 } | |
703 if (sibling == NULL) { | |
704 /* hum going backward in document order ... */ | |
705 indx = 0; | |
706 sibling = node; | |
707 while (sibling != NULL) { | |
708 if (sibling == previous) | |
709 break; | |
710 if ((sibling->type == XML_ELEMENT_NODE) && | |
711 (previous->name != NULL) && | |
712 (sibling->name != NULL) && | |
713 (previous->name[0] == sibling->name[0]) && | |
714 (xmlStrEqual(previous->name, sibling->name))) | |
715 { | |
716 if ((sel->value2 == NULL) || | |
717 ((sibling->ns != NULL) && | |
718 (xmlStrEqual(sel->value2, | |
719 sibling->ns->href)))) | |
720 { | |
721 indx--; | |
722 } | |
723 } | |
724 sibling = sibling->next; | |
725 } | |
726 } | |
727 if (sibling != NULL) { | |
728 pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx; | |
729 /* | |
730 * If the node is in a Value Tree we need to | |
731 * save len, but cannot cache the node! | |
732 * (bugs 153137 and 158840) | |
733 */ | |
734 if (node->doc != NULL) { | |
735 len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); | |
736 if (!isRVT) { | |
737 XSLT_RUNTIME_EXTRA(ctxt, | |
738 sel->previousExtra, ptr) = node; | |
739 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
740 } | |
741 } | |
742 } else | |
743 pos = 0; | |
744 } else { | |
745 /* | |
746 * recompute the index | |
747 */ | |
748 xmlNodePtr parent = node->parent; | |
749 xmlNodePtr siblings = NULL; | |
750 | |
751 if (parent) siblings = parent->children; | |
752 | |
753 while (siblings != NULL) { | |
754 if (siblings->type == XML_ELEMENT_NODE) { | |
755 if (siblings == node) { | |
756 len++; | |
757 pos = len; | |
758 } else if ((node->name != NULL) && | |
759 (siblings->name != NULL) && | |
760 (node->name[0] == siblings->name[0]) && | |
761 (xmlStrEqual(node->name, siblings->name))) { | |
762 if ((sel->value2 == NULL) || | |
763 ((siblings->ns != NULL) && | |
764 (xmlStrEqual(sel->value2, siblings->ns->href)))) | |
765 len++; | |
766 } | |
767 } | |
768 siblings = siblings->next; | |
769 } | |
770 if ((parent == NULL) || (node->doc == NULL)) | |
771 nocache = 1; | |
772 else { | |
773 while (parent->parent != NULL) | |
774 parent = parent->parent; | |
775 if (((parent->type != XML_DOCUMENT_NODE) && | |
776 (parent->type != XML_HTML_DOCUMENT_NODE)) || | |
777 (parent != (xmlNodePtr) node->doc)) | |
778 nocache = 1; | |
779 } | |
780 } | |
781 if (pos != 0) { | |
782 ctxt->xpathCtxt->contextSize = len; | |
783 ctxt->xpathCtxt->proximityPosition = pos; | |
784 /* | |
785 * If the node is in a Value Tree we cannot | |
786 * cache it ! | |
787 */ | |
788 if ((!isRVT) && (node->doc != NULL) && | |
789 (nocache == 0)) { | |
790 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; | |
791 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
792 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; | |
793 } | |
794 } | |
795 } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) && | |
796 (node->type == XML_ELEMENT_NODE)) { | |
797 xmlNodePtr previous; | |
798 int nocache = 0; | |
799 | |
800 previous = (xmlNodePtr) | |
801 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr); | |
802 if ((previous != NULL) && | |
803 (previous->parent == node->parent)) { | |
804 /* | |
805 * just walk back to adjust the index | |
806 */ | |
807 int indx = 0; | |
808 xmlNodePtr sibling = node; | |
809 | |
810 while (sibling != NULL) { | |
811 if (sibling == previous) | |
812 break; | |
813 if (sibling->type == XML_ELEMENT_NODE) | |
814 indx++; | |
815 sibling = sibling->prev; | |
816 } | |
817 if (sibling == NULL) { | |
818 /* hum going backward in document order ... */ | |
819 indx = 0; | |
820 sibling = node; | |
821 while (sibling != NULL) { | |
822 if (sibling == previous) | |
823 break; | |
824 if (sibling->type == XML_ELEMENT_NODE) | |
825 indx--; | |
826 sibling = sibling->next; | |
827 } | |
828 } | |
829 if (sibling != NULL) { | |
830 pos = XSLT_RUNTIME_EXTRA(ctxt, | |
831 sel->indexExtra, ival) + indx; | |
832 /* | |
833 * If the node is in a Value Tree we cannot | |
834 * cache it ! | |
835 */ | |
836 if ((node->doc != NULL) && !isRVT) { | |
837 len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival); | |
838 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; | |
839 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
840 } | |
841 } else | |
842 pos = 0; | |
843 } else { | |
844 /* | |
845 * recompute the index | |
846 */ | |
847 xmlNodePtr parent = node->parent; | |
848 xmlNodePtr siblings = NULL; | |
849 | |
850 if (parent) siblings = parent->children; | |
851 | |
852 while (siblings != NULL) { | |
853 if (siblings->type == XML_ELEMENT_NODE) { | |
854 len++; | |
855 if (siblings == node) { | |
856 pos = len; | |
857 } | |
858 } | |
859 siblings = siblings->next; | |
860 } | |
861 if ((parent == NULL) || (node->doc == NULL)) | |
862 nocache = 1; | |
863 else { | |
864 while (parent->parent != NULL) | |
865 parent = parent->parent; | |
866 if (((parent->type != XML_DOCUMENT_NODE) && | |
867 (parent->type != XML_HTML_DOCUMENT_NODE)) || | |
868 (parent != (xmlNodePtr) node->doc)) | |
869 nocache = 1; | |
870 } | |
871 } | |
872 if (pos != 0) { | |
873 ctxt->xpathCtxt->contextSize = len; | |
874 ctxt->xpathCtxt->proximityPosition = pos; | |
875 /* | |
876 * If the node is in a Value Tree we cannot | |
877 * cache it ! | |
878 */ | |
879 if ((node->doc != NULL) && (nocache == 0) && !isRVT) { | |
880 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node; | |
881 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos; | |
882 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len; | |
883 } | |
884 } | |
885 } | |
886 | |
887 oldNode = ctxt->node; | |
888 ctxt->node = node; | |
889 | |
890 match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr); | |
891 | |
892 if (pos != 0) { | |
893 ctxt->xpathCtxt->contextSize = oldCS; | |
894 ctxt->xpathCtxt->proximityPosition = oldCP; | |
895 } | |
896 ctxt->node = oldNode; | |
897 | |
898 return match; | |
899 } | |
900 | |
901 /** | |
902 * xsltTestCompMatch: | |
903 * @ctxt: a XSLT process context | |
904 * @comp: the precompiled pattern | |
905 * @node: a node | |
906 * @mode: the mode name or NULL | |
907 * @modeURI: the mode URI or NULL | |
908 * | |
909 * Test whether the node matches the pattern | |
910 * | |
911 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
912 */ | |
913 static int | |
914 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp, | |
915 xmlNodePtr matchNode, const xmlChar *mode, | |
916 const xmlChar *modeURI) { | |
917 int i; | |
918 int found = 0; | |
919 xmlNodePtr node = matchNode; | |
920 xmlNodePtr oldInst; | |
921 xsltStepOpPtr step, sel = NULL; | |
922 xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */ | |
923 | |
924 if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) { | |
925 xsltTransformError(ctxt, NULL, node, | |
926 "xsltTestCompMatch: null arg\n"); | |
927 return(-1); | |
928 } | |
929 if (mode != NULL) { | |
930 if (comp->mode == NULL) | |
931 return(0); | |
932 /* | |
933 * both mode strings must be interned on the stylesheet dictionary | |
934 */ | |
935 if (comp->mode != mode) | |
936 return(0); | |
937 } else { | |
938 if (comp->mode != NULL) | |
939 return(0); | |
940 } | |
941 if (modeURI != NULL) { | |
942 if (comp->modeURI == NULL) | |
943 return(0); | |
944 /* | |
945 * both modeURI strings must be interned on the stylesheet dictionary | |
946 */ | |
947 if (comp->modeURI != modeURI) | |
948 return(0); | |
949 } else { | |
950 if (comp->modeURI != NULL) | |
951 return(0); | |
952 } | |
953 | |
954 /* Some XPath functions rely on inst being set correctly. */ | |
955 oldInst = ctxt->inst; | |
956 ctxt->inst = comp->node; | |
957 | |
958 i = 0; | |
959 restart: | |
960 for (;i < comp->nbStep;i++) { | |
961 step = &comp->steps[i]; | |
962 if (step->op != XSLT_OP_PREDICATE) | |
963 sel = step; | |
964 switch (step->op) { | |
965 case XSLT_OP_END: | |
966 goto found; | |
967 case XSLT_OP_ROOT: | |
968 if ((node->type == XML_DOCUMENT_NODE) || | |
969 #ifdef LIBXML_DOCB_ENABLED | |
970 (node->type == XML_DOCB_DOCUMENT_NODE) || | |
971 #endif | |
972 (node->type == XML_HTML_DOCUMENT_NODE)) | |
973 continue; | |
974 if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' ')) | |
975 continue; | |
976 goto rollback; | |
977 case XSLT_OP_ELEM: | |
978 if (node->type != XML_ELEMENT_NODE) | |
979 goto rollback; | |
980 if (step->value == NULL) | |
981 continue; | |
982 if (step->value[0] != node->name[0]) | |
983 goto rollback; | |
984 if (!xmlStrEqual(step->value, node->name)) | |
985 goto rollback; | |
986 | |
987 /* Namespace test */ | |
988 if (node->ns == NULL) { | |
989 if (step->value2 != NULL) | |
990 goto rollback; | |
991 } else if (node->ns->href != NULL) { | |
992 if (step->value2 == NULL) | |
993 goto rollback; | |
994 if (!xmlStrEqual(step->value2, node->ns->href)) | |
995 goto rollback; | |
996 } | |
997 continue; | |
998 case XSLT_OP_ATTR: | |
999 if (node->type != XML_ATTRIBUTE_NODE) | |
1000 goto rollback; | |
1001 if (step->value != NULL) { | |
1002 if (step->value[0] != node->name[0]) | |
1003 goto rollback; | |
1004 if (!xmlStrEqual(step->value, node->name)) | |
1005 goto rollback; | |
1006 } | |
1007 /* Namespace test */ | |
1008 if (node->ns == NULL) { | |
1009 if (step->value2 != NULL) | |
1010 goto rollback; | |
1011 } else if (step->value2 != NULL) { | |
1012 if (!xmlStrEqual(step->value2, node->ns->href)) | |
1013 goto rollback; | |
1014 } | |
1015 continue; | |
1016 case XSLT_OP_PARENT: | |
1017 if ((node->type == XML_DOCUMENT_NODE) || | |
1018 (node->type == XML_HTML_DOCUMENT_NODE) || | |
1019 #ifdef LIBXML_DOCB_ENABLED | |
1020 (node->type == XML_DOCB_DOCUMENT_NODE) || | |
1021 #endif | |
1022 (node->type == XML_NAMESPACE_DECL)) | |
1023 goto rollback; | |
1024 node = node->parent; | |
1025 if (node == NULL) | |
1026 goto rollback; | |
1027 if (step->value == NULL) | |
1028 continue; | |
1029 if (step->value[0] != node->name[0]) | |
1030 goto rollback; | |
1031 if (!xmlStrEqual(step->value, node->name)) | |
1032 goto rollback; | |
1033 /* Namespace test */ | |
1034 if (node->ns == NULL) { | |
1035 if (step->value2 != NULL) | |
1036 goto rollback; | |
1037 } else if (node->ns->href != NULL) { | |
1038 if (step->value2 == NULL) | |
1039 goto rollback; | |
1040 if (!xmlStrEqual(step->value2, node->ns->href)) | |
1041 goto rollback; | |
1042 } | |
1043 continue; | |
1044 case XSLT_OP_ANCESTOR: | |
1045 /* TODO: implement coalescing of ANCESTOR/NODE ops */ | |
1046 if (step->value == NULL) { | |
1047 step = &comp->steps[i+1]; | |
1048 if (step->op == XSLT_OP_ROOT) | |
1049 goto found; | |
1050 /* added NS, ID and KEY as a result of bug 168208 */ | |
1051 if ((step->op != XSLT_OP_ELEM) && | |
1052 (step->op != XSLT_OP_ALL) && | |
1053 (step->op != XSLT_OP_NS) && | |
1054 (step->op != XSLT_OP_ID) && | |
1055 (step->op != XSLT_OP_KEY)) | |
1056 goto rollback; | |
1057 } | |
1058 if (node == NULL) | |
1059 goto rollback; | |
1060 if ((node->type == XML_DOCUMENT_NODE) || | |
1061 (node->type == XML_HTML_DOCUMENT_NODE) || | |
1062 #ifdef LIBXML_DOCB_ENABLED | |
1063 (node->type == XML_DOCB_DOCUMENT_NODE) || | |
1064 #endif | |
1065 (node->type == XML_NAMESPACE_DECL)) | |
1066 goto rollback; | |
1067 node = node->parent; | |
1068 if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) { | |
1069 xsltPatPushState(ctxt, &states, i, node); | |
1070 continue; | |
1071 } | |
1072 i++; | |
1073 if (step->value == NULL) { | |
1074 xsltPatPushState(ctxt, &states, i - 1, node); | |
1075 continue; | |
1076 } | |
1077 while (node != NULL) { | |
1078 if ((node->type == XML_ELEMENT_NODE) && | |
1079 (step->value[0] == node->name[0]) && | |
1080 (xmlStrEqual(step->value, node->name))) { | |
1081 /* Namespace test */ | |
1082 if (node->ns == NULL) { | |
1083 if (step->value2 == NULL) | |
1084 break; | |
1085 } else if (node->ns->href != NULL) { | |
1086 if ((step->value2 != NULL) && | |
1087 (xmlStrEqual(step->value2, node->ns->href))) | |
1088 break; | |
1089 } | |
1090 } | |
1091 node = node->parent; | |
1092 } | |
1093 if (node == NULL) | |
1094 goto rollback; | |
1095 xsltPatPushState(ctxt, &states, i - 1, node); | |
1096 continue; | |
1097 case XSLT_OP_ID: { | |
1098 /* TODO Handle IDs decently, must be done differently */ | |
1099 xmlAttrPtr id; | |
1100 | |
1101 if (node->type != XML_ELEMENT_NODE) | |
1102 goto rollback; | |
1103 | |
1104 id = xmlGetID(node->doc, step->value); | |
1105 if ((id == NULL) || (id->parent != node)) | |
1106 goto rollback; | |
1107 break; | |
1108 } | |
1109 case XSLT_OP_KEY: { | |
1110 xmlNodeSetPtr list; | |
1111 int indx; | |
1112 | |
1113 list = xsltGetKey(ctxt, step->value, | |
1114 step->value3, step->value2); | |
1115 if (list == NULL) | |
1116 goto rollback; | |
1117 for (indx = 0;indx < list->nodeNr;indx++) | |
1118 if (list->nodeTab[indx] == node) | |
1119 break; | |
1120 if (indx >= list->nodeNr) | |
1121 goto rollback; | |
1122 break; | |
1123 } | |
1124 case XSLT_OP_NS: | |
1125 if (node->type != XML_ELEMENT_NODE) | |
1126 goto rollback; | |
1127 if (node->ns == NULL) { | |
1128 if (step->value != NULL) | |
1129 goto rollback; | |
1130 } else if (node->ns->href != NULL) { | |
1131 if (step->value == NULL) | |
1132 goto rollback; | |
1133 if (!xmlStrEqual(step->value, node->ns->href)) | |
1134 goto rollback; | |
1135 } | |
1136 break; | |
1137 case XSLT_OP_ALL: | |
1138 if (node->type != XML_ELEMENT_NODE) | |
1139 goto rollback; | |
1140 break; | |
1141 case XSLT_OP_PREDICATE: { | |
1142 /* | |
1143 * When there is cascading XSLT_OP_PREDICATE or a predicate | |
1144 * after an op which hasn't been optimized yet, then use a | |
1145 * direct computation approach. It's not done directly | |
1146 * at the beginning of the routine to filter out as much | |
1147 * as possible this costly computation. | |
1148 */ | |
1149 if (comp->direct) { | |
1150 found = xsltTestCompMatchDirect(ctxt, comp, matchNode, | |
1151 comp->nsList, comp->nsNr); | |
1152 goto exit; | |
1153 } | |
1154 | |
1155 if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel)) | |
1156 goto rollback; | |
1157 | |
1158 break; | |
1159 } | |
1160 case XSLT_OP_PI: | |
1161 if (node->type != XML_PI_NODE) | |
1162 goto rollback; | |
1163 if (step->value != NULL) { | |
1164 if (!xmlStrEqual(step->value, node->name)) | |
1165 goto rollback; | |
1166 } | |
1167 break; | |
1168 case XSLT_OP_COMMENT: | |
1169 if (node->type != XML_COMMENT_NODE) | |
1170 goto rollback; | |
1171 break; | |
1172 case XSLT_OP_TEXT: | |
1173 if ((node->type != XML_TEXT_NODE) && | |
1174 (node->type != XML_CDATA_SECTION_NODE)) | |
1175 goto rollback; | |
1176 break; | |
1177 case XSLT_OP_NODE: | |
1178 switch (node->type) { | |
1179 case XML_ELEMENT_NODE: | |
1180 case XML_CDATA_SECTION_NODE: | |
1181 case XML_PI_NODE: | |
1182 case XML_COMMENT_NODE: | |
1183 case XML_TEXT_NODE: | |
1184 break; | |
1185 default: | |
1186 goto rollback; | |
1187 } | |
1188 break; | |
1189 } | |
1190 } | |
1191 found: | |
1192 found = 1; | |
1193 exit: | |
1194 ctxt->inst = oldInst; | |
1195 if (states.states != NULL) { | |
1196 /* Free the rollback states */ | |
1197 xmlFree(states.states); | |
1198 } | |
1199 return found; | |
1200 rollback: | |
1201 /* got an error try to rollback */ | |
1202 if (states.states == NULL || states.nbstates <= 0) { | |
1203 found = 0; | |
1204 goto exit; | |
1205 } | |
1206 states.nbstates--; | |
1207 i = states.states[states.nbstates].step; | |
1208 node = states.states[states.nbstates].node; | |
1209 #if 0 | |
1210 fprintf(stderr, "Pop: %d, %s\n", i, node->name); | |
1211 #endif | |
1212 goto restart; | |
1213 } | |
1214 | |
1215 /** | |
1216 * xsltTestCompMatchList: | |
1217 * @ctxt: a XSLT process context | |
1218 * @node: a node | |
1219 * @comp: the precompiled pattern list | |
1220 * | |
1221 * Test whether the node matches one of the patterns in the list | |
1222 * | |
1223 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure | |
1224 */ | |
1225 int | |
1226 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
1227 xsltCompMatchPtr comp) { | |
1228 int ret; | |
1229 | |
1230 if ((ctxt == NULL) || (node == NULL)) | |
1231 return(-1); | |
1232 while (comp != NULL) { | |
1233 ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL); | |
1234 if (ret == 1) | |
1235 return(1); | |
1236 comp = comp->next; | |
1237 } | |
1238 return(0); | |
1239 } | |
1240 | |
1241 /************************************************************************ | |
1242 * * | |
1243 * Dedicated parser for templates * | |
1244 * * | |
1245 ************************************************************************/ | |
1246 | |
1247 #define CUR (*ctxt->cur) | |
1248 #define SKIP(val) ctxt->cur += (val) | |
1249 #define NXT(val) ctxt->cur[(val)] | |
1250 #define CUR_PTR ctxt->cur | |
1251 | |
1252 #define SKIP_BLANKS \ | |
1253 while (IS_BLANK_CH(CUR)) NEXT | |
1254 | |
1255 #define CURRENT (*ctxt->cur) | |
1256 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) | |
1257 | |
1258 | |
1259 #define PUSH(op, val, val2, novar)
\ | |
1260 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto e
rror; | |
1261 | |
1262 #define SWAP() \ | |
1263 xsltSwapTopCompMatch(ctxt->comp); | |
1264 | |
1265 #define XSLT_ERROR(X) \ | |
1266 { xsltError(ctxt, __FILE__, __LINE__, X); \ | |
1267 ctxt->error = (X); return; } | |
1268 | |
1269 #define XSLT_ERROR0(X) \ | |
1270 { xsltError(ctxt, __FILE__, __LINE__, X); \ | |
1271 ctxt->error = (X); return(0); } | |
1272 | |
1273 /** | |
1274 * xsltScanLiteral: | |
1275 * @ctxt: the XPath Parser context | |
1276 * | |
1277 * Parse an XPath Litteral: | |
1278 * | |
1279 * [29] Literal ::= '"' [^"]* '"' | |
1280 * | "'" [^']* "'" | |
1281 * | |
1282 * Returns the Literal parsed or NULL | |
1283 */ | |
1284 | |
1285 static xmlChar * | |
1286 xsltScanLiteral(xsltParserContextPtr ctxt) { | |
1287 const xmlChar *q, *cur; | |
1288 xmlChar *ret = NULL; | |
1289 int val, len; | |
1290 | |
1291 SKIP_BLANKS; | |
1292 if (CUR == '"') { | |
1293 NEXT; | |
1294 cur = q = CUR_PTR; | |
1295 val = xmlStringCurrentChar(NULL, cur, &len); | |
1296 while ((IS_CHAR(val)) && (val != '"')) { | |
1297 cur += len; | |
1298 val = xmlStringCurrentChar(NULL, cur, &len); | |
1299 } | |
1300 if (!IS_CHAR(val)) { | |
1301 ctxt->error = 1; | |
1302 return(NULL); | |
1303 } else { | |
1304 ret = xmlStrndup(q, cur - q); | |
1305 } | |
1306 cur += len; | |
1307 CUR_PTR = cur; | |
1308 } else if (CUR == '\'') { | |
1309 NEXT; | |
1310 cur = q = CUR_PTR; | |
1311 val = xmlStringCurrentChar(NULL, cur, &len); | |
1312 while ((IS_CHAR(val)) && (val != '\'')) { | |
1313 cur += len; | |
1314 val = xmlStringCurrentChar(NULL, cur, &len); | |
1315 } | |
1316 if (!IS_CHAR(val)) { | |
1317 ctxt->error = 1; | |
1318 return(NULL); | |
1319 } else { | |
1320 ret = xmlStrndup(q, cur - q); | |
1321 } | |
1322 cur += len; | |
1323 CUR_PTR = cur; | |
1324 } else { | |
1325 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */ | |
1326 ctxt->error = 1; | |
1327 return(NULL); | |
1328 } | |
1329 return(ret); | |
1330 } | |
1331 | |
1332 /** | |
1333 * xsltScanNCName: | |
1334 * @ctxt: the XPath Parser context | |
1335 * | |
1336 * Parses a non qualified name | |
1337 * | |
1338 * Returns the Name parsed or NULL | |
1339 */ | |
1340 | |
1341 static xmlChar * | |
1342 xsltScanNCName(xsltParserContextPtr ctxt) { | |
1343 const xmlChar *q, *cur; | |
1344 xmlChar *ret = NULL; | |
1345 int val, len; | |
1346 | |
1347 SKIP_BLANKS; | |
1348 | |
1349 cur = q = CUR_PTR; | |
1350 val = xmlStringCurrentChar(NULL, cur, &len); | |
1351 if (!IS_LETTER(val) && (val != '_')) | |
1352 return(NULL); | |
1353 | |
1354 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || | |
1355 (val == '.') || (val == '-') || | |
1356 (val == '_') || | |
1357 (IS_COMBINING(val)) || | |
1358 (IS_EXTENDER(val))) { | |
1359 cur += len; | |
1360 val = xmlStringCurrentChar(NULL, cur, &len); | |
1361 } | |
1362 ret = xmlStrndup(q, cur - q); | |
1363 CUR_PTR = cur; | |
1364 return(ret); | |
1365 } | |
1366 | |
1367 /* | |
1368 * xsltCompileIdKeyPattern: | |
1369 * @ctxt: the compilation context | |
1370 * @name: a preparsed name | |
1371 * @aid: whether id/key are allowed there | |
1372 * @novar: flag to prohibit xslt var | |
1373 * | |
1374 * Compile the XSLT LocationIdKeyPattern | |
1375 * [3] IdKeyPattern ::= 'id' '(' Literal ')' | |
1376 * | 'key' '(' Literal ',' Literal ')' | |
1377 * | |
1378 * also handle NodeType and PI from: | |
1379 * | |
1380 * [7] NodeTest ::= NameTest | |
1381 * | NodeType '(' ')' | |
1382 * | 'processing-instruction' '(' Literal ')' | |
1383 */ | |
1384 static void | |
1385 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, | |
1386 int aid, int novar, xsltAxis axis) { | |
1387 xmlChar *lit = NULL; | |
1388 xmlChar *lit2 = NULL; | |
1389 | |
1390 if (CUR != '(') { | |
1391 xsltTransformError(NULL, NULL, NULL, | |
1392 "xsltCompileIdKeyPattern : ( expected\n"); | |
1393 ctxt->error = 1; | |
1394 return; | |
1395 } | |
1396 if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) { | |
1397 if (axis != 0) { | |
1398 xsltTransformError(NULL, NULL, NULL, | |
1399 "xsltCompileIdKeyPattern : NodeTest expected\n"); | |
1400 ctxt->error = 1; | |
1401 return; | |
1402 } | |
1403 NEXT; | |
1404 SKIP_BLANKS; | |
1405 lit = xsltScanLiteral(ctxt); | |
1406 if (ctxt->error) { | |
1407 xsltTransformError(NULL, NULL, NULL, | |
1408 "xsltCompileIdKeyPattern : Literal expected\n"); | |
1409 return; | |
1410 } | |
1411 SKIP_BLANKS; | |
1412 if (CUR != ')') { | |
1413 xsltTransformError(NULL, NULL, NULL, | |
1414 "xsltCompileIdKeyPattern : ) expected\n"); | |
1415 xmlFree(lit); | |
1416 ctxt->error = 1; | |
1417 return; | |
1418 } | |
1419 NEXT; | |
1420 PUSH(XSLT_OP_ID, lit, NULL, novar); | |
1421 lit = NULL; | |
1422 } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) { | |
1423 if (axis != 0) { | |
1424 xsltTransformError(NULL, NULL, NULL, | |
1425 "xsltCompileIdKeyPattern : NodeTest expected\n"); | |
1426 ctxt->error = 1; | |
1427 return; | |
1428 } | |
1429 NEXT; | |
1430 SKIP_BLANKS; | |
1431 lit = xsltScanLiteral(ctxt); | |
1432 if (ctxt->error) { | |
1433 xsltTransformError(NULL, NULL, NULL, | |
1434 "xsltCompileIdKeyPattern : Literal expected\n"); | |
1435 return; | |
1436 } | |
1437 SKIP_BLANKS; | |
1438 if (CUR != ',') { | |
1439 xsltTransformError(NULL, NULL, NULL, | |
1440 "xsltCompileIdKeyPattern : , expected\n"); | |
1441 xmlFree(lit); | |
1442 ctxt->error = 1; | |
1443 return; | |
1444 } | |
1445 NEXT; | |
1446 SKIP_BLANKS; | |
1447 lit2 = xsltScanLiteral(ctxt); | |
1448 if (ctxt->error) { | |
1449 xsltTransformError(NULL, NULL, NULL, | |
1450 "xsltCompileIdKeyPattern : Literal expected\n"); | |
1451 xmlFree(lit); | |
1452 return; | |
1453 } | |
1454 SKIP_BLANKS; | |
1455 if (CUR != ')') { | |
1456 xsltTransformError(NULL, NULL, NULL, | |
1457 "xsltCompileIdKeyPattern : ) expected\n"); | |
1458 xmlFree(lit); | |
1459 xmlFree(lit2); | |
1460 ctxt->error = 1; | |
1461 return; | |
1462 } | |
1463 NEXT; | |
1464 /* URGENT TODO: support namespace in keys */ | |
1465 PUSH(XSLT_OP_KEY, lit, lit2, novar); | |
1466 lit = NULL; | |
1467 lit2 = NULL; | |
1468 } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) { | |
1469 NEXT; | |
1470 SKIP_BLANKS; | |
1471 if (CUR != ')') { | |
1472 lit = xsltScanLiteral(ctxt); | |
1473 if (ctxt->error) { | |
1474 xsltTransformError(NULL, NULL, NULL, | |
1475 "xsltCompileIdKeyPattern : Literal expected\n"); | |
1476 return; | |
1477 } | |
1478 SKIP_BLANKS; | |
1479 if (CUR != ')') { | |
1480 xsltTransformError(NULL, NULL, NULL, | |
1481 "xsltCompileIdKeyPattern : ) expected\n"); | |
1482 ctxt->error = 1; | |
1483 return; | |
1484 } | |
1485 } | |
1486 NEXT; | |
1487 PUSH(XSLT_OP_PI, lit, NULL, novar); | |
1488 lit = NULL; | |
1489 } else if (xmlStrEqual(name, (const xmlChar *)"text")) { | |
1490 NEXT; | |
1491 SKIP_BLANKS; | |
1492 if (CUR != ')') { | |
1493 xsltTransformError(NULL, NULL, NULL, | |
1494 "xsltCompileIdKeyPattern : ) expected\n"); | |
1495 ctxt->error = 1; | |
1496 return; | |
1497 } | |
1498 NEXT; | |
1499 PUSH(XSLT_OP_TEXT, NULL, NULL, novar); | |
1500 } else if (xmlStrEqual(name, (const xmlChar *)"comment")) { | |
1501 NEXT; | |
1502 SKIP_BLANKS; | |
1503 if (CUR != ')') { | |
1504 xsltTransformError(NULL, NULL, NULL, | |
1505 "xsltCompileIdKeyPattern : ) expected\n"); | |
1506 ctxt->error = 1; | |
1507 return; | |
1508 } | |
1509 NEXT; | |
1510 PUSH(XSLT_OP_COMMENT, NULL, NULL, novar); | |
1511 } else if (xmlStrEqual(name, (const xmlChar *)"node")) { | |
1512 NEXT; | |
1513 SKIP_BLANKS; | |
1514 if (CUR != ')') { | |
1515 xsltTransformError(NULL, NULL, NULL, | |
1516 "xsltCompileIdKeyPattern : ) expected\n"); | |
1517 ctxt->error = 1; | |
1518 return; | |
1519 } | |
1520 NEXT; | |
1521 if (axis == AXIS_ATTRIBUTE) { | |
1522 PUSH(XSLT_OP_ATTR, NULL, NULL, novar); | |
1523 } | |
1524 else { | |
1525 PUSH(XSLT_OP_NODE, NULL, NULL, novar); | |
1526 } | |
1527 } else if (aid) { | |
1528 xsltTransformError(NULL, NULL, NULL, | |
1529 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n"); | |
1530 ctxt->error = 1; | |
1531 return; | |
1532 } else { | |
1533 xsltTransformError(NULL, NULL, NULL, | |
1534 "xsltCompileIdKeyPattern : node type\n"); | |
1535 ctxt->error = 1; | |
1536 return; | |
1537 } | |
1538 error: | |
1539 return; | |
1540 } | |
1541 | |
1542 /** | |
1543 * xsltCompileStepPattern: | |
1544 * @ctxt: the compilation context | |
1545 * @token: a posible precompiled name | |
1546 * @novar: flag to prohibit xslt variables from pattern | |
1547 * | |
1548 * Compile the XSLT StepPattern and generates a precompiled | |
1549 * form suitable for fast matching. | |
1550 * | |
1551 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* | |
1552 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier | |
1553 * | ('child' | 'attribute') '::' | |
1554 * from XPath | |
1555 * [7] NodeTest ::= NameTest | |
1556 * | NodeType '(' ')' | |
1557 * | 'processing-instruction' '(' Literal ')' | |
1558 * [8] Predicate ::= '[' PredicateExpr ']' | |
1559 * [9] PredicateExpr ::= Expr | |
1560 * [13] AbbreviatedAxisSpecifier ::= '@'? | |
1561 * [37] NameTest ::= '*' | NCName ':' '*' | QName | |
1562 */ | |
1563 | |
1564 static void | |
1565 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) { | |
1566 xmlChar *name = NULL; | |
1567 const xmlChar *URI = NULL; | |
1568 xmlChar *URL = NULL; | |
1569 int level; | |
1570 xsltAxis axis = 0; | |
1571 | |
1572 SKIP_BLANKS; | |
1573 if ((token == NULL) && (CUR == '@')) { | |
1574 NEXT; | |
1575 axis = AXIS_ATTRIBUTE; | |
1576 } | |
1577 parse_node_test: | |
1578 if (token == NULL) | |
1579 token = xsltScanNCName(ctxt); | |
1580 if (token == NULL) { | |
1581 if (CUR == '*') { | |
1582 NEXT; | |
1583 if (axis == AXIS_ATTRIBUTE) { | |
1584 PUSH(XSLT_OP_ATTR, NULL, NULL, novar); | |
1585 } | |
1586 else { | |
1587 PUSH(XSLT_OP_ALL, NULL, NULL, novar); | |
1588 } | |
1589 goto parse_predicate; | |
1590 } else { | |
1591 xsltTransformError(NULL, NULL, NULL, | |
1592 "xsltCompileStepPattern : Name expected\n"); | |
1593 ctxt->error = 1; | |
1594 goto error; | |
1595 } | |
1596 } | |
1597 | |
1598 | |
1599 SKIP_BLANKS; | |
1600 if (CUR == '(') { | |
1601 xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis); | |
1602 xmlFree(token); | |
1603 token = NULL; | |
1604 if (ctxt->error) | |
1605 goto error; | |
1606 } else if (CUR == ':') { | |
1607 NEXT; | |
1608 if (CUR != ':') { | |
1609 xmlChar *prefix = token; | |
1610 xmlNsPtr ns; | |
1611 | |
1612 /* | |
1613 * This is a namespace match | |
1614 */ | |
1615 token = xsltScanNCName(ctxt); | |
1616 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix); | |
1617 if (ns == NULL) { | |
1618 xsltTransformError(NULL, NULL, NULL, | |
1619 "xsltCompileStepPattern : no namespace bound to prefix %s\n", | |
1620 prefix); | |
1621 xmlFree(prefix); | |
1622 prefix=NULL; | |
1623 ctxt->error = 1; | |
1624 goto error; | |
1625 } else { | |
1626 URL = xmlStrdup(ns->href); | |
1627 } | |
1628 xmlFree(prefix); | |
1629 prefix=NULL; | |
1630 if (token == NULL) { | |
1631 if (CUR == '*') { | |
1632 NEXT; | |
1633 if (axis == AXIS_ATTRIBUTE) { | |
1634 PUSH(XSLT_OP_ATTR, NULL, URL, novar); | |
1635 URL = NULL; | |
1636 } | |
1637 else { | |
1638 PUSH(XSLT_OP_NS, URL, NULL, novar); | |
1639 URL = NULL; | |
1640 } | |
1641 } else { | |
1642 xsltTransformError(NULL, NULL, NULL, | |
1643 "xsltCompileStepPattern : Name expected\n"); | |
1644 ctxt->error = 1; | |
1645 goto error; | |
1646 } | |
1647 } else { | |
1648 if (axis == AXIS_ATTRIBUTE) { | |
1649 PUSH(XSLT_OP_ATTR, token, URL, novar); | |
1650 token = NULL; | |
1651 URL = NULL; | |
1652 } | |
1653 else { | |
1654 PUSH(XSLT_OP_ELEM, token, URL, novar); | |
1655 token = NULL; | |
1656 URL = NULL; | |
1657 } | |
1658 } | |
1659 } else { | |
1660 if (axis != 0) { | |
1661 xsltTransformError(NULL, NULL, NULL, | |
1662 "xsltCompileStepPattern : NodeTest expected\n"); | |
1663 ctxt->error = 1; | |
1664 goto error; | |
1665 } | |
1666 NEXT; | |
1667 if (xmlStrEqual(token, (const xmlChar *) "child")) { | |
1668 axis = AXIS_CHILD; | |
1669 } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) { | |
1670 axis = AXIS_ATTRIBUTE; | |
1671 } else { | |
1672 xsltTransformError(NULL, NULL, NULL, | |
1673 "xsltCompileStepPattern : 'child' or 'attribute' expected\n"
); | |
1674 ctxt->error = 1; | |
1675 goto error; | |
1676 } | |
1677 xmlFree(token); | |
1678 token = NULL; | |
1679 SKIP_BLANKS; | |
1680 token = xsltScanNCName(ctxt); | |
1681 goto parse_node_test; | |
1682 } | |
1683 } else { | |
1684 URI = xsltGetQNameURI(ctxt->elem, &token); | |
1685 if (token == NULL) { | |
1686 ctxt->error = 1; | |
1687 goto error; | |
1688 } | |
1689 if (URI != NULL) | |
1690 URL = xmlStrdup(URI); | |
1691 if (axis == AXIS_ATTRIBUTE) { | |
1692 PUSH(XSLT_OP_ATTR, token, URL, novar); | |
1693 token = NULL; | |
1694 URL = NULL; | |
1695 } | |
1696 else { | |
1697 PUSH(XSLT_OP_ELEM, token, URL, novar); | |
1698 token = NULL; | |
1699 URL = NULL; | |
1700 } | |
1701 } | |
1702 parse_predicate: | |
1703 SKIP_BLANKS; | |
1704 level = 0; | |
1705 while (CUR == '[') { | |
1706 const xmlChar *q; | |
1707 xmlChar *ret = NULL; | |
1708 | |
1709 level++; | |
1710 NEXT; | |
1711 q = CUR_PTR; | |
1712 while (CUR != 0) { | |
1713 /* Skip over nested predicates */ | |
1714 if (CUR == '[') | |
1715 level++; | |
1716 else if (CUR == ']') { | |
1717 level--; | |
1718 if (level == 0) | |
1719 break; | |
1720 } else if (CUR == '"') { | |
1721 NEXT; | |
1722 while ((CUR != 0) && (CUR != '"')) | |
1723 NEXT; | |
1724 } else if (CUR == '\'') { | |
1725 NEXT; | |
1726 while ((CUR != 0) && (CUR != '\'')) | |
1727 NEXT; | |
1728 } | |
1729 NEXT; | |
1730 } | |
1731 if (CUR == 0) { | |
1732 xsltTransformError(NULL, NULL, NULL, | |
1733 "xsltCompileStepPattern : ']' expected\n"); | |
1734 ctxt->error = 1; | |
1735 return; | |
1736 } | |
1737 ret = xmlStrndup(q, CUR_PTR - q); | |
1738 PUSH(XSLT_OP_PREDICATE, ret, NULL, novar); | |
1739 ret = NULL; | |
1740 /* push the predicate lower than local test */ | |
1741 SWAP(); | |
1742 NEXT; | |
1743 SKIP_BLANKS; | |
1744 } | |
1745 return; | |
1746 error: | |
1747 if (token != NULL) | |
1748 xmlFree(token); | |
1749 if (name != NULL) | |
1750 xmlFree(name); | |
1751 } | |
1752 | |
1753 /** | |
1754 * xsltCompileRelativePathPattern: | |
1755 * @comp: the compilation context | |
1756 * @token: a posible precompiled name | |
1757 * @novar: flag to prohibit xslt variables | |
1758 * | |
1759 * Compile the XSLT RelativePathPattern and generates a precompiled | |
1760 * form suitable for fast matching. | |
1761 * | |
1762 * [4] RelativePathPattern ::= StepPattern | |
1763 * | RelativePathPattern '/' StepPattern | |
1764 * | RelativePathPattern '//' StepPattern | |
1765 */ | |
1766 static void | |
1767 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int no
var) { | |
1768 xsltCompileStepPattern(ctxt, token, novar); | |
1769 if (ctxt->error) | |
1770 goto error; | |
1771 SKIP_BLANKS; | |
1772 while ((CUR != 0) && (CUR != '|')) { | |
1773 if ((CUR == '/') && (NXT(1) == '/')) { | |
1774 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); | |
1775 NEXT; | |
1776 NEXT; | |
1777 SKIP_BLANKS; | |
1778 xsltCompileStepPattern(ctxt, NULL, novar); | |
1779 } else if (CUR == '/') { | |
1780 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); | |
1781 NEXT; | |
1782 SKIP_BLANKS; | |
1783 if ((CUR != 0) && (CUR != '|')) { | |
1784 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1785 } | |
1786 } else { | |
1787 ctxt->error = 1; | |
1788 } | |
1789 if (ctxt->error) | |
1790 goto error; | |
1791 SKIP_BLANKS; | |
1792 } | |
1793 error: | |
1794 return; | |
1795 } | |
1796 | |
1797 /** | |
1798 * xsltCompileLocationPathPattern: | |
1799 * @ctxt: the compilation context | |
1800 * @novar: flag to prohibit xslt variables | |
1801 * | |
1802 * Compile the XSLT LocationPathPattern and generates a precompiled | |
1803 * form suitable for fast matching. | |
1804 * | |
1805 * [2] LocationPathPattern ::= '/' RelativePathPattern? | |
1806 * | IdKeyPattern (('/' | '//') RelativePathPattern)? | |
1807 * | '//'? RelativePathPattern | |
1808 */ | |
1809 static void | |
1810 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) { | |
1811 SKIP_BLANKS; | |
1812 if ((CUR == '/') && (NXT(1) == '/')) { | |
1813 /* | |
1814 * since we reverse the query | |
1815 * a leading // can be safely ignored | |
1816 */ | |
1817 NEXT; | |
1818 NEXT; | |
1819 ctxt->comp->priority = 0.5; /* '//' means not 0 priority */ | |
1820 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1821 } else if (CUR == '/') { | |
1822 /* | |
1823 * We need to find root as the parent | |
1824 */ | |
1825 NEXT; | |
1826 SKIP_BLANKS; | |
1827 PUSH(XSLT_OP_ROOT, NULL, NULL, novar); | |
1828 if ((CUR != 0) && (CUR != '|')) { | |
1829 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); | |
1830 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1831 } | |
1832 } else if (CUR == '*') { | |
1833 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1834 } else if (CUR == '@') { | |
1835 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1836 } else { | |
1837 xmlChar *name; | |
1838 name = xsltScanNCName(ctxt); | |
1839 if (name == NULL) { | |
1840 xsltTransformError(NULL, NULL, NULL, | |
1841 "xsltCompileLocationPathPattern : Name expected\n"); | |
1842 ctxt->error = 1; | |
1843 return; | |
1844 } | |
1845 SKIP_BLANKS; | |
1846 if ((CUR == '(') && !xmlXPathIsNodeType(name)) { | |
1847 xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0); | |
1848 xmlFree(name); | |
1849 name = NULL; | |
1850 if ((CUR == '/') && (NXT(1) == '/')) { | |
1851 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar); | |
1852 NEXT; | |
1853 NEXT; | |
1854 SKIP_BLANKS; | |
1855 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1856 } else if (CUR == '/') { | |
1857 PUSH(XSLT_OP_PARENT, NULL, NULL, novar); | |
1858 NEXT; | |
1859 SKIP_BLANKS; | |
1860 xsltCompileRelativePathPattern(ctxt, NULL, novar); | |
1861 } | |
1862 return; | |
1863 } | |
1864 xsltCompileRelativePathPattern(ctxt, name, novar); | |
1865 } | |
1866 error: | |
1867 return; | |
1868 } | |
1869 | |
1870 /** | |
1871 * xsltCompilePatternInternal: | |
1872 * @pattern: an XSLT pattern | |
1873 * @doc: the containing document | |
1874 * @node: the containing element | |
1875 * @style: the stylesheet | |
1876 * @runtime: the transformation context, if done at run-time | |
1877 * @novar: flag to prohibit xslt variables | |
1878 * | |
1879 * Compile the XSLT pattern and generates a list of precompiled form suitable | |
1880 * for fast matching. | |
1881 * | |
1882 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern | |
1883 * | |
1884 * Returns the generated pattern list or NULL in case of failure | |
1885 */ | |
1886 | |
1887 static xsltCompMatchPtr | |
1888 xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc, | |
1889 xmlNodePtr node, xsltStylesheetPtr style, | |
1890 xsltTransformContextPtr runtime, int novar) { | |
1891 xsltParserContextPtr ctxt = NULL; | |
1892 xsltCompMatchPtr element, first = NULL, previous = NULL; | |
1893 int current, start, end, level, j; | |
1894 | |
1895 if (pattern == NULL) { | |
1896 xsltTransformError(NULL, NULL, node, | |
1897 "xsltCompilePattern : NULL pattern\n"); | |
1898 return(NULL); | |
1899 } | |
1900 | |
1901 ctxt = xsltNewParserContext(style, runtime); | |
1902 if (ctxt == NULL) | |
1903 return(NULL); | |
1904 ctxt->doc = doc; | |
1905 ctxt->elem = node; | |
1906 current = end = 0; | |
1907 while (pattern[current] != 0) { | |
1908 start = current; | |
1909 while (IS_BLANK_CH(pattern[current])) | |
1910 current++; | |
1911 end = current; | |
1912 level = 0; | |
1913 while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) { | |
1914 if (pattern[end] == '[') | |
1915 level++; | |
1916 else if (pattern[end] == ']') | |
1917 level--; | |
1918 else if (pattern[end] == '\'') { | |
1919 end++; | |
1920 while ((pattern[end] != 0) && (pattern[end] != '\'')) | |
1921 end++; | |
1922 } else if (pattern[end] == '"') { | |
1923 end++; | |
1924 while ((pattern[end] != 0) && (pattern[end] != '"')) | |
1925 end++; | |
1926 } | |
1927 if (pattern[end] == 0) | |
1928 break; | |
1929 end++; | |
1930 } | |
1931 if (current == end) { | |
1932 xsltTransformError(NULL, NULL, node, | |
1933 "xsltCompilePattern : NULL pattern\n"); | |
1934 goto error; | |
1935 } | |
1936 element = xsltNewCompMatch(); | |
1937 if (element == NULL) { | |
1938 goto error; | |
1939 } | |
1940 if (first == NULL) | |
1941 first = element; | |
1942 else if (previous != NULL) | |
1943 previous->next = element; | |
1944 previous = element; | |
1945 | |
1946 ctxt->comp = element; | |
1947 ctxt->base = xmlStrndup(&pattern[start], end - start); | |
1948 if (ctxt->base == NULL) | |
1949 goto error; | |
1950 ctxt->cur = &(ctxt->base)[current - start]; | |
1951 element->pattern = ctxt->base; | |
1952 element->node = node; | |
1953 element->nsList = xmlGetNsList(doc, node); | |
1954 j = 0; | |
1955 if (element->nsList != NULL) { | |
1956 while (element->nsList[j] != NULL) | |
1957 j++; | |
1958 } | |
1959 element->nsNr = j; | |
1960 | |
1961 | |
1962 #ifdef WITH_XSLT_DEBUG_PATTERN | |
1963 xsltGenericDebug(xsltGenericDebugContext, | |
1964 "xsltCompilePattern : parsing '%s'\n", | |
1965 element->pattern); | |
1966 #endif | |
1967 /* | |
1968 Preset default priority to be zero. | |
1969 This may be changed by xsltCompileLocationPathPattern. | |
1970 */ | |
1971 element->priority = 0; | |
1972 xsltCompileLocationPathPattern(ctxt, novar); | |
1973 if (ctxt->error) { | |
1974 xsltTransformError(NULL, style, node, | |
1975 "xsltCompilePattern : failed to compile '%s'\n", | |
1976 element->pattern); | |
1977 if (style != NULL) style->errors++; | |
1978 goto error; | |
1979 } | |
1980 | |
1981 /* | |
1982 * Reverse for faster interpretation. | |
1983 */ | |
1984 xsltReverseCompMatch(ctxt, element); | |
1985 | |
1986 /* | |
1987 * Set-up the priority | |
1988 */ | |
1989 if (element->priority == 0) { /* if not yet determined */ | |
1990 if (((element->steps[0].op == XSLT_OP_ELEM) || | |
1991 (element->steps[0].op == XSLT_OP_ATTR) || | |
1992 (element->steps[0].op == XSLT_OP_PI)) && | |
1993 (element->steps[0].value != NULL) && | |
1994 (element->steps[1].op == XSLT_OP_END)) { | |
1995 ; /* previously preset */ | |
1996 } else if ((element->steps[0].op == XSLT_OP_ATTR) && | |
1997 (element->steps[0].value2 != NULL) && | |
1998 (element->steps[1].op == XSLT_OP_END)) { | |
1999 element->priority = -0.25; | |
2000 } else if ((element->steps[0].op == XSLT_OP_NS) && | |
2001 (element->steps[0].value != NULL) && | |
2002 (element->steps[1].op == XSLT_OP_END)) { | |
2003 element->priority = -0.25; | |
2004 } else if ((element->steps[0].op == XSLT_OP_ATTR) && | |
2005 (element->steps[0].value == NULL) && | |
2006 (element->steps[0].value2 == NULL) && | |
2007 (element->steps[1].op == XSLT_OP_END)) { | |
2008 element->priority = -0.5; | |
2009 } else if (((element->steps[0].op == XSLT_OP_PI) || | |
2010 (element->steps[0].op == XSLT_OP_TEXT) || | |
2011 (element->steps[0].op == XSLT_OP_ALL) || | |
2012 (element->steps[0].op == XSLT_OP_NODE) || | |
2013 (element->steps[0].op == XSLT_OP_COMMENT)) && | |
2014 (element->steps[1].op == XSLT_OP_END)) { | |
2015 element->priority = -0.5; | |
2016 } else { | |
2017 element->priority = 0.5; | |
2018 } | |
2019 } | |
2020 #ifdef WITH_XSLT_DEBUG_PATTERN | |
2021 xsltGenericDebug(xsltGenericDebugContext, | |
2022 "xsltCompilePattern : parsed %s, default priority %f\n", | |
2023 element->pattern, element->priority); | |
2024 #endif | |
2025 if (pattern[end] == '|') | |
2026 end++; | |
2027 current = end; | |
2028 } | |
2029 if (end == 0) { | |
2030 xsltTransformError(NULL, style, node, | |
2031 "xsltCompilePattern : NULL pattern\n"); | |
2032 if (style != NULL) style->errors++; | |
2033 goto error; | |
2034 } | |
2035 | |
2036 xsltFreeParserContext(ctxt); | |
2037 return(first); | |
2038 | |
2039 error: | |
2040 if (ctxt != NULL) | |
2041 xsltFreeParserContext(ctxt); | |
2042 if (first != NULL) | |
2043 xsltFreeCompMatchList(first); | |
2044 return(NULL); | |
2045 } | |
2046 | |
2047 /** | |
2048 * xsltCompilePattern: | |
2049 * @pattern: an XSLT pattern | |
2050 * @doc: the containing document | |
2051 * @node: the containing element | |
2052 * @style: the stylesheet | |
2053 * @runtime: the transformation context, if done at run-time | |
2054 * | |
2055 * Compile the XSLT pattern and generates a list of precompiled form suitable | |
2056 * for fast matching. | |
2057 * | |
2058 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern | |
2059 * | |
2060 * Returns the generated pattern list or NULL in case of failure | |
2061 */ | |
2062 | |
2063 xsltCompMatchPtr | |
2064 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, | |
2065 xmlNodePtr node, xsltStylesheetPtr style, | |
2066 xsltTransformContextPtr runtime) { | |
2067 return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0)); | |
2068 } | |
2069 | |
2070 /************************************************************************ | |
2071 * * | |
2072 * Module interfaces * | |
2073 * * | |
2074 ************************************************************************/ | |
2075 | |
2076 /** | |
2077 * xsltAddTemplate: | |
2078 * @style: an XSLT stylesheet | |
2079 * @cur: an XSLT template | |
2080 * @mode: the mode name or NULL | |
2081 * @modeURI: the mode URI or NULL | |
2082 * | |
2083 * Register the XSLT pattern associated to @cur | |
2084 * | |
2085 * Returns -1 in case of error, 0 otherwise | |
2086 */ | |
2087 int | |
2088 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur, | |
2089 const xmlChar *mode, const xmlChar *modeURI) { | |
2090 xsltCompMatchPtr pat, list, next; | |
2091 /* | |
2092 * 'top' will point to style->xxxMatch ptr - declaring as 'void' | |
2093 * avoids gcc 'type-punned pointer' warning. | |
2094 */ | |
2095 void **top = NULL; | |
2096 const xmlChar *name = NULL; | |
2097 float priority; /* the priority */ | |
2098 | |
2099 if ((style == NULL) || (cur == NULL)) | |
2100 return(-1); | |
2101 | |
2102 /* Register named template */ | |
2103 if (cur->name != NULL) { | |
2104 if (style->namedTemplates == NULL) { | |
2105 style->namedTemplates = xmlHashCreate(10); | |
2106 if (style->namedTemplates == NULL) | |
2107 return(-1); | |
2108 } | |
2109 else { | |
2110 void *dup = xmlHashLookup2(style->namedTemplates, cur->name, | |
2111 cur->nameURI); | |
2112 if (dup != NULL) { | |
2113 xsltTransformError(NULL, style, cur->elem, | |
2114 "xsl:template: error duplicate name '%s'\n", | |
2115 cur->name); | |
2116 style->errors++; | |
2117 return(-1); | |
2118 } | |
2119 } | |
2120 | |
2121 xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur); | |
2122 } | |
2123 | |
2124 if (cur->match == NULL) | |
2125 return(0); | |
2126 | |
2127 priority = cur->priority; | |
2128 pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem, | |
2129 style, NULL, 1); | |
2130 if (pat == NULL) | |
2131 return(-1); | |
2132 while (pat) { | |
2133 next = pat->next; | |
2134 pat->next = NULL; | |
2135 name = NULL; | |
2136 | |
2137 pat->template = cur; | |
2138 if (mode != NULL) | |
2139 pat->mode = xmlDictLookup(style->dict, mode, -1); | |
2140 if (modeURI != NULL) | |
2141 pat->modeURI = xmlDictLookup(style->dict, modeURI, -1); | |
2142 if (priority != XSLT_PAT_NO_PRIORITY) | |
2143 pat->priority = priority; | |
2144 | |
2145 /* | |
2146 * insert it in the hash table list corresponding to its lookup name | |
2147 */ | |
2148 switch (pat->steps[0].op) { | |
2149 case XSLT_OP_ATTR: | |
2150 if (pat->steps[0].value != NULL) | |
2151 name = pat->steps[0].value; | |
2152 else | |
2153 top = &(style->attrMatch); | |
2154 break; | |
2155 case XSLT_OP_PARENT: | |
2156 case XSLT_OP_ANCESTOR: | |
2157 top = &(style->elemMatch); | |
2158 break; | |
2159 case XSLT_OP_ROOT: | |
2160 top = &(style->rootMatch); | |
2161 break; | |
2162 case XSLT_OP_KEY: | |
2163 top = &(style->keyMatch); | |
2164 break; | |
2165 case XSLT_OP_ID: | |
2166 /* TODO optimize ID !!! */ | |
2167 case XSLT_OP_NS: | |
2168 case XSLT_OP_ALL: | |
2169 top = &(style->elemMatch); | |
2170 break; | |
2171 case XSLT_OP_END: | |
2172 case XSLT_OP_PREDICATE: | |
2173 xsltTransformError(NULL, style, NULL, | |
2174 "xsltAddTemplate: invalid compiled pattern\n"); | |
2175 xsltFreeCompMatch(pat); | |
2176 return(-1); | |
2177 /* | |
2178 * TODO: some flags at the top level about type based patterns | |
2179 * would be faster than inclusion in the hash table. | |
2180 */ | |
2181 case XSLT_OP_PI: | |
2182 if (pat->steps[0].value != NULL) | |
2183 name = pat->steps[0].value; | |
2184 else | |
2185 top = &(style->piMatch); | |
2186 break; | |
2187 case XSLT_OP_COMMENT: | |
2188 top = &(style->commentMatch); | |
2189 break; | |
2190 case XSLT_OP_TEXT: | |
2191 top = &(style->textMatch); | |
2192 break; | |
2193 case XSLT_OP_ELEM: | |
2194 case XSLT_OP_NODE: | |
2195 if (pat->steps[0].value != NULL) | |
2196 name = pat->steps[0].value; | |
2197 else | |
2198 top = &(style->elemMatch); | |
2199 break; | |
2200 } | |
2201 if (name != NULL) { | |
2202 if (style->templatesHash == NULL) { | |
2203 style->templatesHash = xmlHashCreate(1024); | |
2204 if (style->templatesHash == NULL) { | |
2205 xsltFreeCompMatch(pat); | |
2206 return(-1); | |
2207 } | |
2208 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat)
; | |
2209 } else { | |
2210 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash, | |
2211 name, mode, modeURI); | |
2212 if (list == NULL) { | |
2213 xmlHashAddEntry3(style->templatesHash, name, | |
2214 mode, modeURI, pat); | |
2215 } else { | |
2216 /* | |
2217 * Note '<=' since one must choose among the matching | |
2218 * template rules that are left, the one that occurs | |
2219 * last in the stylesheet | |
2220 */ | |
2221 if (list->priority <= pat->priority) { | |
2222 pat->next = list; | |
2223 xmlHashUpdateEntry3(style->templatesHash, name, | |
2224 mode, modeURI, pat, NULL); | |
2225 } else { | |
2226 while (list->next != NULL) { | |
2227 if (list->next->priority <= pat->priority) | |
2228 break; | |
2229 list = list->next; | |
2230 } | |
2231 pat->next = list->next; | |
2232 list->next = pat; | |
2233 } | |
2234 } | |
2235 } | |
2236 } else if (top != NULL) { | |
2237 list = *top; | |
2238 if (list == NULL) { | |
2239 *top = pat; | |
2240 pat->next = NULL; | |
2241 } else if (list->priority <= pat->priority) { | |
2242 pat->next = list; | |
2243 *top = pat; | |
2244 } else { | |
2245 while (list->next != NULL) { | |
2246 if (list->next->priority <= pat->priority) | |
2247 break; | |
2248 list = list->next; | |
2249 } | |
2250 pat->next = list->next; | |
2251 list->next = pat; | |
2252 } | |
2253 } else { | |
2254 xsltTransformError(NULL, style, NULL, | |
2255 "xsltAddTemplate: invalid compiled pattern\n"); | |
2256 xsltFreeCompMatch(pat); | |
2257 return(-1); | |
2258 } | |
2259 #ifdef WITH_XSLT_DEBUG_PATTERN | |
2260 if (mode) | |
2261 xsltGenericDebug(xsltGenericDebugContext, | |
2262 "added pattern : '%s' mode '%s' priority %f\n", | |
2263 pat->pattern, pat->mode, pat->priority); | |
2264 else | |
2265 xsltGenericDebug(xsltGenericDebugContext, | |
2266 "added pattern : '%s' priority %f\n", | |
2267 pat->pattern, pat->priority); | |
2268 #endif | |
2269 | |
2270 pat = next; | |
2271 } | |
2272 return(0); | |
2273 } | |
2274 | |
2275 static int | |
2276 xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode) | |
2277 { | |
2278 if ((ctxt == NULL) || (contextNode == NULL)) { | |
2279 xsltTransformError(ctxt, NULL, ctxt->inst, | |
2280 "Internal error in xsltComputeAllKeys(): " | |
2281 "Bad arguments.\n"); | |
2282 return(-1); | |
2283 } | |
2284 | |
2285 if (ctxt->document == NULL) { | |
2286 /* | |
2287 * The document info will only be NULL if we have a RTF. | |
2288 */ | |
2289 if (contextNode->doc->_private != NULL) | |
2290 goto doc_info_mismatch; | |
2291 /* | |
2292 * On-demand creation of the document info (needed for keys). | |
2293 */ | |
2294 ctxt->document = xsltNewDocument(ctxt, contextNode->doc); | |
2295 if (ctxt->document == NULL) | |
2296 return(-1); | |
2297 } | |
2298 return xsltInitAllDocKeys(ctxt); | |
2299 | |
2300 doc_info_mismatch: | |
2301 xsltTransformError(ctxt, NULL, ctxt->inst, | |
2302 "Internal error in xsltComputeAllKeys(): " | |
2303 "The context's document info doesn't match the " | |
2304 "document info of the current result tree.\n"); | |
2305 ctxt->state = XSLT_STATE_STOPPED; | |
2306 return(-1); | |
2307 } | |
2308 | |
2309 /** | |
2310 * xsltGetTemplate: | |
2311 * @ctxt: a XSLT process context | |
2312 * @node: the node being processed | |
2313 * @style: the current style | |
2314 * | |
2315 * Finds the template applying to this node, if @style is non-NULL | |
2316 * it means one needs to look for the next imported template in scope. | |
2317 * | |
2318 * Returns the xsltTemplatePtr or NULL if not found | |
2319 */ | |
2320 xsltTemplatePtr | |
2321 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node, | |
2322 xsltStylesheetPtr style) | |
2323 { | |
2324 xsltStylesheetPtr curstyle; | |
2325 xsltTemplatePtr ret = NULL; | |
2326 const xmlChar *name = NULL; | |
2327 xsltCompMatchPtr list = NULL; | |
2328 float priority; | |
2329 int keyed = 0; | |
2330 | |
2331 if ((ctxt == NULL) || (node == NULL)) | |
2332 return(NULL); | |
2333 | |
2334 if (style == NULL) { | |
2335 curstyle = ctxt->style; | |
2336 } else { | |
2337 curstyle = xsltNextImport(style); | |
2338 } | |
2339 | |
2340 while ((curstyle != NULL) && (curstyle != style)) { | |
2341 priority = XSLT_PAT_NO_PRIORITY; | |
2342 /* TODO : handle IDs/keys here ! */ | |
2343 if (curstyle->templatesHash != NULL) { | |
2344 /* | |
2345 * Use the top name as selector | |
2346 */ | |
2347 switch (node->type) { | |
2348 case XML_ELEMENT_NODE: | |
2349 if (node->name[0] == ' ') | |
2350 break; | |
2351 case XML_ATTRIBUTE_NODE: | |
2352 case XML_PI_NODE: | |
2353 name = node->name; | |
2354 break; | |
2355 case XML_DOCUMENT_NODE: | |
2356 case XML_HTML_DOCUMENT_NODE: | |
2357 case XML_TEXT_NODE: | |
2358 case XML_CDATA_SECTION_NODE: | |
2359 case XML_COMMENT_NODE: | |
2360 case XML_ENTITY_REF_NODE: | |
2361 case XML_ENTITY_NODE: | |
2362 case XML_DOCUMENT_TYPE_NODE: | |
2363 case XML_DOCUMENT_FRAG_NODE: | |
2364 case XML_NOTATION_NODE: | |
2365 case XML_DTD_NODE: | |
2366 case XML_ELEMENT_DECL: | |
2367 case XML_ATTRIBUTE_DECL: | |
2368 case XML_ENTITY_DECL: | |
2369 case XML_NAMESPACE_DECL: | |
2370 case XML_XINCLUDE_START: | |
2371 case XML_XINCLUDE_END: | |
2372 break; | |
2373 default: | |
2374 return(NULL); | |
2375 | |
2376 } | |
2377 } | |
2378 if (name != NULL) { | |
2379 /* | |
2380 * find the list of applicable expressions based on the name | |
2381 */ | |
2382 list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash, | |
2383 name, ctxt->mode, ctxt->modeURI); | |
2384 } else | |
2385 list = NULL; | |
2386 while (list != NULL) { | |
2387 if (xsltTestCompMatch(ctxt, list, node, | |
2388 ctxt->mode, ctxt->modeURI)) { | |
2389 ret = list->template; | |
2390 priority = list->priority; | |
2391 break; | |
2392 } | |
2393 list = list->next; | |
2394 } | |
2395 list = NULL; | |
2396 | |
2397 /* | |
2398 * find alternate generic matches | |
2399 */ | |
2400 switch (node->type) { | |
2401 case XML_ELEMENT_NODE: | |
2402 if (node->name[0] == ' ') | |
2403 list = curstyle->rootMatch; | |
2404 else | |
2405 list = curstyle->elemMatch; | |
2406 if (node->psvi != NULL) keyed = 1; | |
2407 break; | |
2408 case XML_ATTRIBUTE_NODE: { | |
2409 xmlAttrPtr attr; | |
2410 | |
2411 list = curstyle->attrMatch; | |
2412 attr = (xmlAttrPtr) node; | |
2413 if (attr->psvi != NULL) keyed = 1; | |
2414 break; | |
2415 } | |
2416 case XML_PI_NODE: | |
2417 list = curstyle->piMatch; | |
2418 if (node->psvi != NULL) keyed = 1; | |
2419 break; | |
2420 case XML_DOCUMENT_NODE: | |
2421 case XML_HTML_DOCUMENT_NODE: { | |
2422 xmlDocPtr doc; | |
2423 | |
2424 list = curstyle->rootMatch; | |
2425 doc = (xmlDocPtr) node; | |
2426 if (doc->psvi != NULL) keyed = 1; | |
2427 break; | |
2428 } | |
2429 case XML_TEXT_NODE: | |
2430 case XML_CDATA_SECTION_NODE: | |
2431 list = curstyle->textMatch; | |
2432 if (node->psvi != NULL) keyed = 1; | |
2433 break; | |
2434 case XML_COMMENT_NODE: | |
2435 list = curstyle->commentMatch; | |
2436 if (node->psvi != NULL) keyed = 1; | |
2437 break; | |
2438 case XML_ENTITY_REF_NODE: | |
2439 case XML_ENTITY_NODE: | |
2440 case XML_DOCUMENT_TYPE_NODE: | |
2441 case XML_DOCUMENT_FRAG_NODE: | |
2442 case XML_NOTATION_NODE: | |
2443 case XML_DTD_NODE: | |
2444 case XML_ELEMENT_DECL: | |
2445 case XML_ATTRIBUTE_DECL: | |
2446 case XML_ENTITY_DECL: | |
2447 case XML_NAMESPACE_DECL: | |
2448 case XML_XINCLUDE_START: | |
2449 case XML_XINCLUDE_END: | |
2450 break; | |
2451 default: | |
2452 break; | |
2453 } | |
2454 while ((list != NULL) && | |
2455 ((ret == NULL) || (list->priority > priority))) { | |
2456 if (xsltTestCompMatch(ctxt, list, node, | |
2457 ctxt->mode, ctxt->modeURI)) { | |
2458 ret = list->template; | |
2459 priority = list->priority; | |
2460 break; | |
2461 } | |
2462 list = list->next; | |
2463 } | |
2464 /* | |
2465 * Some of the tests for elements can also apply to documents | |
2466 */ | |
2467 if ((node->type == XML_DOCUMENT_NODE) || | |
2468 (node->type == XML_HTML_DOCUMENT_NODE) || | |
2469 (node->type == XML_TEXT_NODE)) { | |
2470 list = curstyle->elemMatch; | |
2471 while ((list != NULL) && | |
2472 ((ret == NULL) || (list->priority > priority))) { | |
2473 if (xsltTestCompMatch(ctxt, list, node, | |
2474 ctxt->mode, ctxt->modeURI)) { | |
2475 ret = list->template; | |
2476 priority = list->priority; | |
2477 break; | |
2478 } | |
2479 list = list->next; | |
2480 } | |
2481 } else if ((node->type == XML_PI_NODE) || | |
2482 (node->type == XML_COMMENT_NODE)) { | |
2483 list = curstyle->elemMatch; | |
2484 while ((list != NULL) && | |
2485 ((ret == NULL) || (list->priority > priority))) { | |
2486 if (xsltTestCompMatch(ctxt, list, node, | |
2487 ctxt->mode, ctxt->modeURI)) { | |
2488 ret = list->template; | |
2489 priority = list->priority; | |
2490 break; | |
2491 } | |
2492 list = list->next; | |
2493 } | |
2494 } | |
2495 | |
2496 keyed_match: | |
2497 if (keyed) { | |
2498 list = curstyle->keyMatch; | |
2499 while ((list != NULL) && | |
2500 ((ret == NULL) || (list->priority > priority))) { | |
2501 if (xsltTestCompMatch(ctxt, list, node, | |
2502 ctxt->mode, ctxt->modeURI)) { | |
2503 ret = list->template; | |
2504 priority = list->priority; | |
2505 break; | |
2506 } | |
2507 list = list->next; | |
2508 } | |
2509 } | |
2510 else if (ctxt->hasTemplKeyPatterns && | |
2511 ((ctxt->document == NULL) || | |
2512 (ctxt->document->nbKeysComputed < ctxt->nbKeys))) | |
2513 { | |
2514 /* | |
2515 * Compute all remaining keys for this document. | |
2516 * | |
2517 * REVISIT TODO: I think this could be further optimized. | |
2518 */ | |
2519 if (xsltComputeAllKeys(ctxt, node) == -1) | |
2520 goto error; | |
2521 | |
2522 switch (node->type) { | |
2523 case XML_ELEMENT_NODE: | |
2524 if (node->psvi != NULL) keyed = 1; | |
2525 break; | |
2526 case XML_ATTRIBUTE_NODE: | |
2527 if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1; | |
2528 break; | |
2529 case XML_TEXT_NODE: | |
2530 case XML_CDATA_SECTION_NODE: | |
2531 case XML_COMMENT_NODE: | |
2532 case XML_PI_NODE: | |
2533 if (node->psvi != NULL) keyed = 1; | |
2534 break; | |
2535 case XML_DOCUMENT_NODE: | |
2536 case XML_HTML_DOCUMENT_NODE: | |
2537 if (((xmlDocPtr) node)->psvi != NULL) keyed = 1; | |
2538 break; | |
2539 default: | |
2540 break; | |
2541 } | |
2542 if (keyed) | |
2543 goto keyed_match; | |
2544 } | |
2545 if (ret != NULL) | |
2546 return(ret); | |
2547 | |
2548 /* | |
2549 * Cycle on next curstylesheet import. | |
2550 */ | |
2551 curstyle = xsltNextImport(curstyle); | |
2552 } | |
2553 | |
2554 error: | |
2555 return(NULL); | |
2556 } | |
2557 | |
2558 /** | |
2559 * xsltCleanupTemplates: | |
2560 * @style: an XSLT stylesheet | |
2561 * | |
2562 * Cleanup the state of the templates used by the stylesheet and | |
2563 * the ones it imports. | |
2564 */ | |
2565 void | |
2566 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) { | |
2567 } | |
2568 | |
2569 /** | |
2570 * xsltFreeTemplateHashes: | |
2571 * @style: an XSLT stylesheet | |
2572 * | |
2573 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism | |
2574 */ | |
2575 void | |
2576 xsltFreeTemplateHashes(xsltStylesheetPtr style) { | |
2577 if (style->templatesHash != NULL) | |
2578 xmlHashFree((xmlHashTablePtr) style->templatesHash, | |
2579 (xmlHashDeallocator) xsltFreeCompMatchList); | |
2580 if (style->rootMatch != NULL) | |
2581 xsltFreeCompMatchList(style->rootMatch); | |
2582 if (style->keyMatch != NULL) | |
2583 xsltFreeCompMatchList(style->keyMatch); | |
2584 if (style->elemMatch != NULL) | |
2585 xsltFreeCompMatchList(style->elemMatch); | |
2586 if (style->attrMatch != NULL) | |
2587 xsltFreeCompMatchList(style->attrMatch); | |
2588 if (style->parentMatch != NULL) | |
2589 xsltFreeCompMatchList(style->parentMatch); | |
2590 if (style->textMatch != NULL) | |
2591 xsltFreeCompMatchList(style->textMatch); | |
2592 if (style->piMatch != NULL) | |
2593 xsltFreeCompMatchList(style->piMatch); | |
2594 if (style->commentMatch != NULL) | |
2595 xsltFreeCompMatchList(style->commentMatch); | |
2596 if (style->namedTemplates != NULL) | |
2597 xmlHashFree(style->namedTemplates, NULL); | |
2598 } | |
2599 | |
OLD | NEW |