2 * functions.c: Implementation of the XSLT extra functions
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
9 * Daniel.Veillard@imag.fr
10 * Bjorn Reese <breese@users.sourceforge.net> for number formatting
13 #include "xsltconfig.h"
17 #ifdef HAVE_SYS_TYPES_H
18 #include <sys/types.h>
24 #include <libxml/xmlmemory.h>
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27 #include <libxml/valid.h>
28 #include <libxml/hash.h>
29 #include <libxml/xmlerror.h>
30 #include <libxml/xpath.h>
31 #include <libxml/xpathInternals.h>
32 #include <libxml/parserInternals.h>
33 #include <libxml/uri.h>
35 #include "xsltInternals.h"
36 #include "xsltutils.h"
37 #include "functions.h"
38 #include "numbersInternals.h"
40 #include "documents.h"
42 #ifdef WITH_XSLT_DEBUG
43 #define WITH_XSLT_DEBUG_FUNCTION
47 /************************************************************************
51 ************************************************************************/
54 * xsltDocumentFunction:
55 * @ctxt: the XPath Parser context
56 * @nargs: the number of arguments
58 * Implement the document() XSLT function
59 * node-set document(object, node-set?)
62 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
64 xmlXPathObjectPtr obj, obj2 = NULL;
65 xmlChar *base = NULL, *URI;
68 if ((nargs < 1) || (nargs > 2)) {
69 xsltGenericError(xsltGenericErrorContext,
70 "document() : invalid number of args %d\n", nargs);
71 ctxt->error = XPATH_INVALID_ARITY;
74 if (ctxt->value == NULL) {
75 xsltGenericError(xsltGenericErrorContext,
76 "document() : invalid arg value\n");
77 ctxt->error = XPATH_INVALID_TYPE;
82 if (ctxt->value->type != XPATH_NODESET) {
83 xsltGenericError(xsltGenericErrorContext,
84 "document() : invalid arg expecting a nodeset\n");
85 ctxt->error = XPATH_INVALID_TYPE;
89 obj2 = valuePop(ctxt);
92 if (ctxt->value->type == XPATH_NODESET) {
94 xmlXPathObjectPtr newobj, ret;
97 ret = xmlXPathNewNodeSet(NULL);
99 if (obj->nodesetval) {
100 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
102 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
103 xmlXPathStringFunction(ctxt, 1);
105 valuePush(ctxt, xmlXPathObjectCopy(obj2));
108 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
110 xsltDocumentFunction(ctxt, 2);
111 newobj = valuePop(ctxt);
112 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
114 xmlXPathFreeObject(newobj);
118 xmlXPathFreeObject(obj);
120 xmlXPathFreeObject(obj2);
121 valuePush(ctxt, ret);
125 * Make sure it's converted to a string
127 xmlXPathStringFunction(ctxt, 1);
128 if (ctxt->value->type != XPATH_STRING) {
129 xsltGenericError(xsltGenericErrorContext,
130 "document() : invalid arg expecting a string\n");
131 ctxt->error = XPATH_INVALID_TYPE;
133 xmlXPathFreeObject(obj2);
136 obj = valuePop(ctxt);
137 if (obj->stringval == NULL) {
138 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
140 if ((obj2 != NULL) && (obj2->nodesetval != NULL) &&
141 (obj2->nodesetval->nodeNr > 0)) {
144 target = obj2->nodesetval->nodeTab[0];
145 if (target->type == XML_ATTRIBUTE_NODE) {
146 target = ((xmlAttrPtr) target)->parent;
148 base = xmlNodeGetBase(target->doc, target);
150 xsltTransformContextPtr tctxt = ctxt->context->extra;
151 if ((tctxt != NULL) && (tctxt->inst != NULL)) {
152 base = xmlNodeGetBase(tctxt->inst->doc, tctxt->inst);
153 } else if ((tctxt != NULL) && (tctxt->style != NULL) &&
154 (tctxt->style->doc != NULL)) {
155 base = xmlNodeGetBase(tctxt->style->doc,
156 (xmlNodePtr) tctxt->style->doc);
159 URI = xmlBuildURI(obj->stringval, base);
163 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
165 xsltTransformContextPtr tctxt;
167 tctxt = (xsltTransformContextPtr) ctxt->context->extra;
169 xsltGenericError(xsltGenericErrorContext,
170 "document() : internal error tctxt == NULL\n");
171 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
173 doc = xsltLoadDocument(tctxt, URI);
175 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
177 /* TODO: use XPointer of HTML location for fragment ID */
178 /* pbm #xxx can lead to location sets, not nodesets :-) */
179 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc->doc));
185 xmlXPathFreeObject(obj);
187 xmlXPathFreeObject(obj2);
192 * @ctxt: the XPath Parser context
193 * @nargs: the number of arguments
195 * Implement the key() XSLT function
196 * node-set key(string, object)
199 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
200 xmlNodeSetPtr nodelist;
201 xmlXPathObjectPtr obj1, obj2;
202 xmlChar *key = NULL, *value;
203 const xmlChar *keyURI;
204 xsltTransformContextPtr tctxt;
206 tctxt = ((xsltTransformContextPtr)ctxt->context->extra);
209 xsltGenericError(xsltGenericErrorContext,
210 "key() : expects two arguments\n");
211 ctxt->error = XPATH_INVALID_ARITY;
215 obj2 = valuePop(ctxt);
216 if ((obj2 == NULL) ||
217 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
218 xsltGenericError(xsltGenericErrorContext,
219 "key() : invalid arg expecting a string\n");
220 ctxt->error = XPATH_INVALID_TYPE;
221 xmlXPathFreeObject(obj2);
225 obj1 = valuePop(ctxt);
227 if (obj2->type == XPATH_NODESET) {
229 xmlXPathObjectPtr newobj, ret;
231 ret = xmlXPathNewNodeSet(NULL);
233 if (obj2->nodesetval != NULL) {
234 for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
235 valuePush(ctxt, xmlXPathObjectCopy(obj1));
237 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
238 xmlXPathStringFunction(ctxt, 1);
239 xsltKeyFunction(ctxt, 2);
240 newobj = valuePop(ctxt);
241 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
243 xmlXPathFreeObject(newobj);
246 valuePush(ctxt, ret);
248 xmlChar *qname, *prefix;
251 * Get the associated namespace URI if qualified name
253 qname = obj1->stringval;
254 key = xmlSplitQName2(qname, &prefix);
256 key = xmlStrdup(obj1->stringval);
261 if (prefix != NULL) {
262 keyURI = xmlXPathNsLookup(ctxt->context, prefix);
263 if (keyURI == NULL) {
264 xsltGenericError(xsltGenericErrorContext,
265 "key() : prefix %s is not bound\n", prefix);
274 * Force conversion of first arg to string
276 valuePush(ctxt, obj2);
277 xmlXPathStringFunction(ctxt, 1);
278 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
279 xsltGenericError(xsltGenericErrorContext,
280 "key() : invalid arg expecting a string\n");
281 ctxt->error = XPATH_INVALID_TYPE;
282 xmlXPathFreeObject(obj1);
286 obj2 = valuePop(ctxt);
287 value = obj2->stringval;
289 nodelist = xsltGetKey(tctxt, key, keyURI, value);
290 valuePush(ctxt, xmlXPathWrapNodeSet(
291 xmlXPathNodeSetMerge(NULL, nodelist)));
296 xmlXPathFreeObject(obj1);
298 xmlXPathFreeObject(obj2);
304 * xsltUnparsedEntityURIFunction:
305 * @ctxt: the XPath Parser context
306 * @nargs: the number of arguments
308 * Implement the unparsed-entity-uri() XSLT function
309 * string unparsed-entity-uri(string)
312 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
313 xmlXPathObjectPtr obj;
316 if ((nargs != 1) || (ctxt->value == NULL)) {
317 xsltGenericError(xsltGenericErrorContext,
318 "unparsed-entity-uri() : expects one string arg\n");
319 ctxt->error = XPATH_INVALID_ARITY;
322 obj = valuePop(ctxt);
323 if (obj->type != XPATH_STRING) {
324 obj = xmlXPathConvertString(obj);
327 str = obj->stringval;
329 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
333 entity = xmlGetDocEntity(ctxt->context->doc, str);
334 if (entity == NULL) {
335 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
337 if (entity->URI != NULL)
338 valuePush(ctxt, xmlXPathNewString(entity->URI));
340 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
343 xmlXPathFreeObject(obj);
347 * xsltFormatNumberFunction:
348 * @ctxt: the XPath Parser context
349 * @nargs: the number of arguments
351 * Implement the format-number() XSLT function
352 * string format-number(number, string, string?)
355 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
357 xmlXPathObjectPtr numberObj = NULL;
358 xmlXPathObjectPtr formatObj = NULL;
359 xmlXPathObjectPtr decimalObj = NULL;
360 xsltStylesheetPtr sheet;
361 xsltDecimalFormatPtr formatValues;
364 sheet = ((xsltTransformContextPtr)ctxt->context->extra)->style;
365 formatValues = sheet->decimalFormat;
370 decimalObj = valuePop(ctxt);
371 formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
372 /* Intentional fall-through */
375 formatObj = valuePop(ctxt);
377 numberObj = valuePop(ctxt);
380 XP_ERROR(XPATH_INVALID_ARITY);
383 if (xsltFormatNumberConversion(formatValues,
384 formatObj->stringval,
386 &result) == XPATH_EXPRESSION_OK) {
387 valuePush(ctxt, xmlXPathNewString(result));
391 xmlXPathFreeObject(numberObj);
392 xmlXPathFreeObject(formatObj);
393 xmlXPathFreeObject(decimalObj);
397 * xsltGenerateIdFunction:
398 * @ctxt: the XPath Parser context
399 * @nargs: the number of arguments
401 * Implement the generate-id() XSLT function
402 * string generate-id(node-set?)
405 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
406 xmlNodePtr cur = NULL;
411 cur = ctxt->context->node;
412 } else if (nargs == 1) {
413 xmlXPathObjectPtr obj;
414 xmlNodeSetPtr nodelist;
417 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
418 ctxt->error = XPATH_INVALID_TYPE;
419 xsltGenericError(xsltGenericErrorContext,
420 "generate-id() : invalid arg expecting a node-set\n");
423 obj = valuePop(ctxt);
424 nodelist = obj->nodesetval;
425 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
426 xmlXPathFreeObject(obj);
427 valuePush(ctxt, xmlXPathNewCString(""));
430 cur = nodelist->nodeTab[0];
431 for (i = 1;i < nodelist->nodeNr;i++) {
432 ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
434 cur = nodelist->nodeTab[i];
436 xmlXPathFreeObject(obj);
438 xsltGenericError(xsltGenericErrorContext,
439 "generate-id() : invalid number of args %d\n", nargs);
440 ctxt->error = XPATH_INVALID_ARITY;
444 * Okay this is ugly but should work, use the NodePtr address
447 val = (unsigned long)((char *)cur - (char *)0);
448 val /= sizeof(xmlNode);
449 sprintf((char *)str, "id%ld", val);
450 valuePush(ctxt, xmlXPathNewString(str));
454 * xsltSystemPropertyFunction:
455 * @ctxt: the XPath Parser context
456 * @nargs: the number of arguments
458 * Implement the system-property() XSLT function
459 * object system-property(string)
462 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
463 xmlXPathObjectPtr obj;
464 xmlChar *prefix, *name;
465 const xmlChar *nsURI = NULL;
468 xsltGenericError(xsltGenericErrorContext,
469 "system-property() : expects one string arg\n");
470 ctxt->error = XPATH_INVALID_ARITY;
473 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
474 xsltGenericError(xsltGenericErrorContext,
475 "system-property() : invalid arg expecting a string\n");
476 ctxt->error = XPATH_INVALID_TYPE;
479 obj = valuePop(ctxt);
480 if (obj->stringval == NULL) {
481 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
483 name = xmlSplitQName2(obj->stringval, &prefix);
485 name = xmlStrdup(obj->stringval);
487 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
489 xsltGenericError(xsltGenericErrorContext,
490 "system-property(): prefix %s is not bound\n", prefix);
494 if (!xmlStrcmp(nsURI, XSLT_NAMESPACE)) {
495 if (!xmlStrcmp(name, (const xmlChar *)"version")) {
496 valuePush(ctxt, xmlXPathNewString(
497 (const xmlChar *)XSLT_DEFAULT_VERSION));
498 } else if (!xmlStrcmp(name, (const xmlChar *)"vendor")) {
499 valuePush(ctxt, xmlXPathNewString(
500 (const xmlChar *)XSLT_DEFAULT_VENDOR));
501 } else if (!xmlStrcmp(name, (const xmlChar *)"vendor-url")) {
502 valuePush(ctxt, xmlXPathNewString(
503 (const xmlChar *)XSLT_DEFAULT_URL));
505 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
513 xmlXPathFreeObject(obj);
517 * xsltElementAvailableFunction:
518 * @ctxt: the XPath Parser context
519 * @nargs: the number of arguments
521 * Implement the element-available() XSLT function
522 * boolean element-available(string)
525 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
526 xmlXPathObjectPtr obj;
529 xsltGenericError(xsltGenericErrorContext,
530 "element-available() : expects one string arg\n");
531 ctxt->error = XPATH_INVALID_ARITY;
534 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
535 xsltGenericError(xsltGenericErrorContext,
536 "element-available invalid arg expecting a string\n");
537 ctxt->error = XPATH_INVALID_TYPE;
540 obj = valuePop(ctxt);
541 xmlXPathFreeObject(obj);
542 valuePush(ctxt, xmlXPathNewBoolean(0));
546 * xsltFunctionAvailableFunction:
547 * @ctxt: the XPath Parser context
548 * @nargs: the number of arguments
550 * Implement the function-available() XSLT function
551 * boolean function-available(string)
554 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
555 xmlXPathObjectPtr obj;
556 xmlChar *prefix, *name;
557 const xmlChar *nsURI = NULL;
560 xsltGenericError(xsltGenericErrorContext,
561 "function-available() : expects one string arg\n");
562 ctxt->error = XPATH_INVALID_ARITY;
565 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
566 xsltGenericError(xsltGenericErrorContext,
567 "function-available invalid arg expecting a string\n");
568 ctxt->error = XPATH_INVALID_TYPE;
571 obj = valuePop(ctxt);
573 name = xmlSplitQName2(obj->stringval, &prefix);
575 name = xmlStrdup(obj->stringval);
577 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
579 xsltGenericError(xsltGenericErrorContext,
580 "function-available(): prefix %s is not bound\n", prefix);
584 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
585 valuePush(ctxt, xmlXPathNewBoolean(1));
587 valuePush(ctxt, xmlXPathNewBoolean(0));
590 xmlXPathFreeObject(obj);
598 * xsltCurrentFunction:
599 * @ctxt: the XPath Parser context
600 * @nargs: the number of arguments
602 * Implement the current() XSLT function
606 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
607 xsltTransformContextPtr tctxt;
610 xsltGenericError(xsltGenericErrorContext,
611 "document() : function uses no argument\n");
612 ctxt->error = XPATH_INVALID_ARITY;
615 tctxt = (xsltTransformContextPtr) ctxt->context->extra;
617 xsltGenericError(xsltGenericErrorContext,
618 "current() : internal error tctxt == NULL\n");
619 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
621 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
626 * xsltRegisterAllFunctions:
627 * @ctxt: the XPath context
629 * Registers all default XSLT functions in this context
632 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) {
633 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"current",
634 xsltCurrentFunction);
635 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"document",
636 xsltDocumentFunction);
637 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"key",
639 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"unparsed-entity-uri",
640 xsltUnparsedEntityURIFunction);
641 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"format-number",
642 xsltFormatNumberFunction);
643 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"generate-id",
644 xsltGenerateIdFunction);
645 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"system-property",
646 xsltSystemPropertyFunction);
647 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"element-available",
648 xsltElementAvailableFunction);
649 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"function-available",
650 xsltFunctionAvailableFunction);