1 | /*
|
---|
2 | * transform.c: Implementation of the XSL Transformation 1.0 engine
|
---|
3 | * transform part, i.e. applying a Stylesheet to a document
|
---|
4 | *
|
---|
5 | * References:
|
---|
6 | * http://www.w3.org/TR/1999/REC-xslt-19991116
|
---|
7 | *
|
---|
8 | * Michael Kay "XSLT Programmer's Reference" pp 637-643
|
---|
9 | * Writing Multiple Output Files
|
---|
10 | *
|
---|
11 | * XSLT-1.1 Working Draft
|
---|
12 | * http://www.w3.org/TR/xslt11#multiple-output
|
---|
13 | *
|
---|
14 | * See Copyright for the status of this software.
|
---|
15 | *
|
---|
16 | * [email protected]
|
---|
17 | */
|
---|
18 |
|
---|
19 | #define IN_LIBXSLT
|
---|
20 | #include "libxslt.h"
|
---|
21 |
|
---|
22 | #include <string.h>
|
---|
23 |
|
---|
24 | #include <libxml/xmlmemory.h>
|
---|
25 | #include <libxml/parser.h>
|
---|
26 | #include <libxml/tree.h>
|
---|
27 | #include <libxml/valid.h>
|
---|
28 | #include <libxml/hash.h>
|
---|
29 | #include <libxml/encoding.h>
|
---|
30 | #include <libxml/xmlerror.h>
|
---|
31 | #include <libxml/xpath.h>
|
---|
32 | #include <libxml/parserInternals.h>
|
---|
33 | #include <libxml/xpathInternals.h>
|
---|
34 | #include <libxml/HTMLtree.h>
|
---|
35 | #include <libxml/debugXML.h>
|
---|
36 | #include <libxml/uri.h>
|
---|
37 | #include "xslt.h"
|
---|
38 | #include "xsltInternals.h"
|
---|
39 | #include "xsltutils.h"
|
---|
40 | #include "pattern.h"
|
---|
41 | #include "transform.h"
|
---|
42 | #include "variables.h"
|
---|
43 | #include "numbersInternals.h"
|
---|
44 | #include "namespaces.h"
|
---|
45 | #include "attributes.h"
|
---|
46 | #include "templates.h"
|
---|
47 | #include "imports.h"
|
---|
48 | #include "keys.h"
|
---|
49 | #include "documents.h"
|
---|
50 | #include "extensions.h"
|
---|
51 | #include "extra.h"
|
---|
52 | #include "preproc.h"
|
---|
53 | #include "security.h"
|
---|
54 |
|
---|
55 | #ifdef WITH_XSLT_DEBUG
|
---|
56 | #define WITH_XSLT_DEBUG_EXTRA
|
---|
57 | #define WITH_XSLT_DEBUG_PROCESS
|
---|
58 | #endif
|
---|
59 |
|
---|
60 | #define XSLT_GENERATE_HTML_DOCTYPE
|
---|
61 | #ifdef XSLT_GENERATE_HTML_DOCTYPE
|
---|
62 | static int xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
|
---|
63 | const xmlChar **systemID);
|
---|
64 | #endif
|
---|
65 |
|
---|
66 | int xsltMaxDepth = 3000;
|
---|
67 |
|
---|
68 | /*
|
---|
69 | * Useful macros
|
---|
70 | */
|
---|
71 |
|
---|
72 | #ifndef FALSE
|
---|
73 | # define FALSE (0 == 1)
|
---|
74 | # define TRUE (!FALSE)
|
---|
75 | #endif
|
---|
76 |
|
---|
77 | #define IS_BLANK_NODE(n) \
|
---|
78 | (((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
|
---|
79 |
|
---|
80 |
|
---|
81 | /*
|
---|
82 | * Forward declarations
|
---|
83 | */
|
---|
84 |
|
---|
85 | static xmlNsPtr
|
---|
86 | xsltCopyNamespaceListInternal(xmlNodePtr node, xmlNsPtr cur);
|
---|
87 |
|
---|
88 | static xmlNodePtr
|
---|
89 | xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
|
---|
90 | xmlNodePtr invocNode,
|
---|
91 | xmlNodePtr node,
|
---|
92 | xmlNodePtr insert, int isLRE, int topElemVisited);
|
---|
93 |
|
---|
94 | static void
|
---|
95 | xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
|
---|
96 | xmlNodePtr contextNode, xmlNodePtr list,
|
---|
97 | xsltTemplatePtr templ);
|
---|
98 |
|
---|
99 | static void
|
---|
100 | xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
|
---|
101 | xmlNodePtr contextNode,
|
---|
102 | xmlNodePtr list,
|
---|
103 | xsltTemplatePtr templ,
|
---|
104 | xsltStackElemPtr withParams);
|
---|
105 |
|
---|
106 | /**
|
---|
107 | * templPush:
|
---|
108 | * @ctxt: the transformation context
|
---|
109 | * @value: the template to push on the stack
|
---|
110 | *
|
---|
111 | * Push a template on the stack
|
---|
112 | *
|
---|
113 | * Returns the new index in the stack or 0 in case of error
|
---|
114 | */
|
---|
115 | static int
|
---|
116 | templPush(xsltTransformContextPtr ctxt, xsltTemplatePtr value)
|
---|
117 | {
|
---|
118 | if (ctxt->templMax == 0) {
|
---|
119 | ctxt->templMax = 4;
|
---|
120 | ctxt->templTab =
|
---|
121 | (xsltTemplatePtr *) xmlMalloc(ctxt->templMax *
|
---|
122 | sizeof(ctxt->templTab[0]));
|
---|
123 | if (ctxt->templTab == NULL) {
|
---|
124 | xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
|
---|
125 | return (0);
|
---|
126 | }
|
---|
127 | }
|
---|
128 | if (ctxt->templNr >= ctxt->templMax) {
|
---|
129 | ctxt->templMax *= 2;
|
---|
130 | ctxt->templTab =
|
---|
131 | (xsltTemplatePtr *) xmlRealloc(ctxt->templTab,
|
---|
132 | ctxt->templMax *
|
---|
133 | sizeof(ctxt->templTab[0]));
|
---|
134 | if (ctxt->templTab == NULL) {
|
---|
135 | xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
|
---|
136 | return (0);
|
---|
137 | }
|
---|
138 | }
|
---|
139 | ctxt->templTab[ctxt->templNr] = value;
|
---|
140 | ctxt->templ = value;
|
---|
141 | return (ctxt->templNr++);
|
---|
142 | }
|
---|
143 | /**
|
---|
144 | * templPop:
|
---|
145 | * @ctxt: the transformation context
|
---|
146 | *
|
---|
147 | * Pop a template value from the stack
|
---|
148 | *
|
---|
149 | * Returns the stored template value
|
---|
150 | */
|
---|
151 | static xsltTemplatePtr
|
---|
152 | templPop(xsltTransformContextPtr ctxt)
|
---|
153 | {
|
---|
154 | xsltTemplatePtr ret;
|
---|
155 |
|
---|
156 | if (ctxt->templNr <= 0)
|
---|
157 | return (0);
|
---|
158 | ctxt->templNr--;
|
---|
159 | if (ctxt->templNr > 0)
|
---|
160 | ctxt->templ = ctxt->templTab[ctxt->templNr - 1];
|
---|
161 | else
|
---|
162 | ctxt->templ = (xsltTemplatePtr) 0;
|
---|
163 | ret = ctxt->templTab[ctxt->templNr];
|
---|
164 | ctxt->templTab[ctxt->templNr] = 0;
|
---|
165 | return (ret);
|
---|
166 | }
|
---|
167 |
|
---|
168 | /**
|
---|
169 | * xsltLocalVariablePop:
|
---|
170 | * @ctxt: the transformation context
|
---|
171 | * @limitNr: number of variables which should remain
|
---|
172 | * @level: the depth in the xsl:template's tree
|
---|
173 | *
|
---|
174 | * Pops all variable values at the given @depth from the stack.
|
---|
175 | *
|
---|
176 | * Returns the stored variable value
|
---|
177 | * **NOTE:**
|
---|
178 | * This is an internal routine and should not be called by users!
|
---|
179 | */
|
---|
180 | void
|
---|
181 | xsltLocalVariablePop(xsltTransformContextPtr ctxt, int limitNr, int level)
|
---|
182 | {
|
---|
183 | xsltStackElemPtr variable;
|
---|
184 |
|
---|
185 | if (ctxt->varsNr <= 0)
|
---|
186 | return;
|
---|
187 |
|
---|
188 | do {
|
---|
189 | if (ctxt->varsNr <= limitNr)
|
---|
190 | break;
|
---|
191 | variable = ctxt->varsTab[ctxt->varsNr - 1];
|
---|
192 | if (variable->level <= level)
|
---|
193 | break;
|
---|
194 | if (variable->level >= 0)
|
---|
195 | xsltFreeStackElemList(variable);
|
---|
196 | ctxt->varsNr--;
|
---|
197 | } while (ctxt->varsNr != 0);
|
---|
198 | if (ctxt->varsNr > 0)
|
---|
199 | ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
|
---|
200 | else
|
---|
201 | ctxt->vars = NULL;
|
---|
202 | }
|
---|
203 |
|
---|
204 | /**
|
---|
205 | * xsltTemplateParamsCleanup:
|
---|
206 | *
|
---|
207 | * Removes xsl:param and xsl:with-param items from the
|
---|
208 | * variable-stack. Only xsl:with-param items are not freed.
|
---|
209 | */
|
---|
210 | static void
|
---|
211 | xsltTemplateParamsCleanup(xsltTransformContextPtr ctxt)
|
---|
212 | {
|
---|
213 | xsltStackElemPtr param;
|
---|
214 |
|
---|
215 | for (; ctxt->varsNr > ctxt->varsBase; ctxt->varsNr--) {
|
---|
216 | param = ctxt->varsTab[ctxt->varsNr -1];
|
---|
217 | /*
|
---|
218 | * Free xsl:param items.
|
---|
219 | * xsl:with-param items will have a level of -1 or -2.
|
---|
220 | */
|
---|
221 | if (param->level >= 0) {
|
---|
222 | xsltFreeStackElemList(param);
|
---|
223 | }
|
---|
224 | }
|
---|
225 | if (ctxt->varsNr > 0)
|
---|
226 | ctxt->vars = ctxt->varsTab[ctxt->varsNr - 1];
|
---|
227 | else
|
---|
228 | ctxt->vars = NULL;
|
---|
229 | }
|
---|
230 |
|
---|
231 | /**
|
---|
232 | * profPush:
|
---|
233 | * @ctxt: the transformation context
|
---|
234 | * @value: the profiling value to push on the stack
|
---|
235 | *
|
---|
236 | * Push a profiling value on the stack
|
---|
237 | *
|
---|
238 | * Returns the new index in the stack or 0 in case of error
|
---|
239 | */
|
---|
240 | static int
|
---|
241 | profPush(xsltTransformContextPtr ctxt, long value)
|
---|
242 | {
|
---|
243 | if (ctxt->profMax == 0) {
|
---|
244 | ctxt->profMax = 4;
|
---|
245 | ctxt->profTab =
|
---|
246 | (long *) xmlMalloc(ctxt->profMax * sizeof(ctxt->profTab[0]));
|
---|
247 | if (ctxt->profTab == NULL) {
|
---|
248 | xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
|
---|
249 | return (0);
|
---|
250 | }
|
---|
251 | }
|
---|
252 | if (ctxt->profNr >= ctxt->profMax) {
|
---|
253 | ctxt->profMax *= 2;
|
---|
254 | ctxt->profTab =
|
---|
255 | (long *) xmlRealloc(ctxt->profTab,
|
---|
256 | ctxt->profMax * sizeof(ctxt->profTab[0]));
|
---|
257 | if (ctxt->profTab == NULL) {
|
---|
258 | xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
|
---|
259 | return (0);
|
---|
260 | }
|
---|
261 | }
|
---|
262 | ctxt->profTab[ctxt->profNr] = value;
|
---|
263 | ctxt->prof = value;
|
---|
264 | return (ctxt->profNr++);
|
---|
265 | }
|
---|
266 | /**
|
---|
267 | * profPop:
|
---|
268 | * @ctxt: the transformation context
|
---|
269 | *
|
---|
270 | * Pop a profiling value from the stack
|
---|
271 | *
|
---|
272 | * Returns the stored profiling value
|
---|
273 | */
|
---|
274 | static long
|
---|
275 | profPop(xsltTransformContextPtr ctxt)
|
---|
276 | {
|
---|
277 | long ret;
|
---|
278 |
|
---|
279 | if (ctxt->profNr <= 0)
|
---|
280 | return (0);
|
---|
281 | ctxt->profNr--;
|
---|
282 | if (ctxt->profNr > 0)
|
---|
283 | ctxt->prof = ctxt->profTab[ctxt->profNr - 1];
|
---|
284 | else
|
---|
285 | ctxt->prof = (long) 0;
|
---|
286 | ret = ctxt->profTab[ctxt->profNr];
|
---|
287 | ctxt->profTab[ctxt->profNr] = 0;
|
---|
288 | return (ret);
|
---|
289 | }
|
---|
290 |
|
---|
291 | /************************************************************************
|
---|
292 | * *
|
---|
293 | * XInclude default settings *
|
---|
294 | * *
|
---|
295 | ************************************************************************/
|
---|
296 |
|
---|
297 | static int xsltDoXIncludeDefault = 0;
|
---|
298 |
|
---|
299 | /**
|
---|
300 | * xsltSetXIncludeDefault:
|
---|
301 | * @xinclude: whether to do XInclude processing
|
---|
302 | *
|
---|
303 | * Set whether XInclude should be processed on document being loaded by default
|
---|
304 | */
|
---|
305 | void
|
---|
306 | xsltSetXIncludeDefault(int xinclude) {
|
---|
307 | xsltDoXIncludeDefault = (xinclude != 0);
|
---|
308 | }
|
---|
309 |
|
---|
310 | /**
|
---|
311 | * xsltGetXIncludeDefault:
|
---|
312 | *
|
---|
313 | * Provides the default state for XInclude processing
|
---|
314 | *
|
---|
315 | * Returns 0 if there is no processing 1 otherwise
|
---|
316 | */
|
---|
317 | int
|
---|
318 | xsltGetXIncludeDefault(void) {
|
---|
319 | return(xsltDoXIncludeDefault);
|
---|
320 | }
|
---|
321 |
|
---|
322 | unsigned long xsltDefaultTrace = (unsigned long) XSLT_TRACE_ALL;
|
---|
323 |
|
---|
324 | /**
|
---|
325 | * xsltDebugSetDefaultTrace:
|
---|
326 | * @val: tracing level mask
|
---|
327 | *
|
---|
328 | * Set the default debug tracing level mask
|
---|
329 | */
|
---|
330 | void xsltDebugSetDefaultTrace(xsltDebugTraceCodes val) {
|
---|
331 | xsltDefaultTrace = val;
|
---|
332 | }
|
---|
333 |
|
---|
334 | /**
|
---|
335 | * xsltDebugGetDefaultTrace:
|
---|
336 | *
|
---|
337 | * Get the current default debug tracing level mask
|
---|
338 | *
|
---|
339 | * Returns the current default debug tracing level mask
|
---|
340 | */
|
---|
341 | xsltDebugTraceCodes xsltDebugGetDefaultTrace() {
|
---|
342 | return xsltDefaultTrace;
|
---|
343 | }
|
---|
344 |
|
---|
345 | /************************************************************************
|
---|
346 | * *
|
---|
347 | * Handling of Transformation Contexts *
|
---|
348 | * *
|
---|
349 | ************************************************************************/
|
---|
350 |
|
---|
351 | static xsltTransformCachePtr
|
---|
352 | xsltTransformCacheCreate(void)
|
---|
353 | {
|
---|
354 | xsltTransformCachePtr ret;
|
---|
355 |
|
---|
356 | ret = (xsltTransformCachePtr) xmlMalloc(sizeof(xsltTransformCache));
|
---|
357 | if (ret == NULL) {
|
---|
358 | xsltTransformError(NULL, NULL, NULL,
|
---|
359 | "xsltTransformCacheCreate : malloc failed\n");
|
---|
360 | return(NULL);
|
---|
361 | }
|
---|
362 | memset(ret, 0, sizeof(xsltTransformCache));
|
---|
363 | return(ret);
|
---|
364 | }
|
---|
365 |
|
---|
366 | static void
|
---|
367 | xsltTransformCacheFree(xsltTransformCachePtr cache)
|
---|
368 | {
|
---|
369 | if (cache == NULL)
|
---|
370 | return;
|
---|
371 | /*
|
---|
372 | * Free tree fragments.
|
---|
373 | */
|
---|
374 | if (cache->RVT) {
|
---|
375 | xmlDocPtr tmp, cur = cache->RVT;
|
---|
376 | while (cur) {
|
---|
377 | tmp = cur;
|
---|
378 | cur = (xmlDocPtr) cur->next;
|
---|
379 | if (tmp->_private != NULL) {
|
---|
380 | /*
|
---|
381 | * Tree the document info.
|
---|
382 | */
|
---|
383 | xsltFreeDocumentKeys((xsltDocumentPtr) tmp->_private);
|
---|
384 | xmlFree(tmp->_private);
|
---|
385 | }
|
---|
386 | xmlFreeDoc(tmp);
|
---|
387 | }
|
---|
388 | }
|
---|
389 | /*
|
---|
390 | * Free vars/params.
|
---|
391 | */
|
---|
392 | if (cache->stackItems) {
|
---|
393 | xsltStackElemPtr tmp, cur = cache->stackItems;
|
---|
394 | while (cur) {
|
---|
395 | tmp = cur;
|
---|
396 | cur = cur->next;
|
---|
397 | /*
|
---|
398 | * REVISIT TODO: Should be call a destruction-function
|
---|
399 | * instead?
|
---|
400 | */
|
---|
401 | xmlFree(tmp);
|
---|
402 | }
|
---|
403 | }
|
---|
404 | xmlFree(cache);
|
---|
405 | }
|
---|
406 |
|
---|
407 | /**
|
---|
408 | * xsltNewTransformContext:
|
---|
409 | * @style: a parsed XSLT stylesheet
|
---|
410 | * @doc: the input document
|
---|
411 | *
|
---|
412 | * Create a new XSLT TransformContext
|
---|
413 | *
|
---|
414 | * Returns the newly allocated xsltTransformContextPtr or NULL in case of error
|
---|
415 | */
|
---|
416 | xsltTransformContextPtr
|
---|
417 | xsltNewTransformContext(xsltStylesheetPtr style, xmlDocPtr doc) {
|
---|
418 | xsltTransformContextPtr cur;
|
---|
419 | xsltDocumentPtr docu;
|
---|
420 | int i;
|
---|
421 |
|
---|
422 | cur = (xsltTransformContextPtr) xmlMalloc(sizeof(xsltTransformContext));
|
---|
423 | if (cur == NULL) {
|
---|
424 | xsltTransformError(NULL, NULL, (xmlNodePtr)doc,
|
---|
425 | "xsltNewTransformContext : malloc failed\n");
|
---|
426 | return(NULL);
|
---|
427 | }
|
---|
428 | memset(cur, 0, sizeof(xsltTransformContext));
|
---|
429 |
|
---|
430 | cur->cache = xsltTransformCacheCreate();
|
---|
431 | if (cur->cache == NULL)
|
---|
432 | goto internal_err;
|
---|
433 | /*
|
---|
434 | * setup of the dictionary must be done early as some of the
|
---|
435 | * processing later like key handling may need it.
|
---|
436 | */
|
---|
437 | cur->dict = xmlDictCreateSub(style->dict);
|
---|
438 | cur->internalized = ((style->internalized) && (cur->dict != NULL));
|
---|
439 | #ifdef WITH_XSLT_DEBUG
|
---|
440 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
441 | "Creating sub-dictionary from stylesheet for transformation\n");
|
---|
442 | #endif
|
---|
443 |
|
---|
444 | /*
|
---|
445 | * initialize the template stack
|
---|
446 | */
|
---|
447 | cur->templTab = (xsltTemplatePtr *)
|
---|
448 | xmlMalloc(10 * sizeof(xsltTemplatePtr));
|
---|
449 | if (cur->templTab == NULL) {
|
---|
450 | xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
|
---|
451 | "xsltNewTransformContext: out of memory\n");
|
---|
452 | goto internal_err;
|
---|
453 | }
|
---|
454 | cur->templNr = 0;
|
---|
455 | cur->templMax = 5;
|
---|
456 | cur->templ = NULL;
|
---|
457 |
|
---|
458 | /*
|
---|
459 | * initialize the variables stack
|
---|
460 | */
|
---|
461 | cur->varsTab = (xsltStackElemPtr *)
|
---|
462 | xmlMalloc(10 * sizeof(xsltStackElemPtr));
|
---|
463 | if (cur->varsTab == NULL) {
|
---|
464 | xmlGenericError(xmlGenericErrorContext,
|
---|
465 | "xsltNewTransformContext: out of memory\n");
|
---|
466 | goto internal_err;
|
---|
467 | }
|
---|
468 | cur->varsNr = 0;
|
---|
469 | cur->varsMax = 10;
|
---|
470 | cur->vars = NULL;
|
---|
471 | cur->varsBase = 0;
|
---|
472 |
|
---|
473 | /*
|
---|
474 | * the profiling stack is not initialized by default
|
---|
475 | */
|
---|
476 | cur->profTab = NULL;
|
---|
477 | cur->profNr = 0;
|
---|
478 | cur->profMax = 0;
|
---|
479 | cur->prof = 0;
|
---|
480 |
|
---|
481 | cur->style = style;
|
---|
482 | xmlXPathInit();
|
---|
483 | cur->xpathCtxt = xmlXPathNewContext(doc);
|
---|
484 | if (cur->xpathCtxt == NULL) {
|
---|
485 | xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
|
---|
486 | "xsltNewTransformContext : xmlXPathNewContext failed\n");
|
---|
487 | goto internal_err;
|
---|
488 | }
|
---|
489 | /*
|
---|
490 | * Create an XPath cache.
|
---|
491 | */
|
---|
492 | if (xmlXPathContextSetCache(cur->xpathCtxt, 1, -1, 0) == -1)
|
---|
493 | goto internal_err;
|
---|
494 | /*
|
---|
495 | * Initialize the extras array
|
---|
496 | */
|
---|
497 | if (style->extrasNr != 0) {
|
---|
498 | cur->extrasMax = style->extrasNr + 20;
|
---|
499 | cur->extras = (xsltRuntimeExtraPtr)
|
---|
500 | xmlMalloc(cur->extrasMax * sizeof(xsltRuntimeExtra));
|
---|
501 | if (cur->extras == NULL) {
|
---|
502 | xmlGenericError(xmlGenericErrorContext,
|
---|
503 | "xsltNewTransformContext: out of memory\n");
|
---|
504 | goto internal_err;
|
---|
505 | }
|
---|
506 | cur->extrasNr = style->extrasNr;
|
---|
507 | for (i = 0;i < cur->extrasMax;i++) {
|
---|
508 | cur->extras[i].info = NULL;
|
---|
509 | cur->extras[i].deallocate = NULL;
|
---|
510 | cur->extras[i].val.ptr = NULL;
|
---|
511 | }
|
---|
512 | } else {
|
---|
513 | cur->extras = NULL;
|
---|
514 | cur->extrasNr = 0;
|
---|
515 | cur->extrasMax = 0;
|
---|
516 | }
|
---|
517 |
|
---|
518 | XSLT_REGISTER_VARIABLE_LOOKUP(cur);
|
---|
519 | XSLT_REGISTER_FUNCTION_LOOKUP(cur);
|
---|
520 | cur->xpathCtxt->nsHash = style->nsHash;
|
---|
521 | /*
|
---|
522 | * Initialize the registered external modules
|
---|
523 | */
|
---|
524 | xsltInitCtxtExts(cur);
|
---|
525 | /*
|
---|
526 | * Setup document element ordering for later efficiencies
|
---|
527 | * (bug 133289)
|
---|
528 | */
|
---|
529 | if (xslDebugStatus == XSLT_DEBUG_NONE)
|
---|
530 | xmlXPathOrderDocElems(doc);
|
---|
531 | /*
|
---|
532 | * Must set parserOptions before calling xsltNewDocument
|
---|
533 | * (bug 164530)
|
---|
534 | */
|
---|
535 | cur->parserOptions = XSLT_PARSE_OPTIONS;
|
---|
536 | docu = xsltNewDocument(cur, doc);
|
---|
537 | if (docu == NULL) {
|
---|
538 | xsltTransformError(cur, NULL, (xmlNodePtr)doc,
|
---|
539 | "xsltNewTransformContext : xsltNewDocument failed\n");
|
---|
540 | goto internal_err;
|
---|
541 | }
|
---|
542 | docu->main = 1;
|
---|
543 | cur->document = docu;
|
---|
544 | cur->inst = NULL;
|
---|
545 | cur->outputFile = NULL;
|
---|
546 | cur->sec = xsltGetDefaultSecurityPrefs();
|
---|
547 | cur->debugStatus = xslDebugStatus;
|
---|
548 | cur->traceCode = (unsigned long*) &xsltDefaultTrace;
|
---|
549 | cur->xinclude = xsltGetXIncludeDefault();
|
---|
550 |
|
---|
551 | return(cur);
|
---|
552 |
|
---|
553 | internal_err:
|
---|
554 | if (cur != NULL)
|
---|
555 | xsltFreeTransformContext(cur);
|
---|
556 | return(NULL);
|
---|
557 | }
|
---|
558 |
|
---|
559 | /**
|
---|
560 | * xsltFreeTransformContext:
|
---|
561 | * @ctxt: an XSLT parser context
|
---|
562 | *
|
---|
563 | * Free up the memory allocated by @ctxt
|
---|
564 | */
|
---|
565 | void
|
---|
566 | xsltFreeTransformContext(xsltTransformContextPtr ctxt) {
|
---|
567 | if (ctxt == NULL)
|
---|
568 | return;
|
---|
569 |
|
---|
570 | /*
|
---|
571 | * Shutdown the extension modules associated to the stylesheet
|
---|
572 | * used if needed.
|
---|
573 | */
|
---|
574 | xsltShutdownCtxtExts(ctxt);
|
---|
575 |
|
---|
576 | if (ctxt->xpathCtxt != NULL) {
|
---|
577 | ctxt->xpathCtxt->nsHash = NULL;
|
---|
578 | xmlXPathFreeContext(ctxt->xpathCtxt);
|
---|
579 | }
|
---|
580 | if (ctxt->templTab != NULL)
|
---|
581 | xmlFree(ctxt->templTab);
|
---|
582 | if (ctxt->varsTab != NULL)
|
---|
583 | xmlFree(ctxt->varsTab);
|
---|
584 | if (ctxt->profTab != NULL)
|
---|
585 | xmlFree(ctxt->profTab);
|
---|
586 | if ((ctxt->extrasNr > 0) && (ctxt->extras != NULL)) {
|
---|
587 | int i;
|
---|
588 |
|
---|
589 | for (i = 0;i < ctxt->extrasNr;i++) {
|
---|
590 | if ((ctxt->extras[i].deallocate != NULL) &&
|
---|
591 | (ctxt->extras[i].info != NULL))
|
---|
592 | ctxt->extras[i].deallocate(ctxt->extras[i].info);
|
---|
593 | }
|
---|
594 | xmlFree(ctxt->extras);
|
---|
595 | }
|
---|
596 | xsltFreeGlobalVariables(ctxt);
|
---|
597 | xsltFreeDocuments(ctxt);
|
---|
598 | xsltFreeCtxtExts(ctxt);
|
---|
599 | xsltFreeRVTs(ctxt);
|
---|
600 | xsltTransformCacheFree(ctxt->cache);
|
---|
601 | xmlDictFree(ctxt->dict);
|
---|
602 | #ifdef WITH_XSLT_DEBUG
|
---|
603 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
604 | "freeing transformation dictionary\n");
|
---|
605 | #endif
|
---|
606 | memset(ctxt, -1, sizeof(xsltTransformContext));
|
---|
607 | xmlFree(ctxt);
|
---|
608 | }
|
---|
609 |
|
---|
610 | /************************************************************************
|
---|
611 | * *
|
---|
612 | * Copy of Nodes in an XSLT fashion *
|
---|
613 | * *
|
---|
614 | ************************************************************************/
|
---|
615 |
|
---|
616 | xmlNodePtr xsltCopyTree(xsltTransformContextPtr ctxt,
|
---|
617 | xmlNodePtr node, xmlNodePtr insert, int literal);
|
---|
618 |
|
---|
619 | /**
|
---|
620 | * xsltAddTextString:
|
---|
621 | * @ctxt: a XSLT process context
|
---|
622 | * @target: the text node where the text will be attached
|
---|
623 | * @string: the text string
|
---|
624 | * @len: the string length in byte
|
---|
625 | *
|
---|
626 | * Extend the current text node with the new string, it handles coalescing
|
---|
627 | *
|
---|
628 | * Returns: the text node
|
---|
629 | */
|
---|
630 | static xmlNodePtr
|
---|
631 | xsltAddTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
|
---|
632 | const xmlChar *string, int len) {
|
---|
633 | /*
|
---|
634 | * optimization
|
---|
635 | */
|
---|
636 | if ((len <= 0) || (string == NULL) || (target == NULL))
|
---|
637 | return(target);
|
---|
638 |
|
---|
639 | if (ctxt->lasttext == target->content) {
|
---|
640 |
|
---|
641 | if (ctxt->lasttuse + len >= ctxt->lasttsize) {
|
---|
642 | xmlChar *newbuf;
|
---|
643 | int size;
|
---|
644 |
|
---|
645 | size = ctxt->lasttsize + len + 100;
|
---|
646 | size *= 2;
|
---|
647 | newbuf = (xmlChar *) xmlRealloc(target->content,size);
|
---|
648 | if (newbuf == NULL) {
|
---|
649 | xsltTransformError(ctxt, NULL, target,
|
---|
650 | "xsltCopyText: text allocation failed\n");
|
---|
651 | return(NULL);
|
---|
652 | }
|
---|
653 | ctxt->lasttsize = size;
|
---|
654 | ctxt->lasttext = newbuf;
|
---|
655 | target->content = newbuf;
|
---|
656 | }
|
---|
657 | memcpy(&(target->content[ctxt->lasttuse]), string, len);
|
---|
658 | ctxt->lasttuse += len;
|
---|
659 | target->content[ctxt->lasttuse] = 0;
|
---|
660 | } else {
|
---|
661 | xmlNodeAddContent(target, string);
|
---|
662 | ctxt->lasttext = target->content;
|
---|
663 | len = xmlStrlen(target->content);
|
---|
664 | ctxt->lasttsize = len;
|
---|
665 | ctxt->lasttuse = len;
|
---|
666 | }
|
---|
667 | return(target);
|
---|
668 | }
|
---|
669 |
|
---|
670 | /**
|
---|
671 | * xsltCopyTextString:
|
---|
672 | * @ctxt: a XSLT process context
|
---|
673 | * @target: the element where the text will be attached
|
---|
674 | * @string: the text string
|
---|
675 | * @noescape: should disable-escaping be activated for this text node.
|
---|
676 | *
|
---|
677 | * Adds @string to a newly created or an existent text node child of
|
---|
678 | * @target.
|
---|
679 | *
|
---|
680 | * Returns: the text node, where the text content of @cur is copied to.
|
---|
681 | * NULL in case of API or internal errors.
|
---|
682 | */
|
---|
683 | xmlNodePtr
|
---|
684 | xsltCopyTextString(xsltTransformContextPtr ctxt, xmlNodePtr target,
|
---|
685 | const xmlChar *string, int noescape)
|
---|
686 | {
|
---|
687 | xmlNodePtr copy;
|
---|
688 | int len;
|
---|
689 |
|
---|
690 | if (string == NULL)
|
---|
691 | return(NULL);
|
---|
692 |
|
---|
693 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
694 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
|
---|
695 | "xsltCopyTextString: copy text %s\n",
|
---|
696 | string));
|
---|
697 | #endif
|
---|
698 |
|
---|
699 | /*
|
---|
700 | * Play save and reset the merging mechanism for every new
|
---|
701 | * target node.
|
---|
702 | */
|
---|
703 | if ((target == NULL) || (target->children == NULL)) {
|
---|
704 | ctxt->lasttext = NULL;
|
---|
705 | }
|
---|
706 |
|
---|
707 | /* handle coalescing of text nodes here */
|
---|
708 | len = xmlStrlen(string);
|
---|
709 | if ((ctxt->type == XSLT_OUTPUT_XML) &&
|
---|
710 | (ctxt->style->cdataSection != NULL) &&
|
---|
711 | (target != NULL) &&
|
---|
712 | (target->type == XML_ELEMENT_NODE) &&
|
---|
713 | (((target->ns == NULL) &&
|
---|
714 | (xmlHashLookup2(ctxt->style->cdataSection,
|
---|
715 | target->name, NULL) != NULL)) ||
|
---|
716 | ((target->ns != NULL) &&
|
---|
717 | (xmlHashLookup2(ctxt->style->cdataSection,
|
---|
718 | target->name, target->ns->href) != NULL))))
|
---|
719 | {
|
---|
720 | /*
|
---|
721 | * Process "cdata-section-elements".
|
---|
722 | */
|
---|
723 | if ((target->last != NULL) &&
|
---|
724 | (target->last->type == XML_CDATA_SECTION_NODE))
|
---|
725 | {
|
---|
726 | return(xsltAddTextString(ctxt, target->last, string, len));
|
---|
727 | }
|
---|
728 | copy = xmlNewCDataBlock(ctxt->output, string, len);
|
---|
729 | } else if (noescape) {
|
---|
730 | /*
|
---|
731 | * Process "disable-output-escaping".
|
---|
732 | */
|
---|
733 | if ((target != NULL) && (target->last != NULL) &&
|
---|
734 | (target->last->type == XML_TEXT_NODE) &&
|
---|
735 | (target->last->name == xmlStringTextNoenc))
|
---|
736 | {
|
---|
737 | return(xsltAddTextString(ctxt, target->last, string, len));
|
---|
738 | }
|
---|
739 | copy = xmlNewTextLen(string, len);
|
---|
740 | if (copy != NULL)
|
---|
741 | copy->name = xmlStringTextNoenc;
|
---|
742 | } else {
|
---|
743 | /*
|
---|
744 | * Default processing.
|
---|
745 | */
|
---|
746 | if ((target != NULL) && (target->last != NULL) &&
|
---|
747 | (target->last->type == XML_TEXT_NODE) &&
|
---|
748 | (target->last->name == xmlStringText)) {
|
---|
749 | return(xsltAddTextString(ctxt, target->last, string, len));
|
---|
750 | }
|
---|
751 | copy = xmlNewTextLen(string, len);
|
---|
752 | }
|
---|
753 | if (copy != NULL) {
|
---|
754 | if (target != NULL)
|
---|
755 | xmlAddChild(target, copy);
|
---|
756 | ctxt->lasttext = copy->content;
|
---|
757 | ctxt->lasttsize = len;
|
---|
758 | ctxt->lasttuse = len;
|
---|
759 | } else {
|
---|
760 | xsltTransformError(ctxt, NULL, target,
|
---|
761 | "xsltCopyTextString: text copy failed\n");
|
---|
762 | ctxt->lasttext = NULL;
|
---|
763 | }
|
---|
764 | return(copy);
|
---|
765 | }
|
---|
766 |
|
---|
767 | /**
|
---|
768 | * xsltCopyText:
|
---|
769 | * @ctxt: a XSLT process context
|
---|
770 | * @target: the element where the text will be attached
|
---|
771 | * @cur: the text or CDATA node
|
---|
772 | * @interned: the string is in the target doc dictionary
|
---|
773 | *
|
---|
774 | * Copy the text content of @cur and append it to @target's children.
|
---|
775 | *
|
---|
776 | * Returns: the text node, where the text content of @cur is copied to.
|
---|
777 | * NULL in case of API or internal errors.
|
---|
778 | */
|
---|
779 | static xmlNodePtr
|
---|
780 | xsltCopyText(xsltTransformContextPtr ctxt, xmlNodePtr target,
|
---|
781 | xmlNodePtr cur, int interned)
|
---|
782 | {
|
---|
783 | xmlNodePtr copy;
|
---|
784 |
|
---|
785 | if ((cur->type != XML_TEXT_NODE) &&
|
---|
786 | (cur->type != XML_CDATA_SECTION_NODE))
|
---|
787 | return(NULL);
|
---|
788 | if (cur->content == NULL)
|
---|
789 | return(NULL);
|
---|
790 |
|
---|
791 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
792 | if (cur->type == XML_CDATA_SECTION_NODE) {
|
---|
793 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
|
---|
794 | "xsltCopyText: copy CDATA text %s\n",
|
---|
795 | cur->content));
|
---|
796 | } else if (cur->name == xmlStringTextNoenc) {
|
---|
797 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
|
---|
798 | "xsltCopyText: copy unescaped text %s\n",
|
---|
799 | cur->content));
|
---|
800 | } else {
|
---|
801 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_TEXT,xsltGenericDebug(xsltGenericDebugContext,
|
---|
802 | "xsltCopyText: copy text %s\n",
|
---|
803 | cur->content));
|
---|
804 | }
|
---|
805 | #endif
|
---|
806 |
|
---|
807 | /*
|
---|
808 | * Play save and reset the merging mechanism for every new
|
---|
809 | * target node.
|
---|
810 | */
|
---|
811 | if ((target == NULL) || (target->children == NULL)) {
|
---|
812 | ctxt->lasttext = NULL;
|
---|
813 | }
|
---|
814 |
|
---|
815 | if ((ctxt->style->cdataSection != NULL) &&
|
---|
816 | (ctxt->type == XSLT_OUTPUT_XML) &&
|
---|
817 | (target != NULL) &&
|
---|
818 | (target->type == XML_ELEMENT_NODE) &&
|
---|
819 | (((target->ns == NULL) &&
|
---|
820 | (xmlHashLookup2(ctxt->style->cdataSection,
|
---|
821 | target->name, NULL) != NULL)) ||
|
---|
822 | ((target->ns != NULL) &&
|
---|
823 | (xmlHashLookup2(ctxt->style->cdataSection,
|
---|
824 | target->name, target->ns->href) != NULL))))
|
---|
825 | {
|
---|
826 | /*
|
---|
827 | * Process "cdata-section-elements".
|
---|
828 | */
|
---|
829 | /*
|
---|
830 | * OPTIMIZE TODO: xsltCopyText() is also used for attribute content.
|
---|
831 | */
|
---|
832 | /*
|
---|
833 | * TODO: Since this doesn't merge adjacent CDATA-section nodes,
|
---|
834 | * we'll get: <![CDATA[x]]><!CDATA[y]]>.
|
---|
835 | * TODO: Reported in #321505.
|
---|
836 | */
|
---|
837 | if ((target->last != NULL) &&
|
---|
838 | (target->last->type == XML_CDATA_SECTION_NODE))
|
---|
839 | {
|
---|
840 | /*
|
---|
841 | * Append to existing CDATA-section node.
|
---|
842 | */
|
---|
843 | copy = xsltAddTextString(ctxt, target->last, cur->content,
|
---|
844 | xmlStrlen(cur->content));
|
---|
845 | goto exit;
|
---|
846 | } else {
|
---|
847 | unsigned int len;
|
---|
848 |
|
---|
849 | len = xmlStrlen(cur->content);
|
---|
850 | copy = xmlNewCDataBlock(ctxt->output, cur->content, len);
|
---|
851 | if (copy == NULL)
|
---|
852 | goto exit;
|
---|
853 | ctxt->lasttext = copy->content;
|
---|
854 | ctxt->lasttsize = len;
|
---|
855 | ctxt->lasttuse = len;
|
---|
856 | }
|
---|
857 | } else if ((target != NULL) &&
|
---|
858 | (target->last != NULL) &&
|
---|
859 | /* both escaped or both non-escaped text-nodes */
|
---|
860 | (((target->last->type == XML_TEXT_NODE) &&
|
---|
861 | (target->last->name == cur->name)) ||
|
---|
862 | /* non-escaped text nodes and CDATA-section nodes */
|
---|
863 | (((target->last->type == XML_CDATA_SECTION_NODE) &&
|
---|
864 | (cur->name == xmlStringTextNoenc)))))
|
---|
865 | {
|
---|
866 | /*
|
---|
867 | * we are appending to an existing text node
|
---|
868 | */
|
---|
869 | copy = xsltAddTextString(ctxt, target->last, cur->content,
|
---|
870 | xmlStrlen(cur->content));
|
---|
871 | goto exit;
|
---|
872 | } else if ((interned) && (target != NULL) &&
|
---|
873 | (target->doc != NULL) &&
|
---|
874 | (target->doc->dict == ctxt->dict))
|
---|
875 | {
|
---|
876 | /*
|
---|
877 | * TODO: DO we want to use this also for "text" output?
|
---|
878 | */
|
---|
879 | copy = xmlNewTextLen(NULL, 0);
|
---|
880 | if (copy == NULL)
|
---|
881 | goto exit;
|
---|
882 | if (cur->name == xmlStringTextNoenc)
|
---|
883 | copy->name = xmlStringTextNoenc;
|
---|
884 |
|
---|
885 | /*
|
---|
886 | * Must confirm that content is in dict (bug 302821)
|
---|
887 | * TODO: This check should be not needed for text coming
|
---|
888 | * from the stylesheets
|
---|
889 | */
|
---|
890 | if (xmlDictOwns(ctxt->dict, cur->content))
|
---|
891 | copy->content = cur->content;
|
---|
892 | else {
|
---|
893 | if ((copy->content = xmlStrdup(cur->content)) == NULL)
|
---|
894 | return NULL;
|
---|
895 | }
|
---|
896 | } else {
|
---|
897 | /*
|
---|
898 | * normal processing. keep counters to extend the text node
|
---|
899 | * in xsltAddTextString if needed.
|
---|
900 | */
|
---|
901 | unsigned int len;
|
---|
902 |
|
---|
903 | len = xmlStrlen(cur->content);
|
---|
904 | copy = xmlNewTextLen(cur->content, len);
|
---|
905 | if (copy == NULL)
|
---|
906 | goto exit;
|
---|
907 | if (cur->name == xmlStringTextNoenc)
|
---|
908 | copy->name = xmlStringTextNoenc;
|
---|
909 | ctxt->lasttext = copy->content;
|
---|
910 | ctxt->lasttsize = len;
|
---|
911 | ctxt->lasttuse = len;
|
---|
912 | }
|
---|
913 | if (copy != NULL) {
|
---|
914 | if (target != NULL) {
|
---|
915 | copy->doc = target->doc;
|
---|
916 | /*
|
---|
917 | * MAYBE TODO: Maybe we should reset the ctxt->lasttext here
|
---|
918 | * to ensure that the optimized text-merging mechanism
|
---|
919 | * won't interfere with normal node-merging in any case.
|
---|
920 | */
|
---|
921 | xmlAddChild(target, copy);
|
---|
922 | }
|
---|
923 | } else {
|
---|
924 | xsltTransformError(ctxt, NULL, target,
|
---|
925 | "xsltCopyText: text copy failed\n");
|
---|
926 | }
|
---|
927 |
|
---|
928 | exit:
|
---|
929 | if ((copy == NULL) || (copy->content == NULL)) {
|
---|
930 | xsltTransformError(ctxt, NULL, target,
|
---|
931 | "Internal error in xsltCopyText(): "
|
---|
932 | "Failed to copy the string.\n");
|
---|
933 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
934 | }
|
---|
935 | return(copy);
|
---|
936 | }
|
---|
937 |
|
---|
938 | /**
|
---|
939 | * xsltShallowCopyAttr:
|
---|
940 | * @ctxt: a XSLT process context
|
---|
941 | * @invocNode: responsible node in the stylesheet; used for error reports
|
---|
942 | * @target: the element where the attribute will be grafted
|
---|
943 | * @attr: the attribute to be copied
|
---|
944 | *
|
---|
945 | * Do a copy of an attribute.
|
---|
946 | * Called by:
|
---|
947 | * - xsltCopyTreeInternal()
|
---|
948 | * - xsltCopyOf()
|
---|
949 | * - xsltCopy()
|
---|
950 | *
|
---|
951 | * Returns: a new xmlAttrPtr, or NULL in case of error.
|
---|
952 | */
|
---|
953 | static xmlAttrPtr
|
---|
954 | xsltShallowCopyAttr(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
|
---|
955 | xmlNodePtr target, xmlAttrPtr attr)
|
---|
956 | {
|
---|
957 | xmlAttrPtr copy;
|
---|
958 | xmlChar *value;
|
---|
959 |
|
---|
960 | if (attr == NULL)
|
---|
961 | return(NULL);
|
---|
962 |
|
---|
963 | if (target->type != XML_ELEMENT_NODE) {
|
---|
964 | xsltTransformError(ctxt, NULL, invocNode,
|
---|
965 | "Cannot add an attribute node to a non-element node.\n");
|
---|
966 | return(NULL);
|
---|
967 | }
|
---|
968 |
|
---|
969 | if (target->children != NULL) {
|
---|
970 | xsltTransformError(ctxt, NULL, invocNode,
|
---|
971 | "Attribute nodes must be added before "
|
---|
972 | "any child nodes to an element.\n");
|
---|
973 | return(NULL);
|
---|
974 | }
|
---|
975 |
|
---|
976 | value = xmlNodeListGetString(attr->doc, attr->children, 1);
|
---|
977 | if (attr->ns != NULL) {
|
---|
978 | xmlNsPtr ns;
|
---|
979 |
|
---|
980 | ns = xsltGetSpecialNamespace(ctxt, invocNode,
|
---|
981 | attr->ns->href, attr->ns->prefix, target);
|
---|
982 | if (ns == NULL) {
|
---|
983 | xsltTransformError(ctxt, NULL, invocNode,
|
---|
984 | "Namespace fixup error: Failed to acquire an in-scope "
|
---|
985 | "namespace binding of the copied attribute '{%s}%s'.\n",
|
---|
986 | attr->ns->href, attr->name);
|
---|
987 | /*
|
---|
988 | * TODO: Should we just stop here?
|
---|
989 | */
|
---|
990 | }
|
---|
991 | /*
|
---|
992 | * Note that xmlSetNsProp() will take care of duplicates
|
---|
993 | * and assigns the new namespace even to a duplicate.
|
---|
994 | */
|
---|
995 | copy = xmlSetNsProp(target, ns, attr->name, value);
|
---|
996 | } else {
|
---|
997 | copy = xmlSetNsProp(target, NULL, attr->name, value);
|
---|
998 | }
|
---|
999 | if (value != NULL)
|
---|
1000 | xmlFree(value);
|
---|
1001 |
|
---|
1002 | if (copy == NULL)
|
---|
1003 | return(NULL);
|
---|
1004 |
|
---|
1005 | #if 0
|
---|
1006 | /*
|
---|
1007 | * NOTE: This was optimized according to bug #342695.
|
---|
1008 | * TODO: Can this further be optimized, if source and target
|
---|
1009 | * share the same dict and attr->children is just 1 text node
|
---|
1010 | * which is in the dict? How probable is such a case?
|
---|
1011 | */
|
---|
1012 | /*
|
---|
1013 | * TODO: Do we need to create an empty text node if the value
|
---|
1014 | * is the empty string?
|
---|
1015 | */
|
---|
1016 | value = xmlNodeListGetString(attr->doc, attr->children, 1);
|
---|
1017 | if (value != NULL) {
|
---|
1018 | txtNode = xmlNewDocText(target->doc, NULL);
|
---|
1019 | if (txtNode == NULL)
|
---|
1020 | return(NULL);
|
---|
1021 | if ((target->doc != NULL) &&
|
---|
1022 | (target->doc->dict != NULL))
|
---|
1023 | {
|
---|
1024 | txtNode->content =
|
---|
1025 | (xmlChar *) xmlDictLookup(target->doc->dict,
|
---|
1026 | BAD_CAST value, -1);
|
---|
1027 | xmlFree(value);
|
---|
1028 | } else
|
---|
1029 | txtNode->content = value;
|
---|
1030 | copy->children = txtNode;
|
---|
1031 | }
|
---|
1032 | #endif
|
---|
1033 |
|
---|
1034 | return(copy);
|
---|
1035 | }
|
---|
1036 |
|
---|
1037 | /**
|
---|
1038 | * xsltCopyAttrListNoOverwrite:
|
---|
1039 | * @ctxt: a XSLT process context
|
---|
1040 | * @invocNode: responsible node in the stylesheet; used for error reports
|
---|
1041 | * @target: the element where the new attributes will be grafted
|
---|
1042 | * @attr: the first attribute in the list to be copied
|
---|
1043 | *
|
---|
1044 | * Copies a list of attribute nodes, starting with @attr, over to the
|
---|
1045 | * @target element node.
|
---|
1046 | *
|
---|
1047 | * Called by:
|
---|
1048 | * - xsltCopyTreeInternal()
|
---|
1049 | *
|
---|
1050 | * Returns 0 on success and -1 on errors and internal errors.
|
---|
1051 | */
|
---|
1052 | static int
|
---|
1053 | xsltCopyAttrListNoOverwrite(xsltTransformContextPtr ctxt,
|
---|
1054 | xmlNodePtr invocNode,
|
---|
1055 | xmlNodePtr target, xmlAttrPtr attr)
|
---|
1056 | {
|
---|
1057 | xmlAttrPtr last = NULL, copy;
|
---|
1058 | xmlNsPtr origNs = NULL, copyNs = NULL;
|
---|
1059 | xmlChar *value = NULL;
|
---|
1060 |
|
---|
1061 | /*
|
---|
1062 | * Don't use xmlCopyProp() here, since it will try to
|
---|
1063 | * reconciliate namespaces.
|
---|
1064 | */
|
---|
1065 | while (attr != NULL) {
|
---|
1066 | /*
|
---|
1067 | * Find a namespace node in the tree of @target.
|
---|
1068 | * Avoid searching for the same ns.
|
---|
1069 | */
|
---|
1070 | if (attr->ns != origNs) {
|
---|
1071 | origNs = attr->ns;
|
---|
1072 | if (attr->ns != NULL) {
|
---|
1073 | copyNs = xsltGetSpecialNamespace(ctxt, invocNode,
|
---|
1074 | attr->ns->href, attr->ns->prefix, target);
|
---|
1075 | if (copyNs == NULL)
|
---|
1076 | return(-1);
|
---|
1077 | } else
|
---|
1078 | copyNs = NULL;
|
---|
1079 | }
|
---|
1080 | /*
|
---|
1081 | * If attribute has a value, we need to copy it (watching out
|
---|
1082 | * for possible entities)
|
---|
1083 | */
|
---|
1084 | if (attr->children)
|
---|
1085 | value = xmlNodeListGetString(attr->doc, attr->children, 0);
|
---|
1086 | /*
|
---|
1087 | * REVISIT: I think xmlNewDocProp() is the only attr function
|
---|
1088 | * which does not eval if the attr is of type ID. This is good,
|
---|
1089 | * since we don't need this.
|
---|
1090 | */
|
---|
1091 | copy = xmlNewDocProp(target->doc, attr->name, BAD_CAST value);
|
---|
1092 | if (copy == NULL)
|
---|
1093 | return(-1);
|
---|
1094 | copy->parent = target;
|
---|
1095 | copy->ns = copyNs;
|
---|
1096 |
|
---|
1097 | if (last == NULL) {
|
---|
1098 | target->properties = copy;
|
---|
1099 | last = copy;
|
---|
1100 | } else {
|
---|
1101 | last->next = copy;
|
---|
1102 | copy->prev = last;
|
---|
1103 | last = copy;
|
---|
1104 | }
|
---|
1105 | /*
|
---|
1106 | * OPTIMIZE TODO: How to avoid this intermediate string?
|
---|
1107 | */
|
---|
1108 | if (value != NULL) {
|
---|
1109 | xmlFree(value);
|
---|
1110 | value = NULL;
|
---|
1111 | }
|
---|
1112 | attr = attr->next;
|
---|
1113 | }
|
---|
1114 | return(0);
|
---|
1115 | }
|
---|
1116 |
|
---|
1117 | /**
|
---|
1118 | * xsltShallowCopyElem:
|
---|
1119 | * @ctxt: the XSLT process context
|
---|
1120 | * @node: the element node in the source tree
|
---|
1121 | * or the Literal Result Element
|
---|
1122 | * @insert: the parent in the result tree
|
---|
1123 | * @isLRE: if @node is a Literal Result Element
|
---|
1124 | *
|
---|
1125 | * Make a copy of the element node @node
|
---|
1126 | * and insert it as last child of @insert.
|
---|
1127 | *
|
---|
1128 | * URGENT TODO: The problem with this one (for the non-refactored code)
|
---|
1129 | * is that it is used for both, Literal Result Elements *and*
|
---|
1130 | * copying input nodes.
|
---|
1131 | *
|
---|
1132 | * BIG NOTE: This is only called for XML_ELEMENT_NODEs.
|
---|
1133 | *
|
---|
1134 | * Called from:
|
---|
1135 | * xsltApplySequenceConstructor()
|
---|
1136 | * (for Literal Result Elements - which is a problem)
|
---|
1137 | * xsltCopy() (for shallow-copying elements via xsl:copy)
|
---|
1138 | *
|
---|
1139 | * Returns a pointer to the new node, or NULL in case of error
|
---|
1140 | */
|
---|
1141 | static xmlNodePtr
|
---|
1142 | xsltShallowCopyElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
1143 | xmlNodePtr insert, int isLRE)
|
---|
1144 | {
|
---|
1145 | xmlNodePtr copy;
|
---|
1146 |
|
---|
1147 | if ((node->type == XML_DTD_NODE) || (insert == NULL))
|
---|
1148 | return(NULL);
|
---|
1149 | if ((node->type == XML_TEXT_NODE) ||
|
---|
1150 | (node->type == XML_CDATA_SECTION_NODE))
|
---|
1151 | return(xsltCopyText(ctxt, insert, node, 0));
|
---|
1152 |
|
---|
1153 | copy = xmlDocCopyNode(node, insert->doc, 0);
|
---|
1154 | if (copy != NULL) {
|
---|
1155 | copy->doc = ctxt->output;
|
---|
1156 | xmlAddChild(insert, copy);
|
---|
1157 |
|
---|
1158 | if (node->type == XML_ELEMENT_NODE) {
|
---|
1159 | /*
|
---|
1160 | * Add namespaces as they are needed
|
---|
1161 | */
|
---|
1162 | if (node->nsDef != NULL) {
|
---|
1163 | /*
|
---|
1164 | * TODO: Remove the LRE case in the refactored code
|
---|
1165 | * gets enabled.
|
---|
1166 | */
|
---|
1167 | if (isLRE)
|
---|
1168 | xsltCopyNamespaceList(ctxt, copy, node->nsDef);
|
---|
1169 | else
|
---|
1170 | xsltCopyNamespaceListInternal(copy, node->nsDef);
|
---|
1171 | }
|
---|
1172 |
|
---|
1173 | /*
|
---|
1174 | * URGENT TODO: The problem with this is that it does not
|
---|
1175 | * copy over all namespace nodes in scope.
|
---|
1176 | * The damn thing about this is, that we would need to
|
---|
1177 | * use the xmlGetNsList(), for every single node; this is
|
---|
1178 | * also done in xsltCopyTreeInternal(), but only for the top node.
|
---|
1179 | */
|
---|
1180 | if (node->ns != NULL) {
|
---|
1181 | if (isLRE) {
|
---|
1182 | /*
|
---|
1183 | * REVISIT TODO: Since the non-refactored code still does
|
---|
1184 | * ns-aliasing, we need to call xsltGetNamespace() here.
|
---|
1185 | * Remove this when ready.
|
---|
1186 | */
|
---|
1187 | copy->ns = xsltGetNamespace(ctxt, node, node->ns, copy);
|
---|
1188 | } else {
|
---|
1189 | copy->ns = xsltGetSpecialNamespace(ctxt,
|
---|
1190 | node, node->ns->href, node->ns->prefix, copy);
|
---|
1191 |
|
---|
1192 | }
|
---|
1193 | } else if ((insert->type == XML_ELEMENT_NODE) &&
|
---|
1194 | (insert->ns != NULL))
|
---|
1195 | {
|
---|
1196 | /*
|
---|
1197 | * "Undeclare" the default namespace.
|
---|
1198 | */
|
---|
1199 | xsltGetSpecialNamespace(ctxt, node, NULL, NULL, copy);
|
---|
1200 | }
|
---|
1201 | }
|
---|
1202 | } else {
|
---|
1203 | xsltTransformError(ctxt, NULL, node,
|
---|
1204 | "xsltShallowCopyElem: copy %s failed\n", node->name);
|
---|
1205 | }
|
---|
1206 | return(copy);
|
---|
1207 | }
|
---|
1208 |
|
---|
1209 | /**
|
---|
1210 | * xsltCopyTreeList:
|
---|
1211 | * @ctxt: a XSLT process context
|
---|
1212 | * @invocNode: responsible node in the stylesheet; used for error reports
|
---|
1213 | * @list: the list of element nodes in the source tree.
|
---|
1214 | * @insert: the parent in the result tree.
|
---|
1215 | * @isLRE: is this a literal result element list
|
---|
1216 | * @topElemVisited: indicates if a top-most element was already processed
|
---|
1217 | *
|
---|
1218 | * Make a copy of the full list of tree @list
|
---|
1219 | * and insert it as last children of @insert
|
---|
1220 | *
|
---|
1221 | * NOTE: Not to be used for Literal Result Elements.
|
---|
1222 | *
|
---|
1223 | * Used by:
|
---|
1224 | * - xsltCopyOf()
|
---|
1225 | *
|
---|
1226 | * Returns a pointer to the new list, or NULL in case of error
|
---|
1227 | */
|
---|
1228 | static xmlNodePtr
|
---|
1229 | xsltCopyTreeList(xsltTransformContextPtr ctxt, xmlNodePtr invocNode,
|
---|
1230 | xmlNodePtr list,
|
---|
1231 | xmlNodePtr insert, int isLRE, int topElemVisited)
|
---|
1232 | {
|
---|
1233 | xmlNodePtr copy, ret = NULL;
|
---|
1234 |
|
---|
1235 | while (list != NULL) {
|
---|
1236 | copy = xsltCopyTreeInternal(ctxt, invocNode,
|
---|
1237 | list, insert, isLRE, topElemVisited);
|
---|
1238 | if (copy != NULL) {
|
---|
1239 | if (ret == NULL) {
|
---|
1240 | ret = copy;
|
---|
1241 | }
|
---|
1242 | }
|
---|
1243 | list = list->next;
|
---|
1244 | }
|
---|
1245 | return(ret);
|
---|
1246 | }
|
---|
1247 |
|
---|
1248 | /**
|
---|
1249 | * xsltCopyNamespaceListInternal:
|
---|
1250 | * @node: the target node
|
---|
1251 | * @cur: the first namespace
|
---|
1252 | *
|
---|
1253 | * Do a copy of a namespace list. If @node is non-NULL the
|
---|
1254 | * new namespaces are added automatically.
|
---|
1255 | * Called by:
|
---|
1256 | * xsltCopyTreeInternal()
|
---|
1257 | *
|
---|
1258 | * QUESTION: What is the exact difference between this function
|
---|
1259 | * and xsltCopyNamespaceList() in "namespaces.c"?
|
---|
1260 | * ANSWER: xsltCopyNamespaceList() tries to apply ns-aliases.
|
---|
1261 | *
|
---|
1262 | * Returns: a new xmlNsPtr, or NULL in case of error.
|
---|
1263 | */
|
---|
1264 | static xmlNsPtr
|
---|
1265 | xsltCopyNamespaceListInternal(xmlNodePtr elem, xmlNsPtr ns) {
|
---|
1266 | xmlNsPtr ret = NULL;
|
---|
1267 | xmlNsPtr p = NULL, q, luNs;
|
---|
1268 |
|
---|
1269 | if (ns == NULL)
|
---|
1270 | return(NULL);
|
---|
1271 | /*
|
---|
1272 | * One can add namespaces only on element nodes
|
---|
1273 | */
|
---|
1274 | if ((elem != NULL) && (elem->type != XML_ELEMENT_NODE))
|
---|
1275 | elem = NULL;
|
---|
1276 |
|
---|
1277 | do {
|
---|
1278 | if (ns->type != XML_NAMESPACE_DECL)
|
---|
1279 | break;
|
---|
1280 | /*
|
---|
1281 | * Avoid duplicating namespace declarations on the tree.
|
---|
1282 | */
|
---|
1283 | if (elem != NULL) {
|
---|
1284 | if ((elem->ns != NULL) &&
|
---|
1285 | xmlStrEqual(elem->ns->prefix, ns->prefix) &&
|
---|
1286 | xmlStrEqual(elem->ns->href, ns->href))
|
---|
1287 | {
|
---|
1288 | ns = ns->next;
|
---|
1289 | continue;
|
---|
1290 | }
|
---|
1291 | luNs = xmlSearchNs(elem->doc, elem, ns->prefix);
|
---|
1292 | if ((luNs != NULL) && (xmlStrEqual(luNs->href, ns->href)))
|
---|
1293 | {
|
---|
1294 | ns = ns->next;
|
---|
1295 | continue;
|
---|
1296 | }
|
---|
1297 | }
|
---|
1298 | q = xmlNewNs(elem, ns->href, ns->prefix);
|
---|
1299 | if (p == NULL) {
|
---|
1300 | ret = p = q;
|
---|
1301 | } else if (q != NULL) {
|
---|
1302 | p->next = q;
|
---|
1303 | p = q;
|
---|
1304 | }
|
---|
1305 | ns = ns->next;
|
---|
1306 | } while (ns != NULL);
|
---|
1307 | return(ret);
|
---|
1308 | }
|
---|
1309 |
|
---|
1310 | /**
|
---|
1311 | * xsltShallowCopyNsNode:
|
---|
1312 | * @ctxt: the XSLT transformation context
|
---|
1313 | * @invocNode: responsible node in the stylesheet; used for error reports
|
---|
1314 | * @insert: the target element node in the result tree
|
---|
1315 | * @ns: the namespace node
|
---|
1316 | *
|
---|
1317 | * This is used for copying ns-nodes with xsl:copy-of and xsl:copy.
|
---|
1318 | *
|
---|
1319 | * Returns a new/existing ns-node, or NULL.
|
---|
1320 | */
|
---|
1321 | static xmlNsPtr
|
---|
1322 | xsltShallowCopyNsNode(xsltTransformContextPtr ctxt,
|
---|
1323 | xmlNodePtr invocNode,
|
---|
1324 | xmlNodePtr insert,
|
---|
1325 | xmlNsPtr ns)
|
---|
1326 | {
|
---|
1327 | /*
|
---|
1328 | * TODO: Contrary to header comments, this is declared as int.
|
---|
1329 | * be modified to return a node pointer, or NULL if any error
|
---|
1330 | */
|
---|
1331 | xmlNsPtr tmpns;
|
---|
1332 |
|
---|
1333 | if ((insert == NULL) || (insert->type != XML_ELEMENT_NODE))
|
---|
1334 | return(NULL);
|
---|
1335 |
|
---|
1336 | if (insert->children != NULL) {
|
---|
1337 | xsltTransformError(ctxt, NULL, invocNode,
|
---|
1338 | "Namespace nodes must be added before "
|
---|
1339 | "any child nodes are added to an element.\n");
|
---|
1340 | return(NULL);
|
---|
1341 | }
|
---|
1342 | /*
|
---|
1343 | *
|
---|
1344 | * BIG NOTE: Xalan-J simply overwrites any ns-decls with
|
---|
1345 | * an equal prefix. We definitively won't do that.
|
---|
1346 | *
|
---|
1347 | * MSXML 4.0 and the .NET ignores ns-decls for which an
|
---|
1348 | * equal prefix is already in use.
|
---|
1349 | *
|
---|
1350 | * Saxon raises an error like:
|
---|
1351 | * "net.sf.saxon.xpath.DynamicError: Cannot create two namespace
|
---|
1352 | * nodes with the same name".
|
---|
1353 | *
|
---|
1354 | * NOTE: We'll currently follow MSXML here.
|
---|
1355 | * REVISIT TODO: Check if it's better to follow Saxon here.
|
---|
1356 | */
|
---|
1357 | if (ns->prefix == NULL) {
|
---|
1358 | /*
|
---|
1359 | * If we are adding ns-nodes to an element using e.g.
|
---|
1360 | * <xsl:copy-of select="/foo/namespace::*">, then we need
|
---|
1361 | * to ensure that we don't incorrectly declare a default
|
---|
1362 | * namespace on an element in no namespace, which otherwise
|
---|
1363 | * would move the element incorrectly into a namespace, if
|
---|
1364 | * the node tree is serialized.
|
---|
1365 | */
|
---|
1366 | if (insert->ns == NULL)
|
---|
1367 | goto occupied;
|
---|
1368 | } else if ((ns->prefix[0] == 'x') &&
|
---|
1369 | xmlStrEqual(ns->prefix, BAD_CAST "xml"))
|
---|
1370 | {
|
---|
1371 | /*
|
---|
1372 | * The XML namespace is built in.
|
---|
1373 | */
|
---|
1374 | return(NULL);
|
---|
1375 | }
|
---|
1376 |
|
---|
1377 | if (insert->nsDef != NULL) {
|
---|
1378 | tmpns = insert->nsDef;
|
---|
1379 | do {
|
---|
1380 | if ((tmpns->prefix == NULL) == (ns->prefix == NULL)) {
|
---|
1381 | if ((tmpns->prefix == ns->prefix) ||
|
---|
1382 | xmlStrEqual(tmpns->prefix, ns->prefix))
|
---|
1383 | {
|
---|
1384 | /*
|
---|
1385 | * Same prefix.
|
---|
1386 | */
|
---|
1387 | if (xmlStrEqual(tmpns->href, ns->href))
|
---|
1388 | return(NULL);
|
---|
1389 | goto occupied;
|
---|
1390 | }
|
---|
1391 | }
|
---|
1392 | tmpns = tmpns->next;
|
---|
1393 | } while (tmpns != NULL);
|
---|
1394 | }
|
---|
1395 | tmpns = xmlSearchNs(insert->doc, insert, ns->prefix);
|
---|
1396 | if ((tmpns != NULL) && xmlStrEqual(tmpns->href, ns->href))
|
---|
1397 | return(NULL);
|
---|
1398 | /*
|
---|
1399 | * Declare a new namespace.
|
---|
1400 | * TODO: The problem (wrt efficiency) with this xmlNewNs() is
|
---|
1401 | * that it will again search the already declared namespaces
|
---|
1402 | * for a duplicate :-/
|
---|
1403 | */
|
---|
1404 | return(xmlNewNs(insert, ns->href, ns->prefix));
|
---|
1405 |
|
---|
1406 | occupied:
|
---|
1407 | /*
|
---|
1408 | * TODO: We could as well raise an error here (like Saxon does),
|
---|
1409 | * or at least generate a warning.
|
---|
1410 | */
|
---|
1411 | return(NULL);
|
---|
1412 | }
|
---|
1413 |
|
---|
1414 | /**
|
---|
1415 | * xsltCopyTreeInternal:
|
---|
1416 | * @ctxt: the XSLT transformation context
|
---|
1417 | * @invocNode: responsible node in the stylesheet; used for error reports
|
---|
1418 | * @node: the element node in the source tree
|
---|
1419 | * @insert: the parent in the result tree
|
---|
1420 | * @isLRE: indicates if @node is a Literal Result Element
|
---|
1421 | * @topElemVisited: indicates if a top-most element was already processed
|
---|
1422 | *
|
---|
1423 | * Make a copy of the full tree under the element node @node
|
---|
1424 | * and insert it as last child of @insert
|
---|
1425 | *
|
---|
1426 | * NOTE: Not to be used for Literal Result Elements.
|
---|
1427 | *
|
---|
1428 | * Used by:
|
---|
1429 | * - xsltCopyOf()
|
---|
1430 | *
|
---|
1431 | * Returns a pointer to the new tree, or NULL in case of error
|
---|
1432 | */
|
---|
1433 | static xmlNodePtr
|
---|
1434 | xsltCopyTreeInternal(xsltTransformContextPtr ctxt,
|
---|
1435 | xmlNodePtr invocNode,
|
---|
1436 | xmlNodePtr node,
|
---|
1437 | xmlNodePtr insert, int isLRE, int topElemVisited)
|
---|
1438 | {
|
---|
1439 | xmlNodePtr copy;
|
---|
1440 |
|
---|
1441 | if (node == NULL)
|
---|
1442 | return(NULL);
|
---|
1443 | switch (node->type) {
|
---|
1444 | case XML_ELEMENT_NODE:
|
---|
1445 | case XML_ENTITY_REF_NODE:
|
---|
1446 | case XML_ENTITY_NODE:
|
---|
1447 | case XML_PI_NODE:
|
---|
1448 | case XML_COMMENT_NODE:
|
---|
1449 | case XML_DOCUMENT_NODE:
|
---|
1450 | case XML_HTML_DOCUMENT_NODE:
|
---|
1451 | #ifdef LIBXML_DOCB_ENABLED
|
---|
1452 | case XML_DOCB_DOCUMENT_NODE:
|
---|
1453 | #endif
|
---|
1454 | break;
|
---|
1455 | case XML_TEXT_NODE: {
|
---|
1456 | int noenc = (node->name == xmlStringTextNoenc);
|
---|
1457 | return(xsltCopyTextString(ctxt, insert, node->content, noenc));
|
---|
1458 | }
|
---|
1459 | case XML_CDATA_SECTION_NODE:
|
---|
1460 | return(xsltCopyTextString(ctxt, insert, node->content, 0));
|
---|
1461 | case XML_ATTRIBUTE_NODE:
|
---|
1462 | return((xmlNodePtr)
|
---|
1463 | xsltShallowCopyAttr(ctxt, invocNode, insert, (xmlAttrPtr) node));
|
---|
1464 | case XML_NAMESPACE_DECL:
|
---|
1465 | return((xmlNodePtr) xsltShallowCopyNsNode(ctxt, invocNode,
|
---|
1466 | insert, (xmlNsPtr) node));
|
---|
1467 |
|
---|
1468 | case XML_DOCUMENT_TYPE_NODE:
|
---|
1469 | case XML_DOCUMENT_FRAG_NODE:
|
---|
1470 | case XML_NOTATION_NODE:
|
---|
1471 | case XML_DTD_NODE:
|
---|
1472 | case XML_ELEMENT_DECL:
|
---|
1473 | case XML_ATTRIBUTE_DECL:
|
---|
1474 | case XML_ENTITY_DECL:
|
---|
1475 | case XML_XINCLUDE_START:
|
---|
1476 | case XML_XINCLUDE_END:
|
---|
1477 | return(NULL);
|
---|
1478 | }
|
---|
1479 | if (XSLT_IS_RES_TREE_FRAG(node)) {
|
---|
1480 | if (node->children != NULL)
|
---|
1481 | copy = xsltCopyTreeList(ctxt, invocNode,
|
---|
1482 | node->children, insert, 0, 0);
|
---|
1483 | else
|
---|
1484 | copy = NULL;
|
---|
1485 | return(copy);
|
---|
1486 | }
|
---|
1487 | copy = xmlDocCopyNode(node, insert->doc, 0);
|
---|
1488 | if (copy != NULL) {
|
---|
1489 | copy->doc = ctxt->output;
|
---|
1490 | xmlAddChild(insert, copy);
|
---|
1491 | /*
|
---|
1492 | * The node may have been coalesced into another text node.
|
---|
1493 | */
|
---|
1494 | if (insert->last != copy)
|
---|
1495 | return(insert->last);
|
---|
1496 | copy->next = NULL;
|
---|
1497 |
|
---|
1498 | if (node->type == XML_ELEMENT_NODE) {
|
---|
1499 | /*
|
---|
1500 | * Copy in-scope namespace nodes.
|
---|
1501 | *
|
---|
1502 | * REVISIT: Since we try to reuse existing in-scope ns-decls by
|
---|
1503 | * using xmlSearchNsByHref(), this will eventually change
|
---|
1504 | * the prefix of an original ns-binding; thus it might
|
---|
1505 | * break QNames in element/attribute content.
|
---|
1506 | * OPTIMIZE TODO: If we had a xmlNsPtr * on the transformation
|
---|
1507 | * context, plus a ns-lookup function, which writes directly
|
---|
1508 | * to a given list, then we wouldn't need to create/free the
|
---|
1509 | * nsList every time.
|
---|
1510 | */
|
---|
1511 | if ((topElemVisited == 0) &&
|
---|
1512 | (node->parent != NULL) &&
|
---|
1513 | (node->parent->type != XML_DOCUMENT_NODE) &&
|
---|
1514 | (node->parent->type != XML_HTML_DOCUMENT_NODE))
|
---|
1515 | {
|
---|
1516 | xmlNsPtr *nsList, *curns, ns;
|
---|
1517 |
|
---|
1518 | /*
|
---|
1519 | * If this is a top-most element in a tree to be
|
---|
1520 | * copied, then we need to ensure that all in-scope
|
---|
1521 | * namespaces are copied over. For nodes deeper in the
|
---|
1522 | * tree, it is sufficient to reconcile only the ns-decls
|
---|
1523 | * (node->nsDef entries).
|
---|
1524 | */
|
---|
1525 |
|
---|
1526 | nsList = xmlGetNsList(node->doc, node);
|
---|
1527 | if (nsList != NULL) {
|
---|
1528 | curns = nsList;
|
---|
1529 | do {
|
---|
1530 | /*
|
---|
1531 | * Search by prefix first in order to break as less
|
---|
1532 | * QNames in element/attribute content as possible.
|
---|
1533 | */
|
---|
1534 | ns = xmlSearchNs(insert->doc, insert,
|
---|
1535 | (*curns)->prefix);
|
---|
1536 |
|
---|
1537 | if ((ns == NULL) ||
|
---|
1538 | (! xmlStrEqual(ns->href, (*curns)->href)))
|
---|
1539 | {
|
---|
1540 | ns = NULL;
|
---|
1541 | /*
|
---|
1542 | * Search by namespace name.
|
---|
1543 | * REVISIT TODO: Currently disabled.
|
---|
1544 | */
|
---|
1545 | #if 0
|
---|
1546 | ns = xmlSearchNsByHref(insert->doc,
|
---|
1547 | insert, (*curns)->href);
|
---|
1548 | #endif
|
---|
1549 | }
|
---|
1550 | if (ns == NULL) {
|
---|
1551 | /*
|
---|
1552 | * Declare a new namespace on the copied element.
|
---|
1553 | */
|
---|
1554 | ns = xmlNewNs(copy, (*curns)->href,
|
---|
1555 | (*curns)->prefix);
|
---|
1556 | /* TODO: Handle errors */
|
---|
1557 | }
|
---|
1558 | if (node->ns == *curns) {
|
---|
1559 | /*
|
---|
1560 | * If this was the original's namespace then set
|
---|
1561 | * the generated counterpart on the copy.
|
---|
1562 | */
|
---|
1563 | copy->ns = ns;
|
---|
1564 | }
|
---|
1565 | curns++;
|
---|
1566 | } while (*curns != NULL);
|
---|
1567 | xmlFree(nsList);
|
---|
1568 | }
|
---|
1569 | } else if (node->nsDef != NULL) {
|
---|
1570 | /*
|
---|
1571 | * Copy over all namespace declaration attributes.
|
---|
1572 | */
|
---|
1573 | if (node->nsDef != NULL) {
|
---|
1574 | if (isLRE)
|
---|
1575 | xsltCopyNamespaceList(ctxt, copy, node->nsDef);
|
---|
1576 | else
|
---|
1577 | xsltCopyNamespaceListInternal(copy, node->nsDef);
|
---|
1578 | }
|
---|
1579 | }
|
---|
1580 | /*
|
---|
1581 | * Set the namespace.
|
---|
1582 | */
|
---|
1583 | if (node->ns != NULL) {
|
---|
1584 | if (copy->ns == NULL) {
|
---|
1585 | /*
|
---|
1586 | * This will map copy->ns to one of the newly created
|
---|
1587 | * in-scope ns-decls, OR create a new ns-decl on @copy.
|
---|
1588 | */
|
---|
1589 | copy->ns = xsltGetSpecialNamespace(ctxt, invocNode,
|
---|
1590 | node->ns->href, node->ns->prefix, copy);
|
---|
1591 | }
|
---|
1592 | } else if ((insert->type == XML_ELEMENT_NODE) &&
|
---|
1593 | (insert->ns != NULL))
|
---|
1594 | {
|
---|
1595 | /*
|
---|
1596 | * "Undeclare" the default namespace on @copy with xmlns="".
|
---|
1597 | */
|
---|
1598 | xsltGetSpecialNamespace(ctxt, invocNode, NULL, NULL, copy);
|
---|
1599 | }
|
---|
1600 | /*
|
---|
1601 | * Copy attribute nodes.
|
---|
1602 | */
|
---|
1603 | if (node->properties != NULL) {
|
---|
1604 | xsltCopyAttrListNoOverwrite(ctxt, invocNode,
|
---|
1605 | copy, node->properties);
|
---|
1606 | }
|
---|
1607 | if (topElemVisited == 0)
|
---|
1608 | topElemVisited = 1;
|
---|
1609 | }
|
---|
1610 | /*
|
---|
1611 | * Copy the subtree.
|
---|
1612 | */
|
---|
1613 | if (node->children != NULL) {
|
---|
1614 | xsltCopyTreeList(ctxt, invocNode,
|
---|
1615 | node->children, copy, isLRE, topElemVisited);
|
---|
1616 | }
|
---|
1617 | } else {
|
---|
1618 | xsltTransformError(ctxt, NULL, invocNode,
|
---|
1619 | "xsltCopyTreeInternal: Copying of '%s' failed.\n", node->name);
|
---|
1620 | }
|
---|
1621 | return(copy);
|
---|
1622 | }
|
---|
1623 |
|
---|
1624 | /**
|
---|
1625 | * xsltCopyTree:
|
---|
1626 | * @ctxt: the XSLT transformation context
|
---|
1627 | * @node: the element node in the source tree
|
---|
1628 | * @insert: the parent in the result tree
|
---|
1629 | * @literal: indicates if @node is a Literal Result Element
|
---|
1630 | *
|
---|
1631 | * Make a copy of the full tree under the element node @node
|
---|
1632 | * and insert it as last child of @insert
|
---|
1633 | * For literal result element, some of the namespaces may not be copied
|
---|
1634 | * over according to section 7.1.
|
---|
1635 | * TODO: Why is this a public function?
|
---|
1636 | *
|
---|
1637 | * Returns a pointer to the new tree, or NULL in case of error
|
---|
1638 | */
|
---|
1639 | xmlNodePtr
|
---|
1640 | xsltCopyTree(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
1641 | xmlNodePtr insert, int literal)
|
---|
1642 | {
|
---|
1643 | return(xsltCopyTreeInternal(ctxt, node, node, insert, literal, 0));
|
---|
1644 |
|
---|
1645 | }
|
---|
1646 |
|
---|
1647 | /************************************************************************
|
---|
1648 | * *
|
---|
1649 | * Error/fallback processing *
|
---|
1650 | * *
|
---|
1651 | ************************************************************************/
|
---|
1652 |
|
---|
1653 | /**
|
---|
1654 | * xsltApplyFallbacks:
|
---|
1655 | * @ctxt: a XSLT process context
|
---|
1656 | * @node: the node in the source tree.
|
---|
1657 | * @inst: the node generating the error
|
---|
1658 | *
|
---|
1659 | * Process possible xsl:fallback nodes present under @inst
|
---|
1660 | *
|
---|
1661 | * Returns the number of xsl:fallback element found and processed
|
---|
1662 | */
|
---|
1663 | static int
|
---|
1664 | xsltApplyFallbacks(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
1665 | xmlNodePtr inst) {
|
---|
1666 |
|
---|
1667 | xmlNodePtr child;
|
---|
1668 | int ret = 0;
|
---|
1669 |
|
---|
1670 | if ((ctxt == NULL) || (node == NULL) || (inst == NULL) ||
|
---|
1671 | (inst->children == NULL))
|
---|
1672 | return(0);
|
---|
1673 |
|
---|
1674 | child = inst->children;
|
---|
1675 | while (child != NULL) {
|
---|
1676 | if ((IS_XSLT_ELEM(child)) &&
|
---|
1677 | (xmlStrEqual(child->name, BAD_CAST "fallback"))) {
|
---|
1678 | #ifdef WITH_XSLT_DEBUG_PARSING
|
---|
1679 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
1680 | "applying xsl:fallback\n");
|
---|
1681 | #endif
|
---|
1682 | ret++;
|
---|
1683 | xsltApplySequenceConstructor(ctxt, node, child->children,
|
---|
1684 | NULL);
|
---|
1685 | }
|
---|
1686 | child = child->next;
|
---|
1687 | }
|
---|
1688 | return(ret);
|
---|
1689 | }
|
---|
1690 |
|
---|
1691 | /************************************************************************
|
---|
1692 | * *
|
---|
1693 | * Default processing *
|
---|
1694 | * *
|
---|
1695 | ************************************************************************/
|
---|
1696 |
|
---|
1697 | void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
1698 | xsltStackElemPtr params);
|
---|
1699 | /**
|
---|
1700 | * xsltDefaultProcessOneNode:
|
---|
1701 | * @ctxt: a XSLT process context
|
---|
1702 | * @node: the node in the source tree.
|
---|
1703 | * @params: extra parameters passed to the template if any
|
---|
1704 | *
|
---|
1705 | * Process the source node with the default built-in template rule:
|
---|
1706 | * <xsl:template match="*|/">
|
---|
1707 | * <xsl:apply-templates/>
|
---|
1708 | * </xsl:template>
|
---|
1709 | *
|
---|
1710 | * and
|
---|
1711 | *
|
---|
1712 | * <xsl:template match="text()|@*">
|
---|
1713 | * <xsl:value-of select="."/>
|
---|
1714 | * </xsl:template>
|
---|
1715 | *
|
---|
1716 | * Note also that namespace declarations are copied directly:
|
---|
1717 | *
|
---|
1718 | * the built-in template rule is the only template rule that is applied
|
---|
1719 | * for namespace nodes.
|
---|
1720 | */
|
---|
1721 | static void
|
---|
1722 | xsltDefaultProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
1723 | xsltStackElemPtr params) {
|
---|
1724 | xmlNodePtr copy;
|
---|
1725 | xmlNodePtr delete = NULL, cur;
|
---|
1726 | int nbchild = 0, oldSize;
|
---|
1727 | int childno = 0, oldPos;
|
---|
1728 | xsltTemplatePtr template;
|
---|
1729 |
|
---|
1730 | CHECK_STOPPED;
|
---|
1731 | /*
|
---|
1732 | * Handling of leaves
|
---|
1733 | */
|
---|
1734 | switch (node->type) {
|
---|
1735 | case XML_DOCUMENT_NODE:
|
---|
1736 | case XML_HTML_DOCUMENT_NODE:
|
---|
1737 | case XML_ELEMENT_NODE:
|
---|
1738 | break;
|
---|
1739 | case XML_CDATA_SECTION_NODE:
|
---|
1740 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1741 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1742 | "xsltDefaultProcessOneNode: copy CDATA %s\n",
|
---|
1743 | node->content));
|
---|
1744 | #endif
|
---|
1745 | copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
|
---|
1746 | if (copy == NULL) {
|
---|
1747 | xsltTransformError(ctxt, NULL, node,
|
---|
1748 | "xsltDefaultProcessOneNode: cdata copy failed\n");
|
---|
1749 | }
|
---|
1750 | return;
|
---|
1751 | case XML_TEXT_NODE:
|
---|
1752 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1753 | if (node->content == NULL) {
|
---|
1754 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1755 | "xsltDefaultProcessOneNode: copy empty text\n"));
|
---|
1756 | return;
|
---|
1757 | } else {
|
---|
1758 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1759 | "xsltDefaultProcessOneNode: copy text %s\n",
|
---|
1760 | node->content));
|
---|
1761 | }
|
---|
1762 | #endif
|
---|
1763 | copy = xsltCopyText(ctxt, ctxt->insert, node, 0);
|
---|
1764 | if (copy == NULL) {
|
---|
1765 | xsltTransformError(ctxt, NULL, node,
|
---|
1766 | "xsltDefaultProcessOneNode: text copy failed\n");
|
---|
1767 | }
|
---|
1768 | return;
|
---|
1769 | case XML_ATTRIBUTE_NODE:
|
---|
1770 | cur = node->children;
|
---|
1771 | while ((cur != NULL) && (cur->type != XML_TEXT_NODE))
|
---|
1772 | cur = cur->next;
|
---|
1773 | if (cur == NULL) {
|
---|
1774 | xsltTransformError(ctxt, NULL, node,
|
---|
1775 | "xsltDefaultProcessOneNode: no text for attribute\n");
|
---|
1776 | } else {
|
---|
1777 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1778 | if (cur->content == NULL) {
|
---|
1779 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1780 | "xsltDefaultProcessOneNode: copy empty text\n"));
|
---|
1781 | } else {
|
---|
1782 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1783 | "xsltDefaultProcessOneNode: copy text %s\n",
|
---|
1784 | cur->content));
|
---|
1785 | }
|
---|
1786 | #endif
|
---|
1787 | copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
|
---|
1788 | if (copy == NULL) {
|
---|
1789 | xsltTransformError(ctxt, NULL, node,
|
---|
1790 | "xsltDefaultProcessOneNode: text copy failed\n");
|
---|
1791 | }
|
---|
1792 | }
|
---|
1793 | return;
|
---|
1794 | default:
|
---|
1795 | return;
|
---|
1796 | }
|
---|
1797 | /*
|
---|
1798 | * Handling of Elements: first pass, cleanup and counting
|
---|
1799 | */
|
---|
1800 | cur = node->children;
|
---|
1801 | while (cur != NULL) {
|
---|
1802 | switch (cur->type) {
|
---|
1803 | case XML_TEXT_NODE:
|
---|
1804 | case XML_CDATA_SECTION_NODE:
|
---|
1805 | case XML_DOCUMENT_NODE:
|
---|
1806 | case XML_HTML_DOCUMENT_NODE:
|
---|
1807 | case XML_ELEMENT_NODE:
|
---|
1808 | case XML_PI_NODE:
|
---|
1809 | case XML_COMMENT_NODE:
|
---|
1810 | nbchild++;
|
---|
1811 | break;
|
---|
1812 | case XML_DTD_NODE:
|
---|
1813 | /* Unlink the DTD, it's still reachable using doc->intSubset */
|
---|
1814 | if (cur->next != NULL)
|
---|
1815 | cur->next->prev = cur->prev;
|
---|
1816 | if (cur->prev != NULL)
|
---|
1817 | cur->prev->next = cur->next;
|
---|
1818 | break;
|
---|
1819 | default:
|
---|
1820 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1821 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1822 | "xsltDefaultProcessOneNode: skipping node type %d\n",
|
---|
1823 | cur->type));
|
---|
1824 | #endif
|
---|
1825 | delete = cur;
|
---|
1826 | }
|
---|
1827 | cur = cur->next;
|
---|
1828 | if (delete != NULL) {
|
---|
1829 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1830 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1831 | "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
|
---|
1832 | #endif
|
---|
1833 | xmlUnlinkNode(delete);
|
---|
1834 | xmlFreeNode(delete);
|
---|
1835 | delete = NULL;
|
---|
1836 | }
|
---|
1837 | }
|
---|
1838 | if (delete != NULL) {
|
---|
1839 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1840 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1841 | "xsltDefaultProcessOneNode: removing ignorable blank node\n"));
|
---|
1842 | #endif
|
---|
1843 | xmlUnlinkNode(delete);
|
---|
1844 | xmlFreeNode(delete);
|
---|
1845 | delete = NULL;
|
---|
1846 | }
|
---|
1847 |
|
---|
1848 | /*
|
---|
1849 | * Handling of Elements: second pass, actual processing
|
---|
1850 | */
|
---|
1851 | oldSize = ctxt->xpathCtxt->contextSize;
|
---|
1852 | oldPos = ctxt->xpathCtxt->proximityPosition;
|
---|
1853 | cur = node->children;
|
---|
1854 | while (cur != NULL) {
|
---|
1855 | childno++;
|
---|
1856 | switch (cur->type) {
|
---|
1857 | case XML_DOCUMENT_NODE:
|
---|
1858 | case XML_HTML_DOCUMENT_NODE:
|
---|
1859 | case XML_ELEMENT_NODE:
|
---|
1860 | ctxt->xpathCtxt->contextSize = nbchild;
|
---|
1861 | ctxt->xpathCtxt->proximityPosition = childno;
|
---|
1862 | xsltProcessOneNode(ctxt, cur, params);
|
---|
1863 | break;
|
---|
1864 | case XML_CDATA_SECTION_NODE:
|
---|
1865 | template = xsltGetTemplate(ctxt, cur, NULL);
|
---|
1866 | if (template) {
|
---|
1867 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1868 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1869 | "xsltDefaultProcessOneNode: applying template for CDATA %s\n",
|
---|
1870 | cur->content));
|
---|
1871 | #endif
|
---|
1872 | /*
|
---|
1873 | * Instantiate the xsl:template.
|
---|
1874 | */
|
---|
1875 | xsltApplyXSLTTemplate(ctxt, cur, template->content,
|
---|
1876 | template, params);
|
---|
1877 | } else /* if (ctxt->mode == NULL) */ {
|
---|
1878 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1879 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1880 | "xsltDefaultProcessOneNode: copy CDATA %s\n",
|
---|
1881 | cur->content));
|
---|
1882 | #endif
|
---|
1883 | copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
|
---|
1884 | if (copy == NULL) {
|
---|
1885 | xsltTransformError(ctxt, NULL, cur,
|
---|
1886 | "xsltDefaultProcessOneNode: cdata copy failed\n");
|
---|
1887 | }
|
---|
1888 | }
|
---|
1889 | break;
|
---|
1890 | case XML_TEXT_NODE:
|
---|
1891 | template = xsltGetTemplate(ctxt, cur, NULL);
|
---|
1892 | if (template) {
|
---|
1893 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1894 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1895 | "xsltDefaultProcessOneNode: applying template for text %s\n",
|
---|
1896 | cur->content));
|
---|
1897 | #endif
|
---|
1898 | ctxt->xpathCtxt->contextSize = nbchild;
|
---|
1899 | ctxt->xpathCtxt->proximityPosition = childno;
|
---|
1900 | /*
|
---|
1901 | * Instantiate the xsl:template.
|
---|
1902 | */
|
---|
1903 | xsltApplyXSLTTemplate(ctxt, cur, template->content,
|
---|
1904 | template, params);
|
---|
1905 | } else /* if (ctxt->mode == NULL) */ {
|
---|
1906 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1907 | if (cur->content == NULL) {
|
---|
1908 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1909 | "xsltDefaultProcessOneNode: copy empty text\n"));
|
---|
1910 | } else {
|
---|
1911 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1912 | "xsltDefaultProcessOneNode: copy text %s\n",
|
---|
1913 | cur->content));
|
---|
1914 | }
|
---|
1915 | #endif
|
---|
1916 | copy = xsltCopyText(ctxt, ctxt->insert, cur, 0);
|
---|
1917 | if (copy == NULL) {
|
---|
1918 | xsltTransformError(ctxt, NULL, cur,
|
---|
1919 | "xsltDefaultProcessOneNode: text copy failed\n");
|
---|
1920 | }
|
---|
1921 | }
|
---|
1922 | break;
|
---|
1923 | case XML_PI_NODE:
|
---|
1924 | case XML_COMMENT_NODE:
|
---|
1925 | template = xsltGetTemplate(ctxt, cur, NULL);
|
---|
1926 | if (template) {
|
---|
1927 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1928 | if (cur->type == XML_PI_NODE) {
|
---|
1929 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1930 | "xsltDefaultProcessOneNode: template found for PI %s\n",
|
---|
1931 | cur->name));
|
---|
1932 | } else if (cur->type == XML_COMMENT_NODE) {
|
---|
1933 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1934 | "xsltDefaultProcessOneNode: template found for comment\n"));
|
---|
1935 | }
|
---|
1936 | #endif
|
---|
1937 | ctxt->xpathCtxt->contextSize = nbchild;
|
---|
1938 | ctxt->xpathCtxt->proximityPosition = childno;
|
---|
1939 | /*
|
---|
1940 | * Instantiate the xsl:template.
|
---|
1941 | */
|
---|
1942 | xsltApplyXSLTTemplate(ctxt, cur, template->content,
|
---|
1943 | template, params);
|
---|
1944 | }
|
---|
1945 | break;
|
---|
1946 | default:
|
---|
1947 | break;
|
---|
1948 | }
|
---|
1949 | cur = cur->next;
|
---|
1950 | }
|
---|
1951 | ctxt->xpathCtxt->contextSize = oldSize;
|
---|
1952 | ctxt->xpathCtxt->proximityPosition = oldPos;
|
---|
1953 | }
|
---|
1954 |
|
---|
1955 | /**
|
---|
1956 | * xsltProcessOneNode:
|
---|
1957 | * @ctxt: a XSLT process context
|
---|
1958 | * @contextNode: the "current node" in the source tree
|
---|
1959 | * @withParams: extra parameters (e.g. xsl:with-param) passed to the
|
---|
1960 | * template if any
|
---|
1961 | *
|
---|
1962 | * Process the source node.
|
---|
1963 | */
|
---|
1964 | void
|
---|
1965 | xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
|
---|
1966 | xsltStackElemPtr withParams)
|
---|
1967 | {
|
---|
1968 | xsltTemplatePtr templ;
|
---|
1969 | xmlNodePtr oldNode;
|
---|
1970 |
|
---|
1971 | templ = xsltGetTemplate(ctxt, contextNode, NULL);
|
---|
1972 | /*
|
---|
1973 | * If no template is found, apply the default rule.
|
---|
1974 | */
|
---|
1975 | if (templ == NULL) {
|
---|
1976 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
1977 | if (contextNode->type == XML_DOCUMENT_NODE) {
|
---|
1978 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1979 | "xsltProcessOneNode: no template found for /\n"));
|
---|
1980 | } else if (contextNode->type == XML_CDATA_SECTION_NODE) {
|
---|
1981 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1982 | "xsltProcessOneNode: no template found for CDATA\n"));
|
---|
1983 | } else if (contextNode->type == XML_ATTRIBUTE_NODE) {
|
---|
1984 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1985 | "xsltProcessOneNode: no template found for attribute %s\n",
|
---|
1986 | ((xmlAttrPtr) contextNode)->name));
|
---|
1987 | } else {
|
---|
1988 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
1989 | "xsltProcessOneNode: no template found for %s\n", contextNode->name));
|
---|
1990 | }
|
---|
1991 | #endif
|
---|
1992 | oldNode = ctxt->node;
|
---|
1993 | ctxt->node = contextNode;
|
---|
1994 | xsltDefaultProcessOneNode(ctxt, contextNode, withParams);
|
---|
1995 | ctxt->node = oldNode;
|
---|
1996 | return;
|
---|
1997 | }
|
---|
1998 |
|
---|
1999 | if (contextNode->type == XML_ATTRIBUTE_NODE) {
|
---|
2000 | xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
|
---|
2001 | /*
|
---|
2002 | * Set the "current template rule".
|
---|
2003 | */
|
---|
2004 | ctxt->currentTemplateRule = templ;
|
---|
2005 |
|
---|
2006 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2007 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2008 | "xsltProcessOneNode: applying template '%s' for attribute %s\n",
|
---|
2009 | templ->match, contextNode->name));
|
---|
2010 | #endif
|
---|
2011 | xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
|
---|
2012 |
|
---|
2013 | ctxt->currentTemplateRule = oldCurTempRule;
|
---|
2014 | } else {
|
---|
2015 | xsltTemplatePtr oldCurTempRule = ctxt->currentTemplateRule;
|
---|
2016 | /*
|
---|
2017 | * Set the "current template rule".
|
---|
2018 | */
|
---|
2019 | ctxt->currentTemplateRule = templ;
|
---|
2020 |
|
---|
2021 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2022 | if (contextNode->type == XML_DOCUMENT_NODE) {
|
---|
2023 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2024 | "xsltProcessOneNode: applying template '%s' for /\n",
|
---|
2025 | templ->match));
|
---|
2026 | } else {
|
---|
2027 | XSLT_TRACE(ctxt,XSLT_TRACE_PROCESS_NODE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2028 | "xsltProcessOneNode: applying template '%s' for %s\n",
|
---|
2029 | templ->match, contextNode->name));
|
---|
2030 | }
|
---|
2031 | #endif
|
---|
2032 | xsltApplyXSLTTemplate(ctxt, contextNode, templ->content, templ, withParams);
|
---|
2033 |
|
---|
2034 | ctxt->currentTemplateRule = oldCurTempRule;
|
---|
2035 | }
|
---|
2036 | }
|
---|
2037 |
|
---|
2038 | #ifdef WITH_DEBUGGER
|
---|
2039 | static xmlNodePtr
|
---|
2040 | xsltDebuggerStartSequenceConstructor(xsltTransformContextPtr ctxt,
|
---|
2041 | xmlNodePtr contextNode,
|
---|
2042 | xmlNodePtr list,
|
---|
2043 | xsltTemplatePtr templ,
|
---|
2044 | int *addCallResult)
|
---|
2045 | {
|
---|
2046 | xmlNodePtr debugedNode = NULL;
|
---|
2047 |
|
---|
2048 | if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
|
---|
2049 | if (templ) {
|
---|
2050 | *addCallResult = xslAddCall(templ, templ->elem);
|
---|
2051 | } else {
|
---|
2052 | *addCallResult = xslAddCall(NULL, list);
|
---|
2053 | }
|
---|
2054 | switch (ctxt->debugStatus) {
|
---|
2055 | case XSLT_DEBUG_RUN_RESTART:
|
---|
2056 | case XSLT_DEBUG_QUIT:
|
---|
2057 | if (*addCallResult)
|
---|
2058 | xslDropCall();
|
---|
2059 | return(NULL);
|
---|
2060 | }
|
---|
2061 | if (templ) {
|
---|
2062 | xslHandleDebugger(templ->elem, contextNode, templ, ctxt);
|
---|
2063 | debugedNode = templ->elem;
|
---|
2064 | } else if (list) {
|
---|
2065 | xslHandleDebugger(list, contextNode, templ, ctxt);
|
---|
2066 | debugedNode = list;
|
---|
2067 | } else if (ctxt->inst) {
|
---|
2068 | xslHandleDebugger(ctxt->inst, contextNode, templ, ctxt);
|
---|
2069 | debugedNode = ctxt->inst;
|
---|
2070 | }
|
---|
2071 | }
|
---|
2072 | return(debugedNode);
|
---|
2073 | }
|
---|
2074 | #endif
|
---|
2075 |
|
---|
2076 | /**
|
---|
2077 | * xsltLocalVariablePush:
|
---|
2078 | * @ctxt: the transformation context
|
---|
2079 | * @variable: variable to be pushed to the variable stack
|
---|
2080 | * @level: new value for variable's level
|
---|
2081 | *
|
---|
2082 | * Places the variable onto the local variable stack
|
---|
2083 | *
|
---|
2084 | * Returns: 0 for success, -1 for any error
|
---|
2085 | * **NOTE:**
|
---|
2086 | * This is an internal routine and should not be called by users!
|
---|
2087 | */
|
---|
2088 | int
|
---|
2089 | xsltLocalVariablePush(xsltTransformContextPtr ctxt,
|
---|
2090 | xsltStackElemPtr variable,
|
---|
2091 | int level)
|
---|
2092 | {
|
---|
2093 | if (ctxt->varsMax == 0) {
|
---|
2094 | ctxt->varsMax = 10;
|
---|
2095 | ctxt->varsTab =
|
---|
2096 | (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax *
|
---|
2097 | sizeof(ctxt->varsTab[0]));
|
---|
2098 | if (ctxt->varsTab == NULL) {
|
---|
2099 | xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
|
---|
2100 | return (-1);
|
---|
2101 | }
|
---|
2102 | }
|
---|
2103 | if (ctxt->varsNr >= ctxt->varsMax) {
|
---|
2104 | ctxt->varsMax *= 2;
|
---|
2105 | ctxt->varsTab =
|
---|
2106 | (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab,
|
---|
2107 | ctxt->varsMax *
|
---|
2108 | sizeof(ctxt->varsTab[0]));
|
---|
2109 | if (ctxt->varsTab == NULL) {
|
---|
2110 | xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
|
---|
2111 | return (-1);
|
---|
2112 | }
|
---|
2113 | }
|
---|
2114 | ctxt->varsTab[ctxt->varsNr++] = variable;
|
---|
2115 | ctxt->vars = variable;
|
---|
2116 | variable->level = level;
|
---|
2117 | return(0);
|
---|
2118 | }
|
---|
2119 |
|
---|
2120 | /**
|
---|
2121 | * xsltReleaseLocalRVTs:
|
---|
2122 | *
|
---|
2123 | * Fragments which are results of extension instructions
|
---|
2124 | * are preserved; all other fragments are freed/cached.
|
---|
2125 | */
|
---|
2126 | static void
|
---|
2127 | xsltReleaseLocalRVTs(xsltTransformContextPtr ctxt, xmlDocPtr base)
|
---|
2128 | {
|
---|
2129 | xmlDocPtr cur = ctxt->localRVT, tmp;
|
---|
2130 |
|
---|
2131 | while ((cur != NULL) && (cur != base)) {
|
---|
2132 | if (cur->psvi == (void *) ((long) 1)) {
|
---|
2133 | cur = (xmlDocPtr) cur->next;
|
---|
2134 | } else {
|
---|
2135 | tmp = cur;
|
---|
2136 | cur = (xmlDocPtr) cur->next;
|
---|
2137 |
|
---|
2138 | if (tmp == ctxt->localRVT)
|
---|
2139 | ctxt->localRVT = cur;
|
---|
2140 |
|
---|
2141 | /*
|
---|
2142 | * We need ctxt->localRVTBase for extension instructions
|
---|
2143 | * which return values (like EXSLT's function).
|
---|
2144 | */
|
---|
2145 | if (tmp == ctxt->localRVTBase)
|
---|
2146 | ctxt->localRVTBase = cur;
|
---|
2147 |
|
---|
2148 | if (tmp->prev)
|
---|
2149 | tmp->prev->next = (xmlNodePtr) cur;
|
---|
2150 | if (cur)
|
---|
2151 | cur->prev = tmp->prev;
|
---|
2152 | xsltReleaseRVT(ctxt, tmp);
|
---|
2153 | }
|
---|
2154 | }
|
---|
2155 | }
|
---|
2156 |
|
---|
2157 | /**
|
---|
2158 | * xsltApplySequenceConstructor:
|
---|
2159 | * @ctxt: a XSLT process context
|
---|
2160 | * @contextNode: the "current node" in the source tree
|
---|
2161 | * @list: the nodes of a sequence constructor;
|
---|
2162 | * (plus leading xsl:param elements)
|
---|
2163 | * @templ: the compiled xsl:template (optional)
|
---|
2164 | *
|
---|
2165 | * Processes a sequence constructor.
|
---|
2166 | *
|
---|
2167 | * NOTE: ctxt->currentTemplateRule was introduced to reflect the
|
---|
2168 | * semantics of "current template rule". I.e. the field ctxt->templ
|
---|
2169 | * is not intended to reflect this, thus always pushed onto the
|
---|
2170 | * template stack.
|
---|
2171 | */
|
---|
2172 | static void
|
---|
2173 | xsltApplySequenceConstructor(xsltTransformContextPtr ctxt,
|
---|
2174 | xmlNodePtr contextNode, xmlNodePtr list,
|
---|
2175 | xsltTemplatePtr templ)
|
---|
2176 | {
|
---|
2177 | xmlNodePtr oldInsert, oldInst, oldCurInst, oldContextNode;
|
---|
2178 | xmlNodePtr cur, insert, copy = NULL;
|
---|
2179 | int level = 0, oldVarsNr;
|
---|
2180 | xmlDocPtr oldLocalFragmentTop, oldLocalFragmentBase;
|
---|
2181 |
|
---|
2182 | #ifdef XSLT_REFACTORED
|
---|
2183 | xsltStylePreCompPtr info;
|
---|
2184 | #endif
|
---|
2185 |
|
---|
2186 | #ifdef WITH_DEBUGGER
|
---|
2187 | int addCallResult = 0;
|
---|
2188 | xmlNodePtr debuggedNode = NULL;
|
---|
2189 | #endif
|
---|
2190 |
|
---|
2191 | if (ctxt == NULL)
|
---|
2192 | return;
|
---|
2193 |
|
---|
2194 | #ifdef WITH_DEBUGGER
|
---|
2195 | if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
|
---|
2196 | debuggedNode =
|
---|
2197 | xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
|
---|
2198 | list, templ, &addCallResult);
|
---|
2199 | if (debuggedNode == NULL)
|
---|
2200 | return;
|
---|
2201 | }
|
---|
2202 | #endif
|
---|
2203 |
|
---|
2204 | if (list == NULL)
|
---|
2205 | return;
|
---|
2206 | CHECK_STOPPED;
|
---|
2207 |
|
---|
2208 | oldLocalFragmentTop = ctxt->localRVT;
|
---|
2209 | oldInsert = insert = ctxt->insert;
|
---|
2210 | oldInst = oldCurInst = ctxt->inst;
|
---|
2211 | oldContextNode = ctxt->node;
|
---|
2212 | /*
|
---|
2213 | * Save current number of variables on the stack; new vars are popped when
|
---|
2214 | * exiting.
|
---|
2215 | */
|
---|
2216 | oldVarsNr = ctxt->varsNr;
|
---|
2217 | /*
|
---|
2218 | * Process the sequence constructor.
|
---|
2219 | */
|
---|
2220 | cur = list;
|
---|
2221 | while (cur != NULL) {
|
---|
2222 | ctxt->inst = cur;
|
---|
2223 |
|
---|
2224 | #ifdef WITH_DEBUGGER
|
---|
2225 | switch (ctxt->debugStatus) {
|
---|
2226 | case XSLT_DEBUG_RUN_RESTART:
|
---|
2227 | case XSLT_DEBUG_QUIT:
|
---|
2228 | break;
|
---|
2229 |
|
---|
2230 | }
|
---|
2231 | #endif
|
---|
2232 | /*
|
---|
2233 | * Test; we must have a valid insertion point.
|
---|
2234 | */
|
---|
2235 | if (insert == NULL) {
|
---|
2236 |
|
---|
2237 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2238 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2239 | "xsltApplySequenceConstructor: insert == NULL !\n"));
|
---|
2240 | #endif
|
---|
2241 | goto error;
|
---|
2242 | }
|
---|
2243 |
|
---|
2244 | #ifdef WITH_DEBUGGER
|
---|
2245 | if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (debuggedNode != cur))
|
---|
2246 | xslHandleDebugger(cur, contextNode, templ, ctxt);
|
---|
2247 | #endif
|
---|
2248 |
|
---|
2249 | #ifdef XSLT_REFACTORED
|
---|
2250 | if (cur->type == XML_ELEMENT_NODE) {
|
---|
2251 | info = (xsltStylePreCompPtr) cur->psvi;
|
---|
2252 | /*
|
---|
2253 | * We expect a compiled representation on:
|
---|
2254 | * 1) XSLT instructions of this XSLT version (1.0)
|
---|
2255 | * (with a few exceptions)
|
---|
2256 | * 2) Literal result elements
|
---|
2257 | * 3) Extension instructions
|
---|
2258 | * 4) XSLT instructions of future XSLT versions
|
---|
2259 | * (forwards-compatible mode).
|
---|
2260 | */
|
---|
2261 | if (info == NULL) {
|
---|
2262 | /*
|
---|
2263 | * Handle the rare cases where we don't expect a compiled
|
---|
2264 | * representation on an XSLT element.
|
---|
2265 | */
|
---|
2266 | if (IS_XSLT_ELEM_FAST(cur) && IS_XSLT_NAME(cur, "message")) {
|
---|
2267 | xsltMessage(ctxt, contextNode, cur);
|
---|
2268 | goto skip_children;
|
---|
2269 | }
|
---|
2270 | /*
|
---|
2271 | * Something really went wrong:
|
---|
2272 | */
|
---|
2273 | xsltTransformError(ctxt, NULL, cur,
|
---|
2274 | "Internal error in xsltApplySequenceConstructor(): "
|
---|
2275 | "The element '%s' in the stylesheet has no compiled "
|
---|
2276 | "representation.\n",
|
---|
2277 | cur->name);
|
---|
2278 | goto skip_children;
|
---|
2279 | }
|
---|
2280 |
|
---|
2281 | if (info->type == XSLT_FUNC_LITERAL_RESULT_ELEMENT) {
|
---|
2282 | xsltStyleItemLRElementInfoPtr lrInfo =
|
---|
2283 | (xsltStyleItemLRElementInfoPtr) info;
|
---|
2284 | /*
|
---|
2285 | * Literal result elements
|
---|
2286 | * --------------------------------------------------------
|
---|
2287 | */
|
---|
2288 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2289 | XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
|
---|
2290 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
2291 | "xsltApplySequenceConstructor: copy literal result "
|
---|
2292 | "element '%s'\n", cur->name));
|
---|
2293 | #endif
|
---|
2294 | /*
|
---|
2295 | * Copy the raw element-node.
|
---|
2296 | * OLD: if ((copy = xsltShallowCopyElem(ctxt, cur, insert))
|
---|
2297 | * == NULL)
|
---|
2298 | * goto error;
|
---|
2299 | */
|
---|
2300 | copy = xmlDocCopyNode(cur, insert->doc, 0);
|
---|
2301 | if (copy == NULL) {
|
---|
2302 | xsltTransformError(ctxt, NULL, cur,
|
---|
2303 | "Internal error in xsltApplySequenceConstructor(): "
|
---|
2304 | "Failed to copy literal result element '%s'.\n",
|
---|
2305 | cur->name);
|
---|
2306 | goto error;
|
---|
2307 | } else {
|
---|
2308 | /*
|
---|
2309 | * Add the element-node to the result tree.
|
---|
2310 | */
|
---|
2311 | copy->doc = ctxt->output;
|
---|
2312 | xmlAddChild(insert, copy);
|
---|
2313 | /*
|
---|
2314 | * Create effective namespaces declarations.
|
---|
2315 | * OLD: xsltCopyNamespaceList(ctxt, copy, cur->nsDef);
|
---|
2316 | */
|
---|
2317 | if (lrInfo->effectiveNs != NULL) {
|
---|
2318 | xsltEffectiveNsPtr effNs = lrInfo->effectiveNs;
|
---|
2319 | xmlNsPtr ns, lastns = NULL;
|
---|
2320 |
|
---|
2321 | while (effNs != NULL) {
|
---|
2322 | /*
|
---|
2323 | * Avoid generating redundant namespace
|
---|
2324 | * declarations; thus lookup if there is already
|
---|
2325 | * such a ns-decl in the result.
|
---|
2326 | */
|
---|
2327 | ns = xmlSearchNs(copy->doc, copy, effNs->prefix);
|
---|
2328 | if ((ns != NULL) &&
|
---|
2329 | (xmlStrEqual(ns->href, effNs->nsName)))
|
---|
2330 | {
|
---|
2331 | effNs = effNs->next;
|
---|
2332 | continue;
|
---|
2333 | }
|
---|
2334 | ns = xmlNewNs(copy, effNs->nsName, effNs->prefix);
|
---|
2335 | if (ns == NULL) {
|
---|
2336 | xsltTransformError(ctxt, NULL, cur,
|
---|
2337 | "Internal error in "
|
---|
2338 | "xsltApplySequenceConstructor(): "
|
---|
2339 | "Failed to copy a namespace "
|
---|
2340 | "declaration.\n");
|
---|
2341 | goto error;
|
---|
2342 | }
|
---|
2343 |
|
---|
2344 | if (lastns == NULL)
|
---|
2345 | copy->nsDef = ns;
|
---|
2346 | else
|
---|
2347 | lastns->next =ns;
|
---|
2348 | lastns = ns;
|
---|
2349 |
|
---|
2350 | effNs = effNs->next;
|
---|
2351 | }
|
---|
2352 |
|
---|
2353 | }
|
---|
2354 | /*
|
---|
2355 | * NOTE that we don't need to apply ns-alising: this was
|
---|
2356 | * already done at compile-time.
|
---|
2357 | */
|
---|
2358 | if (cur->ns != NULL) {
|
---|
2359 | /*
|
---|
2360 | * If there's no such ns-decl in the result tree,
|
---|
2361 | * then xsltGetSpecialNamespace() will
|
---|
2362 | * create a ns-decl on the copied node.
|
---|
2363 | */
|
---|
2364 | copy->ns = xsltGetSpecialNamespace(ctxt, cur,
|
---|
2365 | cur->ns->href, cur->ns->prefix, copy);
|
---|
2366 | } else {
|
---|
2367 | /*
|
---|
2368 | * Undeclare the default namespace if needed.
|
---|
2369 | * This can be skipped, if the result element has
|
---|
2370 | * no ns-decls, in which case the result element
|
---|
2371 | * obviously does not declare a default namespace;
|
---|
2372 | * AND there's either no parent, or the parent
|
---|
2373 | * element is in no namespace; this means there's no
|
---|
2374 | * default namespace is scope to care about.
|
---|
2375 | *
|
---|
2376 | * REVISIT: This might result in massive
|
---|
2377 | * generation of ns-decls if nodes in a default
|
---|
2378 | * namespaces are mixed with nodes in no namespace.
|
---|
2379 | *
|
---|
2380 | */
|
---|
2381 | if (copy->nsDef ||
|
---|
2382 | ((insert != NULL) &&
|
---|
2383 | (insert->type == XML_ELEMENT_NODE) &&
|
---|
2384 | (insert->ns != NULL)))
|
---|
2385 | {
|
---|
2386 | xsltGetSpecialNamespace(ctxt, cur,
|
---|
2387 | NULL, NULL, copy);
|
---|
2388 | }
|
---|
2389 | }
|
---|
2390 | }
|
---|
2391 | /*
|
---|
2392 | * SPEC XSLT 2.0 "Each attribute of the literal result
|
---|
2393 | * element, other than an attribute in the XSLT namespace,
|
---|
2394 | * is processed to produce an attribute for the element in
|
---|
2395 | * the result tree."
|
---|
2396 | * NOTE: See bug #341325.
|
---|
2397 | */
|
---|
2398 | if (cur->properties != NULL) {
|
---|
2399 | xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
|
---|
2400 | }
|
---|
2401 | } else if (IS_XSLT_ELEM_FAST(cur)) {
|
---|
2402 | /*
|
---|
2403 | * XSLT instructions
|
---|
2404 | * --------------------------------------------------------
|
---|
2405 | */
|
---|
2406 | if (info->type == XSLT_FUNC_UNKOWN_FORWARDS_COMPAT) {
|
---|
2407 | /*
|
---|
2408 | * We hit an unknown XSLT element.
|
---|
2409 | * Try to apply one of the fallback cases.
|
---|
2410 | */
|
---|
2411 | ctxt->insert = insert;
|
---|
2412 | if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
|
---|
2413 | xsltTransformError(ctxt, NULL, cur,
|
---|
2414 | "The is no fallback behaviour defined for "
|
---|
2415 | "the unknown XSLT element '%s'.\n",
|
---|
2416 | cur->name);
|
---|
2417 | }
|
---|
2418 | ctxt->insert = oldInsert;
|
---|
2419 | } else if (info->func != NULL) {
|
---|
2420 | /*
|
---|
2421 | * Execute the XSLT instruction.
|
---|
2422 | */
|
---|
2423 | ctxt->insert = insert;
|
---|
2424 |
|
---|
2425 | info->func(ctxt, contextNode, cur,
|
---|
2426 | (xsltElemPreCompPtr) info);
|
---|
2427 |
|
---|
2428 | /*
|
---|
2429 | * Cleanup temporary tree fragments.
|
---|
2430 | */
|
---|
2431 | if (oldLocalFragmentTop != ctxt->localRVT)
|
---|
2432 | xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
|
---|
2433 |
|
---|
2434 | ctxt->insert = oldInsert;
|
---|
2435 | } else if (info->type == XSLT_FUNC_VARIABLE) {
|
---|
2436 | xsltStackElemPtr tmpvar = ctxt->vars;
|
---|
2437 |
|
---|
2438 | xsltParseStylesheetVariable(ctxt, cur);
|
---|
2439 |
|
---|
2440 | if (tmpvar != ctxt->vars) {
|
---|
2441 | /*
|
---|
2442 | * TODO: Using a @tmpvar is an annoying workaround, but
|
---|
2443 | * the current mechanisms do not provide any other way
|
---|
2444 | * of knowing if the var was really pushed onto the
|
---|
2445 | * stack.
|
---|
2446 | */
|
---|
2447 | ctxt->vars->level = level;
|
---|
2448 | }
|
---|
2449 | } else if (info->type == XSLT_FUNC_MESSAGE) {
|
---|
2450 | /*
|
---|
2451 | * TODO: Won't be hit, since we don't compile xsl:message.
|
---|
2452 | */
|
---|
2453 | xsltMessage(ctxt, contextNode, cur);
|
---|
2454 | } else {
|
---|
2455 | xsltTransformError(ctxt, NULL, cur,
|
---|
2456 | "Unexpected XSLT element '%s'.\n", cur->name);
|
---|
2457 | }
|
---|
2458 | goto skip_children;
|
---|
2459 |
|
---|
2460 | } else {
|
---|
2461 | xsltTransformFunction func;
|
---|
2462 | /*
|
---|
2463 | * Extension intructions (elements)
|
---|
2464 | * --------------------------------------------------------
|
---|
2465 | */
|
---|
2466 | if (cur->psvi == xsltExtMarker) {
|
---|
2467 | /*
|
---|
2468 | * The xsltExtMarker was set during the compilation
|
---|
2469 | * of extension instructions if there was no registered
|
---|
2470 | * handler for this specific extension function at
|
---|
2471 | * compile-time.
|
---|
2472 | * Libxslt will now lookup if a handler is
|
---|
2473 | * registered in the context of this transformation.
|
---|
2474 | */
|
---|
2475 | func = (xsltTransformFunction)
|
---|
2476 | xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
|
---|
2477 | } else
|
---|
2478 | func = ((xsltElemPreCompPtr) cur->psvi)->func;
|
---|
2479 |
|
---|
2480 | if (func == NULL) {
|
---|
2481 | /*
|
---|
2482 | * No handler available.
|
---|
2483 | * Try to execute fallback behaviour via xsl:fallback.
|
---|
2484 | */
|
---|
2485 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2486 | XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
|
---|
2487 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
2488 | "xsltApplySequenceConstructor: unknown extension %s\n",
|
---|
2489 | cur->name));
|
---|
2490 | #endif
|
---|
2491 | ctxt->insert = insert;
|
---|
2492 | if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
|
---|
2493 | xsltTransformError(ctxt, NULL, cur,
|
---|
2494 | "Unknown extension instruction '{%s}%s'.\n",
|
---|
2495 | cur->ns->href, cur->name);
|
---|
2496 | }
|
---|
2497 | ctxt->insert = oldInsert;
|
---|
2498 | } else {
|
---|
2499 | /*
|
---|
2500 | * Execute the handler-callback.
|
---|
2501 | */
|
---|
2502 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2503 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2504 | "xsltApplySequenceConstructor: extension construct %s\n",
|
---|
2505 | cur->name));
|
---|
2506 | #endif
|
---|
2507 | ctxt->insert = insert;
|
---|
2508 | /*
|
---|
2509 | * We need the fragment base for extension instructions
|
---|
2510 | * which return values (like EXSLT's function).
|
---|
2511 | */
|
---|
2512 | oldLocalFragmentBase = ctxt->localRVTBase;
|
---|
2513 | ctxt->localRVTBase = NULL;
|
---|
2514 |
|
---|
2515 | func(ctxt, contextNode, cur, cur->psvi);
|
---|
2516 |
|
---|
2517 | ctxt->localRVTBase = oldLocalFragmentBase;
|
---|
2518 | /*
|
---|
2519 | * Cleanup temporary tree fragments.
|
---|
2520 | */
|
---|
2521 | if (oldLocalFragmentTop != ctxt->localRVT)
|
---|
2522 | xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
|
---|
2523 |
|
---|
2524 | ctxt->insert = oldInsert;
|
---|
2525 | }
|
---|
2526 | goto skip_children;
|
---|
2527 | }
|
---|
2528 |
|
---|
2529 | } else if (XSLT_IS_TEXT_NODE(cur)) {
|
---|
2530 | /*
|
---|
2531 | * Text
|
---|
2532 | * ------------------------------------------------------------
|
---|
2533 | */
|
---|
2534 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2535 | if (cur->name == xmlStringTextNoenc) {
|
---|
2536 | XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
|
---|
2537 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
2538 | "xsltApplySequenceConstructor: copy unescaped text '%s'\n",
|
---|
2539 | cur->content));
|
---|
2540 | } else {
|
---|
2541 | XSLT_TRACE(ctxt, XSLT_TRACE_APPLY_TEMPLATE,
|
---|
2542 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
2543 | "xsltApplySequenceConstructor: copy text '%s'\n",
|
---|
2544 | cur->content));
|
---|
2545 | }
|
---|
2546 | #endif
|
---|
2547 | if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
|
---|
2548 | goto error;
|
---|
2549 | }
|
---|
2550 |
|
---|
2551 | #else /* XSLT_REFACTORED */
|
---|
2552 |
|
---|
2553 | if (IS_XSLT_ELEM(cur)) {
|
---|
2554 | /*
|
---|
2555 | * This is an XSLT node
|
---|
2556 | */
|
---|
2557 | xsltStylePreCompPtr info = (xsltStylePreCompPtr) cur->psvi;
|
---|
2558 |
|
---|
2559 | if (info == NULL) {
|
---|
2560 | if (IS_XSLT_NAME(cur, "message")) {
|
---|
2561 | xsltMessage(ctxt, contextNode, cur);
|
---|
2562 | } else {
|
---|
2563 | /*
|
---|
2564 | * That's an error try to apply one of the fallback cases
|
---|
2565 | */
|
---|
2566 | ctxt->insert = insert;
|
---|
2567 | if (!xsltApplyFallbacks(ctxt, contextNode, cur)) {
|
---|
2568 | xsltGenericError(xsltGenericErrorContext,
|
---|
2569 | "xsltApplySequenceConstructor: %s was not compiled\n",
|
---|
2570 | cur->name);
|
---|
2571 | }
|
---|
2572 | ctxt->insert = oldInsert;
|
---|
2573 | }
|
---|
2574 | goto skip_children;
|
---|
2575 | }
|
---|
2576 |
|
---|
2577 | if (info->func != NULL) {
|
---|
2578 | oldCurInst = ctxt->inst;
|
---|
2579 | ctxt->inst = cur;
|
---|
2580 | ctxt->insert = insert;
|
---|
2581 | oldLocalFragmentBase = ctxt->localRVTBase;
|
---|
2582 | ctxt->localRVTBase = NULL;
|
---|
2583 |
|
---|
2584 | info->func(ctxt, contextNode, cur, (xsltElemPreCompPtr) info);
|
---|
2585 |
|
---|
2586 | ctxt->localRVTBase = oldLocalFragmentBase;
|
---|
2587 | /*
|
---|
2588 | * Cleanup temporary tree fragments.
|
---|
2589 | */
|
---|
2590 | if (oldLocalFragmentTop != ctxt->localRVT)
|
---|
2591 | xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
|
---|
2592 |
|
---|
2593 | ctxt->insert = oldInsert;
|
---|
2594 | ctxt->inst = oldCurInst;
|
---|
2595 | goto skip_children;
|
---|
2596 | }
|
---|
2597 |
|
---|
2598 | if (IS_XSLT_NAME(cur, "variable")) {
|
---|
2599 | xsltStackElemPtr tmpvar = ctxt->vars;
|
---|
2600 |
|
---|
2601 | oldCurInst = ctxt->inst;
|
---|
2602 | ctxt->inst = cur;
|
---|
2603 |
|
---|
2604 | xsltParseStylesheetVariable(ctxt, cur);
|
---|
2605 |
|
---|
2606 | ctxt->inst = oldCurInst;
|
---|
2607 |
|
---|
2608 | if (tmpvar != ctxt->vars) {
|
---|
2609 | /*
|
---|
2610 | * TODO: Using a @tmpvar is an annoying workaround, but
|
---|
2611 | * the current mechanisms do not provide any other way
|
---|
2612 | * of knowing if the var was really pushed onto the
|
---|
2613 | * stack.
|
---|
2614 | */
|
---|
2615 | ctxt->vars->level = level;
|
---|
2616 | }
|
---|
2617 | } else if (IS_XSLT_NAME(cur, "message")) {
|
---|
2618 | xsltMessage(ctxt, contextNode, cur);
|
---|
2619 | } else {
|
---|
2620 | xsltTransformError(ctxt, NULL, cur,
|
---|
2621 | "Unexpected XSLT element '%s'.\n", cur->name);
|
---|
2622 | }
|
---|
2623 | goto skip_children;
|
---|
2624 | } else if ((cur->type == XML_TEXT_NODE) ||
|
---|
2625 | (cur->type == XML_CDATA_SECTION_NODE)) {
|
---|
2626 |
|
---|
2627 | /*
|
---|
2628 | * This text comes from the stylesheet
|
---|
2629 | * For stylesheets, the set of whitespace-preserving
|
---|
2630 | * element names consists of just xsl:text.
|
---|
2631 | */
|
---|
2632 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2633 | if (cur->type == XML_CDATA_SECTION_NODE) {
|
---|
2634 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2635 | "xsltApplySequenceConstructor: copy CDATA text %s\n",
|
---|
2636 | cur->content));
|
---|
2637 | } else if (cur->name == xmlStringTextNoenc) {
|
---|
2638 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2639 | "xsltApplySequenceConstructor: copy unescaped text %s\n",
|
---|
2640 | cur->content));
|
---|
2641 | } else {
|
---|
2642 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2643 | "xsltApplySequenceConstructor: copy text %s\n",
|
---|
2644 | cur->content));
|
---|
2645 | }
|
---|
2646 | #endif
|
---|
2647 | if (xsltCopyText(ctxt, insert, cur, ctxt->internalized) == NULL)
|
---|
2648 | goto error;
|
---|
2649 | } else if ((cur->type == XML_ELEMENT_NODE) &&
|
---|
2650 | (cur->ns != NULL) && (cur->psvi != NULL)) {
|
---|
2651 | xsltTransformFunction function;
|
---|
2652 |
|
---|
2653 | oldCurInst = ctxt->inst;
|
---|
2654 | ctxt->inst = cur;
|
---|
2655 | /*
|
---|
2656 | * Flagged as an extension element
|
---|
2657 | */
|
---|
2658 | if (cur->psvi == xsltExtMarker)
|
---|
2659 | function = (xsltTransformFunction)
|
---|
2660 | xsltExtElementLookup(ctxt, cur->name, cur->ns->href);
|
---|
2661 | else
|
---|
2662 | function = ((xsltElemPreCompPtr) cur->psvi)->func;
|
---|
2663 |
|
---|
2664 | if (function == NULL) {
|
---|
2665 | xmlNodePtr child;
|
---|
2666 | int found = 0;
|
---|
2667 |
|
---|
2668 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2669 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2670 | "xsltApplySequenceConstructor: unknown extension %s\n",
|
---|
2671 | cur->name));
|
---|
2672 | #endif
|
---|
2673 | /*
|
---|
2674 | * Search if there are fallbacks
|
---|
2675 | */
|
---|
2676 | child = cur->children;
|
---|
2677 | while (child != NULL) {
|
---|
2678 | if ((IS_XSLT_ELEM(child)) &&
|
---|
2679 | (IS_XSLT_NAME(child, "fallback")))
|
---|
2680 | {
|
---|
2681 | found = 1;
|
---|
2682 | xsltApplySequenceConstructor(ctxt, contextNode,
|
---|
2683 | child->children, NULL);
|
---|
2684 | }
|
---|
2685 | child = child->next;
|
---|
2686 | }
|
---|
2687 |
|
---|
2688 | if (!found) {
|
---|
2689 | xsltTransformError(ctxt, NULL, cur,
|
---|
2690 | "xsltApplySequenceConstructor: failed to find extension %s\n",
|
---|
2691 | cur->name);
|
---|
2692 | }
|
---|
2693 | } else {
|
---|
2694 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2695 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2696 | "xsltApplySequenceConstructor: extension construct %s\n",
|
---|
2697 | cur->name));
|
---|
2698 | #endif
|
---|
2699 |
|
---|
2700 | ctxt->insert = insert;
|
---|
2701 | /*
|
---|
2702 | * We need the fragment base for extension instructions
|
---|
2703 | * which return values (like EXSLT's function).
|
---|
2704 | */
|
---|
2705 | oldLocalFragmentBase = ctxt->localRVTBase;
|
---|
2706 | ctxt->localRVTBase = NULL;
|
---|
2707 |
|
---|
2708 | function(ctxt, contextNode, cur, cur->psvi);
|
---|
2709 | /*
|
---|
2710 | * Cleanup temporary tree fragments.
|
---|
2711 | */
|
---|
2712 | if (oldLocalFragmentTop != ctxt->localRVT)
|
---|
2713 | xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
|
---|
2714 |
|
---|
2715 | ctxt->localRVTBase = oldLocalFragmentBase;
|
---|
2716 | ctxt->insert = oldInsert;
|
---|
2717 |
|
---|
2718 | }
|
---|
2719 | ctxt->inst = oldCurInst;
|
---|
2720 | goto skip_children;
|
---|
2721 | } else if (cur->type == XML_ELEMENT_NODE) {
|
---|
2722 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2723 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2724 | "xsltApplySequenceConstructor: copy node %s\n",
|
---|
2725 | cur->name));
|
---|
2726 | #endif
|
---|
2727 | oldCurInst = ctxt->inst;
|
---|
2728 | ctxt->inst = cur;
|
---|
2729 |
|
---|
2730 | if ((copy = xsltShallowCopyElem(ctxt, cur, insert, 1)) == NULL)
|
---|
2731 | goto error;
|
---|
2732 | /*
|
---|
2733 | * Add extra namespaces inherited from the current template
|
---|
2734 | * if we are in the first level children and this is a
|
---|
2735 | * "real" template.
|
---|
2736 | */
|
---|
2737 | if ((templ != NULL) && (oldInsert == insert) &&
|
---|
2738 | (ctxt->templ != NULL) && (ctxt->templ->inheritedNs != NULL)) {
|
---|
2739 | int i;
|
---|
2740 | xmlNsPtr ns, ret;
|
---|
2741 |
|
---|
2742 | for (i = 0; i < ctxt->templ->inheritedNsNr; i++) {
|
---|
2743 | const xmlChar *URI = NULL;
|
---|
2744 | xsltStylesheetPtr style;
|
---|
2745 | ns = ctxt->templ->inheritedNs[i];
|
---|
2746 |
|
---|
2747 | /* Note that the XSLT namespace was already excluded
|
---|
2748 | * in xsltGetInheritedNsList().
|
---|
2749 | */
|
---|
2750 | #if 0
|
---|
2751 | if (xmlStrEqual(ns->href, XSLT_NAMESPACE))
|
---|
2752 | continue;
|
---|
2753 | #endif
|
---|
2754 | style = ctxt->style;
|
---|
2755 | while (style != NULL) {
|
---|
2756 | if (style->nsAliases != NULL)
|
---|
2757 | URI = (const xmlChar *)
|
---|
2758 | xmlHashLookup(style->nsAliases, ns->href);
|
---|
2759 | if (URI != NULL)
|
---|
2760 | break;
|
---|
2761 |
|
---|
2762 | style = xsltNextImport(style);
|
---|
2763 | }
|
---|
2764 | if (URI == UNDEFINED_DEFAULT_NS)
|
---|
2765 | continue;
|
---|
2766 | if (URI == NULL)
|
---|
2767 | URI = ns->href;
|
---|
2768 | /*
|
---|
2769 | * TODO: The following will still be buggy for the
|
---|
2770 | * non-refactored code.
|
---|
2771 | */
|
---|
2772 | ret = xmlSearchNs(copy->doc, copy, ns->prefix);
|
---|
2773 | if ((ret == NULL) || (!xmlStrEqual(ret->href, URI)))
|
---|
2774 | {
|
---|
2775 | xmlNewNs(copy, URI, ns->prefix);
|
---|
2776 | }
|
---|
2777 | }
|
---|
2778 | if (copy->ns != NULL) {
|
---|
2779 | /*
|
---|
2780 | * Fix the node namespace if needed
|
---|
2781 | */
|
---|
2782 | copy->ns = xsltGetNamespace(ctxt, cur, copy->ns, copy);
|
---|
2783 | }
|
---|
2784 | }
|
---|
2785 | /*
|
---|
2786 | * all the attributes are directly inherited
|
---|
2787 | */
|
---|
2788 | if (cur->properties != NULL) {
|
---|
2789 | xsltAttrListTemplateProcess(ctxt, copy, cur->properties);
|
---|
2790 | }
|
---|
2791 | ctxt->inst = oldCurInst;
|
---|
2792 | }
|
---|
2793 | #endif /* else of XSLT_REFACTORED */
|
---|
2794 |
|
---|
2795 | /*
|
---|
2796 | * Descend into content in document order.
|
---|
2797 | */
|
---|
2798 | if (cur->children != NULL) {
|
---|
2799 | if (cur->children->type != XML_ENTITY_DECL) {
|
---|
2800 | cur = cur->children;
|
---|
2801 | level++;
|
---|
2802 | if (copy != NULL)
|
---|
2803 | insert = copy;
|
---|
2804 | continue;
|
---|
2805 | }
|
---|
2806 | }
|
---|
2807 |
|
---|
2808 | skip_children:
|
---|
2809 | /*
|
---|
2810 | * If xslt:message was just processed, we might have hit a
|
---|
2811 | * terminate='yes'; if so, then break the loop and clean up.
|
---|
2812 | * TODO: Do we need to check this also before trying to descend
|
---|
2813 | * into the content?
|
---|
2814 | */
|
---|
2815 | if (ctxt->state == XSLT_STATE_STOPPED)
|
---|
2816 | break;
|
---|
2817 | if (cur->next != NULL) {
|
---|
2818 | cur = cur->next;
|
---|
2819 | continue;
|
---|
2820 | }
|
---|
2821 |
|
---|
2822 | do {
|
---|
2823 | cur = cur->parent;
|
---|
2824 | level--;
|
---|
2825 | /*
|
---|
2826 | * Pop variables/params (xsl:variable and xsl:param).
|
---|
2827 | */
|
---|
2828 | if ((ctxt->varsNr > oldVarsNr) && (ctxt->vars->level > level)) {
|
---|
2829 | xsltLocalVariablePop(ctxt, oldVarsNr, level);
|
---|
2830 | }
|
---|
2831 |
|
---|
2832 | insert = insert->parent;
|
---|
2833 | if (cur == NULL)
|
---|
2834 | break;
|
---|
2835 | if (cur == list->parent) {
|
---|
2836 | cur = NULL;
|
---|
2837 | break;
|
---|
2838 | }
|
---|
2839 | if (cur->next != NULL) {
|
---|
2840 | cur = cur->next;
|
---|
2841 | break;
|
---|
2842 | }
|
---|
2843 | } while (cur != NULL);
|
---|
2844 | }
|
---|
2845 |
|
---|
2846 | error:
|
---|
2847 | /*
|
---|
2848 | * In case of errors: pop remaining variables.
|
---|
2849 | */
|
---|
2850 | if (ctxt->varsNr > oldVarsNr)
|
---|
2851 | xsltLocalVariablePop(ctxt, oldVarsNr, -1);
|
---|
2852 |
|
---|
2853 | ctxt->node = oldContextNode;
|
---|
2854 | ctxt->inst = oldInst;
|
---|
2855 | ctxt->insert = oldInsert;
|
---|
2856 |
|
---|
2857 | #ifdef WITH_DEBUGGER
|
---|
2858 | if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
|
---|
2859 | xslDropCall();
|
---|
2860 | }
|
---|
2861 | #endif
|
---|
2862 | }
|
---|
2863 |
|
---|
2864 | /*
|
---|
2865 | * xsltApplyXSLTTemplate:
|
---|
2866 | * @ctxt: a XSLT transformation context
|
---|
2867 | * @contextNode: the node in the source tree.
|
---|
2868 | * @list: the nodes of a sequence constructor;
|
---|
2869 | * (plus leading xsl:param elements)
|
---|
2870 | * @templ: the compiled xsl:template declaration;
|
---|
2871 | * NULL if a sequence constructor
|
---|
2872 | * @withParams: a set of caller-parameters (xsl:with-param) or NULL
|
---|
2873 | *
|
---|
2874 | * Called by:
|
---|
2875 | * - xsltApplyImports()
|
---|
2876 | * - xsltCallTemplate()
|
---|
2877 | * - xsltDefaultProcessOneNode()
|
---|
2878 | * - xsltProcessOneNode()
|
---|
2879 | */
|
---|
2880 | static void
|
---|
2881 | xsltApplyXSLTTemplate(xsltTransformContextPtr ctxt,
|
---|
2882 | xmlNodePtr contextNode,
|
---|
2883 | xmlNodePtr list,
|
---|
2884 | xsltTemplatePtr templ,
|
---|
2885 | xsltStackElemPtr withParams)
|
---|
2886 | {
|
---|
2887 | int oldVarsBase = 0;
|
---|
2888 | long start = 0;
|
---|
2889 | xmlNodePtr cur;
|
---|
2890 | xsltStackElemPtr tmpParam = NULL;
|
---|
2891 | xmlDocPtr oldUserFragmentTop, oldLocalFragmentTop;
|
---|
2892 |
|
---|
2893 | #ifdef XSLT_REFACTORED
|
---|
2894 | xsltStyleItemParamPtr iparam;
|
---|
2895 | #else
|
---|
2896 | xsltStylePreCompPtr iparam;
|
---|
2897 | #endif
|
---|
2898 |
|
---|
2899 | #ifdef WITH_DEBUGGER
|
---|
2900 | int addCallResult = 0;
|
---|
2901 | #endif
|
---|
2902 |
|
---|
2903 | if (ctxt == NULL)
|
---|
2904 | return;
|
---|
2905 | if (templ == NULL) {
|
---|
2906 | xsltTransformError(ctxt, NULL, list,
|
---|
2907 | "xsltApplyXSLTTemplate: Bad arguments; @templ is mandatory.\n");
|
---|
2908 | return;
|
---|
2909 | }
|
---|
2910 |
|
---|
2911 | #ifdef WITH_DEBUGGER
|
---|
2912 | if (ctxt->debugStatus != XSLT_DEBUG_NONE) {
|
---|
2913 | if (xsltDebuggerStartSequenceConstructor(ctxt, contextNode,
|
---|
2914 | list, templ, &addCallResult) == NULL)
|
---|
2915 | return;
|
---|
2916 | }
|
---|
2917 | #endif
|
---|
2918 |
|
---|
2919 | if (list == NULL)
|
---|
2920 | return;
|
---|
2921 | CHECK_STOPPED;
|
---|
2922 |
|
---|
2923 | /*
|
---|
2924 | * Check for infinite recursion: stop if the maximum of nested templates
|
---|
2925 | * is excceeded. Adjust xsltMaxDepth if you need more.
|
---|
2926 | */
|
---|
2927 | if (((ctxt->templNr >= xsltMaxDepth) ||
|
---|
2928 | (ctxt->varsNr >= 5 * xsltMaxDepth)))
|
---|
2929 | {
|
---|
2930 | xsltTransformError(ctxt, NULL, list,
|
---|
2931 | "xsltApplyXSLTTemplate: A potential infinite template recursion "
|
---|
2932 | "was detected.\n"
|
---|
2933 | "You can adjust xsltMaxDepth (--maxdepth) in order to "
|
---|
2934 | "raise the maximum number of nested template calls and "
|
---|
2935 | "variables/params (currently set to %d).\n",
|
---|
2936 | xsltMaxDepth);
|
---|
2937 | xsltDebug(ctxt, contextNode, list, NULL);
|
---|
2938 | return;
|
---|
2939 | }
|
---|
2940 |
|
---|
2941 | oldUserFragmentTop = ctxt->tmpRVT;
|
---|
2942 | ctxt->tmpRVT = NULL;
|
---|
2943 | oldLocalFragmentTop = ctxt->localRVT;
|
---|
2944 |
|
---|
2945 | /*
|
---|
2946 | * Initiate a distinct scope of local params/variables.
|
---|
2947 | */
|
---|
2948 | oldVarsBase = ctxt->varsBase;
|
---|
2949 | ctxt->varsBase = ctxt->varsNr;
|
---|
2950 |
|
---|
2951 | ctxt->node = contextNode;
|
---|
2952 | if (ctxt->profile) {
|
---|
2953 | templ->nbCalls++;
|
---|
2954 | start = xsltTimestamp();
|
---|
2955 | profPush(ctxt, 0);
|
---|
2956 | }
|
---|
2957 | /*
|
---|
2958 | * Push the xsl:template declaration onto the stack.
|
---|
2959 | */
|
---|
2960 | templPush(ctxt, templ);
|
---|
2961 |
|
---|
2962 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
2963 | if (templ->name != NULL)
|
---|
2964 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
2965 | "applying xsl:template '%s'\n", templ->name));
|
---|
2966 | #endif
|
---|
2967 | /*
|
---|
2968 | * Process xsl:param instructions and skip those elements for
|
---|
2969 | * further processing.
|
---|
2970 | */
|
---|
2971 | cur = list;
|
---|
2972 | do {
|
---|
2973 | if (cur->type == XML_TEXT_NODE) {
|
---|
2974 | cur = cur->next;
|
---|
2975 | continue;
|
---|
2976 | }
|
---|
2977 | if ((cur->type != XML_ELEMENT_NODE) ||
|
---|
2978 | (cur->name[0] != 'p') ||
|
---|
2979 | (cur->psvi == NULL) ||
|
---|
2980 | (! xmlStrEqual(cur->name, BAD_CAST "param")) ||
|
---|
2981 | (! IS_XSLT_ELEM(cur)))
|
---|
2982 | {
|
---|
2983 | break;
|
---|
2984 | }
|
---|
2985 |
|
---|
2986 | list = cur->next;
|
---|
2987 |
|
---|
2988 | #ifdef XSLT_REFACTORED
|
---|
2989 | iparam = (xsltStyleItemParamPtr) cur->psvi;
|
---|
2990 | #else
|
---|
2991 | iparam = (xsltStylePreCompPtr) cur->psvi;
|
---|
2992 | #endif
|
---|
2993 |
|
---|
2994 | /*
|
---|
2995 | * Substitute xsl:param for a given xsl:with-param.
|
---|
2996 | * Since the XPath expression will reference the params/vars
|
---|
2997 | * by index, we need to slot the xsl:with-params in the
|
---|
2998 | * order of encountered xsl:params to keep the sequence of
|
---|
2999 | * params/variables in the stack exactly as it was at
|
---|
3000 | * compile time,
|
---|
3001 | */
|
---|
3002 | tmpParam = NULL;
|
---|
3003 | if (withParams) {
|
---|
3004 | tmpParam = withParams;
|
---|
3005 | do {
|
---|
3006 | if ((tmpParam->name == (iparam->name)) &&
|
---|
3007 | (tmpParam->nameURI == (iparam->ns)))
|
---|
3008 | {
|
---|
3009 | /*
|
---|
3010 | * Push the caller-parameter.
|
---|
3011 | */
|
---|
3012 | xsltLocalVariablePush(ctxt, tmpParam, -1);
|
---|
3013 | break;
|
---|
3014 | }
|
---|
3015 | tmpParam = tmpParam->next;
|
---|
3016 | } while (tmpParam != NULL);
|
---|
3017 | }
|
---|
3018 | /*
|
---|
3019 | * Push the xsl:param.
|
---|
3020 | */
|
---|
3021 | if (tmpParam == NULL) {
|
---|
3022 | /*
|
---|
3023 | * Note that we must assume that the added parameter
|
---|
3024 | * has a @depth of 0.
|
---|
3025 | */
|
---|
3026 | xsltParseStylesheetParam(ctxt, cur);
|
---|
3027 | }
|
---|
3028 | cur = cur->next;
|
---|
3029 | } while (cur != NULL);
|
---|
3030 | /*
|
---|
3031 | * Process the sequence constructor.
|
---|
3032 | */
|
---|
3033 | xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
|
---|
3034 |
|
---|
3035 | /*
|
---|
3036 | * Remove remaining xsl:param and xsl:with-param items from
|
---|
3037 | * the stack. Don't free xsl:with-param items.
|
---|
3038 | */
|
---|
3039 | if (ctxt->varsNr > ctxt->varsBase)
|
---|
3040 | xsltTemplateParamsCleanup(ctxt);
|
---|
3041 | ctxt->varsBase = oldVarsBase;
|
---|
3042 |
|
---|
3043 | /*
|
---|
3044 | * Clean up remaining local tree fragments.
|
---|
3045 | * This also frees fragments which are the result of
|
---|
3046 | * extension instructions. Should normally not be hit; but
|
---|
3047 | * just for the case xsltExtensionInstructionResultFinalize()
|
---|
3048 | * was not called by the extension author.
|
---|
3049 | */
|
---|
3050 | if (oldLocalFragmentTop != ctxt->localRVT) {
|
---|
3051 | xmlDocPtr curdoc = ctxt->localRVT, tmp;
|
---|
3052 |
|
---|
3053 | do {
|
---|
3054 | tmp = curdoc;
|
---|
3055 | curdoc = (xmlDocPtr) curdoc->next;
|
---|
3056 | /* Need to housekeep localRVTBase */
|
---|
3057 | if (tmp == ctxt->localRVTBase)
|
---|
3058 | ctxt->localRVTBase = curdoc;
|
---|
3059 | if (tmp->prev)
|
---|
3060 | tmp->prev->next = (xmlNodePtr) curdoc;
|
---|
3061 | if (curdoc)
|
---|
3062 | curdoc->prev = tmp->prev;
|
---|
3063 | xsltReleaseRVT(ctxt, tmp);
|
---|
3064 | } while (curdoc != oldLocalFragmentTop);
|
---|
3065 | }
|
---|
3066 | ctxt->localRVT = oldLocalFragmentTop;
|
---|
3067 |
|
---|
3068 | /*
|
---|
3069 | * Release user-created fragments stored in the scope
|
---|
3070 | * of xsl:template. Note that this mechanism is deprecated:
|
---|
3071 | * user code should now use xsltRegisterLocalRVT() instead
|
---|
3072 | * of the obsolete xsltRegisterTmpRVT().
|
---|
3073 | */
|
---|
3074 | if (ctxt->tmpRVT) {
|
---|
3075 | xmlDocPtr curdoc = ctxt->tmpRVT, tmp;
|
---|
3076 |
|
---|
3077 | while (curdoc != NULL) {
|
---|
3078 | tmp = curdoc;
|
---|
3079 | curdoc = (xmlDocPtr) curdoc->next;
|
---|
3080 | xsltReleaseRVT(ctxt, tmp);
|
---|
3081 | }
|
---|
3082 | }
|
---|
3083 | ctxt->tmpRVT = oldUserFragmentTop;
|
---|
3084 |
|
---|
3085 | /*
|
---|
3086 | * Pop the xsl:template declaration from the stack.
|
---|
3087 | */
|
---|
3088 | templPop(ctxt);
|
---|
3089 | if (ctxt->profile) {
|
---|
3090 | long spent, child, total, end;
|
---|
3091 |
|
---|
3092 | end = xsltTimestamp();
|
---|
3093 | child = profPop(ctxt);
|
---|
3094 | total = end - start;
|
---|
3095 | spent = total - child;
|
---|
3096 | if (spent <= 0) {
|
---|
3097 | /*
|
---|
3098 | * Not possible unless the original calibration failed
|
---|
3099 | * we can try to correct it on the fly.
|
---|
3100 | */
|
---|
3101 | xsltCalibrateAdjust(spent);
|
---|
3102 | spent = 0;
|
---|
3103 | }
|
---|
3104 |
|
---|
3105 | templ->time += spent;
|
---|
3106 | if (ctxt->profNr > 0)
|
---|
3107 | ctxt->profTab[ctxt->profNr - 1] += total;
|
---|
3108 | }
|
---|
3109 |
|
---|
3110 | #ifdef WITH_DEBUGGER
|
---|
3111 | if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && (addCallResult)) {
|
---|
3112 | xslDropCall();
|
---|
3113 | }
|
---|
3114 | #endif
|
---|
3115 | }
|
---|
3116 |
|
---|
3117 |
|
---|
3118 | /**
|
---|
3119 | * xsltApplyOneTemplate:
|
---|
3120 | * @ctxt: a XSLT process context
|
---|
3121 | * @contextNode: the node in the source tree.
|
---|
3122 | * @list: the nodes of a sequence constructor
|
---|
3123 | * @templ: not used
|
---|
3124 | * @params: a set of parameters (xsl:param) or NULL
|
---|
3125 | *
|
---|
3126 | * Processes a sequence constructor on the current node in the source tree.
|
---|
3127 | *
|
---|
3128 | * @params are the already computed variable stack items; this function
|
---|
3129 | * pushes them on the variable stack, and pops them before exiting; it's
|
---|
3130 | * left to the caller to free or reuse @params afterwards. The initial
|
---|
3131 | * states of the variable stack will always be restored before this
|
---|
3132 | * function exits.
|
---|
3133 | * NOTE that this does *not* initiate a new distinct variable scope; i.e.
|
---|
3134 | * variables already on the stack are visible to the process. The caller's
|
---|
3135 | * side needs to start a new variable scope if needed (e.g. in exsl:function).
|
---|
3136 | *
|
---|
3137 | * @templ is obsolete and not used anymore (e.g. <exslt:function> does not
|
---|
3138 | * provide a @templ); a non-NULL @templ might raise an error in the future.
|
---|
3139 | *
|
---|
3140 | * BIG NOTE: This function is not intended to process the content of an
|
---|
3141 | * xsl:template; it does not expect xsl:param instructions in @list and
|
---|
3142 | * will report errors if found.
|
---|
3143 | *
|
---|
3144 | * Called by:
|
---|
3145 | * - xsltEvalVariable() (variables.c)
|
---|
3146 | * - exsltFuncFunctionFunction() (libexsl/functions.c)
|
---|
3147 | */
|
---|
3148 | void
|
---|
3149 | xsltApplyOneTemplate(xsltTransformContextPtr ctxt,
|
---|
3150 | xmlNodePtr contextNode,
|
---|
3151 | xmlNodePtr list,
|
---|
3152 | xsltTemplatePtr templ ATTRIBUTE_UNUSED,
|
---|
3153 | xsltStackElemPtr params)
|
---|
3154 | {
|
---|
3155 | if ((ctxt == NULL) || (list == NULL))
|
---|
3156 | return;
|
---|
3157 | CHECK_STOPPED;
|
---|
3158 |
|
---|
3159 | if (params) {
|
---|
3160 | /*
|
---|
3161 | * This code should be obsolete - was previously used
|
---|
3162 | * by libexslt/functions.c, but due to bug 381319 the
|
---|
3163 | * logic there was changed.
|
---|
3164 | */
|
---|
3165 | int oldVarsNr = ctxt->varsNr;
|
---|
3166 |
|
---|
3167 | /*
|
---|
3168 | * Push the given xsl:param(s) onto the variable stack.
|
---|
3169 | */
|
---|
3170 | while (params != NULL) {
|
---|
3171 | xsltLocalVariablePush(ctxt, params, -1);
|
---|
3172 | params = params->next;
|
---|
3173 | }
|
---|
3174 | xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
|
---|
3175 | /*
|
---|
3176 | * Pop the given xsl:param(s) from the stack but don't free them.
|
---|
3177 | */
|
---|
3178 | xsltLocalVariablePop(ctxt, oldVarsNr, -2);
|
---|
3179 | } else
|
---|
3180 | xsltApplySequenceConstructor(ctxt, contextNode, list, templ);
|
---|
3181 | }
|
---|
3182 |
|
---|
3183 | /************************************************************************
|
---|
3184 | * *
|
---|
3185 | * XSLT-1.1 extensions *
|
---|
3186 | * *
|
---|
3187 | ************************************************************************/
|
---|
3188 |
|
---|
3189 | /**
|
---|
3190 | * xsltDocumentElem:
|
---|
3191 | * @ctxt: an XSLT processing context
|
---|
3192 | * @node: The current node
|
---|
3193 | * @inst: the instruction in the stylesheet
|
---|
3194 | * @castedComp: precomputed information
|
---|
3195 | *
|
---|
3196 | * Process an EXSLT/XSLT-1.1 document element
|
---|
3197 | */
|
---|
3198 | void
|
---|
3199 | xsltDocumentElem(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
3200 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
3201 | {
|
---|
3202 | #ifdef XSLT_REFACTORED
|
---|
3203 | xsltStyleItemDocumentPtr comp = (xsltStyleItemDocumentPtr) castedComp;
|
---|
3204 | #else
|
---|
3205 | xsltStylePreCompPtr comp = castedComp;
|
---|
3206 | #endif
|
---|
3207 | xsltStylesheetPtr style = NULL;
|
---|
3208 | int ret;
|
---|
3209 | xmlChar *filename = NULL, *prop, *elements;
|
---|
3210 | xmlChar *element, *end;
|
---|
3211 | xmlDocPtr res = NULL;
|
---|
3212 | xmlDocPtr oldOutput;
|
---|
3213 | xmlNodePtr oldInsert, root;
|
---|
3214 | const char *oldOutputFile;
|
---|
3215 | xsltOutputType oldType;
|
---|
3216 | xmlChar *URL = NULL;
|
---|
3217 | const xmlChar *method;
|
---|
3218 | const xmlChar *doctypePublic;
|
---|
3219 | const xmlChar *doctypeSystem;
|
---|
3220 | const xmlChar *version;
|
---|
3221 |
|
---|
3222 | if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
|
---|
3223 | return;
|
---|
3224 |
|
---|
3225 | if (comp->filename == NULL) {
|
---|
3226 |
|
---|
3227 | if (xmlStrEqual(inst->name, (const xmlChar *) "output")) {
|
---|
3228 | /*
|
---|
3229 | * The element "output" is in the namespace XSLT_SAXON_NAMESPACE
|
---|
3230 | * (http://icl.com/saxon)
|
---|
3231 | * The @file is in no namespace.
|
---|
3232 | */
|
---|
3233 | #ifdef WITH_XSLT_DEBUG_EXTRA
|
---|
3234 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3235 | "Found saxon:output extension\n");
|
---|
3236 | #endif
|
---|
3237 | URL = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3238 | (const xmlChar *) "file",
|
---|
3239 | XSLT_SAXON_NAMESPACE);
|
---|
3240 |
|
---|
3241 | if (URL == NULL)
|
---|
3242 | URL = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3243 | (const xmlChar *) "href",
|
---|
3244 | XSLT_SAXON_NAMESPACE);
|
---|
3245 | } else if (xmlStrEqual(inst->name, (const xmlChar *) "write")) {
|
---|
3246 | #ifdef WITH_XSLT_DEBUG_EXTRA
|
---|
3247 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3248 | "Found xalan:write extension\n");
|
---|
3249 | #endif
|
---|
3250 | URL = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3251 | (const xmlChar *)
|
---|
3252 | "select",
|
---|
3253 | XSLT_XALAN_NAMESPACE);
|
---|
3254 | if (URL != NULL) {
|
---|
3255 | xmlXPathCompExprPtr cmp;
|
---|
3256 | xmlChar *val;
|
---|
3257 |
|
---|
3258 | /*
|
---|
3259 | * Trying to handle bug #59212
|
---|
3260 | * The value of the "select" attribute is an
|
---|
3261 | * XPath expression.
|
---|
3262 | * (see http://xml.apache.org/xalan-j/extensionslib.html#redirect)
|
---|
3263 | */
|
---|
3264 | cmp = xmlXPathCompile(URL);
|
---|
3265 | val = xsltEvalXPathString(ctxt, cmp);
|
---|
3266 | xmlXPathFreeCompExpr(cmp);
|
---|
3267 | xmlFree(URL);
|
---|
3268 | URL = val;
|
---|
3269 | }
|
---|
3270 | if (URL == NULL)
|
---|
3271 | URL = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3272 | (const xmlChar *)
|
---|
3273 | "file",
|
---|
3274 | XSLT_XALAN_NAMESPACE);
|
---|
3275 | if (URL == NULL)
|
---|
3276 | URL = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3277 | (const xmlChar *)
|
---|
3278 | "href",
|
---|
3279 | XSLT_XALAN_NAMESPACE);
|
---|
3280 | } else if (xmlStrEqual(inst->name, (const xmlChar *) "document")) {
|
---|
3281 | URL = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3282 | (const xmlChar *) "href",
|
---|
3283 | NULL);
|
---|
3284 | }
|
---|
3285 |
|
---|
3286 | } else {
|
---|
3287 | URL = xmlStrdup(comp->filename);
|
---|
3288 | }
|
---|
3289 |
|
---|
3290 | if (URL == NULL) {
|
---|
3291 | xsltTransformError(ctxt, NULL, inst,
|
---|
3292 | "xsltDocumentElem: href/URI-Reference not found\n");
|
---|
3293 | return;
|
---|
3294 | }
|
---|
3295 |
|
---|
3296 | /*
|
---|
3297 | * If the computation failed, it's likely that the URL wasn't escaped
|
---|
3298 | */
|
---|
3299 | filename = xmlBuildURI(URL, (const xmlChar *) ctxt->outputFile);
|
---|
3300 | if (filename == NULL) {
|
---|
3301 | xmlChar *escURL;
|
---|
3302 |
|
---|
3303 | escURL=xmlURIEscapeStr(URL, BAD_CAST ":/.?,");
|
---|
3304 | if (escURL != NULL) {
|
---|
3305 | filename = xmlBuildURI(escURL, (const xmlChar *) ctxt->outputFile);
|
---|
3306 | xmlFree(escURL);
|
---|
3307 | }
|
---|
3308 | }
|
---|
3309 |
|
---|
3310 | if (filename == NULL) {
|
---|
3311 | xsltTransformError(ctxt, NULL, inst,
|
---|
3312 | "xsltDocumentElem: URL computation failed for %s\n",
|
---|
3313 | URL);
|
---|
3314 | xmlFree(URL);
|
---|
3315 | return;
|
---|
3316 | }
|
---|
3317 |
|
---|
3318 | /*
|
---|
3319 | * Security checking: can we write to this resource
|
---|
3320 | */
|
---|
3321 | if (ctxt->sec != NULL) {
|
---|
3322 | ret = xsltCheckWrite(ctxt->sec, ctxt, filename);
|
---|
3323 | if (ret == 0) {
|
---|
3324 | xsltTransformError(ctxt, NULL, inst,
|
---|
3325 | "xsltDocumentElem: write rights for %s denied\n",
|
---|
3326 | filename);
|
---|
3327 | xmlFree(URL);
|
---|
3328 | xmlFree(filename);
|
---|
3329 | return;
|
---|
3330 | }
|
---|
3331 | }
|
---|
3332 |
|
---|
3333 | oldOutputFile = ctxt->outputFile;
|
---|
3334 | oldOutput = ctxt->output;
|
---|
3335 | oldInsert = ctxt->insert;
|
---|
3336 | oldType = ctxt->type;
|
---|
3337 | ctxt->outputFile = (const char *) filename;
|
---|
3338 |
|
---|
3339 | style = xsltNewStylesheet();
|
---|
3340 | if (style == NULL) {
|
---|
3341 | xsltTransformError(ctxt, NULL, inst,
|
---|
3342 | "xsltDocumentElem: out of memory\n");
|
---|
3343 | goto error;
|
---|
3344 | }
|
---|
3345 |
|
---|
3346 | /*
|
---|
3347 | * Version described in 1.1 draft allows full parameterization
|
---|
3348 | * of the output.
|
---|
3349 | */
|
---|
3350 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3351 | (const xmlChar *) "version",
|
---|
3352 | NULL);
|
---|
3353 | if (prop != NULL) {
|
---|
3354 | if (style->version != NULL)
|
---|
3355 | xmlFree(style->version);
|
---|
3356 | style->version = prop;
|
---|
3357 | }
|
---|
3358 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3359 | (const xmlChar *) "encoding",
|
---|
3360 | NULL);
|
---|
3361 | if (prop != NULL) {
|
---|
3362 | if (style->encoding != NULL)
|
---|
3363 | xmlFree(style->encoding);
|
---|
3364 | style->encoding = prop;
|
---|
3365 | }
|
---|
3366 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3367 | (const xmlChar *) "method",
|
---|
3368 | NULL);
|
---|
3369 | if (prop != NULL) {
|
---|
3370 | const xmlChar *URI;
|
---|
3371 |
|
---|
3372 | if (style->method != NULL)
|
---|
3373 | xmlFree(style->method);
|
---|
3374 | style->method = NULL;
|
---|
3375 | if (style->methodURI != NULL)
|
---|
3376 | xmlFree(style->methodURI);
|
---|
3377 | style->methodURI = NULL;
|
---|
3378 |
|
---|
3379 | URI = xsltGetQNameURI(inst, &prop);
|
---|
3380 | if (prop == NULL) {
|
---|
3381 | if (style != NULL) style->errors++;
|
---|
3382 | } else if (URI == NULL) {
|
---|
3383 | if ((xmlStrEqual(prop, (const xmlChar *) "xml")) ||
|
---|
3384 | (xmlStrEqual(prop, (const xmlChar *) "html")) ||
|
---|
3385 | (xmlStrEqual(prop, (const xmlChar *) "text"))) {
|
---|
3386 | style->method = prop;
|
---|
3387 | } else {
|
---|
3388 | xsltTransformError(ctxt, NULL, inst,
|
---|
3389 | "invalid value for method: %s\n", prop);
|
---|
3390 | if (style != NULL) style->warnings++;
|
---|
3391 | }
|
---|
3392 | } else {
|
---|
3393 | style->method = prop;
|
---|
3394 | style->methodURI = xmlStrdup(URI);
|
---|
3395 | }
|
---|
3396 | }
|
---|
3397 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3398 | (const xmlChar *)
|
---|
3399 | "doctype-system", NULL);
|
---|
3400 | if (prop != NULL) {
|
---|
3401 | if (style->doctypeSystem != NULL)
|
---|
3402 | xmlFree(style->doctypeSystem);
|
---|
3403 | style->doctypeSystem = prop;
|
---|
3404 | }
|
---|
3405 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3406 | (const xmlChar *)
|
---|
3407 | "doctype-public", NULL);
|
---|
3408 | if (prop != NULL) {
|
---|
3409 | if (style->doctypePublic != NULL)
|
---|
3410 | xmlFree(style->doctypePublic);
|
---|
3411 | style->doctypePublic = prop;
|
---|
3412 | }
|
---|
3413 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3414 | (const xmlChar *) "standalone",
|
---|
3415 | NULL);
|
---|
3416 | if (prop != NULL) {
|
---|
3417 | if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
|
---|
3418 | style->standalone = 1;
|
---|
3419 | } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
|
---|
3420 | style->standalone = 0;
|
---|
3421 | } else {
|
---|
3422 | xsltTransformError(ctxt, NULL, inst,
|
---|
3423 | "invalid value for standalone: %s\n",
|
---|
3424 | prop);
|
---|
3425 | if (style != NULL) style->warnings++;
|
---|
3426 | }
|
---|
3427 | xmlFree(prop);
|
---|
3428 | }
|
---|
3429 |
|
---|
3430 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3431 | (const xmlChar *) "indent",
|
---|
3432 | NULL);
|
---|
3433 | if (prop != NULL) {
|
---|
3434 | if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
|
---|
3435 | style->indent = 1;
|
---|
3436 | } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
|
---|
3437 | style->indent = 0;
|
---|
3438 | } else {
|
---|
3439 | xsltTransformError(ctxt, NULL, inst,
|
---|
3440 | "invalid value for indent: %s\n", prop);
|
---|
3441 | if (style != NULL) style->warnings++;
|
---|
3442 | }
|
---|
3443 | xmlFree(prop);
|
---|
3444 | }
|
---|
3445 |
|
---|
3446 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3447 | (const xmlChar *)
|
---|
3448 | "omit-xml-declaration",
|
---|
3449 | NULL);
|
---|
3450 | if (prop != NULL) {
|
---|
3451 | if (xmlStrEqual(prop, (const xmlChar *) "yes")) {
|
---|
3452 | style->omitXmlDeclaration = 1;
|
---|
3453 | } else if (xmlStrEqual(prop, (const xmlChar *) "no")) {
|
---|
3454 | style->omitXmlDeclaration = 0;
|
---|
3455 | } else {
|
---|
3456 | xsltTransformError(ctxt, NULL, inst,
|
---|
3457 | "invalid value for omit-xml-declaration: %s\n",
|
---|
3458 | prop);
|
---|
3459 | if (style != NULL) style->warnings++;
|
---|
3460 | }
|
---|
3461 | xmlFree(prop);
|
---|
3462 | }
|
---|
3463 |
|
---|
3464 | elements = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3465 | (const xmlChar *)
|
---|
3466 | "cdata-section-elements",
|
---|
3467 | NULL);
|
---|
3468 | if (elements != NULL) {
|
---|
3469 | if (style->stripSpaces == NULL)
|
---|
3470 | style->stripSpaces = xmlHashCreate(10);
|
---|
3471 | if (style->stripSpaces == NULL)
|
---|
3472 | return;
|
---|
3473 |
|
---|
3474 | element = elements;
|
---|
3475 | while (*element != 0) {
|
---|
3476 | while (IS_BLANK_CH(*element))
|
---|
3477 | element++;
|
---|
3478 | if (*element == 0)
|
---|
3479 | break;
|
---|
3480 | end = element;
|
---|
3481 | while ((*end != 0) && (!IS_BLANK_CH(*end)))
|
---|
3482 | end++;
|
---|
3483 | element = xmlStrndup(element, end - element);
|
---|
3484 | if (element) {
|
---|
3485 | const xmlChar *URI;
|
---|
3486 |
|
---|
3487 | #ifdef WITH_XSLT_DEBUG_PARSING
|
---|
3488 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3489 | "add cdata section output element %s\n",
|
---|
3490 | element);
|
---|
3491 | #endif
|
---|
3492 | URI = xsltGetQNameURI(inst, &element);
|
---|
3493 |
|
---|
3494 | xmlHashAddEntry2(style->stripSpaces, element, URI,
|
---|
3495 | (xmlChar *) "cdata");
|
---|
3496 | xmlFree(element);
|
---|
3497 | }
|
---|
3498 | element = end;
|
---|
3499 | }
|
---|
3500 | xmlFree(elements);
|
---|
3501 | }
|
---|
3502 |
|
---|
3503 | /*
|
---|
3504 | * Create a new document tree and process the element template
|
---|
3505 | */
|
---|
3506 | XSLT_GET_IMPORT_PTR(method, style, method)
|
---|
3507 | XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
|
---|
3508 | XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
|
---|
3509 | XSLT_GET_IMPORT_PTR(version, style, version)
|
---|
3510 |
|
---|
3511 | if ((method != NULL) &&
|
---|
3512 | (!xmlStrEqual(method, (const xmlChar *) "xml"))) {
|
---|
3513 | if (xmlStrEqual(method, (const xmlChar *) "html")) {
|
---|
3514 | ctxt->type = XSLT_OUTPUT_HTML;
|
---|
3515 | if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
|
---|
3516 | res = htmlNewDoc(doctypeSystem, doctypePublic);
|
---|
3517 | else {
|
---|
3518 | if (version != NULL) {
|
---|
3519 | #ifdef XSLT_GENERATE_HTML_DOCTYPE
|
---|
3520 | xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
|
---|
3521 | #endif
|
---|
3522 | }
|
---|
3523 | res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
|
---|
3524 | }
|
---|
3525 | if (res == NULL)
|
---|
3526 | goto error;
|
---|
3527 | res->dict = ctxt->dict;
|
---|
3528 | xmlDictReference(res->dict);
|
---|
3529 | } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
|
---|
3530 | xsltTransformError(ctxt, NULL, inst,
|
---|
3531 | "xsltDocumentElem: unsupported method xhtml\n",
|
---|
3532 | style->method);
|
---|
3533 | ctxt->type = XSLT_OUTPUT_HTML;
|
---|
3534 | res = htmlNewDocNoDtD(doctypeSystem, doctypePublic);
|
---|
3535 | if (res == NULL)
|
---|
3536 | goto error;
|
---|
3537 | res->dict = ctxt->dict;
|
---|
3538 | xmlDictReference(res->dict);
|
---|
3539 | } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
|
---|
3540 | ctxt->type = XSLT_OUTPUT_TEXT;
|
---|
3541 | res = xmlNewDoc(style->version);
|
---|
3542 | if (res == NULL)
|
---|
3543 | goto error;
|
---|
3544 | res->dict = ctxt->dict;
|
---|
3545 | xmlDictReference(res->dict);
|
---|
3546 | #ifdef WITH_XSLT_DEBUG
|
---|
3547 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3548 | "reusing transformation dict for output\n");
|
---|
3549 | #endif
|
---|
3550 | } else {
|
---|
3551 | xsltTransformError(ctxt, NULL, inst,
|
---|
3552 | "xsltDocumentElem: unsupported method %s\n",
|
---|
3553 | style->method);
|
---|
3554 | goto error;
|
---|
3555 | }
|
---|
3556 | } else {
|
---|
3557 | ctxt->type = XSLT_OUTPUT_XML;
|
---|
3558 | res = xmlNewDoc(style->version);
|
---|
3559 | if (res == NULL)
|
---|
3560 | goto error;
|
---|
3561 | res->dict = ctxt->dict;
|
---|
3562 | xmlDictReference(res->dict);
|
---|
3563 | #ifdef WITH_XSLT_DEBUG
|
---|
3564 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3565 | "reusing transformation dict for output\n");
|
---|
3566 | #endif
|
---|
3567 | }
|
---|
3568 | res->charset = XML_CHAR_ENCODING_UTF8;
|
---|
3569 | if (style->encoding != NULL)
|
---|
3570 | res->encoding = xmlStrdup(style->encoding);
|
---|
3571 | ctxt->output = res;
|
---|
3572 | ctxt->insert = (xmlNodePtr) res;
|
---|
3573 | xsltApplySequenceConstructor(ctxt, node, inst->children, NULL);
|
---|
3574 |
|
---|
3575 | /*
|
---|
3576 | * Do some post processing work depending on the generated output
|
---|
3577 | */
|
---|
3578 | root = xmlDocGetRootElement(res);
|
---|
3579 | if (root != NULL) {
|
---|
3580 | const xmlChar *doctype = NULL;
|
---|
3581 |
|
---|
3582 | if ((root->ns != NULL) && (root->ns->prefix != NULL))
|
---|
3583 | doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
|
---|
3584 | if (doctype == NULL)
|
---|
3585 | doctype = root->name;
|
---|
3586 |
|
---|
3587 | /*
|
---|
3588 | * Apply the default selection of the method
|
---|
3589 | */
|
---|
3590 | if ((method == NULL) &&
|
---|
3591 | (root->ns == NULL) &&
|
---|
3592 | (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
|
---|
3593 | xmlNodePtr tmp;
|
---|
3594 |
|
---|
3595 | tmp = res->children;
|
---|
3596 | while ((tmp != NULL) && (tmp != root)) {
|
---|
3597 | if (tmp->type == XML_ELEMENT_NODE)
|
---|
3598 | break;
|
---|
3599 | if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
|
---|
3600 | break;
|
---|
3601 | tmp = tmp->next;
|
---|
3602 | }
|
---|
3603 | if (tmp == root) {
|
---|
3604 | ctxt->type = XSLT_OUTPUT_HTML;
|
---|
3605 | res->type = XML_HTML_DOCUMENT_NODE;
|
---|
3606 | if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
|
---|
3607 | res->intSubset = xmlCreateIntSubset(res, doctype,
|
---|
3608 | doctypePublic,
|
---|
3609 | doctypeSystem);
|
---|
3610 | #ifdef XSLT_GENERATE_HTML_DOCTYPE
|
---|
3611 | } else if (version != NULL) {
|
---|
3612 | xsltGetHTMLIDs(version, &doctypePublic,
|
---|
3613 | &doctypeSystem);
|
---|
3614 | if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
|
---|
3615 | res->intSubset =
|
---|
3616 | xmlCreateIntSubset(res, doctype,
|
---|
3617 | doctypePublic,
|
---|
3618 | doctypeSystem);
|
---|
3619 | #endif
|
---|
3620 | }
|
---|
3621 | }
|
---|
3622 |
|
---|
3623 | }
|
---|
3624 | if (ctxt->type == XSLT_OUTPUT_XML) {
|
---|
3625 | XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
|
---|
3626 | XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
|
---|
3627 | if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
|
---|
3628 | res->intSubset = xmlCreateIntSubset(res, doctype,
|
---|
3629 | doctypePublic,
|
---|
3630 | doctypeSystem);
|
---|
3631 | }
|
---|
3632 | }
|
---|
3633 |
|
---|
3634 | /*
|
---|
3635 | * Save the result
|
---|
3636 | */
|
---|
3637 | ret = xsltSaveResultToFilename((const char *) filename,
|
---|
3638 | res, style, 0);
|
---|
3639 | if (ret < 0) {
|
---|
3640 | xsltTransformError(ctxt, NULL, inst,
|
---|
3641 | "xsltDocumentElem: unable to save to %s\n",
|
---|
3642 | filename);
|
---|
3643 | ctxt->state = XSLT_STATE_ERROR;
|
---|
3644 | #ifdef WITH_XSLT_DEBUG_EXTRA
|
---|
3645 | } else {
|
---|
3646 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3647 | "Wrote %d bytes to %s\n", ret, filename);
|
---|
3648 | #endif
|
---|
3649 | }
|
---|
3650 |
|
---|
3651 | error:
|
---|
3652 | ctxt->output = oldOutput;
|
---|
3653 | ctxt->insert = oldInsert;
|
---|
3654 | ctxt->type = oldType;
|
---|
3655 | ctxt->outputFile = oldOutputFile;
|
---|
3656 | if (URL != NULL)
|
---|
3657 | xmlFree(URL);
|
---|
3658 | if (filename != NULL)
|
---|
3659 | xmlFree(filename);
|
---|
3660 | if (style != NULL)
|
---|
3661 | xsltFreeStylesheet(style);
|
---|
3662 | if (res != NULL)
|
---|
3663 | xmlFreeDoc(res);
|
---|
3664 | }
|
---|
3665 |
|
---|
3666 | /************************************************************************
|
---|
3667 | * *
|
---|
3668 | * Most of the XSLT-1.0 transformations *
|
---|
3669 | * *
|
---|
3670 | ************************************************************************/
|
---|
3671 |
|
---|
3672 | /**
|
---|
3673 | * xsltSort:
|
---|
3674 | * @ctxt: a XSLT process context
|
---|
3675 | * @node: the node in the source tree.
|
---|
3676 | * @inst: the xslt sort node
|
---|
3677 | * @comp: precomputed information
|
---|
3678 | *
|
---|
3679 | * function attached to xsl:sort nodes, but this should not be
|
---|
3680 | * called directly
|
---|
3681 | */
|
---|
3682 | void
|
---|
3683 | xsltSort(xsltTransformContextPtr ctxt,
|
---|
3684 | xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
|
---|
3685 | xsltStylePreCompPtr comp) {
|
---|
3686 | if (comp == NULL) {
|
---|
3687 | xsltTransformError(ctxt, NULL, inst,
|
---|
3688 | "xsl:sort : compilation failed\n");
|
---|
3689 | return;
|
---|
3690 | }
|
---|
3691 | xsltTransformError(ctxt, NULL, inst,
|
---|
3692 | "xsl:sort : improper use this should not be reached\n");
|
---|
3693 | }
|
---|
3694 |
|
---|
3695 | /**
|
---|
3696 | * xsltCopy:
|
---|
3697 | * @ctxt: an XSLT process context
|
---|
3698 | * @node: the node in the source tree
|
---|
3699 | * @inst: the element node of the XSLT-copy instruction
|
---|
3700 | * @castedComp: computed information of the XSLT-copy instruction
|
---|
3701 | *
|
---|
3702 | * Execute the XSLT-copy instruction on the source node.
|
---|
3703 | */
|
---|
3704 | void
|
---|
3705 | xsltCopy(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
3706 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
3707 | {
|
---|
3708 | #ifdef XSLT_REFACTORED
|
---|
3709 | xsltStyleItemCopyPtr comp = (xsltStyleItemCopyPtr) castedComp;
|
---|
3710 | #else
|
---|
3711 | xsltStylePreCompPtr comp = castedComp;
|
---|
3712 | #endif
|
---|
3713 | xmlNodePtr copy, oldInsert;
|
---|
3714 |
|
---|
3715 | oldInsert = ctxt->insert;
|
---|
3716 | if (ctxt->insert != NULL) {
|
---|
3717 | switch (node->type) {
|
---|
3718 | case XML_TEXT_NODE:
|
---|
3719 | case XML_CDATA_SECTION_NODE:
|
---|
3720 | /*
|
---|
3721 | * This text comes from the stylesheet
|
---|
3722 | * For stylesheets, the set of whitespace-preserving
|
---|
3723 | * element names consists of just xsl:text.
|
---|
3724 | */
|
---|
3725 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
3726 | if (node->type == XML_CDATA_SECTION_NODE) {
|
---|
3727 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3728 | "xsltCopy: CDATA text %s\n", node->content));
|
---|
3729 | } else {
|
---|
3730 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3731 | "xsltCopy: text %s\n", node->content));
|
---|
3732 | }
|
---|
3733 | #endif
|
---|
3734 | xsltCopyText(ctxt, ctxt->insert, node, 0);
|
---|
3735 | break;
|
---|
3736 | case XML_DOCUMENT_NODE:
|
---|
3737 | case XML_HTML_DOCUMENT_NODE:
|
---|
3738 | break;
|
---|
3739 | case XML_ELEMENT_NODE:
|
---|
3740 | /*
|
---|
3741 | * REVISIT NOTE: The "fake" is a doc-node, not an element node.
|
---|
3742 | * REMOVED:
|
---|
3743 | * if (xmlStrEqual(node->name, BAD_CAST " fake node libxslt"))
|
---|
3744 | * return;
|
---|
3745 | */
|
---|
3746 |
|
---|
3747 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
3748 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3749 | "xsltCopy: node %s\n", node->name));
|
---|
3750 | #endif
|
---|
3751 | copy = xsltShallowCopyElem(ctxt, node, ctxt->insert, 0);
|
---|
3752 | ctxt->insert = copy;
|
---|
3753 | if (comp->use != NULL) {
|
---|
3754 | xsltApplyAttributeSet(ctxt, node, inst, comp->use);
|
---|
3755 | }
|
---|
3756 | break;
|
---|
3757 | case XML_ATTRIBUTE_NODE: {
|
---|
3758 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
3759 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3760 | "xsltCopy: attribute %s\n", node->name));
|
---|
3761 | #endif
|
---|
3762 | /*
|
---|
3763 | * REVISIT: We could also raise an error if the parent is not
|
---|
3764 | * an element node.
|
---|
3765 | * OPTIMIZE TODO: Can we set the value/children of the
|
---|
3766 | * attribute without an intermediate copy of the string value?
|
---|
3767 | */
|
---|
3768 | xsltShallowCopyAttr(ctxt, inst, ctxt->insert, (xmlAttrPtr) node);
|
---|
3769 | break;
|
---|
3770 | }
|
---|
3771 | case XML_PI_NODE:
|
---|
3772 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
3773 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3774 | "xsltCopy: PI %s\n", node->name));
|
---|
3775 | #endif
|
---|
3776 | copy = xmlNewDocPI(ctxt->insert->doc, node->name,
|
---|
3777 | node->content);
|
---|
3778 | xmlAddChild(ctxt->insert, copy);
|
---|
3779 | break;
|
---|
3780 | case XML_COMMENT_NODE:
|
---|
3781 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
3782 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3783 | "xsltCopy: comment\n"));
|
---|
3784 | #endif
|
---|
3785 | copy = xmlNewComment(node->content);
|
---|
3786 | xmlAddChild(ctxt->insert, copy);
|
---|
3787 | break;
|
---|
3788 | case XML_NAMESPACE_DECL:
|
---|
3789 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
3790 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY,xsltGenericDebug(xsltGenericDebugContext,
|
---|
3791 | "xsltCopy: namespace declaration\n"));
|
---|
3792 | #endif
|
---|
3793 | xsltShallowCopyNsNode(ctxt, inst, ctxt->insert, (xmlNsPtr)node);
|
---|
3794 | break;
|
---|
3795 | default:
|
---|
3796 | break;
|
---|
3797 |
|
---|
3798 | }
|
---|
3799 | }
|
---|
3800 |
|
---|
3801 | switch (node->type) {
|
---|
3802 | case XML_DOCUMENT_NODE:
|
---|
3803 | case XML_HTML_DOCUMENT_NODE:
|
---|
3804 | case XML_ELEMENT_NODE:
|
---|
3805 | xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
|
---|
3806 | NULL);
|
---|
3807 | break;
|
---|
3808 | default:
|
---|
3809 | break;
|
---|
3810 | }
|
---|
3811 | ctxt->insert = oldInsert;
|
---|
3812 | }
|
---|
3813 |
|
---|
3814 | /**
|
---|
3815 | * xsltText:
|
---|
3816 | * @ctxt: a XSLT process context
|
---|
3817 | * @node: the node in the source tree.
|
---|
3818 | * @inst: the xslt text node
|
---|
3819 | * @comp: precomputed information
|
---|
3820 | *
|
---|
3821 | * Process the xslt text node on the source node
|
---|
3822 | */
|
---|
3823 | void
|
---|
3824 | xsltText(xsltTransformContextPtr ctxt, xmlNodePtr node ATTRIBUTE_UNUSED,
|
---|
3825 | xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
|
---|
3826 | if ((inst->children != NULL) && (comp != NULL)) {
|
---|
3827 | xmlNodePtr text = inst->children;
|
---|
3828 | xmlNodePtr copy;
|
---|
3829 |
|
---|
3830 | while (text != NULL) {
|
---|
3831 | if ((text->type != XML_TEXT_NODE) &&
|
---|
3832 | (text->type != XML_CDATA_SECTION_NODE)) {
|
---|
3833 | xsltTransformError(ctxt, NULL, inst,
|
---|
3834 | "xsl:text content problem\n");
|
---|
3835 | break;
|
---|
3836 | }
|
---|
3837 | copy = xmlNewDocText(ctxt->output, text->content);
|
---|
3838 | if (text->type != XML_CDATA_SECTION_NODE) {
|
---|
3839 | #ifdef WITH_XSLT_DEBUG_PARSING
|
---|
3840 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
3841 | "Disable escaping: %s\n", text->content);
|
---|
3842 | #endif
|
---|
3843 | copy->name = xmlStringTextNoenc;
|
---|
3844 | }
|
---|
3845 | xmlAddChild(ctxt->insert, copy);
|
---|
3846 | text = text->next;
|
---|
3847 | }
|
---|
3848 | }
|
---|
3849 | }
|
---|
3850 |
|
---|
3851 | /**
|
---|
3852 | * xsltElement:
|
---|
3853 | * @ctxt: a XSLT process context
|
---|
3854 | * @node: the node in the source tree.
|
---|
3855 | * @inst: the xslt element node
|
---|
3856 | * @castedComp: precomputed information
|
---|
3857 | *
|
---|
3858 | * Process the xslt element node on the source node
|
---|
3859 | */
|
---|
3860 | void
|
---|
3861 | xsltElement(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
3862 | xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
|
---|
3863 | #ifdef XSLT_REFACTORED
|
---|
3864 | xsltStyleItemElementPtr comp = (xsltStyleItemElementPtr) castedComp;
|
---|
3865 | #else
|
---|
3866 | xsltStylePreCompPtr comp = castedComp;
|
---|
3867 | #endif
|
---|
3868 | xmlChar *prop = NULL;
|
---|
3869 | const xmlChar *name, *prefix = NULL, *nsName = NULL;
|
---|
3870 | xmlNodePtr copy;
|
---|
3871 | xmlNodePtr oldInsert;
|
---|
3872 |
|
---|
3873 | if (ctxt->insert == NULL)
|
---|
3874 | return;
|
---|
3875 |
|
---|
3876 | /*
|
---|
3877 | * A comp->has_name == 0 indicates that we need to skip this instruction,
|
---|
3878 | * since it was evaluated to be invalid already during compilation.
|
---|
3879 | */
|
---|
3880 | if (!comp->has_name)
|
---|
3881 | return;
|
---|
3882 |
|
---|
3883 | /*
|
---|
3884 | * stack and saves
|
---|
3885 | */
|
---|
3886 | oldInsert = ctxt->insert;
|
---|
3887 |
|
---|
3888 | if (comp->name == NULL) {
|
---|
3889 | /* TODO: fix attr acquisition wrt to the XSLT namespace */
|
---|
3890 | prop = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3891 | (const xmlChar *) "name", XSLT_NAMESPACE);
|
---|
3892 | if (prop == NULL) {
|
---|
3893 | xsltTransformError(ctxt, NULL, inst,
|
---|
3894 | "xsl:element: The attribute 'name' is missing.\n");
|
---|
3895 | goto error;
|
---|
3896 | }
|
---|
3897 | if (xmlValidateQName(prop, 0)) {
|
---|
3898 | xsltTransformError(ctxt, NULL, inst,
|
---|
3899 | "xsl:element: The effective name '%s' is not a "
|
---|
3900 | "valid QName.\n", prop);
|
---|
3901 | /* we fall through to catch any further errors, if possible */
|
---|
3902 | }
|
---|
3903 | name = xsltSplitQName(ctxt->dict, prop, &prefix);
|
---|
3904 | xmlFree(prop);
|
---|
3905 | if ((prefix != NULL) &&
|
---|
3906 | (!xmlStrncasecmp(prefix, (xmlChar *)"xml", 3)))
|
---|
3907 | {
|
---|
3908 | /*
|
---|
3909 | * TODO: Should we really disallow an "xml" prefix?
|
---|
3910 | */
|
---|
3911 | goto error;
|
---|
3912 | }
|
---|
3913 | } else {
|
---|
3914 | /*
|
---|
3915 | * The "name" value was static.
|
---|
3916 | */
|
---|
3917 | #ifdef XSLT_REFACTORED
|
---|
3918 | prefix = comp->nsPrefix;
|
---|
3919 | name = comp->name;
|
---|
3920 | #else
|
---|
3921 | name = xsltSplitQName(ctxt->dict, comp->name, &prefix);
|
---|
3922 | #endif
|
---|
3923 | }
|
---|
3924 |
|
---|
3925 | /*
|
---|
3926 | * Create the new element
|
---|
3927 | */
|
---|
3928 | if (ctxt->output->dict == ctxt->dict) {
|
---|
3929 | copy = xmlNewDocNodeEatName(ctxt->output, NULL, (xmlChar *)name, NULL);
|
---|
3930 | } else {
|
---|
3931 | copy = xmlNewDocNode(ctxt->output, NULL, (xmlChar *)name, NULL);
|
---|
3932 | }
|
---|
3933 | if (copy == NULL) {
|
---|
3934 | xsltTransformError(ctxt, NULL, inst,
|
---|
3935 | "xsl:element : creation of %s failed\n", name);
|
---|
3936 | return;
|
---|
3937 | }
|
---|
3938 | xmlAddChild(ctxt->insert, copy);
|
---|
3939 |
|
---|
3940 | /*
|
---|
3941 | * Namespace
|
---|
3942 | * ---------
|
---|
3943 | */
|
---|
3944 | if (comp->has_ns) {
|
---|
3945 | if (comp->ns != NULL) {
|
---|
3946 | /*
|
---|
3947 | * No AVT; just plain text for the namespace name.
|
---|
3948 | */
|
---|
3949 | if (comp->ns[0] != 0)
|
---|
3950 | nsName = comp->ns;
|
---|
3951 | } else {
|
---|
3952 | xmlChar *tmpNsName;
|
---|
3953 | /*
|
---|
3954 | * Eval the AVT.
|
---|
3955 | */
|
---|
3956 | /* TODO: check attr acquisition wrt to the XSLT namespace */
|
---|
3957 | tmpNsName = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
3958 | (const xmlChar *) "namespace", XSLT_NAMESPACE);
|
---|
3959 | /*
|
---|
3960 | * SPEC XSLT 1.0:
|
---|
3961 | * "If the string is empty, then the expanded-name of the
|
---|
3962 | * attribute has a null namespace URI."
|
---|
3963 | */
|
---|
3964 | if ((tmpNsName != NULL) && (tmpNsName[0] != 0))
|
---|
3965 | nsName = xmlDictLookup(ctxt->dict, BAD_CAST tmpNsName, -1);
|
---|
3966 | xmlFree(tmpNsName);
|
---|
3967 | };
|
---|
3968 | } else {
|
---|
3969 | xmlNsPtr ns;
|
---|
3970 | /*
|
---|
3971 | * SPEC XSLT 1.0:
|
---|
3972 | * "If the namespace attribute is not present, then the QName is
|
---|
3973 | * expanded into an expanded-name using the namespace declarations
|
---|
3974 | * in effect for the xsl:element element, including any default
|
---|
3975 | * namespace declaration.
|
---|
3976 | */
|
---|
3977 | ns = xmlSearchNs(inst->doc, inst, prefix);
|
---|
3978 | if (ns == NULL) {
|
---|
3979 | /*
|
---|
3980 | * TODO: Check this in the compilation layer in case it's a
|
---|
3981 | * static value.
|
---|
3982 | */
|
---|
3983 | if (prefix != NULL) {
|
---|
3984 | xsltTransformError(ctxt, NULL, inst,
|
---|
3985 | "xsl:element: The QName '%s:%s' has no "
|
---|
3986 | "namespace binding in scope in the stylesheet; "
|
---|
3987 | "this is an error, since the namespace was not "
|
---|
3988 | "specified by the instruction itself.\n", prefix, name);
|
---|
3989 | }
|
---|
3990 | } else
|
---|
3991 | nsName = ns->href;
|
---|
3992 | }
|
---|
3993 | /*
|
---|
3994 | * Find/create a matching ns-decl in the result tree.
|
---|
3995 | */
|
---|
3996 | if (nsName != NULL) {
|
---|
3997 | copy->ns = xsltGetSpecialNamespace(ctxt, inst, nsName, prefix, copy);
|
---|
3998 | } else if ((copy->parent != NULL) &&
|
---|
3999 | (copy->parent->type == XML_ELEMENT_NODE) &&
|
---|
4000 | (copy->parent->ns != NULL))
|
---|
4001 | {
|
---|
4002 | /*
|
---|
4003 | * "Undeclare" the default namespace.
|
---|
4004 | */
|
---|
4005 | xsltGetSpecialNamespace(ctxt, inst, NULL, NULL, copy);
|
---|
4006 | }
|
---|
4007 |
|
---|
4008 | ctxt->insert = copy;
|
---|
4009 |
|
---|
4010 | if (comp->has_use) {
|
---|
4011 | if (comp->use != NULL) {
|
---|
4012 | xsltApplyAttributeSet(ctxt, node, inst, comp->use);
|
---|
4013 | } else {
|
---|
4014 | xmlChar *attrSets = NULL;
|
---|
4015 | /*
|
---|
4016 | * BUG TODO: use-attribute-sets is not a value template.
|
---|
4017 | * use-attribute-sets = qnames
|
---|
4018 | */
|
---|
4019 | attrSets = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
4020 | (const xmlChar *)"use-attribute-sets", NULL);
|
---|
4021 | if (attrSets != NULL) {
|
---|
4022 | xsltApplyAttributeSet(ctxt, node, inst, attrSets);
|
---|
4023 | xmlFree(attrSets);
|
---|
4024 | }
|
---|
4025 | }
|
---|
4026 | }
|
---|
4027 | /*
|
---|
4028 | * Instantiate the sequence constructor.
|
---|
4029 | */
|
---|
4030 | if (inst->children != NULL)
|
---|
4031 | xsltApplySequenceConstructor(ctxt, ctxt->node, inst->children,
|
---|
4032 | NULL);
|
---|
4033 |
|
---|
4034 | error:
|
---|
4035 | ctxt->insert = oldInsert;
|
---|
4036 | return;
|
---|
4037 | }
|
---|
4038 |
|
---|
4039 |
|
---|
4040 | /**
|
---|
4041 | * xsltComment:
|
---|
4042 | * @ctxt: a XSLT process context
|
---|
4043 | * @node: the node in the source tree.
|
---|
4044 | * @inst: the xslt comment node
|
---|
4045 | * @comp: precomputed information
|
---|
4046 | *
|
---|
4047 | * Process the xslt comment node on the source node
|
---|
4048 | */
|
---|
4049 | void
|
---|
4050 | xsltComment(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4051 | xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED) {
|
---|
4052 | xmlChar *value = NULL;
|
---|
4053 | xmlNodePtr commentNode;
|
---|
4054 | int len;
|
---|
4055 |
|
---|
4056 | value = xsltEvalTemplateString(ctxt, node, inst);
|
---|
4057 | /* TODO: use or generate the compiled form */
|
---|
4058 | len = xmlStrlen(value);
|
---|
4059 | if (len > 0) {
|
---|
4060 | if ((value[len-1] == '-') ||
|
---|
4061 | (xmlStrstr(value, BAD_CAST "--"))) {
|
---|
4062 | xsltTransformError(ctxt, NULL, inst,
|
---|
4063 | "xsl:comment : '--' or ending '-' not allowed in comment\n");
|
---|
4064 | /* fall through to try to catch further errors */
|
---|
4065 | }
|
---|
4066 | }
|
---|
4067 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4068 | if (value == NULL) {
|
---|
4069 | XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4070 | "xsltComment: empty\n"));
|
---|
4071 | } else {
|
---|
4072 | XSLT_TRACE(ctxt,XSLT_TRACE_COMMENT,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4073 | "xsltComment: content %s\n", value));
|
---|
4074 | }
|
---|
4075 | #endif
|
---|
4076 |
|
---|
4077 | commentNode = xmlNewComment(value);
|
---|
4078 | xmlAddChild(ctxt->insert, commentNode);
|
---|
4079 |
|
---|
4080 | if (value != NULL)
|
---|
4081 | xmlFree(value);
|
---|
4082 | }
|
---|
4083 |
|
---|
4084 | /**
|
---|
4085 | * xsltProcessingInstruction:
|
---|
4086 | * @ctxt: a XSLT process context
|
---|
4087 | * @node: the node in the source tree.
|
---|
4088 | * @inst: the xslt processing-instruction node
|
---|
4089 | * @castedComp: precomputed information
|
---|
4090 | *
|
---|
4091 | * Process the xslt processing-instruction node on the source node
|
---|
4092 | */
|
---|
4093 | void
|
---|
4094 | xsltProcessingInstruction(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4095 | xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
|
---|
4096 | #ifdef XSLT_REFACTORED
|
---|
4097 | xsltStyleItemPIPtr comp = (xsltStyleItemPIPtr) castedComp;
|
---|
4098 | #else
|
---|
4099 | xsltStylePreCompPtr comp = castedComp;
|
---|
4100 | #endif
|
---|
4101 | const xmlChar *name;
|
---|
4102 | xmlChar *value = NULL;
|
---|
4103 | xmlNodePtr pi;
|
---|
4104 |
|
---|
4105 |
|
---|
4106 | if (ctxt->insert == NULL)
|
---|
4107 | return;
|
---|
4108 | if (comp->has_name == 0)
|
---|
4109 | return;
|
---|
4110 | if (comp->name == NULL) {
|
---|
4111 | name = xsltEvalAttrValueTemplate(ctxt, inst,
|
---|
4112 | (const xmlChar *)"name", NULL);
|
---|
4113 | if (name == NULL) {
|
---|
4114 | xsltTransformError(ctxt, NULL, inst,
|
---|
4115 | "xsl:processing-instruction : name is missing\n");
|
---|
4116 | goto error;
|
---|
4117 | }
|
---|
4118 | } else {
|
---|
4119 | name = comp->name;
|
---|
4120 | }
|
---|
4121 | /* TODO: check that it's both an an NCName and a PITarget. */
|
---|
4122 |
|
---|
4123 |
|
---|
4124 | value = xsltEvalTemplateString(ctxt, node, inst);
|
---|
4125 | if (xmlStrstr(value, BAD_CAST "?>") != NULL) {
|
---|
4126 | xsltTransformError(ctxt, NULL, inst,
|
---|
4127 | "xsl:processing-instruction: '?>' not allowed within PI content\n");
|
---|
4128 | goto error;
|
---|
4129 | }
|
---|
4130 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4131 | if (value == NULL) {
|
---|
4132 | XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4133 | "xsltProcessingInstruction: %s empty\n", name));
|
---|
4134 | } else {
|
---|
4135 | XSLT_TRACE(ctxt,XSLT_TRACE_PI,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4136 | "xsltProcessingInstruction: %s content %s\n", name, value));
|
---|
4137 | }
|
---|
4138 | #endif
|
---|
4139 |
|
---|
4140 | pi = xmlNewDocPI(ctxt->insert->doc, name, value);
|
---|
4141 | xmlAddChild(ctxt->insert, pi);
|
---|
4142 |
|
---|
4143 | error:
|
---|
4144 | if ((name != NULL) && (name != comp->name))
|
---|
4145 | xmlFree((xmlChar *) name);
|
---|
4146 | if (value != NULL)
|
---|
4147 | xmlFree(value);
|
---|
4148 | }
|
---|
4149 |
|
---|
4150 | /**
|
---|
4151 | * xsltCopyOf:
|
---|
4152 | * @ctxt: an XSLT transformation context
|
---|
4153 | * @node: the current node in the source tree
|
---|
4154 | * @inst: the element node of the XSLT copy-of instruction
|
---|
4155 | * @castedComp: precomputed information of the XSLT copy-of instruction
|
---|
4156 | *
|
---|
4157 | * Process the XSLT copy-of instruction.
|
---|
4158 | */
|
---|
4159 | void
|
---|
4160 | xsltCopyOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4161 | xmlNodePtr inst, xsltStylePreCompPtr castedComp) {
|
---|
4162 | #ifdef XSLT_REFACTORED
|
---|
4163 | xsltStyleItemCopyOfPtr comp = (xsltStyleItemCopyOfPtr) castedComp;
|
---|
4164 | #else
|
---|
4165 | xsltStylePreCompPtr comp = castedComp;
|
---|
4166 | #endif
|
---|
4167 | xmlXPathObjectPtr res = NULL;
|
---|
4168 | xmlNodeSetPtr list = NULL;
|
---|
4169 | int i;
|
---|
4170 | xmlDocPtr oldXPContextDoc;
|
---|
4171 | xmlNsPtr *oldXPNamespaces;
|
---|
4172 | xmlNodePtr oldXPContextNode;
|
---|
4173 | int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
|
---|
4174 | xmlXPathContextPtr xpctxt;
|
---|
4175 |
|
---|
4176 | if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
|
---|
4177 | return;
|
---|
4178 | if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
|
---|
4179 | xsltTransformError(ctxt, NULL, inst,
|
---|
4180 | "xsl:copy-of : compilation failed\n");
|
---|
4181 | return;
|
---|
4182 | }
|
---|
4183 |
|
---|
4184 | /*
|
---|
4185 | * SPEC XSLT 1.0:
|
---|
4186 | * "The xsl:copy-of element can be used to insert a result tree
|
---|
4187 | * fragment into the result tree, without first converting it to
|
---|
4188 | * a string as xsl:value-of does (see [7.6.1 Generating Text with
|
---|
4189 | * xsl:value-of]). The required select attribute contains an
|
---|
4190 | * expression. When the result of evaluating the expression is a
|
---|
4191 | * result tree fragment, the complete fragment is copied into the
|
---|
4192 | * result tree. When the result is a node-set, all the nodes in the
|
---|
4193 | * set are copied in document order into the result tree; copying
|
---|
4194 | * an element node copies the attribute nodes, namespace nodes and
|
---|
4195 | * children of the element node as well as the element node itself;
|
---|
4196 | * a root node is copied by copying its children. When the result
|
---|
4197 | * is neither a node-set nor a result tree fragment, the result is
|
---|
4198 | * converted to a string and then inserted into the result tree,
|
---|
4199 | * as with xsl:value-of.
|
---|
4200 | */
|
---|
4201 |
|
---|
4202 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4203 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4204 | "xsltCopyOf: select %s\n", comp->select));
|
---|
4205 | #endif
|
---|
4206 |
|
---|
4207 | /*
|
---|
4208 | * Evaluate the "select" expression.
|
---|
4209 | */
|
---|
4210 | xpctxt = ctxt->xpathCtxt;
|
---|
4211 | oldXPContextDoc = xpctxt->doc;
|
---|
4212 | oldXPContextNode = xpctxt->node;
|
---|
4213 | oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
4214 | oldXPContextSize = xpctxt->contextSize;
|
---|
4215 | oldXPNsNr = xpctxt->nsNr;
|
---|
4216 | oldXPNamespaces = xpctxt->namespaces;
|
---|
4217 |
|
---|
4218 | xpctxt->node = node;
|
---|
4219 | if (comp != NULL) {
|
---|
4220 |
|
---|
4221 | #ifdef XSLT_REFACTORED
|
---|
4222 | if (comp->inScopeNs != NULL) {
|
---|
4223 | xpctxt->namespaces = comp->inScopeNs->list;
|
---|
4224 | xpctxt->nsNr = comp->inScopeNs->xpathNumber;
|
---|
4225 | } else {
|
---|
4226 | xpctxt->namespaces = NULL;
|
---|
4227 | xpctxt->nsNr = 0;
|
---|
4228 | }
|
---|
4229 | #else
|
---|
4230 | xpctxt->namespaces = comp->nsList;
|
---|
4231 | xpctxt->nsNr = comp->nsNr;
|
---|
4232 | #endif
|
---|
4233 | } else {
|
---|
4234 | xpctxt->namespaces = NULL;
|
---|
4235 | xpctxt->nsNr = 0;
|
---|
4236 | }
|
---|
4237 |
|
---|
4238 | res = xmlXPathCompiledEval(comp->comp, xpctxt);
|
---|
4239 |
|
---|
4240 | xpctxt->doc = oldXPContextDoc;
|
---|
4241 | xpctxt->node = oldXPContextNode;
|
---|
4242 | xpctxt->contextSize = oldXPContextSize;
|
---|
4243 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
4244 | xpctxt->nsNr = oldXPNsNr;
|
---|
4245 | xpctxt->namespaces = oldXPNamespaces;
|
---|
4246 |
|
---|
4247 | if (res != NULL) {
|
---|
4248 | if (res->type == XPATH_NODESET) {
|
---|
4249 | /*
|
---|
4250 | * Node-set
|
---|
4251 | * --------
|
---|
4252 | */
|
---|
4253 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4254 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4255 | "xsltCopyOf: result is a node set\n"));
|
---|
4256 | #endif
|
---|
4257 | list = res->nodesetval;
|
---|
4258 | if (list != NULL) {
|
---|
4259 | xmlNodePtr cur;
|
---|
4260 | /*
|
---|
4261 | * The list is already sorted in document order by XPath.
|
---|
4262 | * Append everything in this order under ctxt->insert.
|
---|
4263 | */
|
---|
4264 | for (i = 0;i < list->nodeNr;i++) {
|
---|
4265 | cur = list->nodeTab[i];
|
---|
4266 | if (cur == NULL)
|
---|
4267 | continue;
|
---|
4268 | if ((cur->type == XML_DOCUMENT_NODE) ||
|
---|
4269 | (cur->type == XML_HTML_DOCUMENT_NODE))
|
---|
4270 | {
|
---|
4271 | xsltCopyTreeList(ctxt, inst,
|
---|
4272 | cur->children, ctxt->insert, 0, 0);
|
---|
4273 | } else if (cur->type == XML_ATTRIBUTE_NODE) {
|
---|
4274 | xsltShallowCopyAttr(ctxt, inst,
|
---|
4275 | ctxt->insert, (xmlAttrPtr) cur);
|
---|
4276 | } else {
|
---|
4277 | xsltCopyTreeInternal(ctxt, inst,
|
---|
4278 | cur, ctxt->insert, 0, 0);
|
---|
4279 | }
|
---|
4280 | }
|
---|
4281 | }
|
---|
4282 | } else if (res->type == XPATH_XSLT_TREE) {
|
---|
4283 | /*
|
---|
4284 | * Result tree fragment
|
---|
4285 | * --------------------
|
---|
4286 | * E.g. via <xsl:variable ...><foo/></xsl:variable>
|
---|
4287 | * Note that the root node of such trees is an xmlDocPtr in Libxslt.
|
---|
4288 | */
|
---|
4289 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4290 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4291 | "xsltCopyOf: result is a result tree fragment\n"));
|
---|
4292 | #endif
|
---|
4293 | list = res->nodesetval;
|
---|
4294 | if ((list != NULL) && (list->nodeTab != NULL) &&
|
---|
4295 | (list->nodeTab[0] != NULL) &&
|
---|
4296 | (IS_XSLT_REAL_NODE(list->nodeTab[0])))
|
---|
4297 | {
|
---|
4298 | xsltCopyTreeList(ctxt, inst,
|
---|
4299 | list->nodeTab[0]->children, ctxt->insert, 0, 0);
|
---|
4300 | }
|
---|
4301 | } else {
|
---|
4302 | xmlChar *value = NULL;
|
---|
4303 | /*
|
---|
4304 | * Convert to a string.
|
---|
4305 | */
|
---|
4306 | value = xmlXPathCastToString(res);
|
---|
4307 | if (value == NULL) {
|
---|
4308 | xsltTransformError(ctxt, NULL, inst,
|
---|
4309 | "Internal error in xsltCopyOf(): "
|
---|
4310 | "failed to cast an XPath object to string.\n");
|
---|
4311 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4312 | } else {
|
---|
4313 | if (value[0] != 0) {
|
---|
4314 | /*
|
---|
4315 | * Append content as text node.
|
---|
4316 | */
|
---|
4317 | xsltCopyTextString(ctxt, ctxt->insert, value, 0);
|
---|
4318 | }
|
---|
4319 | xmlFree(value);
|
---|
4320 |
|
---|
4321 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4322 | XSLT_TRACE(ctxt,XSLT_TRACE_COPY_OF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4323 | "xsltCopyOf: result %s\n", res->stringval));
|
---|
4324 | #endif
|
---|
4325 | }
|
---|
4326 | }
|
---|
4327 | } else {
|
---|
4328 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4329 | }
|
---|
4330 |
|
---|
4331 | if (res != NULL)
|
---|
4332 | xmlXPathFreeObject(res);
|
---|
4333 | }
|
---|
4334 |
|
---|
4335 | /**
|
---|
4336 | * xsltValueOf:
|
---|
4337 | * @ctxt: a XSLT process context
|
---|
4338 | * @node: the node in the source tree.
|
---|
4339 | * @inst: the xslt value-of node
|
---|
4340 | * @castedComp: precomputed information
|
---|
4341 | *
|
---|
4342 | * Process the xslt value-of node on the source node
|
---|
4343 | */
|
---|
4344 | void
|
---|
4345 | xsltValueOf(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4346 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
4347 | {
|
---|
4348 | #ifdef XSLT_REFACTORED
|
---|
4349 | xsltStyleItemValueOfPtr comp = (xsltStyleItemValueOfPtr) castedComp;
|
---|
4350 | #else
|
---|
4351 | xsltStylePreCompPtr comp = castedComp;
|
---|
4352 | #endif
|
---|
4353 | xmlXPathObjectPtr res = NULL;
|
---|
4354 | xmlNodePtr copy = NULL;
|
---|
4355 | xmlChar *value = NULL;
|
---|
4356 | xmlDocPtr oldXPContextDoc;
|
---|
4357 | xmlNsPtr *oldXPNamespaces;
|
---|
4358 | xmlNodePtr oldXPContextNode;
|
---|
4359 | int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
|
---|
4360 | xmlXPathContextPtr xpctxt;
|
---|
4361 |
|
---|
4362 | if ((ctxt == NULL) || (node == NULL) || (inst == NULL))
|
---|
4363 | return;
|
---|
4364 |
|
---|
4365 | if ((comp == NULL) || (comp->select == NULL) || (comp->comp == NULL)) {
|
---|
4366 | xsltTransformError(ctxt, NULL, inst,
|
---|
4367 | "Internal error in xsltValueOf(): "
|
---|
4368 | "The XSLT 'value-of' instruction was not compiled.\n");
|
---|
4369 | return;
|
---|
4370 | }
|
---|
4371 |
|
---|
4372 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4373 | XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4374 | "xsltValueOf: select %s\n", comp->select));
|
---|
4375 | #endif
|
---|
4376 |
|
---|
4377 | xpctxt = ctxt->xpathCtxt;
|
---|
4378 | oldXPContextDoc = xpctxt->doc;
|
---|
4379 | oldXPContextNode = xpctxt->node;
|
---|
4380 | oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
4381 | oldXPContextSize = xpctxt->contextSize;
|
---|
4382 | oldXPNsNr = xpctxt->nsNr;
|
---|
4383 | oldXPNamespaces = xpctxt->namespaces;
|
---|
4384 |
|
---|
4385 | xpctxt->node = node;
|
---|
4386 | if (comp != NULL) {
|
---|
4387 |
|
---|
4388 | #ifdef XSLT_REFACTORED
|
---|
4389 | if (comp->inScopeNs != NULL) {
|
---|
4390 | xpctxt->namespaces = comp->inScopeNs->list;
|
---|
4391 | xpctxt->nsNr = comp->inScopeNs->xpathNumber;
|
---|
4392 | } else {
|
---|
4393 | xpctxt->namespaces = NULL;
|
---|
4394 | xpctxt->nsNr = 0;
|
---|
4395 | }
|
---|
4396 | #else
|
---|
4397 | xpctxt->namespaces = comp->nsList;
|
---|
4398 | xpctxt->nsNr = comp->nsNr;
|
---|
4399 | #endif
|
---|
4400 | } else {
|
---|
4401 | xpctxt->namespaces = NULL;
|
---|
4402 | xpctxt->nsNr = 0;
|
---|
4403 | }
|
---|
4404 |
|
---|
4405 | res = xmlXPathCompiledEval(comp->comp, xpctxt);
|
---|
4406 |
|
---|
4407 | xpctxt->doc = oldXPContextDoc;
|
---|
4408 | xpctxt->node = oldXPContextNode;
|
---|
4409 | xpctxt->contextSize = oldXPContextSize;
|
---|
4410 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
4411 | xpctxt->nsNr = oldXPNsNr;
|
---|
4412 | xpctxt->namespaces = oldXPNamespaces;
|
---|
4413 |
|
---|
4414 | /*
|
---|
4415 | * Cast the XPath object to string.
|
---|
4416 | */
|
---|
4417 | if (res != NULL) {
|
---|
4418 | value = xmlXPathCastToString(res);
|
---|
4419 | if (value == NULL) {
|
---|
4420 | xsltTransformError(ctxt, NULL, inst,
|
---|
4421 | "Internal error in xsltValueOf(): "
|
---|
4422 | "failed to cast an XPath object to string.\n");
|
---|
4423 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4424 | goto error;
|
---|
4425 | }
|
---|
4426 | if (value[0] != 0) {
|
---|
4427 | copy = xsltCopyTextString(ctxt,
|
---|
4428 | ctxt->insert, value, comp->noescape);
|
---|
4429 | }
|
---|
4430 | } else {
|
---|
4431 | xsltTransformError(ctxt, NULL, inst,
|
---|
4432 | "XPath evaluation returned no result.\n");
|
---|
4433 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4434 | goto error;
|
---|
4435 | }
|
---|
4436 |
|
---|
4437 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4438 | if (value) {
|
---|
4439 | XSLT_TRACE(ctxt,XSLT_TRACE_VALUE_OF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4440 | "xsltValueOf: result '%s'\n", value));
|
---|
4441 | }
|
---|
4442 | #endif
|
---|
4443 |
|
---|
4444 | error:
|
---|
4445 | if (value != NULL)
|
---|
4446 | xmlFree(value);
|
---|
4447 | if (res != NULL)
|
---|
4448 | xmlXPathFreeObject(res);
|
---|
4449 | }
|
---|
4450 |
|
---|
4451 | /**
|
---|
4452 | * xsltNumber:
|
---|
4453 | * @ctxt: a XSLT process context
|
---|
4454 | * @node: the node in the source tree.
|
---|
4455 | * @inst: the xslt number node
|
---|
4456 | * @castedComp: precomputed information
|
---|
4457 | *
|
---|
4458 | * Process the xslt number node on the source node
|
---|
4459 | */
|
---|
4460 | void
|
---|
4461 | xsltNumber(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4462 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
4463 | {
|
---|
4464 | #ifdef XSLT_REFACTORED
|
---|
4465 | xsltStyleItemNumberPtr comp = (xsltStyleItemNumberPtr) castedComp;
|
---|
4466 | #else
|
---|
4467 | xsltStylePreCompPtr comp = castedComp;
|
---|
4468 | #endif
|
---|
4469 | if (comp == NULL) {
|
---|
4470 | xsltTransformError(ctxt, NULL, inst,
|
---|
4471 | "xsl:number : compilation failed\n");
|
---|
4472 | return;
|
---|
4473 | }
|
---|
4474 |
|
---|
4475 | if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
|
---|
4476 | return;
|
---|
4477 |
|
---|
4478 | comp->numdata.doc = inst->doc;
|
---|
4479 | comp->numdata.node = inst;
|
---|
4480 |
|
---|
4481 | xsltNumberFormat(ctxt, &comp->numdata, node);
|
---|
4482 | }
|
---|
4483 |
|
---|
4484 | /**
|
---|
4485 | * xsltApplyImports:
|
---|
4486 | * @ctxt: an XSLT transformation context
|
---|
4487 | * @contextNode: the current node in the source tree.
|
---|
4488 | * @inst: the element node of the XSLT 'apply-imports' instruction
|
---|
4489 | * @comp: the compiled instruction
|
---|
4490 | *
|
---|
4491 | * Process the XSLT apply-imports element.
|
---|
4492 | */
|
---|
4493 | void
|
---|
4494 | xsltApplyImports(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
|
---|
4495 | xmlNodePtr inst,
|
---|
4496 | xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
|
---|
4497 | {
|
---|
4498 | xsltTemplatePtr templ;
|
---|
4499 |
|
---|
4500 | if ((ctxt == NULL) || (inst == NULL))
|
---|
4501 | return;
|
---|
4502 |
|
---|
4503 | if (comp == NULL) {
|
---|
4504 | xsltTransformError(ctxt, NULL, inst,
|
---|
4505 | "Internal error in xsltApplyImports(): "
|
---|
4506 | "The XSLT 'apply-imports' instruction was not compiled.\n");
|
---|
4507 | return;
|
---|
4508 | }
|
---|
4509 | /*
|
---|
4510 | * NOTE that ctxt->currentTemplateRule and ctxt->templ is not the
|
---|
4511 | * same; the former is the "Current Template Rule" as defined by the
|
---|
4512 | * XSLT spec, the latter is simply the template struct being
|
---|
4513 | * currently processed.
|
---|
4514 | */
|
---|
4515 | if (ctxt->currentTemplateRule == NULL) {
|
---|
4516 | /*
|
---|
4517 | * SPEC XSLT 2.0:
|
---|
4518 | * "[ERR XTDE0560] It is a non-recoverable dynamic error if
|
---|
4519 | * xsl:apply-imports or xsl:next-match is evaluated when the
|
---|
4520 | * current template rule is null."
|
---|
4521 | */
|
---|
4522 | xsltTransformError(ctxt, NULL, inst,
|
---|
4523 | "It is an error to call 'apply-imports' "
|
---|
4524 | "when there's no current template rule.\n");
|
---|
4525 | return;
|
---|
4526 | }
|
---|
4527 | /*
|
---|
4528 | * TODO: Check if this is correct.
|
---|
4529 | */
|
---|
4530 | templ = xsltGetTemplate(ctxt, contextNode,
|
---|
4531 | ctxt->currentTemplateRule->style);
|
---|
4532 |
|
---|
4533 | if (templ != NULL) {
|
---|
4534 | xsltTemplatePtr oldCurTemplRule = ctxt->currentTemplateRule;
|
---|
4535 | /*
|
---|
4536 | * Set the current template rule.
|
---|
4537 | */
|
---|
4538 | ctxt->currentTemplateRule = templ;
|
---|
4539 | /*
|
---|
4540 | * URGENT TODO: Need xsl:with-param be handled somehow here?
|
---|
4541 | */
|
---|
4542 | xsltApplyXSLTTemplate(ctxt, contextNode, templ->content,
|
---|
4543 | templ, NULL);
|
---|
4544 |
|
---|
4545 | ctxt->currentTemplateRule = oldCurTemplRule;
|
---|
4546 | }
|
---|
4547 | }
|
---|
4548 |
|
---|
4549 | /**
|
---|
4550 | * xsltCallTemplate:
|
---|
4551 | * @ctxt: a XSLT transformation context
|
---|
4552 | * @node: the "current node" in the source tree
|
---|
4553 | * @inst: the XSLT 'call-template' instruction
|
---|
4554 | * @castedComp: the compiled information of the instruction
|
---|
4555 | *
|
---|
4556 | * Processes the XSLT call-template instruction on the source node.
|
---|
4557 | */
|
---|
4558 | void
|
---|
4559 | xsltCallTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4560 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
4561 | {
|
---|
4562 | #ifdef XSLT_REFACTORED
|
---|
4563 | xsltStyleItemCallTemplatePtr comp =
|
---|
4564 | (xsltStyleItemCallTemplatePtr) castedComp;
|
---|
4565 | #else
|
---|
4566 | xsltStylePreCompPtr comp = castedComp;
|
---|
4567 | #endif
|
---|
4568 | xsltStackElemPtr withParams = NULL;
|
---|
4569 |
|
---|
4570 | if (ctxt->insert == NULL)
|
---|
4571 | return;
|
---|
4572 | if (comp == NULL) {
|
---|
4573 | xsltTransformError(ctxt, NULL, inst,
|
---|
4574 | "The XSLT 'call-template' instruction was not compiled.\n");
|
---|
4575 | return;
|
---|
4576 | }
|
---|
4577 |
|
---|
4578 | /*
|
---|
4579 | * The template must have been precomputed
|
---|
4580 | */
|
---|
4581 | if (comp->templ == NULL) {
|
---|
4582 | comp->templ = xsltFindTemplate(ctxt, comp->name, comp->ns);
|
---|
4583 | if (comp->templ == NULL) {
|
---|
4584 | if (comp->ns != NULL) {
|
---|
4585 | xsltTransformError(ctxt, NULL, inst,
|
---|
4586 | "The called template '{%s}%s' was not found.\n",
|
---|
4587 | comp->ns, comp->name);
|
---|
4588 | } else {
|
---|
4589 | xsltTransformError(ctxt, NULL, inst,
|
---|
4590 | "The called template '%s' was not found.\n",
|
---|
4591 | comp->name);
|
---|
4592 | }
|
---|
4593 | return;
|
---|
4594 | }
|
---|
4595 | }
|
---|
4596 |
|
---|
4597 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4598 | if ((comp != NULL) && (comp->name != NULL))
|
---|
4599 | XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4600 | "call-template: name %s\n", comp->name));
|
---|
4601 | #endif
|
---|
4602 |
|
---|
4603 | if (inst->children) {
|
---|
4604 | xmlNodePtr cur;
|
---|
4605 | xsltStackElemPtr param;
|
---|
4606 |
|
---|
4607 | cur = inst->children;
|
---|
4608 | while (cur != NULL) {
|
---|
4609 | #ifdef WITH_DEBUGGER
|
---|
4610 | if (ctxt->debugStatus != XSLT_DEBUG_NONE)
|
---|
4611 | xslHandleDebugger(cur, node, comp->templ, ctxt);
|
---|
4612 | #endif
|
---|
4613 | if (ctxt->state == XSLT_STATE_STOPPED) break;
|
---|
4614 | /*
|
---|
4615 | * TODO: The "with-param"s could be part of the "call-template"
|
---|
4616 | * structure. Avoid to "search" for params dynamically
|
---|
4617 | * in the XML tree every time.
|
---|
4618 | */
|
---|
4619 | if (IS_XSLT_ELEM(cur)) {
|
---|
4620 | if (IS_XSLT_NAME(cur, "with-param")) {
|
---|
4621 | param = xsltParseStylesheetCallerParam(ctxt, cur);
|
---|
4622 | if (param != NULL) {
|
---|
4623 | param->next = withParams;
|
---|
4624 | withParams = param;
|
---|
4625 | }
|
---|
4626 | } else {
|
---|
4627 | xsltGenericError(xsltGenericErrorContext,
|
---|
4628 | "xsl:call-template: misplaced xsl:%s\n", cur->name);
|
---|
4629 | }
|
---|
4630 | } else {
|
---|
4631 | xsltGenericError(xsltGenericErrorContext,
|
---|
4632 | "xsl:call-template: misplaced %s element\n", cur->name);
|
---|
4633 | }
|
---|
4634 | cur = cur->next;
|
---|
4635 | }
|
---|
4636 | }
|
---|
4637 | /*
|
---|
4638 | * Create a new frame using the params first
|
---|
4639 | */
|
---|
4640 | xsltApplyXSLTTemplate(ctxt, node, comp->templ->content, comp->templ,
|
---|
4641 | withParams);
|
---|
4642 | if (withParams != NULL)
|
---|
4643 | xsltFreeStackElemList(withParams);
|
---|
4644 |
|
---|
4645 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4646 | if ((comp != NULL) && (comp->name != NULL))
|
---|
4647 | XSLT_TRACE(ctxt,XSLT_TRACE_CALL_TEMPLATE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4648 | "call-template returned: name %s\n", comp->name));
|
---|
4649 | #endif
|
---|
4650 | }
|
---|
4651 |
|
---|
4652 | /**
|
---|
4653 | * xsltApplyTemplates:
|
---|
4654 | * @ctxt: a XSLT transformation context
|
---|
4655 | * @node: the 'current node' in the source tree
|
---|
4656 | * @inst: the element node of an XSLT 'apply-templates' instruction
|
---|
4657 | * @castedComp: the compiled instruction
|
---|
4658 | *
|
---|
4659 | * Processes the XSLT 'apply-templates' instruction on the current node.
|
---|
4660 | */
|
---|
4661 | void
|
---|
4662 | xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node,
|
---|
4663 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
4664 | {
|
---|
4665 | #ifdef XSLT_REFACTORED
|
---|
4666 | xsltStyleItemApplyTemplatesPtr comp =
|
---|
4667 | (xsltStyleItemApplyTemplatesPtr) castedComp;
|
---|
4668 | #else
|
---|
4669 | xsltStylePreCompPtr comp = castedComp;
|
---|
4670 | #endif
|
---|
4671 | int i;
|
---|
4672 | xmlNodePtr cur, delNode = NULL, oldContextNode;
|
---|
4673 | xmlNodeSetPtr list = NULL, oldList;
|
---|
4674 | xsltStackElemPtr withParams = NULL;
|
---|
4675 | int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
|
---|
4676 | const xmlChar *oldMode, *oldModeURI;
|
---|
4677 | xmlDocPtr oldXPDoc;
|
---|
4678 | xsltDocumentPtr oldDocInfo;
|
---|
4679 | xmlXPathContextPtr xpctxt;
|
---|
4680 | xmlNsPtr *oldXPNamespaces;
|
---|
4681 |
|
---|
4682 | if (comp == NULL) {
|
---|
4683 | xsltTransformError(ctxt, NULL, inst,
|
---|
4684 | "xsl:apply-templates : compilation failed\n");
|
---|
4685 | return;
|
---|
4686 | }
|
---|
4687 | if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL))
|
---|
4688 | return;
|
---|
4689 |
|
---|
4690 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4691 | if ((node != NULL) && (node->name != NULL))
|
---|
4692 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4693 | "xsltApplyTemplates: node: '%s'\n", node->name));
|
---|
4694 | #endif
|
---|
4695 |
|
---|
4696 | xpctxt = ctxt->xpathCtxt;
|
---|
4697 | /*
|
---|
4698 | * Save context states.
|
---|
4699 | */
|
---|
4700 | oldContextNode = ctxt->node;
|
---|
4701 | oldMode = ctxt->mode;
|
---|
4702 | oldModeURI = ctxt->modeURI;
|
---|
4703 | oldDocInfo = ctxt->document;
|
---|
4704 | oldList = ctxt->nodeList;
|
---|
4705 |
|
---|
4706 | /*
|
---|
4707 | * The xpath context size and proximity position, as
|
---|
4708 | * well as the xpath and context documents, may be changed
|
---|
4709 | * so we save their initial state and will restore on exit
|
---|
4710 | */
|
---|
4711 | oldXPContextSize = xpctxt->contextSize;
|
---|
4712 | oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
4713 | oldXPDoc = xpctxt->doc;
|
---|
4714 | oldXPNsNr = xpctxt->nsNr;
|
---|
4715 | oldXPNamespaces = xpctxt->namespaces;
|
---|
4716 |
|
---|
4717 | /*
|
---|
4718 | * Set up contexts.
|
---|
4719 | */
|
---|
4720 | ctxt->mode = comp->mode;
|
---|
4721 | ctxt->modeURI = comp->modeURI;
|
---|
4722 |
|
---|
4723 | if (comp->select != NULL) {
|
---|
4724 | xmlXPathObjectPtr res = NULL;
|
---|
4725 |
|
---|
4726 | if (comp->comp == NULL) {
|
---|
4727 | xsltTransformError(ctxt, NULL, inst,
|
---|
4728 | "xsl:apply-templates : compilation failed\n");
|
---|
4729 | goto error;
|
---|
4730 | }
|
---|
4731 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4732 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4733 | "xsltApplyTemplates: select %s\n", comp->select));
|
---|
4734 | #endif
|
---|
4735 |
|
---|
4736 | /*
|
---|
4737 | * Set up XPath.
|
---|
4738 | */
|
---|
4739 | xpctxt->node = node; /* Set the "context node" */
|
---|
4740 | #ifdef XSLT_REFACTORED
|
---|
4741 | if (comp->inScopeNs != NULL) {
|
---|
4742 | xpctxt->namespaces = comp->inScopeNs->list;
|
---|
4743 | xpctxt->nsNr = comp->inScopeNs->xpathNumber;
|
---|
4744 | } else {
|
---|
4745 | xpctxt->namespaces = NULL;
|
---|
4746 | xpctxt->nsNr = 0;
|
---|
4747 | }
|
---|
4748 | #else
|
---|
4749 | xpctxt->namespaces = comp->nsList;
|
---|
4750 | xpctxt->nsNr = comp->nsNr;
|
---|
4751 | #endif
|
---|
4752 | res = xmlXPathCompiledEval(comp->comp, xpctxt);
|
---|
4753 |
|
---|
4754 | xpctxt->contextSize = oldXPContextSize;
|
---|
4755 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
4756 | if (res != NULL) {
|
---|
4757 | if (res->type == XPATH_NODESET) {
|
---|
4758 | list = res->nodesetval; /* consume the node set */
|
---|
4759 | res->nodesetval = NULL;
|
---|
4760 | } else {
|
---|
4761 | xsltTransformError(ctxt, NULL, inst,
|
---|
4762 | "The 'select' expression did not evaluate to a "
|
---|
4763 | "node set.\n");
|
---|
4764 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4765 | xmlXPathFreeObject(res);
|
---|
4766 | goto error;
|
---|
4767 | }
|
---|
4768 | xmlXPathFreeObject(res);
|
---|
4769 | /*
|
---|
4770 | * Note: An xsl:apply-templates with a 'select' attribute,
|
---|
4771 | * can change the current source doc.
|
---|
4772 | */
|
---|
4773 | } else {
|
---|
4774 | xsltTransformError(ctxt, NULL, inst,
|
---|
4775 | "Failed to evaluate the 'select' expression.\n");
|
---|
4776 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4777 | goto error;
|
---|
4778 | }
|
---|
4779 | if (list == NULL) {
|
---|
4780 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4781 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4782 | "xsltApplyTemplates: select didn't evaluate to a node list\n"));
|
---|
4783 | #endif
|
---|
4784 | goto exit;
|
---|
4785 | }
|
---|
4786 | /*
|
---|
4787 | *
|
---|
4788 | * NOTE: Previously a document info (xsltDocument) was
|
---|
4789 | * created and attached to the Result Tree Fragment.
|
---|
4790 | * But such a document info is created on demand in
|
---|
4791 | * xsltKeyFunction() (functions.c), so we need to create
|
---|
4792 | * it here beforehand.
|
---|
4793 | * In order to take care of potential keys we need to
|
---|
4794 | * do some extra work for the case when a Result Tree Fragment
|
---|
4795 | * is converted into a nodeset (e.g. exslt:node-set()) :
|
---|
4796 | * We attach a "pseudo-doc" (xsltDocument) to _private.
|
---|
4797 | * This xsltDocument, together with the keyset, will be freed
|
---|
4798 | * when the Result Tree Fragment is freed.
|
---|
4799 | *
|
---|
4800 | */
|
---|
4801 | #if 0
|
---|
4802 | if ((ctxt->nbKeys > 0) &&
|
---|
4803 | (list->nodeNr != 0) &&
|
---|
4804 | (list->nodeTab[0]->doc != NULL) &&
|
---|
4805 | XSLT_IS_RES_TREE_FRAG(list->nodeTab[0]->doc))
|
---|
4806 | {
|
---|
4807 | /*
|
---|
4808 | * NOTE that it's also OK if @effectiveDocInfo will be
|
---|
4809 | * set to NULL.
|
---|
4810 | */
|
---|
4811 | isRTF = 1;
|
---|
4812 | effectiveDocInfo = list->nodeTab[0]->doc->_private;
|
---|
4813 | }
|
---|
4814 | #endif
|
---|
4815 | } else {
|
---|
4816 | /*
|
---|
4817 | * Build an XPath node set with the children
|
---|
4818 | */
|
---|
4819 | list = xmlXPathNodeSetCreate(NULL);
|
---|
4820 | if (list == NULL)
|
---|
4821 | goto error;
|
---|
4822 | cur = node->children;
|
---|
4823 | while (cur != NULL) {
|
---|
4824 | switch (cur->type) {
|
---|
4825 | case XML_TEXT_NODE:
|
---|
4826 | if ((IS_BLANK_NODE(cur)) &&
|
---|
4827 | (cur->parent != NULL) &&
|
---|
4828 | (cur->parent->type == XML_ELEMENT_NODE) &&
|
---|
4829 | (ctxt->style->stripSpaces != NULL)) {
|
---|
4830 | const xmlChar *val;
|
---|
4831 |
|
---|
4832 | if (cur->parent->ns != NULL) {
|
---|
4833 | val = (const xmlChar *)
|
---|
4834 | xmlHashLookup2(ctxt->style->stripSpaces,
|
---|
4835 | cur->parent->name,
|
---|
4836 | cur->parent->ns->href);
|
---|
4837 | if (val == NULL) {
|
---|
4838 | val = (const xmlChar *)
|
---|
4839 | xmlHashLookup2(ctxt->style->stripSpaces,
|
---|
4840 | BAD_CAST "*",
|
---|
4841 | cur->parent->ns->href);
|
---|
4842 | }
|
---|
4843 | } else {
|
---|
4844 | val = (const xmlChar *)
|
---|
4845 | xmlHashLookup2(ctxt->style->stripSpaces,
|
---|
4846 | cur->parent->name, NULL);
|
---|
4847 | }
|
---|
4848 | if ((val != NULL) &&
|
---|
4849 | (xmlStrEqual(val, (xmlChar *) "strip"))) {
|
---|
4850 | delNode = cur;
|
---|
4851 | break;
|
---|
4852 | }
|
---|
4853 | }
|
---|
4854 | /* no break on purpose */
|
---|
4855 | case XML_ELEMENT_NODE:
|
---|
4856 | case XML_DOCUMENT_NODE:
|
---|
4857 | case XML_HTML_DOCUMENT_NODE:
|
---|
4858 | case XML_CDATA_SECTION_NODE:
|
---|
4859 | case XML_PI_NODE:
|
---|
4860 | case XML_COMMENT_NODE:
|
---|
4861 | xmlXPathNodeSetAddUnique(list, cur);
|
---|
4862 | break;
|
---|
4863 | case XML_DTD_NODE:
|
---|
4864 | /* Unlink the DTD, it's still reachable
|
---|
4865 | * using doc->intSubset */
|
---|
4866 | if (cur->next != NULL)
|
---|
4867 | cur->next->prev = cur->prev;
|
---|
4868 | if (cur->prev != NULL)
|
---|
4869 | cur->prev->next = cur->next;
|
---|
4870 | break;
|
---|
4871 | default:
|
---|
4872 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4873 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4874 | "xsltApplyTemplates: skipping cur type %d\n",
|
---|
4875 | cur->type));
|
---|
4876 | #endif
|
---|
4877 | delNode = cur;
|
---|
4878 | }
|
---|
4879 | cur = cur->next;
|
---|
4880 | if (delNode != NULL) {
|
---|
4881 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4882 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4883 | "xsltApplyTemplates: removing ignorable blank cur\n"));
|
---|
4884 | #endif
|
---|
4885 | xmlUnlinkNode(delNode);
|
---|
4886 | xmlFreeNode(delNode);
|
---|
4887 | delNode = NULL;
|
---|
4888 | }
|
---|
4889 | }
|
---|
4890 | }
|
---|
4891 |
|
---|
4892 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
4893 | if (list != NULL)
|
---|
4894 | XSLT_TRACE(ctxt,XSLT_TRACE_APPLY_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
4895 | "xsltApplyTemplates: list of %d nodes\n", list->nodeNr));
|
---|
4896 | #endif
|
---|
4897 |
|
---|
4898 | if ((list == NULL) || (list->nodeNr == 0))
|
---|
4899 | goto exit;
|
---|
4900 |
|
---|
4901 | /*
|
---|
4902 | * Set the context's node set and size; this is also needed for
|
---|
4903 | * for xsltDoSortFunction().
|
---|
4904 | */
|
---|
4905 | ctxt->nodeList = list;
|
---|
4906 | /*
|
---|
4907 | * Process xsl:with-param and xsl:sort instructions.
|
---|
4908 | * (The code became so verbose just to avoid the
|
---|
4909 | * xmlNodePtr sorts[XSLT_MAX_SORT] if there's no xsl:sort)
|
---|
4910 | * BUG TODO: We are not using namespaced potentially defined on the
|
---|
4911 | * xsl:sort or xsl:with-param elements; XPath expression might fail.
|
---|
4912 | */
|
---|
4913 | if (inst->children) {
|
---|
4914 | xsltStackElemPtr param;
|
---|
4915 |
|
---|
4916 | cur = inst->children;
|
---|
4917 | while (cur) {
|
---|
4918 |
|
---|
4919 | #ifdef WITH_DEBUGGER
|
---|
4920 | if (ctxt->debugStatus != XSLT_DEBUG_NONE)
|
---|
4921 | xslHandleDebugger(cur, node, NULL, ctxt);
|
---|
4922 | #endif
|
---|
4923 | if (ctxt->state == XSLT_STATE_STOPPED)
|
---|
4924 | break;
|
---|
4925 | if (cur->type == XML_TEXT_NODE) {
|
---|
4926 | cur = cur->next;
|
---|
4927 | continue;
|
---|
4928 | }
|
---|
4929 | if (! IS_XSLT_ELEM(cur))
|
---|
4930 | break;
|
---|
4931 | if (IS_XSLT_NAME(cur, "with-param")) {
|
---|
4932 | param = xsltParseStylesheetCallerParam(ctxt, cur);
|
---|
4933 | if (param != NULL) {
|
---|
4934 | param->next = withParams;
|
---|
4935 | withParams = param;
|
---|
4936 | }
|
---|
4937 | }
|
---|
4938 | if (IS_XSLT_NAME(cur, "sort")) {
|
---|
4939 | xsltTemplatePtr oldCurTempRule =
|
---|
4940 | ctxt->currentTemplateRule;
|
---|
4941 | int nbsorts = 0;
|
---|
4942 | xmlNodePtr sorts[XSLT_MAX_SORT];
|
---|
4943 |
|
---|
4944 | sorts[nbsorts++] = cur;
|
---|
4945 |
|
---|
4946 | while (cur) {
|
---|
4947 |
|
---|
4948 | #ifdef WITH_DEBUGGER
|
---|
4949 | if (ctxt->debugStatus != XSLT_DEBUG_NONE)
|
---|
4950 | xslHandleDebugger(cur, node, NULL, ctxt);
|
---|
4951 | #endif
|
---|
4952 | if (ctxt->state == XSLT_STATE_STOPPED)
|
---|
4953 | break;
|
---|
4954 |
|
---|
4955 | if (cur->type == XML_TEXT_NODE) {
|
---|
4956 | cur = cur->next;
|
---|
4957 | continue;
|
---|
4958 | }
|
---|
4959 |
|
---|
4960 | if (! IS_XSLT_ELEM(cur))
|
---|
4961 | break;
|
---|
4962 | if (IS_XSLT_NAME(cur, "with-param")) {
|
---|
4963 | param = xsltParseStylesheetCallerParam(ctxt, cur);
|
---|
4964 | if (param != NULL) {
|
---|
4965 | param->next = withParams;
|
---|
4966 | withParams = param;
|
---|
4967 | }
|
---|
4968 | }
|
---|
4969 | if (IS_XSLT_NAME(cur, "sort")) {
|
---|
4970 | if (nbsorts >= XSLT_MAX_SORT) {
|
---|
4971 | xsltTransformError(ctxt, NULL, cur,
|
---|
4972 | "The number (%d) of xsl:sort instructions exceeds the "
|
---|
4973 | "maximum allowed by this processor's settings.\n",
|
---|
4974 | nbsorts);
|
---|
4975 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
4976 | break;
|
---|
4977 | } else {
|
---|
4978 | sorts[nbsorts++] = cur;
|
---|
4979 | }
|
---|
4980 | }
|
---|
4981 | cur = cur->next;
|
---|
4982 | }
|
---|
4983 | /*
|
---|
4984 | * The "current template rule" is cleared for xsl:sort.
|
---|
4985 | */
|
---|
4986 | ctxt->currentTemplateRule = NULL;
|
---|
4987 | /*
|
---|
4988 | * Sort.
|
---|
4989 | */
|
---|
4990 | xsltDoSortFunction(ctxt, sorts, nbsorts);
|
---|
4991 | ctxt->currentTemplateRule = oldCurTempRule;
|
---|
4992 | break;
|
---|
4993 | }
|
---|
4994 | cur = cur->next;
|
---|
4995 | }
|
---|
4996 | }
|
---|
4997 | xpctxt->contextSize = list->nodeNr;
|
---|
4998 | /*
|
---|
4999 | * Apply templates for all selected source nodes.
|
---|
5000 | */
|
---|
5001 | for (i = 0; i < list->nodeNr; i++) {
|
---|
5002 | cur = list->nodeTab[i];
|
---|
5003 | /*
|
---|
5004 | * The node becomes the "current node".
|
---|
5005 | */
|
---|
5006 | ctxt->node = cur;
|
---|
5007 | /*
|
---|
5008 | * An xsl:apply-templates can change the current context doc.
|
---|
5009 | * OPTIMIZE TODO: Get rid of the need to set the context doc.
|
---|
5010 | */
|
---|
5011 | if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
|
---|
5012 | xpctxt->doc = cur->doc;
|
---|
5013 |
|
---|
5014 | xpctxt->proximityPosition = i + 1;
|
---|
5015 | /*
|
---|
5016 | * Find and apply a template for this node.
|
---|
5017 | */
|
---|
5018 | xsltProcessOneNode(ctxt, cur, withParams);
|
---|
5019 | }
|
---|
5020 |
|
---|
5021 | exit:
|
---|
5022 | error:
|
---|
5023 | /*
|
---|
5024 | * Free the parameter list.
|
---|
5025 | */
|
---|
5026 | if (withParams != NULL)
|
---|
5027 | xsltFreeStackElemList(withParams);
|
---|
5028 | if (list != NULL)
|
---|
5029 | xmlXPathFreeNodeSet(list);
|
---|
5030 | /*
|
---|
5031 | * Restore context states.
|
---|
5032 | */
|
---|
5033 | xpctxt->nsNr = oldXPNsNr;
|
---|
5034 | xpctxt->namespaces = oldXPNamespaces;
|
---|
5035 | xpctxt->doc = oldXPDoc;
|
---|
5036 | xpctxt->contextSize = oldXPContextSize;
|
---|
5037 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5038 |
|
---|
5039 | ctxt->document = oldDocInfo;
|
---|
5040 | ctxt->nodeList = oldList;
|
---|
5041 | ctxt->node = oldContextNode;
|
---|
5042 | ctxt->mode = oldMode;
|
---|
5043 | ctxt->modeURI = oldModeURI;
|
---|
5044 | }
|
---|
5045 |
|
---|
5046 |
|
---|
5047 | /**
|
---|
5048 | * xsltChoose:
|
---|
5049 | * @ctxt: a XSLT process context
|
---|
5050 | * @contextNode: the current node in the source tree
|
---|
5051 | * @inst: the xsl:choose instruction
|
---|
5052 | * @comp: compiled information of the instruction
|
---|
5053 | *
|
---|
5054 | * Processes the xsl:choose instruction on the source node.
|
---|
5055 | */
|
---|
5056 | void
|
---|
5057 | xsltChoose(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
|
---|
5058 | xmlNodePtr inst, xsltStylePreCompPtr comp ATTRIBUTE_UNUSED)
|
---|
5059 | {
|
---|
5060 | xmlNodePtr cur;
|
---|
5061 |
|
---|
5062 | if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
|
---|
5063 | return;
|
---|
5064 |
|
---|
5065 | /*
|
---|
5066 | * TODO: Content model checks should be done only at compilation
|
---|
5067 | * time.
|
---|
5068 | */
|
---|
5069 | cur = inst->children;
|
---|
5070 | if (cur == NULL) {
|
---|
5071 | xsltTransformError(ctxt, NULL, inst,
|
---|
5072 | "xsl:choose: The instruction has no content.\n");
|
---|
5073 | return;
|
---|
5074 | }
|
---|
5075 |
|
---|
5076 | #ifdef XSLT_REFACTORED
|
---|
5077 | /*
|
---|
5078 | * We don't check the content model during transformation.
|
---|
5079 | */
|
---|
5080 | #else
|
---|
5081 | if ((! IS_XSLT_ELEM(cur)) || (! IS_XSLT_NAME(cur, "when"))) {
|
---|
5082 | xsltTransformError(ctxt, NULL, inst,
|
---|
5083 | "xsl:choose: xsl:when expected first\n");
|
---|
5084 | return;
|
---|
5085 | }
|
---|
5086 | #endif
|
---|
5087 |
|
---|
5088 | {
|
---|
5089 | int testRes = 0, res = 0;
|
---|
5090 | xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
|
---|
5091 | xmlDocPtr oldXPContextDoc = xpctxt->doc;
|
---|
5092 | int oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
5093 | int oldXPContextSize = xpctxt->contextSize;
|
---|
5094 | xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
|
---|
5095 | int oldXPNsNr = xpctxt->nsNr;
|
---|
5096 |
|
---|
5097 | #ifdef XSLT_REFACTORED
|
---|
5098 | xsltStyleItemWhenPtr wcomp = NULL;
|
---|
5099 | #else
|
---|
5100 | xsltStylePreCompPtr wcomp = NULL;
|
---|
5101 | #endif
|
---|
5102 |
|
---|
5103 | /*
|
---|
5104 | * Process xsl:when ---------------------------------------------------
|
---|
5105 | */
|
---|
5106 | while (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "when")) {
|
---|
5107 | wcomp = cur->psvi;
|
---|
5108 |
|
---|
5109 | if ((wcomp == NULL) || (wcomp->test == NULL) ||
|
---|
5110 | (wcomp->comp == NULL))
|
---|
5111 | {
|
---|
5112 | xsltTransformError(ctxt, NULL, cur,
|
---|
5113 | "Internal error in xsltChoose(): "
|
---|
5114 | "The XSLT 'when' instruction was not compiled.\n");
|
---|
5115 | goto error;
|
---|
5116 | }
|
---|
5117 |
|
---|
5118 |
|
---|
5119 | #ifdef WITH_DEBUGGER
|
---|
5120 | if (xslDebugStatus != XSLT_DEBUG_NONE) {
|
---|
5121 | /*
|
---|
5122 | * TODO: Isn't comp->templ always NULL for xsl:choose?
|
---|
5123 | */
|
---|
5124 | xslHandleDebugger(cur, contextNode, NULL, ctxt);
|
---|
5125 | }
|
---|
5126 | #endif
|
---|
5127 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5128 | XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5129 | "xsltChoose: test %s\n", wcomp->test));
|
---|
5130 | #endif
|
---|
5131 |
|
---|
5132 | xpctxt->node = contextNode;
|
---|
5133 | xpctxt->doc = oldXPContextDoc;
|
---|
5134 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5135 | xpctxt->contextSize = oldXPContextSize;
|
---|
5136 |
|
---|
5137 | #ifdef XSLT_REFACTORED
|
---|
5138 | if (wcomp->inScopeNs != NULL) {
|
---|
5139 | xpctxt->namespaces = wcomp->inScopeNs->list;
|
---|
5140 | xpctxt->nsNr = wcomp->inScopeNs->xpathNumber;
|
---|
5141 | } else {
|
---|
5142 | xpctxt->namespaces = NULL;
|
---|
5143 | xpctxt->nsNr = 0;
|
---|
5144 | }
|
---|
5145 | #else
|
---|
5146 | xpctxt->namespaces = wcomp->nsList;
|
---|
5147 | xpctxt->nsNr = wcomp->nsNr;
|
---|
5148 | #endif
|
---|
5149 |
|
---|
5150 |
|
---|
5151 | #ifdef XSLT_FAST_IF
|
---|
5152 | res = xmlXPathCompiledEvalToBoolean(wcomp->comp, xpctxt);
|
---|
5153 |
|
---|
5154 | if (res == -1) {
|
---|
5155 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
5156 | goto error;
|
---|
5157 | }
|
---|
5158 | testRes = (res == 1) ? 1 : 0;
|
---|
5159 |
|
---|
5160 | #else /* XSLT_FAST_IF */
|
---|
5161 |
|
---|
5162 | res = xmlXPathCompiledEval(wcomp->comp, xpctxt);
|
---|
5163 |
|
---|
5164 | if (res != NULL) {
|
---|
5165 | if (res->type != XPATH_BOOLEAN)
|
---|
5166 | res = xmlXPathConvertBoolean(res);
|
---|
5167 | if (res->type == XPATH_BOOLEAN)
|
---|
5168 | testRes = res->boolval;
|
---|
5169 | else {
|
---|
5170 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5171 | XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5172 | "xsltChoose: test didn't evaluate to a boolean\n"));
|
---|
5173 | #endif
|
---|
5174 | goto error;
|
---|
5175 | }
|
---|
5176 | xmlXPathFreeObject(res);
|
---|
5177 | res = NULL;
|
---|
5178 | } else {
|
---|
5179 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
5180 | goto error;
|
---|
5181 | }
|
---|
5182 |
|
---|
5183 | #endif /* else of XSLT_FAST_IF */
|
---|
5184 |
|
---|
5185 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5186 | XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5187 | "xsltChoose: test evaluate to %d\n", testRes));
|
---|
5188 | #endif
|
---|
5189 | if (testRes)
|
---|
5190 | goto test_is_true;
|
---|
5191 |
|
---|
5192 | cur = cur->next;
|
---|
5193 | }
|
---|
5194 |
|
---|
5195 | /*
|
---|
5196 | * Process xsl:otherwise ----------------------------------------------
|
---|
5197 | */
|
---|
5198 | if (IS_XSLT_ELEM(cur) && IS_XSLT_NAME(cur, "otherwise")) {
|
---|
5199 |
|
---|
5200 | #ifdef WITH_DEBUGGER
|
---|
5201 | if (xslDebugStatus != XSLT_DEBUG_NONE)
|
---|
5202 | xslHandleDebugger(cur, contextNode, NULL, ctxt);
|
---|
5203 | #endif
|
---|
5204 |
|
---|
5205 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5206 | XSLT_TRACE(ctxt,XSLT_TRACE_CHOOSE,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5207 | "evaluating xsl:otherwise\n"));
|
---|
5208 | #endif
|
---|
5209 | goto test_is_true;
|
---|
5210 | }
|
---|
5211 | xpctxt->node = contextNode;
|
---|
5212 | xpctxt->doc = oldXPContextDoc;
|
---|
5213 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5214 | xpctxt->contextSize = oldXPContextSize;
|
---|
5215 | xpctxt->namespaces = oldXPNamespaces;
|
---|
5216 | xpctxt->nsNr = oldXPNsNr;
|
---|
5217 | goto exit;
|
---|
5218 |
|
---|
5219 | test_is_true:
|
---|
5220 |
|
---|
5221 | xpctxt->node = contextNode;
|
---|
5222 | xpctxt->doc = oldXPContextDoc;
|
---|
5223 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5224 | xpctxt->contextSize = oldXPContextSize;
|
---|
5225 | xpctxt->namespaces = oldXPNamespaces;
|
---|
5226 | xpctxt->nsNr = oldXPNsNr;
|
---|
5227 | goto process_sequence;
|
---|
5228 | }
|
---|
5229 |
|
---|
5230 | process_sequence:
|
---|
5231 |
|
---|
5232 | /*
|
---|
5233 | * Instantiate the sequence constructor.
|
---|
5234 | */
|
---|
5235 | xsltApplySequenceConstructor(ctxt, ctxt->node, cur->children,
|
---|
5236 | NULL);
|
---|
5237 |
|
---|
5238 | exit:
|
---|
5239 | error:
|
---|
5240 | return;
|
---|
5241 | }
|
---|
5242 |
|
---|
5243 | /**
|
---|
5244 | * xsltIf:
|
---|
5245 | * @ctxt: a XSLT process context
|
---|
5246 | * @contextNode: the current node in the source tree
|
---|
5247 | * @inst: the xsl:if instruction
|
---|
5248 | * @castedComp: compiled information of the instruction
|
---|
5249 | *
|
---|
5250 | * Processes the xsl:if instruction on the source node.
|
---|
5251 | */
|
---|
5252 | void
|
---|
5253 | xsltIf(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
|
---|
5254 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
5255 | {
|
---|
5256 | int res = 0;
|
---|
5257 |
|
---|
5258 | #ifdef XSLT_REFACTORED
|
---|
5259 | xsltStyleItemIfPtr comp = (xsltStyleItemIfPtr) castedComp;
|
---|
5260 | #else
|
---|
5261 | xsltStylePreCompPtr comp = castedComp;
|
---|
5262 | #endif
|
---|
5263 |
|
---|
5264 | if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL))
|
---|
5265 | return;
|
---|
5266 | if ((comp == NULL) || (comp->test == NULL) || (comp->comp == NULL)) {
|
---|
5267 | xsltTransformError(ctxt, NULL, inst,
|
---|
5268 | "Internal error in xsltIf(): "
|
---|
5269 | "The XSLT 'if' instruction was not compiled.\n");
|
---|
5270 | return;
|
---|
5271 | }
|
---|
5272 |
|
---|
5273 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5274 | XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5275 | "xsltIf: test %s\n", comp->test));
|
---|
5276 | #endif
|
---|
5277 |
|
---|
5278 | #ifdef XSLT_FAST_IF
|
---|
5279 | {
|
---|
5280 | xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
|
---|
5281 | xmlDocPtr oldXPContextDoc = xpctxt->doc;
|
---|
5282 | xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
|
---|
5283 | xmlNodePtr oldXPContextNode = xpctxt->node;
|
---|
5284 | int oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
5285 | int oldXPContextSize = xpctxt->contextSize;
|
---|
5286 | int oldXPNsNr = xpctxt->nsNr;
|
---|
5287 | xmlDocPtr oldLocalFragmentTop = ctxt->localRVT;
|
---|
5288 |
|
---|
5289 | xpctxt->node = contextNode;
|
---|
5290 | if (comp != NULL) {
|
---|
5291 |
|
---|
5292 | #ifdef XSLT_REFACTORED
|
---|
5293 | if (comp->inScopeNs != NULL) {
|
---|
5294 | xpctxt->namespaces = comp->inScopeNs->list;
|
---|
5295 | xpctxt->nsNr = comp->inScopeNs->xpathNumber;
|
---|
5296 | } else {
|
---|
5297 | xpctxt->namespaces = NULL;
|
---|
5298 | xpctxt->nsNr = 0;
|
---|
5299 | }
|
---|
5300 | #else
|
---|
5301 | xpctxt->namespaces = comp->nsList;
|
---|
5302 | xpctxt->nsNr = comp->nsNr;
|
---|
5303 | #endif
|
---|
5304 | } else {
|
---|
5305 | xpctxt->namespaces = NULL;
|
---|
5306 | xpctxt->nsNr = 0;
|
---|
5307 | }
|
---|
5308 | /*
|
---|
5309 | * This XPath function is optimized for boolean results.
|
---|
5310 | */
|
---|
5311 | res = xmlXPathCompiledEvalToBoolean(comp->comp, xpctxt);
|
---|
5312 |
|
---|
5313 | /*
|
---|
5314 | * Cleanup fragments created during evaluation of the
|
---|
5315 | * "select" expression.
|
---|
5316 | */
|
---|
5317 | if (oldLocalFragmentTop != ctxt->localRVT)
|
---|
5318 | xsltReleaseLocalRVTs(ctxt, oldLocalFragmentTop);
|
---|
5319 |
|
---|
5320 | xpctxt->doc = oldXPContextDoc;
|
---|
5321 | xpctxt->node = oldXPContextNode;
|
---|
5322 | xpctxt->contextSize = oldXPContextSize;
|
---|
5323 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5324 | xpctxt->nsNr = oldXPNsNr;
|
---|
5325 | xpctxt->namespaces = oldXPNamespaces;
|
---|
5326 | }
|
---|
5327 |
|
---|
5328 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5329 | XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5330 | "xsltIf: test evaluate to %d\n", res));
|
---|
5331 | #endif
|
---|
5332 |
|
---|
5333 | if (res == -1) {
|
---|
5334 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
5335 | goto error;
|
---|
5336 | }
|
---|
5337 | if (res == 1) {
|
---|
5338 | /*
|
---|
5339 | * Instantiate the sequence constructor of xsl:if.
|
---|
5340 | */
|
---|
5341 | xsltApplySequenceConstructor(ctxt,
|
---|
5342 | contextNode, inst->children, NULL);
|
---|
5343 | }
|
---|
5344 |
|
---|
5345 | #else /* XSLT_FAST_IF */
|
---|
5346 | {
|
---|
5347 | xmlXPathObjectPtr xpobj = NULL;
|
---|
5348 | /*
|
---|
5349 | * OLD CODE:
|
---|
5350 | */
|
---|
5351 | {
|
---|
5352 | xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
|
---|
5353 | xmlDocPtr oldXPContextDoc = xpctxt->doc;
|
---|
5354 | xmlNsPtr *oldXPNamespaces = xpctxt->namespaces;
|
---|
5355 | xmlNodePtr oldXPContextNode = xpctxt->node;
|
---|
5356 | int oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
5357 | int oldXPContextSize = xpctxt->contextSize;
|
---|
5358 | int oldXPNsNr = xpctxt->nsNr;
|
---|
5359 |
|
---|
5360 | xpctxt->node = contextNode;
|
---|
5361 | if (comp != NULL) {
|
---|
5362 |
|
---|
5363 | #ifdef XSLT_REFACTORED
|
---|
5364 | if (comp->inScopeNs != NULL) {
|
---|
5365 | xpctxt->namespaces = comp->inScopeNs->list;
|
---|
5366 | xpctxt->nsNr = comp->inScopeNs->xpathNumber;
|
---|
5367 | } else {
|
---|
5368 | xpctxt->namespaces = NULL;
|
---|
5369 | xpctxt->nsNr = 0;
|
---|
5370 | }
|
---|
5371 | #else
|
---|
5372 | xpctxt->namespaces = comp->nsList;
|
---|
5373 | xpctxt->nsNr = comp->nsNr;
|
---|
5374 | #endif
|
---|
5375 | } else {
|
---|
5376 | xpctxt->namespaces = NULL;
|
---|
5377 | xpctxt->nsNr = 0;
|
---|
5378 | }
|
---|
5379 |
|
---|
5380 | /*
|
---|
5381 | * This XPath function is optimized for boolean results.
|
---|
5382 | */
|
---|
5383 | xpobj = xmlXPathCompiledEval(comp->comp, xpctxt);
|
---|
5384 |
|
---|
5385 | xpctxt->doc = oldXPContextDoc;
|
---|
5386 | xpctxt->node = oldXPContextNode;
|
---|
5387 | xpctxt->contextSize = oldXPContextSize;
|
---|
5388 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5389 | xpctxt->nsNr = oldXPNsNr;
|
---|
5390 | xpctxt->namespaces = oldXPNamespaces;
|
---|
5391 | }
|
---|
5392 | if (xpobj != NULL) {
|
---|
5393 | if (xpobj->type != XPATH_BOOLEAN)
|
---|
5394 | xpobj = xmlXPathConvertBoolean(xpobj);
|
---|
5395 | if (xpobj->type == XPATH_BOOLEAN) {
|
---|
5396 | res = xpobj->boolval;
|
---|
5397 |
|
---|
5398 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5399 | XSLT_TRACE(ctxt,XSLT_TRACE_IF,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5400 | "xsltIf: test evaluate to %d\n", res));
|
---|
5401 | #endif
|
---|
5402 | if (res) {
|
---|
5403 | xsltApplySequenceConstructor(ctxt,
|
---|
5404 | contextNode, inst->children, NULL);
|
---|
5405 | }
|
---|
5406 | } else {
|
---|
5407 |
|
---|
5408 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5409 | XSLT_TRACE(ctxt, XSLT_TRACE_IF,
|
---|
5410 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
5411 | "xsltIf: test didn't evaluate to a boolean\n"));
|
---|
5412 | #endif
|
---|
5413 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
5414 | }
|
---|
5415 | xmlXPathFreeObject(xpobj);
|
---|
5416 | } else {
|
---|
5417 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
5418 | }
|
---|
5419 | }
|
---|
5420 | #endif /* else of XSLT_FAST_IF */
|
---|
5421 |
|
---|
5422 | error:
|
---|
5423 | return;
|
---|
5424 | }
|
---|
5425 |
|
---|
5426 | /**
|
---|
5427 | * xsltForEach:
|
---|
5428 | * @ctxt: an XSLT transformation context
|
---|
5429 | * @contextNode: the "current node" in the source tree
|
---|
5430 | * @inst: the element node of the xsl:for-each instruction
|
---|
5431 | * @castedComp: the compiled information of the instruction
|
---|
5432 | *
|
---|
5433 | * Process the xslt for-each node on the source node
|
---|
5434 | */
|
---|
5435 | void
|
---|
5436 | xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr contextNode,
|
---|
5437 | xmlNodePtr inst, xsltStylePreCompPtr castedComp)
|
---|
5438 | {
|
---|
5439 | #ifdef XSLT_REFACTORED
|
---|
5440 | xsltStyleItemForEachPtr comp = (xsltStyleItemForEachPtr) castedComp;
|
---|
5441 | #else
|
---|
5442 | xsltStylePreCompPtr comp = castedComp;
|
---|
5443 | #endif
|
---|
5444 | int i;
|
---|
5445 | xmlXPathObjectPtr res = NULL;
|
---|
5446 | xmlNodePtr cur, curInst;
|
---|
5447 | xmlNodeSetPtr list = NULL;
|
---|
5448 | xmlNodeSetPtr oldList;
|
---|
5449 | int oldXPProximityPosition, oldXPContextSize;
|
---|
5450 | xmlNodePtr oldContextNode;
|
---|
5451 | xsltTemplatePtr oldCurTemplRule;
|
---|
5452 | xmlDocPtr oldXPDoc;
|
---|
5453 | xsltDocumentPtr oldDocInfo;
|
---|
5454 | xmlXPathContextPtr xpctxt;
|
---|
5455 |
|
---|
5456 | if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL)) {
|
---|
5457 | xsltGenericError(xsltGenericErrorContext,
|
---|
5458 | "xsltForEach(): Bad arguments.\n");
|
---|
5459 | return;
|
---|
5460 | }
|
---|
5461 |
|
---|
5462 | if (comp == NULL) {
|
---|
5463 | xsltTransformError(ctxt, NULL, inst,
|
---|
5464 | "Internal error in xsltForEach(): "
|
---|
5465 | "The XSLT 'for-each' instruction was not compiled.\n");
|
---|
5466 | return;
|
---|
5467 | }
|
---|
5468 | if ((comp->select == NULL) || (comp->comp == NULL)) {
|
---|
5469 | xsltTransformError(ctxt, NULL, inst,
|
---|
5470 | "Internal error in xsltForEach(): "
|
---|
5471 | "The selecting expression of the XSLT 'for-each' "
|
---|
5472 | "instruction was not compiled correctly.\n");
|
---|
5473 | return;
|
---|
5474 | }
|
---|
5475 | xpctxt = ctxt->xpathCtxt;
|
---|
5476 |
|
---|
5477 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5478 | XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5479 | "xsltForEach: select %s\n", comp->select));
|
---|
5480 | #endif
|
---|
5481 |
|
---|
5482 | /*
|
---|
5483 | * Save context states.
|
---|
5484 | */
|
---|
5485 | oldDocInfo = ctxt->document;
|
---|
5486 | oldList = ctxt->nodeList;
|
---|
5487 | oldContextNode = ctxt->node;
|
---|
5488 | /*
|
---|
5489 | * The "current template rule" is cleared for the instantiation of
|
---|
5490 | * xsl:for-each.
|
---|
5491 | */
|
---|
5492 | oldCurTemplRule = ctxt->currentTemplateRule;
|
---|
5493 | ctxt->currentTemplateRule = NULL;
|
---|
5494 |
|
---|
5495 | oldXPDoc = xpctxt->doc;
|
---|
5496 | oldXPProximityPosition = xpctxt->proximityPosition;
|
---|
5497 | oldXPContextSize = xpctxt->contextSize;
|
---|
5498 | /*
|
---|
5499 | * Set up XPath.
|
---|
5500 | */
|
---|
5501 | xpctxt->node = contextNode;
|
---|
5502 | #ifdef XSLT_REFACTORED
|
---|
5503 | if (comp->inScopeNs != NULL) {
|
---|
5504 | xpctxt->namespaces = comp->inScopeNs->list;
|
---|
5505 | xpctxt->nsNr = comp->inScopeNs->xpathNumber;
|
---|
5506 | } else {
|
---|
5507 | xpctxt->namespaces = NULL;
|
---|
5508 | xpctxt->nsNr = 0;
|
---|
5509 | }
|
---|
5510 | #else
|
---|
5511 | xpctxt->namespaces = comp->nsList;
|
---|
5512 | xpctxt->nsNr = comp->nsNr;
|
---|
5513 | #endif
|
---|
5514 |
|
---|
5515 | /*
|
---|
5516 | * Evaluate the 'select' expression.
|
---|
5517 | */
|
---|
5518 | res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt);
|
---|
5519 |
|
---|
5520 | if (res != NULL) {
|
---|
5521 | if (res->type == XPATH_NODESET)
|
---|
5522 | list = res->nodesetval;
|
---|
5523 | else {
|
---|
5524 | xsltTransformError(ctxt, NULL, inst,
|
---|
5525 | "The 'select' expression does not evaluate to a node set.\n");
|
---|
5526 |
|
---|
5527 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5528 | XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5529 | "xsltForEach: select didn't evaluate to a node list\n"));
|
---|
5530 | #endif
|
---|
5531 | goto error;
|
---|
5532 | }
|
---|
5533 | } else {
|
---|
5534 | xsltTransformError(ctxt, NULL, inst,
|
---|
5535 | "Failed to evaluate the 'select' expression.\n");
|
---|
5536 | ctxt->state = XSLT_STATE_STOPPED;
|
---|
5537 | goto error;
|
---|
5538 | }
|
---|
5539 |
|
---|
5540 | if ((list == NULL) || (list->nodeNr <= 0))
|
---|
5541 | goto exit;
|
---|
5542 |
|
---|
5543 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5544 | XSLT_TRACE(ctxt,XSLT_TRACE_FOR_EACH,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5545 | "xsltForEach: select evaluates to %d nodes\n", list->nodeNr));
|
---|
5546 | #endif
|
---|
5547 |
|
---|
5548 | /*
|
---|
5549 | * Restore XPath states for the "current node".
|
---|
5550 | */
|
---|
5551 | xpctxt->contextSize = oldXPContextSize;
|
---|
5552 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5553 | xpctxt->node = contextNode;
|
---|
5554 |
|
---|
5555 | /*
|
---|
5556 | * Set the list; this has to be done already here for xsltDoSortFunction().
|
---|
5557 | */
|
---|
5558 | ctxt->nodeList = list;
|
---|
5559 | /*
|
---|
5560 | * Handle xsl:sort instructions and skip them for further processing.
|
---|
5561 | * BUG TODO: We are not using namespaced potentially defined on the
|
---|
5562 | * xsl:sort element; XPath expression might fail.
|
---|
5563 | */
|
---|
5564 | curInst = inst->children;
|
---|
5565 | if (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
|
---|
5566 | int nbsorts = 0;
|
---|
5567 | xmlNodePtr sorts[XSLT_MAX_SORT];
|
---|
5568 |
|
---|
5569 | sorts[nbsorts++] = curInst;
|
---|
5570 |
|
---|
5571 | #ifdef WITH_DEBUGGER
|
---|
5572 | if (xslDebugStatus != XSLT_DEBUG_NONE)
|
---|
5573 | xslHandleDebugger(curInst, contextNode, NULL, ctxt);
|
---|
5574 | #endif
|
---|
5575 |
|
---|
5576 | curInst = curInst->next;
|
---|
5577 | while (IS_XSLT_ELEM(curInst) && IS_XSLT_NAME(curInst, "sort")) {
|
---|
5578 | if (nbsorts >= XSLT_MAX_SORT) {
|
---|
5579 | xsltTransformError(ctxt, NULL, curInst,
|
---|
5580 | "The number of xsl:sort instructions exceeds the "
|
---|
5581 | "maximum (%d) allowed by this processor.\n",
|
---|
5582 | XSLT_MAX_SORT);
|
---|
5583 | goto error;
|
---|
5584 | } else {
|
---|
5585 | sorts[nbsorts++] = curInst;
|
---|
5586 | }
|
---|
5587 |
|
---|
5588 | #ifdef WITH_DEBUGGER
|
---|
5589 | if (xslDebugStatus != XSLT_DEBUG_NONE)
|
---|
5590 | xslHandleDebugger(curInst, contextNode, NULL, ctxt);
|
---|
5591 | #endif
|
---|
5592 | curInst = curInst->next;
|
---|
5593 | }
|
---|
5594 | xsltDoSortFunction(ctxt, sorts, nbsorts);
|
---|
5595 | }
|
---|
5596 | xpctxt->contextSize = list->nodeNr;
|
---|
5597 | /*
|
---|
5598 | * Instantiate the sequence constructor for each selected node.
|
---|
5599 | */
|
---|
5600 | for (i = 0; i < list->nodeNr; i++) {
|
---|
5601 | cur = list->nodeTab[i];
|
---|
5602 | /*
|
---|
5603 | * The selected node becomes the "current node".
|
---|
5604 | */
|
---|
5605 | ctxt->node = cur;
|
---|
5606 | /*
|
---|
5607 | * An xsl:for-each can change the current context doc.
|
---|
5608 | * OPTIMIZE TODO: Get rid of the need to set the context doc.
|
---|
5609 | */
|
---|
5610 | if ((cur->type != XML_NAMESPACE_DECL) && (cur->doc != NULL))
|
---|
5611 | xpctxt->doc = cur->doc;
|
---|
5612 |
|
---|
5613 | xpctxt->proximityPosition = i + 1;
|
---|
5614 |
|
---|
5615 | xsltApplySequenceConstructor(ctxt, cur, curInst, NULL);
|
---|
5616 | }
|
---|
5617 |
|
---|
5618 | exit:
|
---|
5619 | error:
|
---|
5620 | if (res != NULL)
|
---|
5621 | xmlXPathFreeObject(res);
|
---|
5622 | /*
|
---|
5623 | * Restore old states.
|
---|
5624 | */
|
---|
5625 | ctxt->document = oldDocInfo;
|
---|
5626 | ctxt->nodeList = oldList;
|
---|
5627 | ctxt->node = oldContextNode;
|
---|
5628 | ctxt->currentTemplateRule = oldCurTemplRule;
|
---|
5629 |
|
---|
5630 | xpctxt->doc = oldXPDoc;
|
---|
5631 | xpctxt->contextSize = oldXPContextSize;
|
---|
5632 | xpctxt->proximityPosition = oldXPProximityPosition;
|
---|
5633 | }
|
---|
5634 |
|
---|
5635 | /************************************************************************
|
---|
5636 | * *
|
---|
5637 | * Generic interface *
|
---|
5638 | * *
|
---|
5639 | ************************************************************************/
|
---|
5640 |
|
---|
5641 | #ifdef XSLT_GENERATE_HTML_DOCTYPE
|
---|
5642 | typedef struct xsltHTMLVersion {
|
---|
5643 | const char *version;
|
---|
5644 | const char *public;
|
---|
5645 | const char *system;
|
---|
5646 | } xsltHTMLVersion;
|
---|
5647 |
|
---|
5648 | static xsltHTMLVersion xsltHTMLVersions[] = {
|
---|
5649 | { "4.01frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
|
---|
5650 | "http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd"},
|
---|
5651 | { "4.01strict", "-//W3C//DTD HTML 4.01//EN",
|
---|
5652 | "http://www.w3.org/TR/1999/REC-html401-19991224/strict.dtd"},
|
---|
5653 | { "4.01trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
|
---|
5654 | "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
|
---|
5655 | { "4.01", "-//W3C//DTD HTML 4.01 Transitional//EN",
|
---|
5656 | "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd"},
|
---|
5657 | { "4.0strict", "-//W3C//DTD HTML 4.01//EN",
|
---|
5658 | "http://www.w3.org/TR/html4/strict.dtd"},
|
---|
5659 | { "4.0trans", "-//W3C//DTD HTML 4.01 Transitional//EN",
|
---|
5660 | "http://www.w3.org/TR/html4/loose.dtd"},
|
---|
5661 | { "4.0frame", "-//W3C//DTD HTML 4.01 Frameset//EN",
|
---|
5662 | "http://www.w3.org/TR/html4/frameset.dtd"},
|
---|
5663 | { "4.0", "-//W3C//DTD HTML 4.01 Transitional//EN",
|
---|
5664 | "http://www.w3.org/TR/html4/loose.dtd"},
|
---|
5665 | { "3.2", "-//W3C//DTD HTML 3.2//EN", NULL }
|
---|
5666 | };
|
---|
5667 |
|
---|
5668 | /**
|
---|
5669 | * xsltGetHTMLIDs:
|
---|
5670 | * @version: the version string
|
---|
5671 | * @publicID: used to return the public ID
|
---|
5672 | * @systemID: used to return the system ID
|
---|
5673 | *
|
---|
5674 | * Returns -1 if not found, 0 otherwise and the system and public
|
---|
5675 | * Identifier for this given verion of HTML
|
---|
5676 | */
|
---|
5677 | static int
|
---|
5678 | xsltGetHTMLIDs(const xmlChar *version, const xmlChar **publicID,
|
---|
5679 | const xmlChar **systemID) {
|
---|
5680 | unsigned int i;
|
---|
5681 | if (version == NULL)
|
---|
5682 | return(-1);
|
---|
5683 | for (i = 0;i < (sizeof(xsltHTMLVersions)/sizeof(xsltHTMLVersions[1]));
|
---|
5684 | i++) {
|
---|
5685 | if (!xmlStrcasecmp(version,
|
---|
5686 | (const xmlChar *) xsltHTMLVersions[i].version)) {
|
---|
5687 | if (publicID != NULL)
|
---|
5688 | *publicID = (const xmlChar *) xsltHTMLVersions[i].public;
|
---|
5689 | if (systemID != NULL)
|
---|
5690 | *systemID = (const xmlChar *) xsltHTMLVersions[i].system;
|
---|
5691 | return(0);
|
---|
5692 | }
|
---|
5693 | }
|
---|
5694 | return(-1);
|
---|
5695 | }
|
---|
5696 | #endif
|
---|
5697 |
|
---|
5698 | /**
|
---|
5699 | * xsltApplyStripSpaces:
|
---|
5700 | * @ctxt: a XSLT process context
|
---|
5701 | * @node: the root of the XML tree
|
---|
5702 | *
|
---|
5703 | * Strip the unwanted ignorable spaces from the input tree
|
---|
5704 | */
|
---|
5705 | void
|
---|
5706 | xsltApplyStripSpaces(xsltTransformContextPtr ctxt, xmlNodePtr node) {
|
---|
5707 | xmlNodePtr current;
|
---|
5708 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5709 | int nb = 0;
|
---|
5710 | #endif
|
---|
5711 |
|
---|
5712 |
|
---|
5713 | current = node;
|
---|
5714 | while (current != NULL) {
|
---|
5715 | /*
|
---|
5716 | * Cleanup children empty nodes if asked for
|
---|
5717 | */
|
---|
5718 | if ((IS_XSLT_REAL_NODE(current)) &&
|
---|
5719 | (current->children != NULL) &&
|
---|
5720 | (xsltFindElemSpaceHandling(ctxt, current))) {
|
---|
5721 | xmlNodePtr delete = NULL, cur = current->children;
|
---|
5722 |
|
---|
5723 | while (cur != NULL) {
|
---|
5724 | if (IS_BLANK_NODE(cur))
|
---|
5725 | delete = cur;
|
---|
5726 |
|
---|
5727 | cur = cur->next;
|
---|
5728 | if (delete != NULL) {
|
---|
5729 | xmlUnlinkNode(delete);
|
---|
5730 | xmlFreeNode(delete);
|
---|
5731 | delete = NULL;
|
---|
5732 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5733 | nb++;
|
---|
5734 | #endif
|
---|
5735 | }
|
---|
5736 | }
|
---|
5737 | }
|
---|
5738 |
|
---|
5739 | /*
|
---|
5740 | * Skip to next node in document order.
|
---|
5741 | */
|
---|
5742 | if (node->type == XML_ENTITY_REF_NODE) {
|
---|
5743 | /* process deep in entities */
|
---|
5744 | xsltApplyStripSpaces(ctxt, node->children);
|
---|
5745 | }
|
---|
5746 | if ((current->children != NULL) &&
|
---|
5747 | (current->type != XML_ENTITY_REF_NODE)) {
|
---|
5748 | current = current->children;
|
---|
5749 | } else if (current->next != NULL) {
|
---|
5750 | current = current->next;
|
---|
5751 | } else {
|
---|
5752 | do {
|
---|
5753 | current = current->parent;
|
---|
5754 | if (current == NULL)
|
---|
5755 | break;
|
---|
5756 | if (current == node)
|
---|
5757 | goto done;
|
---|
5758 | if (current->next != NULL) {
|
---|
5759 | current = current->next;
|
---|
5760 | break;
|
---|
5761 | }
|
---|
5762 | } while (current != NULL);
|
---|
5763 | }
|
---|
5764 | }
|
---|
5765 |
|
---|
5766 | done:
|
---|
5767 | #ifdef WITH_XSLT_DEBUG_PROCESS
|
---|
5768 | XSLT_TRACE(ctxt,XSLT_TRACE_STRIP_SPACES,xsltGenericDebug(xsltGenericDebugContext,
|
---|
5769 | "xsltApplyStripSpaces: removed %d ignorable blank node\n", nb));
|
---|
5770 | #endif
|
---|
5771 | return;
|
---|
5772 | }
|
---|
5773 |
|
---|
5774 | #ifdef XSLT_REFACTORED_KEYCOMP
|
---|
5775 | static int
|
---|
5776 | xsltCountKeys(xsltTransformContextPtr ctxt)
|
---|
5777 | {
|
---|
5778 | xsltStylesheetPtr style;
|
---|
5779 | xsltKeyDefPtr keyd;
|
---|
5780 |
|
---|
5781 | if (ctxt == NULL)
|
---|
5782 | return(-1);
|
---|
5783 |
|
---|
5784 | /*
|
---|
5785 | * Do we have those nastly templates with a key() in the match pattern?
|
---|
5786 | */
|
---|
5787 | ctxt->hasTemplKeyPatterns = 0;
|
---|
5788 | style = ctxt->style;
|
---|
5789 | while (style != NULL) {
|
---|
5790 | if (style->keyMatch != NULL) {
|
---|
5791 | ctxt->hasTemplKeyPatterns = 1;
|
---|
5792 | break;
|
---|
5793 | }
|
---|
5794 | style = xsltNextImport(style);
|
---|
5795 | }
|
---|
5796 | /*
|
---|
5797 | * Count number of key declarations.
|
---|
5798 | */
|
---|
5799 | ctxt->nbKeys = 0;
|
---|
5800 | style = ctxt->style;
|
---|
5801 | while (style != NULL) {
|
---|
5802 | keyd = style->keys;
|
---|
5803 | while (keyd) {
|
---|
5804 | ctxt->nbKeys++;
|
---|
5805 | keyd = keyd->next;
|
---|
5806 | }
|
---|
5807 | style = xsltNextImport(style);
|
---|
5808 | }
|
---|
5809 | return(ctxt->nbKeys);
|
---|
5810 | }
|
---|
5811 | #endif /* XSLT_REFACTORED_KEYCOMP */
|
---|
5812 |
|
---|
5813 | /**
|
---|
5814 | * xsltApplyStylesheetInternal:
|
---|
5815 | * @style: a parsed XSLT stylesheet
|
---|
5816 | * @doc: a parsed XML document
|
---|
5817 | * @params: a NULL terminated array of parameters names/values tuples
|
---|
5818 | * @output: the targetted output
|
---|
5819 | * @profile: profile FILE * output or NULL
|
---|
5820 | * @user: user provided parameter
|
---|
5821 | *
|
---|
5822 | * Apply the stylesheet to the document
|
---|
5823 | * NOTE: This may lead to a non-wellformed output XML wise !
|
---|
5824 | *
|
---|
5825 | * Returns the result document or NULL in case of error
|
---|
5826 | */
|
---|
5827 | static xmlDocPtr
|
---|
5828 | xsltApplyStylesheetInternal(xsltStylesheetPtr style, xmlDocPtr doc,
|
---|
5829 | const char **params, const char *output,
|
---|
5830 | FILE * profile, xsltTransformContextPtr userCtxt)
|
---|
5831 | {
|
---|
5832 | xmlDocPtr res = NULL;
|
---|
5833 | xsltTransformContextPtr ctxt = NULL;
|
---|
5834 | xmlNodePtr root, node;
|
---|
5835 | const xmlChar *method;
|
---|
5836 | const xmlChar *doctypePublic;
|
---|
5837 | const xmlChar *doctypeSystem;
|
---|
5838 | const xmlChar *version;
|
---|
5839 | xsltStackElemPtr variables;
|
---|
5840 | xsltStackElemPtr vptr;
|
---|
5841 |
|
---|
5842 | if ((style == NULL) || (doc == NULL))
|
---|
5843 | return (NULL);
|
---|
5844 |
|
---|
5845 | if (style->internalized == 0) {
|
---|
5846 | #ifdef WITH_XSLT_DEBUG
|
---|
5847 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
5848 | "Stylesheet was not fully internalized !\n");
|
---|
5849 | #endif
|
---|
5850 | }
|
---|
5851 | if (doc->intSubset != NULL) {
|
---|
5852 | /*
|
---|
5853 | * Avoid hitting the DTD when scanning nodes
|
---|
5854 | * but keep it linked as doc->intSubset
|
---|
5855 | */
|
---|
5856 | xmlNodePtr cur = (xmlNodePtr) doc->intSubset;
|
---|
5857 | if (cur->next != NULL)
|
---|
5858 | cur->next->prev = cur->prev;
|
---|
5859 | if (cur->prev != NULL)
|
---|
5860 | cur->prev->next = cur->next;
|
---|
5861 | if (doc->children == cur)
|
---|
5862 | doc->children = cur->next;
|
---|
5863 | if (doc->last == cur)
|
---|
5864 | doc->last = cur->prev;
|
---|
5865 | cur->prev = cur->next = NULL;
|
---|
5866 | }
|
---|
5867 |
|
---|
5868 | /*
|
---|
5869 | * Check for XPath document order availability
|
---|
5870 | */
|
---|
5871 | root = xmlDocGetRootElement(doc);
|
---|
5872 | if (root != NULL) {
|
---|
5873 | if (((long) root->content) >= 0 && (xslDebugStatus == XSLT_DEBUG_NONE))
|
---|
5874 | xmlXPathOrderDocElems(doc);
|
---|
5875 | }
|
---|
5876 |
|
---|
5877 | if (userCtxt != NULL)
|
---|
5878 | ctxt = userCtxt;
|
---|
5879 | else
|
---|
5880 | ctxt = xsltNewTransformContext(style, doc);
|
---|
5881 |
|
---|
5882 | if (ctxt == NULL)
|
---|
5883 | return (NULL);
|
---|
5884 |
|
---|
5885 | ctxt->initialContextDoc = doc;
|
---|
5886 | ctxt->initialContextNode = (xmlNodePtr) doc;
|
---|
5887 |
|
---|
5888 | if (profile != NULL)
|
---|
5889 | ctxt->profile = 1;
|
---|
5890 |
|
---|
5891 | if (output != NULL)
|
---|
5892 | ctxt->outputFile = output;
|
---|
5893 | else
|
---|
5894 | ctxt->outputFile = NULL;
|
---|
5895 |
|
---|
5896 | /*
|
---|
5897 | * internalize the modes if needed
|
---|
5898 | */
|
---|
5899 | if (ctxt->dict != NULL) {
|
---|
5900 | if (ctxt->mode != NULL)
|
---|
5901 | ctxt->mode = xmlDictLookup(ctxt->dict, ctxt->mode, -1);
|
---|
5902 | if (ctxt->modeURI != NULL)
|
---|
5903 | ctxt->modeURI = xmlDictLookup(ctxt->dict, ctxt->modeURI, -1);
|
---|
5904 | }
|
---|
5905 |
|
---|
5906 | XSLT_GET_IMPORT_PTR(method, style, method)
|
---|
5907 | XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
|
---|
5908 | XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
|
---|
5909 | XSLT_GET_IMPORT_PTR(version, style, version)
|
---|
5910 |
|
---|
5911 | if ((method != NULL) &&
|
---|
5912 | (!xmlStrEqual(method, (const xmlChar *) "xml")))
|
---|
5913 | {
|
---|
5914 | if (xmlStrEqual(method, (const xmlChar *) "html")) {
|
---|
5915 | ctxt->type = XSLT_OUTPUT_HTML;
|
---|
5916 | if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
|
---|
5917 | res = htmlNewDoc(doctypeSystem, doctypePublic);
|
---|
5918 | } else {
|
---|
5919 | if (version == NULL) {
|
---|
5920 | xmlDtdPtr dtd;
|
---|
5921 |
|
---|
5922 | res = htmlNewDoc(NULL, NULL);
|
---|
5923 | /*
|
---|
5924 | * Make sure no DTD node is generated in this case
|
---|
5925 | */
|
---|
5926 | if (res != NULL) {
|
---|
5927 | dtd = xmlGetIntSubset(res);
|
---|
5928 | if (dtd != NULL) {
|
---|
5929 | xmlUnlinkNode((xmlNodePtr) dtd);
|
---|
5930 | xmlFreeDtd(dtd);
|
---|
5931 | }
|
---|
5932 | res->intSubset = NULL;
|
---|
5933 | res->extSubset = NULL;
|
---|
5934 | }
|
---|
5935 | } else {
|
---|
5936 |
|
---|
5937 | #ifdef XSLT_GENERATE_HTML_DOCTYPE
|
---|
5938 | xsltGetHTMLIDs(version, &doctypePublic, &doctypeSystem);
|
---|
5939 | #endif
|
---|
5940 | res = htmlNewDoc(doctypeSystem, doctypePublic);
|
---|
5941 | }
|
---|
5942 | }
|
---|
5943 | if (res == NULL)
|
---|
5944 | goto error;
|
---|
5945 | res->dict = ctxt->dict;
|
---|
5946 | xmlDictReference(res->dict);
|
---|
5947 |
|
---|
5948 | #ifdef WITH_XSLT_DEBUG
|
---|
5949 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
5950 | "reusing transformation dict for output\n");
|
---|
5951 | #endif
|
---|
5952 | } else if (xmlStrEqual(method, (const xmlChar *) "xhtml")) {
|
---|
5953 | xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
|
---|
5954 | "xsltApplyStylesheetInternal: unsupported method xhtml, using html\n",
|
---|
5955 | style->method);
|
---|
5956 | ctxt->type = XSLT_OUTPUT_HTML;
|
---|
5957 | res = htmlNewDoc(doctypeSystem, doctypePublic);
|
---|
5958 | if (res == NULL)
|
---|
5959 | goto error;
|
---|
5960 | res->dict = ctxt->dict;
|
---|
5961 | xmlDictReference(res->dict);
|
---|
5962 |
|
---|
5963 | #ifdef WITH_XSLT_DEBUG
|
---|
5964 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
5965 | "reusing transformation dict for output\n");
|
---|
5966 | #endif
|
---|
5967 | } else if (xmlStrEqual(method, (const xmlChar *) "text")) {
|
---|
5968 | ctxt->type = XSLT_OUTPUT_TEXT;
|
---|
5969 | res = xmlNewDoc(style->version);
|
---|
5970 | if (res == NULL)
|
---|
5971 | goto error;
|
---|
5972 | res->dict = ctxt->dict;
|
---|
5973 | xmlDictReference(res->dict);
|
---|
5974 |
|
---|
5975 | #ifdef WITH_XSLT_DEBUG
|
---|
5976 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
5977 | "reusing transformation dict for output\n");
|
---|
5978 | #endif
|
---|
5979 | } else {
|
---|
5980 | xsltTransformError(ctxt, NULL, (xmlNodePtr) doc,
|
---|
5981 | "xsltApplyStylesheetInternal: unsupported method %s\n",
|
---|
5982 | style->method);
|
---|
5983 | goto error;
|
---|
5984 | }
|
---|
5985 | } else {
|
---|
5986 | ctxt->type = XSLT_OUTPUT_XML;
|
---|
5987 | res = xmlNewDoc(style->version);
|
---|
5988 | if (res == NULL)
|
---|
5989 | goto error;
|
---|
5990 | res->dict = ctxt->dict;
|
---|
5991 | xmlDictReference(ctxt->dict);
|
---|
5992 | #ifdef WITH_XSLT_DEBUG
|
---|
5993 | xsltGenericDebug(xsltGenericDebugContext,
|
---|
5994 | "reusing transformation dict for output\n");
|
---|
5995 | #endif
|
---|
5996 | }
|
---|
5997 | res->charset = XML_CHAR_ENCODING_UTF8;
|
---|
5998 | if (style->encoding != NULL)
|
---|
5999 | res->encoding = xmlStrdup(style->encoding);
|
---|
6000 | variables = style->variables;
|
---|
6001 |
|
---|
6002 | /*
|
---|
6003 | * Start the evaluation, evaluate the params, the stylesheets globals
|
---|
6004 | * and start by processing the top node.
|
---|
6005 | */
|
---|
6006 | if (xsltNeedElemSpaceHandling(ctxt))
|
---|
6007 | xsltApplyStripSpaces(ctxt, xmlDocGetRootElement(doc));
|
---|
6008 | /*
|
---|
6009 | * Evaluate global params and user-provided params.
|
---|
6010 | */
|
---|
6011 | ctxt->node = (xmlNodePtr) doc;
|
---|
6012 | if (ctxt->globalVars == NULL)
|
---|
6013 | ctxt->globalVars = xmlHashCreate(20);
|
---|
6014 | if (params != NULL) {
|
---|
6015 | xsltEvalUserParams(ctxt, params);
|
---|
6016 | }
|
---|
6017 | xsltEvalGlobalVariables(ctxt);
|
---|
6018 |
|
---|
6019 | #ifdef XSLT_REFACTORED_KEYCOMP
|
---|
6020 | xsltCountKeys(ctxt);
|
---|
6021 | #endif
|
---|
6022 |
|
---|
6023 | ctxt->node = (xmlNodePtr) doc;
|
---|
6024 | ctxt->output = res;
|
---|
6025 | ctxt->insert = (xmlNodePtr) res;
|
---|
6026 | ctxt->varsBase = ctxt->varsNr - 1;
|
---|
6027 |
|
---|
6028 | ctxt->xpathCtxt->contextSize = 1;
|
---|
6029 | ctxt->xpathCtxt->proximityPosition = 1;
|
---|
6030 | ctxt->xpathCtxt->node = NULL; /* TODO: Set the context node here? */
|
---|
6031 | /*
|
---|
6032 | * Start processing the source tree -----------------------------------
|
---|
6033 | */
|
---|
6034 | xsltProcessOneNode(ctxt, ctxt->node, NULL);
|
---|
6035 | /*
|
---|
6036 | * Remove all remaining vars from the stack.
|
---|
6037 | */
|
---|
6038 | xsltLocalVariablePop(ctxt, 0, -2);
|
---|
6039 | xsltShutdownCtxtExts(ctxt);
|
---|
6040 |
|
---|
6041 | xsltCleanupTemplates(style); /* TODO: <- style should be read only */
|
---|
6042 |
|
---|
6043 | /*
|
---|
6044 | * Now cleanup our variables so stylesheet can be re-used
|
---|
6045 | *
|
---|
6046 | * TODO: this is not needed anymore global variables are copied
|
---|
6047 | * and not evaluated directly anymore, keep this as a check
|
---|
6048 | */
|
---|
6049 | if (style->variables != variables) {
|
---|
6050 | vptr = style->variables;
|
---|
6051 | while (vptr->next != variables)
|
---|
6052 | vptr = vptr->next;
|
---|
6053 | vptr->next = NULL;
|
---|
6054 | xsltFreeStackElemList(style->variables);
|
---|
6055 | style->variables = variables;
|
---|
6056 | }
|
---|
6057 | vptr = style->variables;
|
---|
6058 | while (vptr != NULL) {
|
---|
6059 | if (vptr->computed) {
|
---|
6060 | if (vptr->value != NULL) {
|
---|
6061 | xmlXPathFreeObject(vptr->value);
|
---|
6062 | vptr->value = NULL;
|
---|
6063 | vptr->computed = 0;
|
---|
6064 | }
|
---|
6065 | }
|
---|
6066 | vptr = vptr->next;
|
---|
6067 | }
|
---|
6068 | #if 0
|
---|
6069 | /*
|
---|
6070 | * code disabled by wmb; awaiting kb's review
|
---|
6071 | * problem is that global variable(s) may contain xpath objects
|
---|
6072 | * from doc associated with RVT, so can't be freed at this point.
|
---|
6073 | * xsltFreeTransformContext includes a call to xsltFreeRVTs, so
|
---|
6074 | * I assume this shouldn't be required at this point.
|
---|
6075 | */
|
---|
6076 | /*
|
---|
6077 | * Free all remaining tree fragments.
|
---|
6078 | */
|
---|
6079 | xsltFreeRVTs(ctxt);
|
---|
6080 | #endif
|
---|
6081 | /*
|
---|
6082 | * Do some post processing work depending on the generated output
|
---|
6083 | */
|
---|
6084 | root = xmlDocGetRootElement(res);
|
---|
6085 | if (root != NULL) {
|
---|
6086 | const xmlChar *doctype = NULL;
|
---|
6087 |
|
---|
6088 | if ((root->ns != NULL) && (root->ns->prefix != NULL))
|
---|
6089 | doctype = xmlDictQLookup(ctxt->dict, root->ns->prefix, root->name);
|
---|
6090 | if (doctype == NULL)
|
---|
6091 | doctype = root->name;
|
---|
6092 |
|
---|
6093 | /*
|
---|
6094 | * Apply the default selection of the method
|
---|
6095 | */
|
---|
6096 | if ((method == NULL) &&
|
---|
6097 | (root->ns == NULL) &&
|
---|
6098 | (!xmlStrcasecmp(root->name, (const xmlChar *) "html"))) {
|
---|
6099 | xmlNodePtr tmp;
|
---|
6100 |
|
---|
6101 | tmp = res->children;
|
---|
6102 | while ((tmp != NULL) && (tmp != root)) {
|
---|
6103 | if (tmp->type == XML_ELEMENT_NODE)
|
---|
6104 | break;
|
---|
6105 | if ((tmp->type == XML_TEXT_NODE) && (!xmlIsBlankNode(tmp)))
|
---|
6106 | break;
|
---|
6107 | tmp = tmp->next;
|
---|
6108 | }
|
---|
6109 | if (tmp == root) {
|
---|
6110 | ctxt->type = XSLT_OUTPUT_HTML;
|
---|
6111 | /*
|
---|
6112 | * REVISIT TODO: XML_HTML_DOCUMENT_NODE is set after the
|
---|
6113 | * transformation on the doc, but functions like
|
---|
6114 | */
|
---|
6115 | res->type = XML_HTML_DOCUMENT_NODE;
|
---|
6116 | if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
|
---|
6117 | res->intSubset = xmlCreateIntSubset(res, doctype,
|
---|
6118 | doctypePublic,
|
---|
6119 | doctypeSystem);
|
---|
6120 | #ifdef XSLT_GENERATE_HTML_DOCTYPE
|
---|
6121 | } else if (version != NULL) {
|
---|
6122 | xsltGetHTMLIDs(version, &doctypePublic,
|
---|
6123 | &doctypeSystem);
|
---|
6124 | if (((doctypePublic != NULL) || (doctypeSystem != NULL)))
|
---|
6125 | res->intSubset =
|
---|
6126 | xmlCreateIntSubset(res, doctype,
|
---|
6127 | doctypePublic,
|
---|
6128 | doctypeSystem);
|
---|
6129 | #endif
|
---|
6130 | }
|
---|
6131 | }
|
---|
6132 |
|
---|
6133 | }
|
---|
6134 | if (ctxt->type == XSLT_OUTPUT_XML) {
|
---|
6135 | XSLT_GET_IMPORT_PTR(doctypePublic, style, doctypePublic)
|
---|
6136 | XSLT_GET_IMPORT_PTR(doctypeSystem, style, doctypeSystem)
|
---|
6137 | if (((doctypePublic != NULL) || (doctypeSystem != NULL))) {
|
---|
6138 | xmlNodePtr last;
|
---|
6139 | /* Need a small "hack" here to assure DTD comes before
|
---|
6140 | possible comment nodes */
|
---|
6141 | node = res->children;
|
---|
6142 | last = res->last;
|
---|
6143 | res->children = NULL;
|
---|
6144 | res->last = NULL;
|
---|
6145 | res->intSubset = xmlCreateIntSubset(res, doctype,
|
---|
6146 | doctypePublic,
|
---|
6147 | doctypeSystem);
|
---|
6148 | if (res->children != NULL) {
|
---|
6149 | res->children->next = node;
|
---|
6150 | node->prev = res->children;
|
---|
6151 | res->last = last;
|
---|
6152 | } else {
|
---|
6153 | res->children = node;
|
---|
6154 | res->last = last;
|
---|
6155 | }
|
---|
6156 | }
|
---|
6157 | }
|
---|
6158 | }
|
---|
6159 | xmlXPathFreeNodeSet(ctxt->nodeList);
|
---|
6160 | if (profile != NULL) {
|
---|
6161 | xsltSaveProfiling(ctxt, profile);
|
---|
6162 | }
|
---|
6163 |
|
---|
6164 | /*
|
---|
6165 | * Be pedantic.
|
---|
6166 | */
|
---|
6167 | if ((ctxt != NULL) && (ctxt->state == XSLT_STATE_ERROR)) {
|
---|
6168 | xmlFreeDoc(res);
|
---|
6169 | res = NULL;
|
---|
6170 | }
|
---|
6171 | if ((res != NULL) && (ctxt != NULL) && (output != NULL)) {
|
---|
6172 | int ret;
|
---|
6173 |
|
---|
6174 | ret = xsltCheckWrite(ctxt->sec, ctxt, (const xmlChar *) output);
|
---|
6175 | if (ret == 0) {
|
---|
6176 | xsltTransformError(ctxt, NULL, NULL,
|
---|
6177 | "xsltApplyStylesheet: forbidden to save to %s\n",
|
---|
6178 | output);
|
---|
6179 | } else if (ret < 0) {
|
---|
6180 | xsltTransformError(ctxt, NULL, NULL,
|
---|
6181 | "xsltApplyStylesheet: saving to %s may not be possible\n",
|
---|
6182 | output);
|
---|
6183 | }
|
---|
6184 | }
|
---|
6185 |
|
---|
6186 | #ifdef XSLT_DEBUG_PROFILE_CACHE
|
---|
6187 | printf("# Cache:\n");
|
---|
6188 | printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
|
---|
6189 | printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
|
---|
6190 | #endif
|
---|
6191 |
|
---|
6192 | if ((ctxt != NULL) && (userCtxt == NULL))
|
---|
6193 | xsltFreeTransformContext(ctxt);
|
---|
6194 |
|
---|
6195 | return (res);
|
---|
6196 |
|
---|
6197 | error:
|
---|
6198 | if (res != NULL)
|
---|
6199 | xmlFreeDoc(res);
|
---|
6200 |
|
---|
6201 | #ifdef XSLT_DEBUG_PROFILE_CACHE
|
---|
6202 | printf("# Cache:\n");
|
---|
6203 | printf("# Reused tree fragments: %d\n", ctxt->cache->dbgReusedRVTs);
|
---|
6204 | printf("# Reused variables : %d\n", ctxt->cache->dbgReusedVars);
|
---|
6205 | #endif
|
---|
6206 |
|
---|
6207 | if ((ctxt != NULL) && (userCtxt == NULL))
|
---|
6208 | xsltFreeTransformContext(ctxt);
|
---|
6209 | return (NULL);
|
---|
6210 | }
|
---|
6211 |
|
---|
6212 | /**
|
---|
6213 | * xsltApplyStylesheet:
|
---|
6214 | * @style: a parsed XSLT stylesheet
|
---|
6215 | * @doc: a parsed XML document
|
---|
6216 | * @params: a NULL terminated arry of parameters names/values tuples
|
---|
6217 | *
|
---|
6218 | * Apply the stylesheet to the document
|
---|
6219 | * NOTE: This may lead to a non-wellformed output XML wise !
|
---|
6220 | *
|
---|
6221 | * Returns the result document or NULL in case of error
|
---|
6222 | */
|
---|
6223 | xmlDocPtr
|
---|
6224 | xsltApplyStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
|
---|
6225 | const char **params)
|
---|
6226 | {
|
---|
6227 | return (xsltApplyStylesheetInternal(style, doc, params, NULL, NULL, NULL));
|
---|
6228 | }
|
---|
6229 |
|
---|
6230 | /**
|
---|
6231 | * xsltProfileStylesheet:
|
---|
6232 | * @style: a parsed XSLT stylesheet
|
---|
6233 | * @doc: a parsed XML document
|
---|
6234 | * @params: a NULL terminated arry of parameters names/values tuples
|
---|
6235 | * @output: a FILE * for the profiling output
|
---|
6236 | *
|
---|
6237 | * Apply the stylesheet to the document and dump the profiling to
|
---|
6238 | * the given output.
|
---|
6239 | *
|
---|
6240 | * Returns the result document or NULL in case of error
|
---|
6241 | */
|
---|
6242 | xmlDocPtr
|
---|
6243 | xsltProfileStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
|
---|
6244 | const char **params, FILE * output)
|
---|
6245 | {
|
---|
6246 | xmlDocPtr res;
|
---|
6247 |
|
---|
6248 | res = xsltApplyStylesheetInternal(style, doc, params, NULL, output, NULL);
|
---|
6249 | return (res);
|
---|
6250 | }
|
---|
6251 |
|
---|
6252 | /**
|
---|
6253 | * xsltApplyStylesheetUser:
|
---|
6254 | * @style: a parsed XSLT stylesheet
|
---|
6255 | * @doc: a parsed XML document
|
---|
6256 | * @params: a NULL terminated array of parameters names/values tuples
|
---|
6257 | * @output: the targetted output
|
---|
6258 | * @profile: profile FILE * output or NULL
|
---|
6259 | * @userCtxt: user provided transform context
|
---|
6260 | *
|
---|
6261 | * Apply the stylesheet to the document and allow the user to provide
|
---|
6262 | * its own transformation context.
|
---|
6263 | *
|
---|
6264 | * Returns the result document or NULL in case of error
|
---|
6265 | */
|
---|
6266 | xmlDocPtr
|
---|
6267 | xsltApplyStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
|
---|
6268 | const char **params, const char *output,
|
---|
6269 | FILE * profile, xsltTransformContextPtr userCtxt)
|
---|
6270 | {
|
---|
6271 | xmlDocPtr res;
|
---|
6272 |
|
---|
6273 | res = xsltApplyStylesheetInternal(style, doc, params, output,
|
---|
6274 | profile, userCtxt);
|
---|
6275 | return (res);
|
---|
6276 | }
|
---|
6277 |
|
---|
6278 | /**
|
---|
6279 | * xsltRunStylesheetUser:
|
---|
6280 | * @style: a parsed XSLT stylesheet
|
---|
6281 | * @doc: a parsed XML document
|
---|
6282 | * @params: a NULL terminated array of parameters names/values tuples
|
---|
6283 | * @output: the URL/filename ot the generated resource if available
|
---|
6284 | * @SAX: a SAX handler for progressive callback output (not implemented yet)
|
---|
6285 | * @IObuf: an output buffer for progressive output (not implemented yet)
|
---|
6286 | * @profile: profile FILE * output or NULL
|
---|
6287 | * @userCtxt: user provided transform context
|
---|
6288 | *
|
---|
6289 | * Apply the stylesheet to the document and generate the output according
|
---|
6290 | * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
|
---|
6291 | *
|
---|
6292 | * NOTE: This may lead to a non-wellformed output XML wise !
|
---|
6293 | * NOTE: This may also result in multiple files being generated
|
---|
6294 | * NOTE: using IObuf, the result encoding used will be the one used for
|
---|
6295 | * creating the output buffer, use the following macro to read it
|
---|
6296 | * from the stylesheet
|
---|
6297 | * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
|
---|
6298 | * NOTE: using SAX, any encoding specified in the stylesheet will be lost
|
---|
6299 | * since the interface uses only UTF8
|
---|
6300 | *
|
---|
6301 | * Returns the number of by written to the main resource or -1 in case of
|
---|
6302 | * error.
|
---|
6303 | */
|
---|
6304 | int
|
---|
6305 | xsltRunStylesheetUser(xsltStylesheetPtr style, xmlDocPtr doc,
|
---|
6306 | const char **params, const char *output,
|
---|
6307 | xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf,
|
---|
6308 | FILE * profile, xsltTransformContextPtr userCtxt)
|
---|
6309 | {
|
---|
6310 | xmlDocPtr tmp;
|
---|
6311 | int ret;
|
---|
6312 |
|
---|
6313 | if ((output == NULL) && (SAX == NULL) && (IObuf == NULL))
|
---|
6314 | return (-1);
|
---|
6315 | if ((SAX != NULL) && (IObuf != NULL))
|
---|
6316 | return (-1);
|
---|
6317 |
|
---|
6318 | /* unsupported yet */
|
---|
6319 | if (SAX != NULL) {
|
---|
6320 | XSLT_TODO /* xsltRunStylesheet xmlSAXHandlerPtr SAX */
|
---|
6321 | return (-1);
|
---|
6322 | }
|
---|
6323 |
|
---|
6324 | tmp = xsltApplyStylesheetInternal(style, doc, params, output, profile,
|
---|
6325 | userCtxt);
|
---|
6326 | if (tmp == NULL) {
|
---|
6327 | xsltTransformError(NULL, NULL, (xmlNodePtr) doc,
|
---|
6328 | "xsltRunStylesheet : run failed\n");
|
---|
6329 | return (-1);
|
---|
6330 | }
|
---|
6331 | if (IObuf != NULL) {
|
---|
6332 | /* TODO: incomplete, IObuf output not progressive */
|
---|
6333 | ret = xsltSaveResultTo(IObuf, tmp, style);
|
---|
6334 | } else {
|
---|
6335 | ret = xsltSaveResultToFilename(output, tmp, style, 0);
|
---|
6336 | }
|
---|
6337 | xmlFreeDoc(tmp);
|
---|
6338 | return (ret);
|
---|
6339 | }
|
---|
6340 |
|
---|
6341 | /**
|
---|
6342 | * xsltRunStylesheet:
|
---|
6343 | * @style: a parsed XSLT stylesheet
|
---|
6344 | * @doc: a parsed XML document
|
---|
6345 | * @params: a NULL terminated array of parameters names/values tuples
|
---|
6346 | * @output: the URL/filename ot the generated resource if available
|
---|
6347 | * @SAX: a SAX handler for progressive callback output (not implemented yet)
|
---|
6348 | * @IObuf: an output buffer for progressive output (not implemented yet)
|
---|
6349 | *
|
---|
6350 | * Apply the stylesheet to the document and generate the output according
|
---|
6351 | * to @output @SAX and @IObuf. It's an error to specify both @SAX and @IObuf.
|
---|
6352 | *
|
---|
6353 | * NOTE: This may lead to a non-wellformed output XML wise !
|
---|
6354 | * NOTE: This may also result in multiple files being generated
|
---|
6355 | * NOTE: using IObuf, the result encoding used will be the one used for
|
---|
6356 | * creating the output buffer, use the following macro to read it
|
---|
6357 | * from the stylesheet
|
---|
6358 | * XSLT_GET_IMPORT_PTR(encoding, style, encoding)
|
---|
6359 | * NOTE: using SAX, any encoding specified in the stylesheet will be lost
|
---|
6360 | * since the interface uses only UTF8
|
---|
6361 | *
|
---|
6362 | * Returns the number of bytes written to the main resource or -1 in case of
|
---|
6363 | * error.
|
---|
6364 | */
|
---|
6365 | int
|
---|
6366 | xsltRunStylesheet(xsltStylesheetPtr style, xmlDocPtr doc,
|
---|
6367 | const char **params, const char *output,
|
---|
6368 | xmlSAXHandlerPtr SAX, xmlOutputBufferPtr IObuf)
|
---|
6369 | {
|
---|
6370 | return(xsltRunStylesheetUser(style, doc, params, output, SAX, IObuf,
|
---|
6371 | NULL, NULL));
|
---|
6372 | }
|
---|
6373 |
|
---|
6374 | /**
|
---|
6375 | * xsltRegisterAllElement:
|
---|
6376 | * @ctxt: the XPath context
|
---|
6377 | *
|
---|
6378 | * Registers all default XSLT elements in this context
|
---|
6379 | */
|
---|
6380 | void
|
---|
6381 | xsltRegisterAllElement(xsltTransformContextPtr ctxt)
|
---|
6382 | {
|
---|
6383 | xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-templates",
|
---|
6384 | XSLT_NAMESPACE,
|
---|
6385 | (xsltTransformFunction) xsltApplyTemplates);
|
---|
6386 | xsltRegisterExtElement(ctxt, (const xmlChar *) "apply-imports",
|
---|
6387 | XSLT_NAMESPACE,
|
---|
6388 | (xsltTransformFunction) xsltApplyImports);
|
---|
6389 | xsltRegisterExtElement(ctxt, (const xmlChar *) "call-template",
|
---|
6390 | XSLT_NAMESPACE,
|
---|
6391 | (xsltTransformFunction) xsltCallTemplate);
|
---|
6392 | xsltRegisterExtElement(ctxt, (const xmlChar *) "element",
|
---|
6393 | XSLT_NAMESPACE,
|
---|
6394 | (xsltTransformFunction) xsltElement);
|
---|
6395 | xsltRegisterExtElement(ctxt, (const xmlChar *) "attribute",
|
---|
6396 | XSLT_NAMESPACE,
|
---|
6397 | (xsltTransformFunction) xsltAttribute);
|
---|
6398 | xsltRegisterExtElement(ctxt, (const xmlChar *) "text",
|
---|
6399 | XSLT_NAMESPACE,
|
---|
6400 | (xsltTransformFunction) xsltText);
|
---|
6401 | xsltRegisterExtElement(ctxt, (const xmlChar *) "processing-instruction",
|
---|
6402 | XSLT_NAMESPACE,
|
---|
6403 | (xsltTransformFunction) xsltProcessingInstruction);
|
---|
6404 | xsltRegisterExtElement(ctxt, (const xmlChar *) "comment",
|
---|
6405 | XSLT_NAMESPACE,
|
---|
6406 | (xsltTransformFunction) xsltComment);
|
---|
6407 | xsltRegisterExtElement(ctxt, (const xmlChar *) "copy",
|
---|
6408 | XSLT_NAMESPACE,
|
---|
6409 | (xsltTransformFunction) xsltCopy);
|
---|
6410 | xsltRegisterExtElement(ctxt, (const xmlChar *) "value-of",
|
---|
6411 | XSLT_NAMESPACE,
|
---|
6412 | (xsltTransformFunction) xsltValueOf);
|
---|
6413 | xsltRegisterExtElement(ctxt, (const xmlChar *) "number",
|
---|
6414 | XSLT_NAMESPACE,
|
---|
6415 | (xsltTransformFunction) xsltNumber);
|
---|
6416 | xsltRegisterExtElement(ctxt, (const xmlChar *) "for-each",
|
---|
6417 | XSLT_NAMESPACE,
|
---|
6418 | (xsltTransformFunction) xsltForEach);
|
---|
6419 | xsltRegisterExtElement(ctxt, (const xmlChar *) "if",
|
---|
6420 | XSLT_NAMESPACE,
|
---|
6421 | (xsltTransformFunction) xsltIf);
|
---|
6422 | xsltRegisterExtElement(ctxt, (const xmlChar *) "choose",
|
---|
6423 | XSLT_NAMESPACE,
|
---|
6424 | (xsltTransformFunction) xsltChoose);
|
---|
6425 | xsltRegisterExtElement(ctxt, (const xmlChar *) "sort",
|
---|
6426 | XSLT_NAMESPACE,
|
---|
6427 | (xsltTransformFunction) xsltSort);
|
---|
6428 | xsltRegisterExtElement(ctxt, (const xmlChar *) "copy-of",
|
---|
6429 | XSLT_NAMESPACE,
|
---|
6430 | (xsltTransformFunction) xsltCopyOf);
|
---|
6431 | xsltRegisterExtElement(ctxt, (const xmlChar *) "message",
|
---|
6432 | XSLT_NAMESPACE,
|
---|
6433 | (xsltTransformFunction) xsltMessage);
|
---|
6434 |
|
---|
6435 | /*
|
---|
6436 | * Those don't have callable entry points but are registered anyway
|
---|
6437 | */
|
---|
6438 | xsltRegisterExtElement(ctxt, (const xmlChar *) "variable",
|
---|
6439 | XSLT_NAMESPACE,
|
---|
6440 | (xsltTransformFunction) xsltDebug);
|
---|
6441 | xsltRegisterExtElement(ctxt, (const xmlChar *) "param",
|
---|
6442 | XSLT_NAMESPACE,
|
---|
6443 | (xsltTransformFunction) xsltDebug);
|
---|
6444 | xsltRegisterExtElement(ctxt, (const xmlChar *) "with-param",
|
---|
6445 | XSLT_NAMESPACE,
|
---|
6446 | (xsltTransformFunction) xsltDebug);
|
---|
6447 | xsltRegisterExtElement(ctxt, (const xmlChar *) "decimal-format",
|
---|
6448 | XSLT_NAMESPACE,
|
---|
6449 | (xsltTransformFunction) xsltDebug);
|
---|
6450 | xsltRegisterExtElement(ctxt, (const xmlChar *) "when",
|
---|
6451 | XSLT_NAMESPACE,
|
---|
6452 | (xsltTransformFunction) xsltDebug);
|
---|
6453 | xsltRegisterExtElement(ctxt, (const xmlChar *) "otherwise",
|
---|
6454 | XSLT_NAMESPACE,
|
---|
6455 | (xsltTransformFunction) xsltDebug);
|
---|
6456 | xsltRegisterExtElement(ctxt, (const xmlChar *) "fallback",
|
---|
6457 | XSLT_NAMESPACE,
|
---|
6458 | (xsltTransformFunction) xsltDebug);
|
---|
6459 |
|
---|
6460 | }
|
---|