keys.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  1. /*
  2. * keys.c: Implemetation of the keys support
  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/tree.h>
  16. #include <libxml/valid.h>
  17. #include <libxml/hash.h>
  18. #include <libxml/xmlerror.h>
  19. #include <libxml/parserInternals.h>
  20. #include <libxml/xpathInternals.h>
  21. #include <libxml/xpath.h>
  22. #include "xslt.h"
  23. #include "xsltInternals.h"
  24. #include "xsltutils.h"
  25. #include "imports.h"
  26. #include "templates.h"
  27. #include "keys.h"
  28. #ifdef WITH_XSLT_DEBUG
  29. #define WITH_XSLT_DEBUG_KEYS
  30. #endif
  31. static int
  32. xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
  33. const xmlChar *nameURI);
  34. /************************************************************************
  35. * *
  36. * Type functions *
  37. * *
  38. ************************************************************************/
  39. /**
  40. * xsltNewKeyDef:
  41. * @name: the key name or NULL
  42. * @nameURI: the name URI or NULL
  43. *
  44. * Create a new XSLT KeyDef
  45. *
  46. * Returns the newly allocated xsltKeyDefPtr or NULL in case of error
  47. */
  48. static xsltKeyDefPtr
  49. xsltNewKeyDef(const xmlChar *name, const xmlChar *nameURI) {
  50. xsltKeyDefPtr cur;
  51. cur = (xsltKeyDefPtr) xmlMalloc(sizeof(xsltKeyDef));
  52. if (cur == NULL) {
  53. xsltTransformError(NULL, NULL, NULL,
  54. "xsltNewKeyDef : malloc failed\n");
  55. return(NULL);
  56. }
  57. memset(cur, 0, sizeof(xsltKeyDef));
  58. if (name != NULL)
  59. cur->name = xmlStrdup(name);
  60. if (nameURI != NULL)
  61. cur->nameURI = xmlStrdup(nameURI);
  62. cur->nsList = NULL;
  63. return(cur);
  64. }
  65. /**
  66. * xsltFreeKeyDef:
  67. * @keyd: an XSLT key definition
  68. *
  69. * Free up the memory allocated by @keyd
  70. */
  71. static void
  72. xsltFreeKeyDef(xsltKeyDefPtr keyd) {
  73. if (keyd == NULL)
  74. return;
  75. if (keyd->comp != NULL)
  76. xmlXPathFreeCompExpr(keyd->comp);
  77. if (keyd->usecomp != NULL)
  78. xmlXPathFreeCompExpr(keyd->usecomp);
  79. if (keyd->name != NULL)
  80. xmlFree(keyd->name);
  81. if (keyd->nameURI != NULL)
  82. xmlFree(keyd->nameURI);
  83. if (keyd->match != NULL)
  84. xmlFree(keyd->match);
  85. if (keyd->use != NULL)
  86. xmlFree(keyd->use);
  87. if (keyd->nsList != NULL)
  88. xmlFree(keyd->nsList);
  89. memset(keyd, -1, sizeof(xsltKeyDef));
  90. xmlFree(keyd);
  91. }
  92. /**
  93. * xsltFreeKeyDefList:
  94. * @keyd: an XSLT key definition list
  95. *
  96. * Free up the memory allocated by all the elements of @keyd
  97. */
  98. static void
  99. xsltFreeKeyDefList(xsltKeyDefPtr keyd) {
  100. xsltKeyDefPtr cur;
  101. while (keyd != NULL) {
  102. cur = keyd;
  103. keyd = keyd->next;
  104. xsltFreeKeyDef(cur);
  105. }
  106. }
  107. /**
  108. * xsltNewKeyTable:
  109. * @name: the key name or NULL
  110. * @nameURI: the name URI or NULL
  111. *
  112. * Create a new XSLT KeyTable
  113. *
  114. * Returns the newly allocated xsltKeyTablePtr or NULL in case of error
  115. */
  116. static xsltKeyTablePtr
  117. xsltNewKeyTable(const xmlChar *name, const xmlChar *nameURI) {
  118. xsltKeyTablePtr cur;
  119. cur = (xsltKeyTablePtr) xmlMalloc(sizeof(xsltKeyTable));
  120. if (cur == NULL) {
  121. xsltTransformError(NULL, NULL, NULL,
  122. "xsltNewKeyTable : malloc failed\n");
  123. return(NULL);
  124. }
  125. memset(cur, 0, sizeof(xsltKeyTable));
  126. if (name != NULL)
  127. cur->name = xmlStrdup(name);
  128. if (nameURI != NULL)
  129. cur->nameURI = xmlStrdup(nameURI);
  130. cur->keys = xmlHashCreate(0);
  131. return(cur);
  132. }
  133. static void
  134. xsltFreeNodeSetEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
  135. xmlXPathFreeNodeSet((xmlNodeSetPtr) payload);
  136. }
  137. /**
  138. * xsltFreeKeyTable:
  139. * @keyt: an XSLT key table
  140. *
  141. * Free up the memory allocated by @keyt
  142. */
  143. static void
  144. xsltFreeKeyTable(xsltKeyTablePtr keyt) {
  145. if (keyt == NULL)
  146. return;
  147. if (keyt->name != NULL)
  148. xmlFree(keyt->name);
  149. if (keyt->nameURI != NULL)
  150. xmlFree(keyt->nameURI);
  151. if (keyt->keys != NULL)
  152. xmlHashFree(keyt->keys, xsltFreeNodeSetEntry);
  153. memset(keyt, -1, sizeof(xsltKeyTable));
  154. xmlFree(keyt);
  155. }
  156. /**
  157. * xsltFreeKeyTableList:
  158. * @keyt: an XSLT key table list
  159. *
  160. * Free up the memory allocated by all the elements of @keyt
  161. */
  162. static void
  163. xsltFreeKeyTableList(xsltKeyTablePtr keyt) {
  164. xsltKeyTablePtr cur;
  165. while (keyt != NULL) {
  166. cur = keyt;
  167. keyt = keyt->next;
  168. xsltFreeKeyTable(cur);
  169. }
  170. }
  171. /************************************************************************
  172. * *
  173. * The interpreter for the precompiled patterns *
  174. * *
  175. ************************************************************************/
  176. /**
  177. * xsltFreeKeys:
  178. * @style: an XSLT stylesheet
  179. *
  180. * Free up the memory used by XSLT keys in a stylesheet
  181. */
  182. void
  183. xsltFreeKeys(xsltStylesheetPtr style) {
  184. if (style->keys)
  185. xsltFreeKeyDefList((xsltKeyDefPtr) style->keys);
  186. }
  187. /**
  188. * skipString:
  189. * @cur: the current pointer
  190. * @end: the current offset
  191. *
  192. * skip a string delimited by " or '
  193. *
  194. * Returns the byte after the string or -1 in case of error
  195. */
  196. static int
  197. skipString(const xmlChar *cur, int end) {
  198. xmlChar limit;
  199. if ((cur == NULL) || (end < 0)) return(-1);
  200. if ((cur[end] == '\'') || (cur[end] == '"')) limit = cur[end];
  201. else return(end);
  202. end++;
  203. while (cur[end] != 0) {
  204. if (cur[end] == limit)
  205. return(end + 1);
  206. end++;
  207. }
  208. return(-1);
  209. }
  210. /**
  211. * skipPredicate:
  212. * @cur: the current pointer
  213. * @end: the current offset
  214. *
  215. * skip a predicate
  216. *
  217. * Returns the byte after the predicate or -1 in case of error
  218. */
  219. static int
  220. skipPredicate(const xmlChar *cur, int end) {
  221. int level = 0;
  222. if ((cur == NULL) || (end < 0)) return(-1);
  223. if (cur[end] != '[') return(end);
  224. end++;
  225. while (cur[end] != 0) {
  226. if ((cur[end] == '\'') || (cur[end] == '"')) {
  227. end = skipString(cur, end);
  228. if (end <= 0)
  229. return(-1);
  230. continue;
  231. } else if (cur[end] == '[') {
  232. level += 1;
  233. } else if (cur[end] == ']') {
  234. if (level == 0)
  235. return(end + 1);
  236. level -= 1;
  237. }
  238. end++;
  239. }
  240. return(-1);
  241. }
  242. /**
  243. * xsltAddKey:
  244. * @style: an XSLT stylesheet
  245. * @name: the key name or NULL
  246. * @nameURI: the name URI or NULL
  247. * @match: the match value
  248. * @use: the use value
  249. * @inst: the key instruction
  250. *
  251. * add a key definition to a stylesheet
  252. *
  253. * Returns 0 in case of success, and -1 in case of failure.
  254. */
  255. int
  256. xsltAddKey(xsltStylesheetPtr style, const xmlChar *name,
  257. const xmlChar *nameURI, const xmlChar *match,
  258. const xmlChar *use, xmlNodePtr inst) {
  259. xsltKeyDefPtr key;
  260. xmlChar *pattern = NULL;
  261. int current, end, start, i = 0;
  262. if ((style == NULL) || (name == NULL) || (match == NULL) || (use == NULL))
  263. return(-1);
  264. #ifdef WITH_XSLT_DEBUG_KEYS
  265. xsltGenericDebug(xsltGenericDebugContext,
  266. "Add key %s, match %s, use %s\n", name, match, use);
  267. #endif
  268. key = xsltNewKeyDef(name, nameURI);
  269. key->match = xmlStrdup(match);
  270. key->use = xmlStrdup(use);
  271. key->inst = inst;
  272. key->nsList = xmlGetNsList(inst->doc, inst);
  273. if (key->nsList != NULL) {
  274. while (key->nsList[i] != NULL)
  275. i++;
  276. }
  277. key->nsNr = i;
  278. /*
  279. * Split the | and register it as as many keys
  280. */
  281. current = end = 0;
  282. while (match[current] != 0) {
  283. start = current;
  284. while (IS_BLANK_CH(match[current]))
  285. current++;
  286. end = current;
  287. while ((match[end] != 0) && (match[end] != '|')) {
  288. if (match[end] == '[') {
  289. end = skipPredicate(match, end);
  290. if (end <= 0) {
  291. xsltTransformError(NULL, style, inst,
  292. "xsl:key : 'match' pattern is malformed: %s",
  293. key->match);
  294. if (style != NULL) style->errors++;
  295. goto error;
  296. }
  297. } else
  298. end++;
  299. }
  300. if (current == end) {
  301. xsltTransformError(NULL, style, inst,
  302. "xsl:key : 'match' pattern is empty\n");
  303. if (style != NULL) style->errors++;
  304. goto error;
  305. }
  306. if (match[start] != '/') {
  307. pattern = xmlStrcat(pattern, (xmlChar *)"//");
  308. if (pattern == NULL) {
  309. if (style != NULL) style->errors++;
  310. goto error;
  311. }
  312. }
  313. pattern = xmlStrncat(pattern, &match[start], end - start);
  314. if (pattern == NULL) {
  315. if (style != NULL) style->errors++;
  316. goto error;
  317. }
  318. if (match[end] == '|') {
  319. pattern = xmlStrcat(pattern, (xmlChar *)"|");
  320. end++;
  321. }
  322. current = end;
  323. }
  324. if (pattern == NULL) {
  325. xsltTransformError(NULL, style, inst,
  326. "xsl:key : 'match' pattern is empty\n");
  327. if (style != NULL) style->errors++;
  328. goto error;
  329. }
  330. #ifdef WITH_XSLT_DEBUG_KEYS
  331. xsltGenericDebug(xsltGenericDebugContext,
  332. " resulting pattern %s\n", pattern);
  333. #endif
  334. /*
  335. * XSLT-1: "It is an error for the value of either the use
  336. * attribute or the match attribute to contain a
  337. * VariableReference."
  338. * TODO: We should report a variable-reference at compile-time.
  339. * Maybe a search for "$", if it occurs outside of quotation
  340. * marks, could be sufficient.
  341. */
  342. #ifdef XML_XPATH_NOVAR
  343. key->comp = xsltXPathCompileFlags(style, pattern, XML_XPATH_NOVAR);
  344. #else
  345. key->comp = xsltXPathCompile(style, pattern);
  346. #endif
  347. if (key->comp == NULL) {
  348. xsltTransformError(NULL, style, inst,
  349. "xsl:key : 'match' pattern compilation failed '%s'\n",
  350. pattern);
  351. if (style != NULL) style->errors++;
  352. }
  353. #ifdef XML_XPATH_NOVAR
  354. key->usecomp = xsltXPathCompileFlags(style, use, XML_XPATH_NOVAR);
  355. #else
  356. key->usecomp = xsltXPathCompile(style, use);
  357. #endif
  358. if (key->usecomp == NULL) {
  359. xsltTransformError(NULL, style, inst,
  360. "xsl:key : 'use' expression compilation failed '%s'\n",
  361. use);
  362. if (style != NULL) style->errors++;
  363. }
  364. /*
  365. * Sometimes the stylesheet writer use the order to ease the
  366. * resolution of keys when they are dependant, keep the provided
  367. * order so add the new one at the end.
  368. */
  369. if (style->keys == NULL) {
  370. style->keys = key;
  371. } else {
  372. xsltKeyDefPtr prev = style->keys;
  373. while (prev->next != NULL)
  374. prev = prev->next;
  375. prev->next = key;
  376. }
  377. key->next = NULL;
  378. key = NULL;
  379. error:
  380. if (pattern != NULL)
  381. xmlFree(pattern);
  382. if (key != NULL)
  383. xsltFreeKeyDef(key);
  384. return(0);
  385. }
  386. /**
  387. * xsltGetKey:
  388. * @ctxt: an XSLT transformation context
  389. * @name: the key name or NULL
  390. * @nameURI: the name URI or NULL
  391. * @value: the key value to look for
  392. *
  393. * Looks up a key of the in current source doc (the document info
  394. * on @ctxt->document). Computes the key if not already done
  395. * for the current source doc.
  396. *
  397. * Returns the nodeset resulting from the query or NULL
  398. */
  399. xmlNodeSetPtr
  400. xsltGetKey(xsltTransformContextPtr ctxt, const xmlChar *name,
  401. const xmlChar *nameURI, const xmlChar *value) {
  402. xmlNodeSetPtr ret;
  403. xsltKeyTablePtr table;
  404. int init_table = 0;
  405. if ((ctxt == NULL) || (name == NULL) || (value == NULL) ||
  406. (ctxt->document == NULL))
  407. return(NULL);
  408. #ifdef WITH_XSLT_DEBUG_KEYS
  409. xsltGenericDebug(xsltGenericDebugContext,
  410. "Get key %s, value %s\n", name, value);
  411. #endif
  412. /*
  413. * keys are computed only on-demand on first key access for a document
  414. */
  415. if ((ctxt->document->nbKeysComputed < ctxt->nbKeys) &&
  416. (ctxt->keyInitLevel == 0)) {
  417. /*
  418. * If non-recursive behaviour, just try to initialize all keys
  419. */
  420. if (xsltInitAllDocKeys(ctxt))
  421. return(NULL);
  422. }
  423. retry:
  424. table = (xsltKeyTablePtr) ctxt->document->keys;
  425. while (table != NULL) {
  426. if (((nameURI != NULL) == (table->nameURI != NULL)) &&
  427. xmlStrEqual(table->name, name) &&
  428. xmlStrEqual(table->nameURI, nameURI))
  429. {
  430. ret = (xmlNodeSetPtr)xmlHashLookup(table->keys, value);
  431. return(ret);
  432. }
  433. table = table->next;
  434. }
  435. if ((ctxt->keyInitLevel != 0) && (init_table == 0)) {
  436. /*
  437. * Apparently one key is recursive and this one is needed,
  438. * initialize just it, that time and retry
  439. */
  440. xsltInitDocKeyTable(ctxt, name, nameURI);
  441. init_table = 1;
  442. goto retry;
  443. }
  444. return(NULL);
  445. }
  446. /**
  447. * xsltInitDocKeyTable:
  448. *
  449. * INTERNAL ROUTINE ONLY
  450. *
  451. * Check if any keys on the current document need to be computed
  452. */
  453. static int
  454. xsltInitDocKeyTable(xsltTransformContextPtr ctxt, const xmlChar *name,
  455. const xmlChar *nameURI)
  456. {
  457. xsltStylesheetPtr style;
  458. xsltKeyDefPtr keyd = NULL;
  459. int found = 0;
  460. #ifdef KEY_INIT_DEBUG
  461. fprintf(stderr, "xsltInitDocKeyTable %s\n", name);
  462. #endif
  463. style = ctxt->style;
  464. while (style != NULL) {
  465. keyd = (xsltKeyDefPtr) style->keys;
  466. while (keyd != NULL) {
  467. if (((keyd->nameURI != NULL) ==
  468. (nameURI != NULL)) &&
  469. xmlStrEqual(keyd->name, name) &&
  470. xmlStrEqual(keyd->nameURI, nameURI))
  471. {
  472. xsltInitCtxtKey(ctxt, ctxt->document, keyd);
  473. if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
  474. return(0);
  475. found = 1;
  476. }
  477. keyd = keyd->next;
  478. }
  479. style = xsltNextImport(style);
  480. }
  481. if (found == 0) {
  482. #ifdef WITH_XSLT_DEBUG_KEYS
  483. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
  484. "xsltInitDocKeyTable: did not found %s\n", name));
  485. #endif
  486. xsltTransformError(ctxt, NULL, keyd? keyd->inst : NULL,
  487. "Failed to find key definition for %s\n", name);
  488. ctxt->state = XSLT_STATE_STOPPED;
  489. return(-1);
  490. }
  491. #ifdef KEY_INIT_DEBUG
  492. fprintf(stderr, "xsltInitDocKeyTable %s done\n", name);
  493. #endif
  494. return(0);
  495. }
  496. /**
  497. * xsltInitAllDocKeys:
  498. * @ctxt: transformation context
  499. *
  500. * INTERNAL ROUTINE ONLY
  501. *
  502. * Check if any keys on the current document need to be computed
  503. *
  504. * Returns 0 in case of success, -1 in case of failure
  505. */
  506. int
  507. xsltInitAllDocKeys(xsltTransformContextPtr ctxt)
  508. {
  509. xsltStylesheetPtr style;
  510. xsltKeyDefPtr keyd;
  511. xsltKeyTablePtr table;
  512. if (ctxt == NULL)
  513. return(-1);
  514. #ifdef KEY_INIT_DEBUG
  515. fprintf(stderr, "xsltInitAllDocKeys %d %d\n",
  516. ctxt->document->nbKeysComputed, ctxt->nbKeys);
  517. #endif
  518. if (ctxt->document->nbKeysComputed == ctxt->nbKeys)
  519. return(0);
  520. /*
  521. * TODO: This could be further optimized
  522. */
  523. style = ctxt->style;
  524. while (style) {
  525. keyd = (xsltKeyDefPtr) style->keys;
  526. while (keyd != NULL) {
  527. #ifdef KEY_INIT_DEBUG
  528. fprintf(stderr, "Init key %s\n", keyd->name);
  529. #endif
  530. /*
  531. * Check if keys with this QName have been already
  532. * computed.
  533. */
  534. table = (xsltKeyTablePtr) ctxt->document->keys;
  535. while (table) {
  536. if (((keyd->nameURI != NULL) == (table->nameURI != NULL)) &&
  537. xmlStrEqual(keyd->name, table->name) &&
  538. xmlStrEqual(keyd->nameURI, table->nameURI))
  539. {
  540. break;
  541. }
  542. table = table->next;
  543. }
  544. if (table == NULL) {
  545. /*
  546. * Keys with this QName have not been yet computed.
  547. */
  548. xsltInitDocKeyTable(ctxt, keyd->name, keyd->nameURI);
  549. }
  550. keyd = keyd->next;
  551. }
  552. style = xsltNextImport(style);
  553. }
  554. #ifdef KEY_INIT_DEBUG
  555. fprintf(stderr, "xsltInitAllDocKeys: done\n");
  556. #endif
  557. return(0);
  558. }
  559. /**
  560. * xsltInitCtxtKey:
  561. * @ctxt: an XSLT transformation context
  562. * @idoc: the document information (holds key values)
  563. * @keyDef: the key definition
  564. *
  565. * Computes the key tables this key and for the current input document.
  566. *
  567. * Returns: 0 on success, -1 on error
  568. */
  569. int
  570. xsltInitCtxtKey(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc,
  571. xsltKeyDefPtr keyDef)
  572. {
  573. int i, len, k;
  574. xmlNodeSetPtr matchList = NULL, keylist;
  575. xmlXPathObjectPtr matchRes = NULL, useRes = NULL;
  576. xmlChar *str = NULL;
  577. xsltKeyTablePtr table;
  578. xmlNodePtr oldInst, cur;
  579. xmlNodePtr oldContextNode;
  580. xsltDocumentPtr oldDocInfo;
  581. int oldXPPos, oldXPSize;
  582. xmlNodePtr oldXPNode;
  583. xmlDocPtr oldXPDoc;
  584. int oldXPNsNr;
  585. xmlNsPtr *oldXPNamespaces;
  586. xmlXPathContextPtr xpctxt;
  587. #ifdef KEY_INIT_DEBUG
  588. fprintf(stderr, "xsltInitCtxtKey %s : %d\n", keyDef->name, ctxt->keyInitLevel);
  589. #endif
  590. if ((keyDef->comp == NULL) || (keyDef->usecomp == NULL))
  591. return(-1);
  592. /*
  593. * Detect recursive keys
  594. */
  595. if (ctxt->keyInitLevel > ctxt->nbKeys) {
  596. #ifdef WITH_XSLT_DEBUG_KEYS
  597. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,
  598. xsltGenericDebug(xsltGenericDebugContext,
  599. "xsltInitCtxtKey: key definition of %s is recursive\n",
  600. keyDef->name));
  601. #endif
  602. xsltTransformError(ctxt, NULL, keyDef->inst,
  603. "Key definition for %s is recursive\n", keyDef->name);
  604. ctxt->state = XSLT_STATE_STOPPED;
  605. return(-1);
  606. }
  607. ctxt->keyInitLevel++;
  608. xpctxt = ctxt->xpathCtxt;
  609. idoc->nbKeysComputed++;
  610. /*
  611. * Save context state.
  612. */
  613. oldInst = ctxt->inst;
  614. oldDocInfo = ctxt->document;
  615. oldContextNode = ctxt->node;
  616. oldXPNode = xpctxt->node;
  617. oldXPDoc = xpctxt->doc;
  618. oldXPPos = xpctxt->proximityPosition;
  619. oldXPSize = xpctxt->contextSize;
  620. oldXPNsNr = xpctxt->nsNr;
  621. oldXPNamespaces = xpctxt->namespaces;
  622. /*
  623. * Set up contexts.
  624. */
  625. ctxt->document = idoc;
  626. ctxt->node = (xmlNodePtr) idoc->doc;
  627. ctxt->inst = keyDef->inst;
  628. xpctxt->doc = idoc->doc;
  629. xpctxt->node = (xmlNodePtr) idoc->doc;
  630. /* TODO : clarify the use of namespaces in keys evaluation */
  631. xpctxt->namespaces = keyDef->nsList;
  632. xpctxt->nsNr = keyDef->nsNr;
  633. /*
  634. * Evaluate the 'match' expression of the xsl:key.
  635. * TODO: The 'match' is a *pattern*.
  636. */
  637. matchRes = xmlXPathCompiledEval(keyDef->comp, xpctxt);
  638. if (matchRes == NULL) {
  639. #ifdef WITH_XSLT_DEBUG_KEYS
  640. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
  641. "xsltInitCtxtKey: %s evaluation failed\n", keyDef->match));
  642. #endif
  643. xsltTransformError(ctxt, NULL, keyDef->inst,
  644. "Failed to evaluate the 'match' expression.\n");
  645. ctxt->state = XSLT_STATE_STOPPED;
  646. goto error;
  647. } else {
  648. if (matchRes->type == XPATH_NODESET) {
  649. matchList = matchRes->nodesetval;
  650. #ifdef WITH_XSLT_DEBUG_KEYS
  651. if (matchList != NULL)
  652. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
  653. "xsltInitCtxtKey: %s evaluates to %d nodes\n",
  654. keyDef->match, matchList->nodeNr));
  655. #endif
  656. } else {
  657. /*
  658. * Is not a node set, but must be.
  659. */
  660. #ifdef WITH_XSLT_DEBUG_KEYS
  661. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
  662. "xsltInitCtxtKey: %s is not a node set\n", keyDef->match));
  663. #endif
  664. xsltTransformError(ctxt, NULL, keyDef->inst,
  665. "The 'match' expression did not evaluate to a node set.\n");
  666. ctxt->state = XSLT_STATE_STOPPED;
  667. goto error;
  668. }
  669. }
  670. if ((matchList == NULL) || (matchList->nodeNr <= 0))
  671. goto exit;
  672. /**
  673. * Multiple key definitions for the same name are allowed, so
  674. * we must check if the key is already present for this doc
  675. */
  676. table = (xsltKeyTablePtr) idoc->keys;
  677. while (table != NULL) {
  678. if (xmlStrEqual(table->name, keyDef->name) &&
  679. (((keyDef->nameURI == NULL) && (table->nameURI == NULL)) ||
  680. ((keyDef->nameURI != NULL) && (table->nameURI != NULL) &&
  681. (xmlStrEqual(table->nameURI, keyDef->nameURI)))))
  682. break;
  683. table = table->next;
  684. }
  685. /**
  686. * If the key was not previously defined, create it now and
  687. * chain it to the list of keys for the doc
  688. */
  689. if (table == NULL) {
  690. table = xsltNewKeyTable(keyDef->name, keyDef->nameURI);
  691. if (table == NULL)
  692. goto error;
  693. table->next = idoc->keys;
  694. idoc->keys = table;
  695. }
  696. /*
  697. * SPEC XSLT 1.0 (XSLT 2.0 does not clarify the context size!)
  698. * "...the use attribute of the xsl:key element is evaluated with x as
  699. " the current node and with a node list containing just x as the
  700. * current node list"
  701. */
  702. xpctxt->contextSize = 1;
  703. xpctxt->proximityPosition = 1;
  704. for (i = 0; i < matchList->nodeNr; i++) {
  705. cur = matchList->nodeTab[i];
  706. if (! IS_XSLT_REAL_NODE(cur))
  707. continue;
  708. ctxt->node = cur;
  709. xpctxt->node = cur;
  710. /*
  711. * Process the 'use' of the xsl:key.
  712. * SPEC XSLT 1.0:
  713. * "The use attribute is an expression specifying the values of
  714. * the key; the expression is evaluated once for each node that
  715. * matches the pattern."
  716. */
  717. if (useRes != NULL)
  718. xmlXPathFreeObject(useRes);
  719. useRes = xmlXPathCompiledEval(keyDef->usecomp, xpctxt);
  720. if (useRes == NULL) {
  721. xsltTransformError(ctxt, NULL, keyDef->inst,
  722. "Failed to evaluate the 'use' expression.\n");
  723. ctxt->state = XSLT_STATE_STOPPED;
  724. break;
  725. }
  726. if (useRes->type == XPATH_NODESET) {
  727. if ((useRes->nodesetval != NULL) &&
  728. (useRes->nodesetval->nodeNr != 0))
  729. {
  730. len = useRes->nodesetval->nodeNr;
  731. str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[0]);
  732. } else {
  733. continue;
  734. }
  735. } else {
  736. len = 1;
  737. if (useRes->type == XPATH_STRING) {
  738. /*
  739. * Consume the string value.
  740. */
  741. str = useRes->stringval;
  742. useRes->stringval = NULL;
  743. } else {
  744. str = xmlXPathCastToString(useRes);
  745. }
  746. }
  747. /*
  748. * Process all strings.
  749. */
  750. k = 0;
  751. while (1) {
  752. if (str == NULL)
  753. goto next_string;
  754. #ifdef WITH_XSLT_DEBUG_KEYS
  755. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext,
  756. "xsl:key : node associated to ('%s', '%s')\n", keyDef->name, str));
  757. #endif
  758. keylist = xmlHashLookup(table->keys, str);
  759. if (keylist == NULL) {
  760. keylist = xmlXPathNodeSetCreate(cur);
  761. if (keylist == NULL)
  762. goto error;
  763. xmlHashAddEntry(table->keys, str, keylist);
  764. } else {
  765. /*
  766. * TODO: How do we know if this function failed?
  767. */
  768. xmlXPathNodeSetAdd(keylist, cur);
  769. }
  770. switch (cur->type) {
  771. case XML_ELEMENT_NODE:
  772. case XML_TEXT_NODE:
  773. case XML_CDATA_SECTION_NODE:
  774. case XML_PI_NODE:
  775. case XML_COMMENT_NODE:
  776. cur->psvi = keyDef;
  777. break;
  778. case XML_ATTRIBUTE_NODE:
  779. ((xmlAttrPtr) cur)->psvi = keyDef;
  780. break;
  781. case XML_DOCUMENT_NODE:
  782. case XML_HTML_DOCUMENT_NODE:
  783. ((xmlDocPtr) cur)->psvi = keyDef;
  784. break;
  785. default:
  786. break;
  787. }
  788. xmlFree(str);
  789. str = NULL;
  790. next_string:
  791. k++;
  792. if (k >= len)
  793. break;
  794. str = xmlXPathCastNodeToString(useRes->nodesetval->nodeTab[k]);
  795. }
  796. }
  797. exit:
  798. error:
  799. ctxt->keyInitLevel--;
  800. /*
  801. * Restore context state.
  802. */
  803. xpctxt->node = oldXPNode;
  804. xpctxt->doc = oldXPDoc;
  805. xpctxt->nsNr = oldXPNsNr;
  806. xpctxt->namespaces = oldXPNamespaces;
  807. xpctxt->proximityPosition = oldXPPos;
  808. xpctxt->contextSize = oldXPSize;
  809. ctxt->node = oldContextNode;
  810. ctxt->document = oldDocInfo;
  811. ctxt->inst = oldInst;
  812. if (str)
  813. xmlFree(str);
  814. if (useRes != NULL)
  815. xmlXPathFreeObject(useRes);
  816. if (matchRes != NULL)
  817. xmlXPathFreeObject(matchRes);
  818. return(0);
  819. }
  820. /**
  821. * xsltInitCtxtKeys:
  822. * @ctxt: an XSLT transformation context
  823. * @idoc: a document info
  824. *
  825. * Computes all the keys tables for the current input document.
  826. * Should be done before global varibales are initialized.
  827. * NOTE: Not used anymore in the refactored code.
  828. */
  829. void
  830. xsltInitCtxtKeys(xsltTransformContextPtr ctxt, xsltDocumentPtr idoc) {
  831. xsltStylesheetPtr style;
  832. xsltKeyDefPtr keyDef;
  833. if ((ctxt == NULL) || (idoc == NULL))
  834. return;
  835. #ifdef KEY_INIT_DEBUG
  836. fprintf(stderr, "xsltInitCtxtKeys on document\n");
  837. #endif
  838. #ifdef WITH_XSLT_DEBUG_KEYS
  839. if ((idoc->doc != NULL) && (idoc->doc->URL != NULL))
  840. XSLT_TRACE(ctxt,XSLT_TRACE_KEYS,xsltGenericDebug(xsltGenericDebugContext, "Initializing keys on %s\n",
  841. idoc->doc->URL));
  842. #endif
  843. style = ctxt->style;
  844. while (style != NULL) {
  845. keyDef = (xsltKeyDefPtr) style->keys;
  846. while (keyDef != NULL) {
  847. xsltInitCtxtKey(ctxt, idoc, keyDef);
  848. keyDef = keyDef->next;
  849. }
  850. style = xsltNextImport(style);
  851. }
  852. #ifdef KEY_INIT_DEBUG
  853. fprintf(stderr, "xsltInitCtxtKeys on document: done\n");
  854. #endif
  855. }
  856. /**
  857. * xsltFreeDocumentKeys:
  858. * @idoc: a XSLT document
  859. *
  860. * Free the keys associated to a document
  861. */
  862. void
  863. xsltFreeDocumentKeys(xsltDocumentPtr idoc) {
  864. if (idoc != NULL)
  865. xsltFreeKeyTableList(idoc->keys);
  866. }