/work/workdir/UnpackedTarball/libxslt/libxslt/templates.c
Line | Count | Source |
1 | | /* |
2 | | * templates.c: Implementation of the template processing |
3 | | * |
4 | | * Reference: |
5 | | * http://www.w3.org/TR/1999/REC-xslt-19991116 |
6 | | * |
7 | | * See Copyright for the status of this software. |
8 | | * |
9 | | * daniel@veillard.com |
10 | | */ |
11 | | |
12 | | #define IN_LIBXSLT |
13 | | #include "libxslt.h" |
14 | | |
15 | | #include <string.h> |
16 | | |
17 | | #include <libxml/xmlmemory.h> |
18 | | #include <libxml/globals.h> |
19 | | #include <libxml/xmlerror.h> |
20 | | #include <libxml/tree.h> |
21 | | #include <libxml/dict.h> |
22 | | #include <libxml/xpathInternals.h> |
23 | | #include <libxml/parserInternals.h> |
24 | | #include "xslt.h" |
25 | | #include "xsltInternals.h" |
26 | | #include "xsltutils.h" |
27 | | #include "variables.h" |
28 | | #include "functions.h" |
29 | | #include "templates.h" |
30 | | #include "transform.h" |
31 | | #include "namespaces.h" |
32 | | #include "attributes.h" |
33 | | |
34 | | #ifdef WITH_XSLT_DEBUG |
35 | | #define WITH_XSLT_DEBUG_TEMPLATES |
36 | | #endif |
37 | | |
38 | | /************************************************************************ |
39 | | * * |
40 | | * Module interfaces * |
41 | | * * |
42 | | ************************************************************************/ |
43 | | |
44 | | /** |
45 | | * xsltEvalXPathPredicate: |
46 | | * @ctxt: the XSLT transformation context |
47 | | * @comp: the XPath compiled expression |
48 | | * @nsList: the namespaces in scope |
49 | | * @nsNr: the number of namespaces in scope |
50 | | * |
51 | | * Process the expression using XPath and evaluate the result as |
52 | | * an XPath predicate |
53 | | * |
54 | | * Returns 1 is the predicate was true, 0 otherwise |
55 | | */ |
56 | | int |
57 | | xsltEvalXPathPredicate(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, |
58 | 0 | xmlNsPtr *nsList, int nsNr) { |
59 | 0 | int ret; |
60 | 0 | xmlXPathObjectPtr res; |
61 | 0 | int oldNsNr; |
62 | 0 | xmlNsPtr *oldNamespaces; |
63 | 0 | xmlNodePtr oldInst; |
64 | 0 | xmlNodePtr oldNode; |
65 | 0 | int oldProximityPosition, oldContextSize; |
66 | |
|
67 | 0 | if ((ctxt == NULL) || (ctxt->inst == NULL)) { |
68 | 0 | xsltTransformError(ctxt, NULL, NULL, |
69 | 0 | "xsltEvalXPathPredicate: No context or instruction\n"); |
70 | 0 | return(0); |
71 | 0 | } |
72 | | |
73 | 0 | oldNode = ctxt->xpathCtxt->node; |
74 | 0 | oldContextSize = ctxt->xpathCtxt->contextSize; |
75 | 0 | oldProximityPosition = ctxt->xpathCtxt->proximityPosition; |
76 | 0 | oldNsNr = ctxt->xpathCtxt->nsNr; |
77 | 0 | oldNamespaces = ctxt->xpathCtxt->namespaces; |
78 | 0 | oldInst = ctxt->inst; |
79 | |
|
80 | 0 | ctxt->xpathCtxt->node = ctxt->node; |
81 | 0 | ctxt->xpathCtxt->namespaces = nsList; |
82 | 0 | ctxt->xpathCtxt->nsNr = nsNr; |
83 | |
|
84 | 0 | res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); |
85 | |
|
86 | 0 | if (res != NULL) { |
87 | 0 | ret = xmlXPathEvalPredicate(ctxt->xpathCtxt, res); |
88 | 0 | xmlXPathFreeObject(res); |
89 | | #ifdef WITH_XSLT_DEBUG_TEMPLATES |
90 | | XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, |
91 | | "xsltEvalXPathPredicate: returns %d\n", ret)); |
92 | | #endif |
93 | 0 | } else { |
94 | | #ifdef WITH_XSLT_DEBUG_TEMPLATES |
95 | | XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, |
96 | | "xsltEvalXPathPredicate: failed\n")); |
97 | | #endif |
98 | 0 | ctxt->state = XSLT_STATE_STOPPED; |
99 | 0 | ret = 0; |
100 | 0 | } |
101 | |
|
102 | 0 | ctxt->xpathCtxt->node = oldNode; |
103 | 0 | ctxt->xpathCtxt->nsNr = oldNsNr; |
104 | 0 | ctxt->xpathCtxt->namespaces = oldNamespaces; |
105 | 0 | ctxt->inst = oldInst; |
106 | 0 | ctxt->xpathCtxt->contextSize = oldContextSize; |
107 | 0 | ctxt->xpathCtxt->proximityPosition = oldProximityPosition; |
108 | |
|
109 | 0 | return(ret); |
110 | 0 | } |
111 | | |
112 | | /** |
113 | | * xsltEvalXPathStringNs: |
114 | | * @ctxt: the XSLT transformation context |
115 | | * @comp: the compiled XPath expression |
116 | | * @nsNr: the number of namespaces in the list |
117 | | * @nsList: the list of in-scope namespaces to use |
118 | | * |
119 | | * Process the expression using XPath, allowing to pass a namespace mapping |
120 | | * context and get a string |
121 | | * |
122 | | * Returns the computed string value or NULL, must be deallocated by the |
123 | | * caller. |
124 | | */ |
125 | | xmlChar * |
126 | | xsltEvalXPathStringNs(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp, |
127 | 0 | int nsNr, xmlNsPtr *nsList) { |
128 | 0 | xmlChar *ret = NULL; |
129 | 0 | xmlXPathObjectPtr res; |
130 | 0 | xmlNodePtr oldInst; |
131 | 0 | xmlNodePtr oldNode; |
132 | 0 | int oldPos, oldSize; |
133 | 0 | int oldNsNr; |
134 | 0 | xmlNsPtr *oldNamespaces; |
135 | |
|
136 | 0 | if ((ctxt == NULL) || (ctxt->inst == NULL)) { |
137 | 0 | xsltTransformError(ctxt, NULL, NULL, |
138 | 0 | "xsltEvalXPathStringNs: No context or instruction\n"); |
139 | 0 | return(0); |
140 | 0 | } |
141 | | |
142 | 0 | oldInst = ctxt->inst; |
143 | 0 | oldNode = ctxt->xpathCtxt->node; |
144 | 0 | oldPos = ctxt->xpathCtxt->proximityPosition; |
145 | 0 | oldSize = ctxt->xpathCtxt->contextSize; |
146 | 0 | oldNsNr = ctxt->xpathCtxt->nsNr; |
147 | 0 | oldNamespaces = ctxt->xpathCtxt->namespaces; |
148 | |
|
149 | 0 | ctxt->xpathCtxt->node = ctxt->node; |
150 | | /* TODO: do we need to propagate the namespaces here ? */ |
151 | 0 | ctxt->xpathCtxt->namespaces = nsList; |
152 | 0 | ctxt->xpathCtxt->nsNr = nsNr; |
153 | 0 | res = xmlXPathCompiledEval(comp, ctxt->xpathCtxt); |
154 | 0 | if (res != NULL) { |
155 | 0 | if (res->type != XPATH_STRING) |
156 | 0 | res = xmlXPathConvertString(res); |
157 | 0 | if ((res != NULL) && (res->type == XPATH_STRING)) { |
158 | 0 | ret = res->stringval; |
159 | 0 | res->stringval = NULL; |
160 | 0 | } else { |
161 | 0 | xsltTransformError(ctxt, NULL, NULL, |
162 | 0 | "xpath : string() function didn't return a String\n"); |
163 | 0 | } |
164 | 0 | xmlXPathFreeObject(res); |
165 | 0 | } else { |
166 | 0 | ctxt->state = XSLT_STATE_STOPPED; |
167 | 0 | } |
168 | | #ifdef WITH_XSLT_DEBUG_TEMPLATES |
169 | | XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, |
170 | | "xsltEvalXPathString: returns %s\n", ret)); |
171 | | #endif |
172 | 0 | ctxt->inst = oldInst; |
173 | 0 | ctxt->xpathCtxt->node = oldNode; |
174 | 0 | ctxt->xpathCtxt->contextSize = oldSize; |
175 | 0 | ctxt->xpathCtxt->proximityPosition = oldPos; |
176 | 0 | ctxt->xpathCtxt->nsNr = oldNsNr; |
177 | 0 | ctxt->xpathCtxt->namespaces = oldNamespaces; |
178 | 0 | return(ret); |
179 | 0 | } |
180 | | |
181 | | /** |
182 | | * xsltEvalXPathString: |
183 | | * @ctxt: the XSLT transformation context |
184 | | * @comp: the compiled XPath expression |
185 | | * |
186 | | * Process the expression using XPath and get a string |
187 | | * |
188 | | * Returns the computed string value or NULL, must be deallocated by the |
189 | | * caller. |
190 | | */ |
191 | | xmlChar * |
192 | 0 | xsltEvalXPathString(xsltTransformContextPtr ctxt, xmlXPathCompExprPtr comp) { |
193 | 0 | return(xsltEvalXPathStringNs(ctxt, comp, 0, NULL)); |
194 | 0 | } |
195 | | |
196 | | /** |
197 | | * xsltEvalTemplateString: |
198 | | * @ctxt: the XSLT transformation context |
199 | | * @contextNode: the current node in the source tree |
200 | | * @inst: the XSLT instruction (xsl:comment, xsl:processing-instruction) |
201 | | * |
202 | | * Processes the sequence constructor of the given instruction on |
203 | | * @contextNode and converts the resulting tree to a string. |
204 | | * This is needed by e.g. xsl:comment and xsl:processing-instruction. |
205 | | * |
206 | | * Returns the computed string value or NULL; it's up to the caller to |
207 | | * free the result. |
208 | | */ |
209 | | xmlChar * |
210 | | xsltEvalTemplateString(xsltTransformContextPtr ctxt, |
211 | | xmlNodePtr contextNode, |
212 | | xmlNodePtr inst) |
213 | 0 | { |
214 | 0 | xmlNodePtr oldInsert, insert = NULL; |
215 | 0 | xmlChar *ret; |
216 | 0 | const xmlChar *oldLastText; |
217 | 0 | int oldLastTextSize, oldLastTextUse; |
218 | |
|
219 | 0 | if ((ctxt == NULL) || (contextNode == NULL) || (inst == NULL) || |
220 | 0 | (inst->type != XML_ELEMENT_NODE)) |
221 | 0 | return(NULL); |
222 | | |
223 | 0 | if (inst->children == NULL) |
224 | 0 | return(NULL); |
225 | | |
226 | | /* |
227 | | * This creates a temporary element-node to add the resulting |
228 | | * text content to. |
229 | | * OPTIMIZE TODO: Keep such an element-node in the transformation |
230 | | * context to avoid creating it every time. |
231 | | */ |
232 | 0 | insert = xmlNewDocNode(ctxt->output, NULL, |
233 | 0 | (const xmlChar *)"fake", NULL); |
234 | 0 | if (insert == NULL) { |
235 | 0 | xsltTransformError(ctxt, NULL, inst, |
236 | 0 | "Failed to create temporary node\n"); |
237 | 0 | return(NULL); |
238 | 0 | } |
239 | 0 | oldInsert = ctxt->insert; |
240 | 0 | ctxt->insert = insert; |
241 | 0 | oldLastText = ctxt->lasttext; |
242 | 0 | oldLastTextSize = ctxt->lasttsize; |
243 | 0 | oldLastTextUse = ctxt->lasttuse; |
244 | | /* |
245 | | * OPTIMIZE TODO: if inst->children consists only of text-nodes. |
246 | | */ |
247 | 0 | xsltApplyOneTemplate(ctxt, contextNode, inst->children, NULL, NULL); |
248 | |
|
249 | 0 | ctxt->insert = oldInsert; |
250 | 0 | ctxt->lasttext = oldLastText; |
251 | 0 | ctxt->lasttsize = oldLastTextSize; |
252 | 0 | ctxt->lasttuse = oldLastTextUse; |
253 | |
|
254 | 0 | ret = xmlNodeGetContent(insert); |
255 | 0 | if (insert != NULL) |
256 | 0 | xmlFreeNode(insert); |
257 | 0 | return(ret); |
258 | 0 | } |
259 | | |
260 | | /** |
261 | | * xsltAttrTemplateValueProcessNode: |
262 | | * @ctxt: the XSLT transformation context |
263 | | * @str: the attribute template node value |
264 | | * @inst: the instruction (or LRE) in the stylesheet holding the |
265 | | * attribute with an AVT |
266 | | * |
267 | | * Process the given string, allowing to pass a namespace mapping |
268 | | * context and return the new string value. |
269 | | * |
270 | | * Called by: |
271 | | * - xsltAttrTemplateValueProcess() (templates.c) |
272 | | * - xsltEvalAttrValueTemplate() (templates.c) |
273 | | * |
274 | | * QUESTION: Why is this function public? It is not used outside |
275 | | * of templates.c. |
276 | | * |
277 | | * Returns the computed string value or NULL, must be deallocated by the |
278 | | * caller. |
279 | | */ |
280 | | xmlChar * |
281 | | xsltAttrTemplateValueProcessNode(xsltTransformContextPtr ctxt, |
282 | | const xmlChar *str, xmlNodePtr inst) |
283 | 0 | { |
284 | 0 | xmlChar *ret = NULL; |
285 | 0 | const xmlChar *cur; |
286 | 0 | xmlChar *expr, *val; |
287 | 0 | xmlNsPtr *nsList = NULL; |
288 | 0 | int nsNr = 0; |
289 | |
|
290 | 0 | if (str == NULL) return(NULL); |
291 | 0 | if (*str == 0) |
292 | 0 | return(xmlStrndup((xmlChar *)"", 0)); |
293 | | |
294 | 0 | cur = str; |
295 | 0 | while (*cur != 0) { |
296 | 0 | if (*cur == '{') { |
297 | 0 | if (*(cur+1) == '{') { /* escaped '{' */ |
298 | 0 | cur++; |
299 | 0 | ret = xmlStrncat(ret, str, cur - str); |
300 | 0 | cur++; |
301 | 0 | str = cur; |
302 | 0 | continue; |
303 | 0 | } |
304 | 0 | ret = xmlStrncat(ret, str, cur - str); |
305 | 0 | str = cur; |
306 | 0 | cur++; |
307 | 0 | while ((*cur != 0) && (*cur != '}')) { |
308 | | /* Need to check for literal (bug539741) */ |
309 | 0 | if ((*cur == '\'') || (*cur == '"')) { |
310 | 0 | char delim = *(cur++); |
311 | 0 | while ((*cur != 0) && (*cur != delim)) |
312 | 0 | cur++; |
313 | 0 | if (*cur != 0) |
314 | 0 | cur++; /* skip the ending delimiter */ |
315 | 0 | } else |
316 | 0 | cur++; |
317 | 0 | } |
318 | 0 | if (*cur == 0) { |
319 | 0 | xsltTransformError(ctxt, NULL, inst, |
320 | 0 | "xsltAttrTemplateValueProcessNode: unmatched '{'\n"); |
321 | 0 | ret = xmlStrncat(ret, str, cur - str); |
322 | 0 | goto exit; |
323 | 0 | } |
324 | 0 | str++; |
325 | 0 | expr = xmlStrndup(str, cur - str); |
326 | 0 | if (expr == NULL) |
327 | 0 | goto exit; |
328 | 0 | else if (*expr == '{') { |
329 | 0 | ret = xmlStrcat(ret, expr); |
330 | 0 | xmlFree(expr); |
331 | 0 | } else { |
332 | 0 | xmlXPathCompExprPtr comp; |
333 | | /* |
334 | | * TODO: keep precompiled form around |
335 | | */ |
336 | 0 | if ((nsList == NULL) && (inst != NULL)) { |
337 | 0 | int i = 0; |
338 | |
|
339 | 0 | nsList = xmlGetNsList(inst->doc, inst); |
340 | 0 | if (nsList != NULL) { |
341 | 0 | while (nsList[i] != NULL) |
342 | 0 | i++; |
343 | 0 | nsNr = i; |
344 | 0 | } |
345 | 0 | } |
346 | 0 | comp = xmlXPathCtxtCompile(ctxt->xpathCtxt, expr); |
347 | 0 | val = xsltEvalXPathStringNs(ctxt, comp, nsNr, nsList); |
348 | 0 | xmlXPathFreeCompExpr(comp); |
349 | 0 | xmlFree(expr); |
350 | 0 | if (val != NULL) { |
351 | 0 | ret = xmlStrcat(ret, val); |
352 | 0 | xmlFree(val); |
353 | 0 | } |
354 | 0 | } |
355 | 0 | cur++; |
356 | 0 | str = cur; |
357 | 0 | } else if (*cur == '}') { |
358 | 0 | cur++; |
359 | 0 | if (*cur == '}') { /* escaped '}' */ |
360 | 0 | ret = xmlStrncat(ret, str, cur - str); |
361 | 0 | cur++; |
362 | 0 | str = cur; |
363 | 0 | continue; |
364 | 0 | } else { |
365 | 0 | xsltTransformError(ctxt, NULL, inst, |
366 | 0 | "xsltAttrTemplateValueProcessNode: unmatched '}'\n"); |
367 | 0 | } |
368 | 0 | } else |
369 | 0 | cur++; |
370 | 0 | } |
371 | 0 | if (cur != str) { |
372 | 0 | ret = xmlStrncat(ret, str, cur - str); |
373 | 0 | } |
374 | |
|
375 | 0 | exit: |
376 | 0 | if (nsList != NULL) |
377 | 0 | xmlFree(nsList); |
378 | |
|
379 | 0 | return(ret); |
380 | 0 | } |
381 | | |
382 | | /** |
383 | | * xsltAttrTemplateValueProcess: |
384 | | * @ctxt: the XSLT transformation context |
385 | | * @str: the attribute template node value |
386 | | * |
387 | | * Process the given node and return the new string value. |
388 | | * |
389 | | * Returns the computed string value or NULL, must be deallocated by the |
390 | | * caller. |
391 | | */ |
392 | | xmlChar * |
393 | 0 | xsltAttrTemplateValueProcess(xsltTransformContextPtr ctxt, const xmlChar *str) { |
394 | 0 | return(xsltAttrTemplateValueProcessNode(ctxt, str, NULL)); |
395 | 0 | } |
396 | | |
397 | | /** |
398 | | * xsltEvalAttrValueTemplate: |
399 | | * @ctxt: the XSLT transformation context |
400 | | * @inst: the instruction (or LRE) in the stylesheet holding the |
401 | | * attribute with an AVT |
402 | | * @name: the attribute QName |
403 | | * @ns: the attribute namespace URI |
404 | | * |
405 | | * Evaluate a attribute value template, i.e. the attribute value can |
406 | | * contain expressions contained in curly braces ({}) and those are |
407 | | * substituted by they computed value. |
408 | | * |
409 | | * Returns the computed string value or NULL, must be deallocated by the |
410 | | * caller. |
411 | | */ |
412 | | xmlChar * |
413 | | xsltEvalAttrValueTemplate(xsltTransformContextPtr ctxt, xmlNodePtr inst, |
414 | | const xmlChar *name, const xmlChar *ns) |
415 | 0 | { |
416 | 0 | xmlChar *ret; |
417 | 0 | xmlChar *expr; |
418 | |
|
419 | 0 | if ((ctxt == NULL) || (inst == NULL) || (name == NULL) || |
420 | 0 | (inst->type != XML_ELEMENT_NODE)) |
421 | 0 | return(NULL); |
422 | | |
423 | 0 | expr = xsltGetNsProp(inst, name, ns); |
424 | 0 | if (expr == NULL) |
425 | 0 | return(NULL); |
426 | | |
427 | | /* |
428 | | * TODO: though now {} is detected ahead, it would still be good to |
429 | | * optimize both functions to keep the splitted value if the |
430 | | * attribute content and the XPath precompiled expressions around |
431 | | */ |
432 | | |
433 | 0 | ret = xsltAttrTemplateValueProcessNode(ctxt, expr, inst); |
434 | | #ifdef WITH_XSLT_DEBUG_TEMPLATES |
435 | | XSLT_TRACE(ctxt,XSLT_TRACE_TEMPLATES,xsltGenericDebug(xsltGenericDebugContext, |
436 | | "xsltEvalAttrValueTemplate: %s returns %s\n", expr, ret)); |
437 | | #endif |
438 | 0 | if (expr != NULL) |
439 | 0 | xmlFree(expr); |
440 | 0 | return(ret); |
441 | 0 | } |
442 | | |
443 | | /** |
444 | | * xsltEvalStaticAttrValueTemplate: |
445 | | * @style: the XSLT stylesheet |
446 | | * @inst: the instruction (or LRE) in the stylesheet holding the |
447 | | * attribute with an AVT |
448 | | * @name: the attribute Name |
449 | | * @ns: the attribute namespace URI |
450 | | * @found: indicator whether the attribute is present |
451 | | * |
452 | | * Check if an attribute value template has a static value, i.e. the |
453 | | * attribute value does not contain expressions contained in curly braces ({}) |
454 | | * |
455 | | * Returns the static string value or NULL, must be deallocated by the |
456 | | * caller. |
457 | | */ |
458 | | const xmlChar * |
459 | | xsltEvalStaticAttrValueTemplate(xsltStylesheetPtr style, xmlNodePtr inst, |
460 | 0 | const xmlChar *name, const xmlChar *ns, int *found) { |
461 | 0 | const xmlChar *ret; |
462 | 0 | xmlChar *expr; |
463 | |
|
464 | 0 | if ((style == NULL) || (inst == NULL) || (name == NULL) || |
465 | 0 | (inst->type != XML_ELEMENT_NODE)) |
466 | 0 | return(NULL); |
467 | | |
468 | 0 | expr = xsltGetNsProp(inst, name, ns); |
469 | 0 | if (expr == NULL) { |
470 | 0 | *found = 0; |
471 | 0 | return(NULL); |
472 | 0 | } |
473 | 0 | *found = 1; |
474 | |
|
475 | 0 | ret = xmlStrchr(expr, '{'); |
476 | 0 | if (ret != NULL) { |
477 | 0 | xmlFree(expr); |
478 | 0 | return(NULL); |
479 | 0 | } |
480 | 0 | ret = xmlDictLookup(style->dict, expr, -1); |
481 | 0 | xmlFree(expr); |
482 | 0 | return(ret); |
483 | 0 | } |
484 | | |
485 | | /** |
486 | | * xsltAttrTemplateProcess: |
487 | | * @ctxt: the XSLT transformation context |
488 | | * @target: the element where the attribute will be grafted |
489 | | * @attr: the attribute node of a literal result element |
490 | | * |
491 | | * Process one attribute of a Literal Result Element (in the stylesheet). |
492 | | * Evaluates Attribute Value Templates and copies the attribute over to |
493 | | * the result element. |
494 | | * This does *not* process attribute sets (xsl:use-attribute-set). |
495 | | * |
496 | | * |
497 | | * Returns the generated attribute node. |
498 | | */ |
499 | | xmlAttrPtr |
500 | | xsltAttrTemplateProcess(xsltTransformContextPtr ctxt, xmlNodePtr target, |
501 | | xmlAttrPtr attr) |
502 | 0 | { |
503 | 0 | const xmlChar *value; |
504 | 0 | xmlAttrPtr ret; |
505 | |
|
506 | 0 | if ((ctxt == NULL) || (attr == NULL) || (target == NULL) || |
507 | 0 | (target->type != XML_ELEMENT_NODE)) |
508 | 0 | return(NULL); |
509 | | |
510 | 0 | if (attr->type != XML_ATTRIBUTE_NODE) |
511 | 0 | return(NULL); |
512 | | |
513 | | /* |
514 | | * Skip all XSLT attributes. |
515 | | */ |
516 | | #ifdef XSLT_REFACTORED |
517 | | if (attr->psvi == xsltXSLTAttrMarker) |
518 | | return(NULL); |
519 | | #else |
520 | 0 | if ((attr->ns != NULL) && xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) |
521 | 0 | return(NULL); |
522 | 0 | #endif |
523 | | /* |
524 | | * Get the value. |
525 | | */ |
526 | 0 | if (attr->children != NULL) { |
527 | 0 | if ((attr->children->type != XML_TEXT_NODE) || |
528 | 0 | (attr->children->next != NULL)) |
529 | 0 | { |
530 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
531 | 0 | "Internal error: The children of an attribute node of a " |
532 | 0 | "literal result element are not in the expected form.\n"); |
533 | 0 | return(NULL); |
534 | 0 | } |
535 | 0 | value = attr->children->content; |
536 | 0 | if (value == NULL) |
537 | 0 | value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); |
538 | 0 | } else |
539 | 0 | value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); |
540 | | /* |
541 | | * Overwrite duplicates. |
542 | | */ |
543 | 0 | ret = target->properties; |
544 | 0 | while (ret != NULL) { |
545 | 0 | if (((attr->ns != NULL) == (ret->ns != NULL)) && |
546 | 0 | xmlStrEqual(ret->name, attr->name) && |
547 | 0 | ((attr->ns == NULL) || xmlStrEqual(ret->ns->href, attr->ns->href))) |
548 | 0 | { |
549 | 0 | break; |
550 | 0 | } |
551 | 0 | ret = ret->next; |
552 | 0 | } |
553 | 0 | if (ret != NULL) { |
554 | | /* free the existing value */ |
555 | 0 | xmlFreeNodeList(ret->children); |
556 | 0 | ret->children = ret->last = NULL; |
557 | | /* |
558 | | * Adjust ns-prefix if needed. |
559 | | */ |
560 | 0 | if ((ret->ns != NULL) && |
561 | 0 | (! xmlStrEqual(ret->ns->prefix, attr->ns->prefix))) |
562 | 0 | { |
563 | 0 | ret->ns = xsltGetNamespace(ctxt, attr->parent, attr->ns, target); |
564 | 0 | } |
565 | 0 | } else { |
566 | | /* create a new attribute */ |
567 | 0 | if (attr->ns != NULL) |
568 | 0 | ret = xmlNewNsProp(target, |
569 | 0 | xsltGetNamespace(ctxt, attr->parent, attr->ns, target), |
570 | 0 | attr->name, NULL); |
571 | 0 | else |
572 | 0 | ret = xmlNewNsProp(target, NULL, attr->name, NULL); |
573 | 0 | } |
574 | | /* |
575 | | * Set the value. |
576 | | */ |
577 | 0 | if (ret != NULL) { |
578 | 0 | xmlNodePtr text; |
579 | |
|
580 | 0 | text = xmlNewText(NULL); |
581 | 0 | if (text != NULL) { |
582 | 0 | ret->last = ret->children = text; |
583 | 0 | text->parent = (xmlNodePtr) ret; |
584 | 0 | text->doc = ret->doc; |
585 | |
|
586 | 0 | if (attr->psvi != NULL) { |
587 | | /* |
588 | | * Evaluate the Attribute Value Template. |
589 | | */ |
590 | 0 | xmlChar *val; |
591 | 0 | val = xsltEvalAVT(ctxt, attr->psvi, attr->parent); |
592 | 0 | if (val == NULL) { |
593 | | /* |
594 | | * TODO: Damn, we need an easy mechanism to report |
595 | | * qualified names! |
596 | | */ |
597 | 0 | if (attr->ns) { |
598 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
599 | 0 | "Internal error: Failed to evaluate the AVT " |
600 | 0 | "of attribute '{%s}%s'.\n", |
601 | 0 | attr->ns->href, attr->name); |
602 | 0 | } else { |
603 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
604 | 0 | "Internal error: Failed to evaluate the AVT " |
605 | 0 | "of attribute '%s'.\n", |
606 | 0 | attr->name); |
607 | 0 | } |
608 | 0 | text->content = xmlStrdup(BAD_CAST ""); |
609 | 0 | } else { |
610 | 0 | text->content = val; |
611 | 0 | } |
612 | 0 | } else if ((ctxt->internalized) && (target != NULL) && |
613 | 0 | (target->doc != NULL) && |
614 | 0 | (target->doc->dict == ctxt->dict) && |
615 | 0 | xmlDictOwns(ctxt->dict, value)) { |
616 | 0 | text->content = (xmlChar *) value; |
617 | 0 | } else { |
618 | 0 | text->content = xmlStrdup(value); |
619 | 0 | } |
620 | 0 | } |
621 | 0 | } else { |
622 | 0 | if (attr->ns) { |
623 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
624 | 0 | "Internal error: Failed to create attribute '{%s}%s'.\n", |
625 | 0 | attr->ns->href, attr->name); |
626 | 0 | } else { |
627 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
628 | 0 | "Internal error: Failed to create attribute '%s'.\n", |
629 | 0 | attr->name); |
630 | 0 | } |
631 | 0 | } |
632 | 0 | return(ret); |
633 | 0 | } |
634 | | |
635 | | |
636 | | /** |
637 | | * xsltAttrListTemplateProcess: |
638 | | * @ctxt: the XSLT transformation context |
639 | | * @target: the element where the attributes will be grafted |
640 | | * @attrs: the first attribute |
641 | | * |
642 | | * Processes all attributes of a Literal Result Element. |
643 | | * Attribute references are applied via xsl:use-attribute-set |
644 | | * attributes. |
645 | | * Copies all non XSLT-attributes over to the @target element |
646 | | * and evaluates Attribute Value Templates. |
647 | | * |
648 | | * Called by xsltApplySequenceConstructor() (transform.c). |
649 | | * |
650 | | * Returns a new list of attribute nodes, or NULL in case of error. |
651 | | * (Don't assign the result to @target->properties; if |
652 | | * the result is NULL, you'll get memory leaks, since the |
653 | | * attributes will be disattached.) |
654 | | */ |
655 | | xmlAttrPtr |
656 | | xsltAttrListTemplateProcess(xsltTransformContextPtr ctxt, |
657 | | xmlNodePtr target, xmlAttrPtr attrs) |
658 | 0 | { |
659 | 0 | xmlAttrPtr attr, copy, last = NULL; |
660 | 0 | xmlNodePtr oldInsert, text; |
661 | 0 | xmlNsPtr origNs = NULL, copyNs = NULL; |
662 | 0 | const xmlChar *value; |
663 | 0 | xmlChar *valueAVT; |
664 | 0 | int hasAttr = 0; |
665 | |
|
666 | 0 | if ((ctxt == NULL) || (target == NULL) || (attrs == NULL) || |
667 | 0 | (target->type != XML_ELEMENT_NODE)) |
668 | 0 | return(NULL); |
669 | | |
670 | 0 | oldInsert = ctxt->insert; |
671 | 0 | ctxt->insert = target; |
672 | | |
673 | | /* |
674 | | * Apply attribute-sets. |
675 | | */ |
676 | 0 | attr = attrs; |
677 | 0 | do { |
678 | | #ifdef XSLT_REFACTORED |
679 | | if ((attr->psvi == xsltXSLTAttrMarker) && |
680 | | xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets")) |
681 | | { |
682 | | xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); |
683 | | } |
684 | | #else |
685 | 0 | if ((attr->ns != NULL) && |
686 | 0 | xmlStrEqual(attr->name, (const xmlChar *)"use-attribute-sets") && |
687 | 0 | xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) |
688 | 0 | { |
689 | 0 | xsltApplyAttributeSet(ctxt, ctxt->node, (xmlNodePtr) attr, NULL); |
690 | 0 | } |
691 | 0 | #endif |
692 | 0 | attr = attr->next; |
693 | 0 | } while (attr != NULL); |
694 | |
|
695 | 0 | if (target->properties != NULL) { |
696 | 0 | hasAttr = 1; |
697 | 0 | } |
698 | | |
699 | | /* |
700 | | * Instantiate LRE-attributes. |
701 | | */ |
702 | 0 | attr = attrs; |
703 | 0 | do { |
704 | | /* |
705 | | * Skip XSLT attributes. |
706 | | */ |
707 | | #ifdef XSLT_REFACTORED |
708 | | if (attr->psvi == xsltXSLTAttrMarker) { |
709 | | goto next_attribute; |
710 | | } |
711 | | #else |
712 | 0 | if ((attr->ns != NULL) && |
713 | 0 | xmlStrEqual(attr->ns->href, XSLT_NAMESPACE)) |
714 | 0 | { |
715 | 0 | goto next_attribute; |
716 | 0 | } |
717 | 0 | #endif |
718 | | /* |
719 | | * Get the value. |
720 | | */ |
721 | 0 | if (attr->children != NULL) { |
722 | 0 | if ((attr->children->type != XML_TEXT_NODE) || |
723 | 0 | (attr->children->next != NULL)) |
724 | 0 | { |
725 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
726 | 0 | "Internal error: The children of an attribute node of a " |
727 | 0 | "literal result element are not in the expected form.\n"); |
728 | 0 | goto error; |
729 | 0 | } |
730 | 0 | value = attr->children->content; |
731 | 0 | if (value == NULL) |
732 | 0 | value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); |
733 | 0 | } else |
734 | 0 | value = xmlDictLookup(ctxt->dict, BAD_CAST "", 0); |
735 | | |
736 | | /* |
737 | | * Get the namespace. Avoid lookups of same namespaces. |
738 | | */ |
739 | 0 | if (attr->ns != origNs) { |
740 | 0 | origNs = attr->ns; |
741 | 0 | if (attr->ns != NULL) { |
742 | | #ifdef XSLT_REFACTORED |
743 | | copyNs = xsltGetSpecialNamespace(ctxt, attr->parent, |
744 | | attr->ns->href, attr->ns->prefix, target); |
745 | | #else |
746 | 0 | copyNs = xsltGetNamespace(ctxt, attr->parent, |
747 | 0 | attr->ns, target); |
748 | 0 | #endif |
749 | 0 | if (copyNs == NULL) |
750 | 0 | goto error; |
751 | 0 | } else |
752 | 0 | copyNs = NULL; |
753 | 0 | } |
754 | | /* |
755 | | * Create a new attribute. |
756 | | */ |
757 | 0 | if (hasAttr) { |
758 | 0 | copy = xmlSetNsProp(target, copyNs, attr->name, NULL); |
759 | 0 | } else { |
760 | | /* |
761 | | * Avoid checking for duplicate attributes if there aren't |
762 | | * any attribute sets. |
763 | | */ |
764 | 0 | copy = xmlNewDocProp(target->doc, attr->name, NULL); |
765 | |
|
766 | 0 | if (copy != NULL) { |
767 | 0 | copy->ns = copyNs; |
768 | | |
769 | | /* |
770 | | * Attach it to the target element. |
771 | | */ |
772 | 0 | copy->parent = target; |
773 | 0 | if (last == NULL) { |
774 | 0 | target->properties = copy; |
775 | 0 | last = copy; |
776 | 0 | } else { |
777 | 0 | last->next = copy; |
778 | 0 | copy->prev = last; |
779 | 0 | last = copy; |
780 | 0 | } |
781 | 0 | } |
782 | 0 | } |
783 | 0 | if (copy == NULL) { |
784 | 0 | if (attr->ns) { |
785 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
786 | 0 | "Internal error: Failed to create attribute '{%s}%s'.\n", |
787 | 0 | attr->ns->href, attr->name); |
788 | 0 | } else { |
789 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
790 | 0 | "Internal error: Failed to create attribute '%s'.\n", |
791 | 0 | attr->name); |
792 | 0 | } |
793 | 0 | goto error; |
794 | 0 | } |
795 | | |
796 | | /* |
797 | | * Set the value. |
798 | | */ |
799 | 0 | text = xmlNewText(NULL); |
800 | 0 | if (text != NULL) { |
801 | 0 | copy->last = copy->children = text; |
802 | 0 | text->parent = (xmlNodePtr) copy; |
803 | 0 | text->doc = copy->doc; |
804 | |
|
805 | 0 | if (attr->psvi != NULL) { |
806 | | /* |
807 | | * Evaluate the Attribute Value Template. |
808 | | */ |
809 | 0 | valueAVT = xsltEvalAVT(ctxt, attr->psvi, attr->parent); |
810 | 0 | if (valueAVT == NULL) { |
811 | | /* |
812 | | * TODO: Damn, we need an easy mechanism to report |
813 | | * qualified names! |
814 | | */ |
815 | 0 | if (attr->ns) { |
816 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
817 | 0 | "Internal error: Failed to evaluate the AVT " |
818 | 0 | "of attribute '{%s}%s'.\n", |
819 | 0 | attr->ns->href, attr->name); |
820 | 0 | } else { |
821 | 0 | xsltTransformError(ctxt, NULL, attr->parent, |
822 | 0 | "Internal error: Failed to evaluate the AVT " |
823 | 0 | "of attribute '%s'.\n", |
824 | 0 | attr->name); |
825 | 0 | } |
826 | 0 | text->content = xmlStrdup(BAD_CAST ""); |
827 | 0 | goto error; |
828 | 0 | } else { |
829 | 0 | text->content = valueAVT; |
830 | 0 | } |
831 | 0 | } else if ((ctxt->internalized) && |
832 | 0 | (target->doc != NULL) && |
833 | 0 | (target->doc->dict == ctxt->dict) && |
834 | 0 | xmlDictOwns(ctxt->dict, value)) |
835 | 0 | { |
836 | 0 | text->content = (xmlChar *) value; |
837 | 0 | } else { |
838 | 0 | text->content = xmlStrdup(value); |
839 | 0 | } |
840 | 0 | if ((copy != NULL) && (text != NULL) && |
841 | 0 | (xmlIsID(copy->doc, copy->parent, copy))) |
842 | 0 | xmlAddID(NULL, copy->doc, text->content, copy); |
843 | 0 | } |
844 | | |
845 | 0 | next_attribute: |
846 | 0 | attr = attr->next; |
847 | 0 | } while (attr != NULL); |
848 | | |
849 | 0 | ctxt->insert = oldInsert; |
850 | 0 | return(target->properties); |
851 | | |
852 | 0 | error: |
853 | 0 | ctxt->insert = oldInsert; |
854 | 0 | return(NULL); |
855 | 0 | } |
856 | | |
857 | | |
858 | | /** |
859 | | * xsltTemplateProcess: |
860 | | * @ctxt: the XSLT transformation context |
861 | | * @node: the attribute template node |
862 | | * |
863 | | * Obsolete. Don't use it. |
864 | | * |
865 | | * Returns NULL. |
866 | | */ |
867 | | xmlNodePtr * |
868 | 0 | xsltTemplateProcess(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { |
869 | 0 | if (node == NULL) |
870 | 0 | return(NULL); |
871 | | |
872 | 0 | return(0); |
873 | 0 | } |
874 | | |
875 | | |