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