templates.c 23 KB

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