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 #define DEBUG_FUNCTION
45 /************************************************************************
49 ************************************************************************/
52 * xsltDocumentFunction:
53 * @ctxt: the XPath Parser context
54 * @nargs: the number of arguments
56 * Implement the document() XSLT function
57 * node-set document(object, node-set?)
60 xsltDocumentFunction(xmlXPathParserContextPtr ctxt, int nargs){
62 xmlXPathObjectPtr obj, obj2=NULL;
66 if ((nargs < 1) || (nargs > 2)) {
67 xsltGenericError(xsltGenericErrorContext,
68 "document() : invalid number of args %d\n", nargs);
69 ctxt->error = XPATH_INVALID_ARITY;
72 if (ctxt->value == NULL) {
73 xsltGenericError(xsltGenericErrorContext,
74 "document() : invalid arg value\n");
75 ctxt->error = XPATH_INVALID_TYPE;
80 if (ctxt->value->type != XPATH_NODESET) {
81 xsltGenericError(xsltGenericErrorContext,
82 "document() : invalid arg expecting a nodeset\n");
83 ctxt->error = XPATH_INVALID_TYPE;
87 obj2 = valuePop(ctxt);
90 if (ctxt->value->type == XPATH_NODESET) {
92 xmlXPathObjectPtr newobj, ret;
95 ret = xmlXPathNewNodeSet(NULL);
97 if (obj->nodesetval) {
98 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
100 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
101 xmlXPathStringFunction(ctxt, 1);
103 valuePush(ctxt, xmlXPathObjectCopy(obj2));
106 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
108 xsltDocumentFunction(ctxt, 2);
109 newobj = valuePop(ctxt);
110 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
112 xmlXPathFreeObject(newobj);
116 xmlXPathFreeObject(obj);
118 xmlXPathFreeObject(obj2);
119 valuePush(ctxt, ret);
123 * Make sure it's converted to a string
125 xmlXPathStringFunction(ctxt, 1);
126 if (ctxt->value->type != XPATH_STRING) {
127 xsltGenericError(xsltGenericErrorContext,
128 "document() : invalid arg expecting a string\n");
129 ctxt->error = XPATH_INVALID_TYPE;
131 xmlXPathFreeObject(obj2);
134 obj = valuePop(ctxt);
135 if (obj->stringval == NULL) {
136 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
139 /* obj2 should be ordered in document order !!!!! */
140 base = xmlNodeGetBase(obj2->nodesetval->nodeTab[0]->doc,
141 obj->nodesetval->nodeTab[0]);
143 base = xmlNodeGetBase(
144 ((xsltTransformContextPtr)ctxt->context->extra)->style->doc,
145 ctxt->context->node);
147 URI = xmlBuildURI(obj->stringval, base);
151 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
153 xsltTransformContextPtr tctxt;
155 tctxt = (xsltTransformContextPtr) ctxt->context->extra;
157 xsltGenericError(xsltGenericErrorContext,
158 "document() : internal error tctxt == NULL\n");
159 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
161 doc = xsltLoadDocument(tctxt, URI);
163 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
165 /* TODO: use XPointer of HTML location for fragment ID */
166 /* pbm #xxx can lead to location sets, not nodesets :-) */
167 valuePush(ctxt, xmlXPathNewNodeSet((xmlNodePtr) doc->doc));
173 xmlXPathFreeObject(obj);
175 xmlXPathFreeObject(obj2);
180 * @ctxt: the XPath Parser context
181 * @nargs: the number of arguments
183 * Implement the key() XSLT function
184 * node-set key(string, object)
187 xsltKeyFunction(xmlXPathParserContextPtr ctxt, int nargs){
188 xmlNodeSetPtr nodelist;
189 xmlXPathObjectPtr obj1, obj2;
190 xmlChar *key = NULL, *value;
191 const xmlChar *keyURI;
192 xsltTransformContextPtr tctxt;
194 tctxt = ((xsltTransformContextPtr)ctxt->context->extra);
197 xsltGenericError(xsltGenericErrorContext,
198 "key() : expects two arguments\n");
199 ctxt->error = XPATH_INVALID_ARITY;
203 obj2 = valuePop(ctxt);
204 if ((obj2 == NULL) ||
205 (ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
206 xsltGenericError(xsltGenericErrorContext,
207 "key() : invalid arg expecting a string\n");
208 ctxt->error = XPATH_INVALID_TYPE;
209 xmlXPathFreeObject(obj2);
213 obj1 = valuePop(ctxt);
215 if (obj2->type == XPATH_NODESET) {
217 xmlXPathObjectPtr newobj, ret;
219 ret = xmlXPathNewNodeSet(NULL);
221 if (obj2->nodesetval != NULL) {
222 for (i = 0; i < obj2->nodesetval->nodeNr; i++) {
223 valuePush(ctxt, xmlXPathObjectCopy(obj1));
225 xmlXPathNewNodeSet(obj2->nodesetval->nodeTab[i]));
226 xmlXPathStringFunction(ctxt, 1);
227 xsltKeyFunction(ctxt, 2);
228 newobj = valuePop(ctxt);
229 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
231 xmlXPathFreeObject(newobj);
234 valuePush(ctxt, ret);
236 xmlChar *qname, *prefix;
239 * Get the associated namespace URI if qualified name
241 qname = obj1->stringval;
242 key = xmlSplitQName2(qname, &prefix);
244 key = xmlStrdup(obj1->stringval);
249 if (prefix != NULL) {
250 keyURI = xmlXPathNsLookup(ctxt->context, prefix);
251 if (keyURI == NULL) {
252 xsltGenericError(xsltGenericErrorContext,
253 "key() : prefix %s is not bound\n", prefix);
262 * Force conversion of first arg to string
264 valuePush(ctxt, obj2);
265 xmlXPathStringFunction(ctxt, 1);
266 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
267 xsltGenericError(xsltGenericErrorContext,
268 "key() : invalid arg expecting a string\n");
269 ctxt->error = XPATH_INVALID_TYPE;
270 xmlXPathFreeObject(obj1);
274 obj2 = valuePop(ctxt);
275 value = obj2->stringval;
277 nodelist = xsltGetKey(tctxt, key, keyURI, value);
278 valuePush(ctxt, xmlXPathWrapNodeSet(
279 xmlXPathNodeSetMerge(NULL, nodelist)));
284 xmlXPathFreeObject(obj1);
286 xmlXPathFreeObject(obj2);
292 * xsltUnparsedEntityURIFunction:
293 * @ctxt: the XPath Parser context
294 * @nargs: the number of arguments
296 * Implement the unparsed-entity-uri() XSLT function
297 * string unparsed-entity-uri(string)
300 xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
301 xmlXPathObjectPtr obj;
304 if ((nargs != 1) || (ctxt->value == NULL)) {
305 xsltGenericError(xsltGenericErrorContext,
306 "unparsed-entity-uri() : expects one string arg\n");
307 ctxt->error = XPATH_INVALID_ARITY;
310 obj = valuePop(ctxt);
311 if (obj->type != XPATH_STRING) {
312 obj = xmlXPathConvertString(obj);
315 str = obj->stringval;
317 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
321 entity = xmlGetDocEntity(ctxt->context->doc, str);
322 if (entity == NULL) {
323 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
325 if (entity->URI != NULL)
326 valuePush(ctxt, xmlXPathNewString(entity->URI));
328 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
331 xmlXPathFreeObject(obj);
335 * xsltFormatNumberFunction:
336 * @ctxt: the XPath Parser context
337 * @nargs: the number of arguments
339 * Implement the format-number() XSLT function
340 * string format-number(number, string, string?)
343 xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
345 xmlXPathObjectPtr numberObj = NULL;
346 xmlXPathObjectPtr formatObj = NULL;
347 xmlXPathObjectPtr decimalObj = NULL;
348 xsltStylesheetPtr sheet;
349 xsltDecimalFormatPtr formatValues;
352 sheet = ((xsltTransformContextPtr)ctxt->context->extra)->style;
353 formatValues = sheet->decimalFormat;
358 decimalObj = valuePop(ctxt);
359 formatValues = xsltDecimalFormatGetByName(sheet, decimalObj->stringval);
360 /* Intentional fall-through */
363 formatObj = valuePop(ctxt);
365 numberObj = valuePop(ctxt);
368 XP_ERROR(XPATH_INVALID_ARITY);
372 if (xsltFormatNumberConversion(formatValues,
373 formatObj->stringval,
375 &result) == XPATH_EXPRESSION_OK) {
376 valuePush(ctxt, xmlXPathNewString(result));
380 xmlXPathFreeObject(numberObj);
381 xmlXPathFreeObject(formatObj);
382 xmlXPathFreeObject(decimalObj);
386 * xsltGenerateIdFunction:
387 * @ctxt: the XPath Parser context
388 * @nargs: the number of arguments
390 * Implement the generate-id() XSLT function
391 * string generate-id(node-set?)
394 xsltGenerateIdFunction(xmlXPathParserContextPtr ctxt, int nargs){
395 xmlNodePtr cur = NULL;
400 cur = ctxt->context->node;
401 } else if (nargs == 1) {
402 xmlXPathObjectPtr obj;
403 xmlNodeSetPtr nodelist;
406 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
407 ctxt->error = XPATH_INVALID_TYPE;
408 xsltGenericError(xsltGenericErrorContext,
409 "generate-id() : invalid arg expecting a node-set\n");
412 obj = valuePop(ctxt);
413 nodelist = obj->nodesetval;
414 if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
415 xmlXPathFreeObject(obj);
416 valuePush(ctxt, xmlXPathNewCString(""));
419 cur = nodelist->nodeTab[0];
420 for (i = 1;i < nodelist->nodeNr;i++) {
421 ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
423 cur = nodelist->nodeTab[i];
425 xmlXPathFreeObject(obj);
427 xsltGenericError(xsltGenericErrorContext,
428 "generate-id() : invalid number of args %d\n", nargs);
429 ctxt->error = XPATH_INVALID_ARITY;
433 * Okay this is ugly but should work, use the NodePtr address
436 val = (unsigned long)((char *)cur - (char *)0);
437 val /= sizeof(xmlNode);
438 sprintf((char *)str, "id%ld", val);
439 valuePush(ctxt, xmlXPathNewString(str));
443 * xsltSystemPropertyFunction:
444 * @ctxt: the XPath Parser context
445 * @nargs: the number of arguments
447 * Implement the system-property() XSLT function
448 * object system-property(string)
451 xsltSystemPropertyFunction(xmlXPathParserContextPtr ctxt, int nargs){
452 xmlXPathObjectPtr obj;
453 xmlChar *prefix, *name;
454 const xmlChar *nsURI = NULL;
457 xsltGenericError(xsltGenericErrorContext,
458 "system-property() : expects one string arg\n");
459 ctxt->error = XPATH_INVALID_ARITY;
462 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
463 xsltGenericError(xsltGenericErrorContext,
464 "system-property() : invalid arg expecting a string\n");
465 ctxt->error = XPATH_INVALID_TYPE;
468 obj = valuePop(ctxt);
469 if (obj->stringval == NULL) {
470 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
472 name = xmlSplitQName2(obj->stringval, &prefix);
474 name = xmlStrdup(obj->stringval);
476 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
478 xsltGenericError(xsltGenericErrorContext,
479 "system-property(): prefix %s is not bound\n", prefix);
483 if (!xmlStrcmp(nsURI, XSLT_NAMESPACE)) {
484 if (!xmlStrcmp(name, (const xmlChar *)"version")) {
485 valuePush(ctxt, xmlXPathNewString(
486 (const xmlChar *)XSLT_DEFAULT_VERSION));
487 } else if (!xmlStrcmp(name, (const xmlChar *)"vendor")) {
488 valuePush(ctxt, xmlXPathNewString(
489 (const xmlChar *)XSLT_DEFAULT_VENDOR));
490 } else if (!xmlStrcmp(name, (const xmlChar *)"vendor-url")) {
491 valuePush(ctxt, xmlXPathNewString(
492 (const xmlChar *)XSLT_DEFAULT_URL));
494 valuePush(ctxt, xmlXPathNewString((const xmlChar *)""));
502 xmlXPathFreeObject(obj);
506 * xsltElementAvailableFunction:
507 * @ctxt: the XPath Parser context
508 * @nargs: the number of arguments
510 * Implement the element-available() XSLT function
511 * boolean element-available(string)
514 xsltElementAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
515 xmlXPathObjectPtr obj;
518 xsltGenericError(xsltGenericErrorContext,
519 "element-available() : expects one string arg\n");
520 ctxt->error = XPATH_INVALID_ARITY;
523 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
524 xsltGenericError(xsltGenericErrorContext,
525 "element-available invalid arg expecting a string\n");
526 ctxt->error = XPATH_INVALID_TYPE;
529 obj = valuePop(ctxt);
530 xmlXPathFreeObject(obj);
531 valuePush(ctxt, xmlXPathNewBoolean(0));
535 * xsltFunctionAvailableFunction:
536 * @ctxt: the XPath Parser context
537 * @nargs: the number of arguments
539 * Implement the function-available() XSLT function
540 * boolean function-available(string)
543 xsltFunctionAvailableFunction(xmlXPathParserContextPtr ctxt, int nargs){
544 xmlXPathObjectPtr obj;
545 xmlChar *prefix, *name;
546 const xmlChar *nsURI = NULL;
549 xsltGenericError(xsltGenericErrorContext,
550 "function-available() : expects one string arg\n");
551 ctxt->error = XPATH_INVALID_ARITY;
554 if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_STRING)) {
555 xsltGenericError(xsltGenericErrorContext,
556 "function-available invalid arg expecting a string\n");
557 ctxt->error = XPATH_INVALID_TYPE;
560 obj = valuePop(ctxt);
562 name = xmlSplitQName2(obj->stringval, &prefix);
564 name = xmlStrdup(obj->stringval);
566 nsURI = xmlXPathNsLookup(ctxt->context, prefix);
568 xsltGenericError(xsltGenericErrorContext,
569 "function-available(): prefix %s is not bound\n", prefix);
573 if (xmlXPathFunctionLookupNS(ctxt->context, name, nsURI) != NULL) {
574 valuePush(ctxt, xmlXPathNewBoolean(1));
576 valuePush(ctxt, xmlXPathNewBoolean(0));
579 xmlXPathFreeObject(obj);
587 * xsltCurrentFunction:
588 * @ctxt: the XPath Parser context
589 * @nargs: the number of arguments
591 * Implement the current() XSLT function
595 xsltCurrentFunction(xmlXPathParserContextPtr ctxt, int nargs){
596 xsltTransformContextPtr tctxt;
599 xsltGenericError(xsltGenericErrorContext,
600 "document() : function uses no argument\n");
601 ctxt->error = XPATH_INVALID_ARITY;
604 tctxt = (xsltTransformContextPtr) ctxt->context->extra;
606 xsltGenericError(xsltGenericErrorContext,
607 "current() : internal error tctxt == NULL\n");
608 valuePush(ctxt, xmlXPathNewNodeSet(NULL));
610 valuePush(ctxt, xmlXPathNewNodeSet(tctxt->node)); /* current */
615 * xsltRegisterAllFunctions:
616 * @ctxt: the XPath context
618 * Registers all default XSLT functions in this context
621 xsltRegisterAllFunctions(xmlXPathContextPtr ctxt) {
622 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"current",
623 xsltCurrentFunction);
624 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"document",
625 xsltDocumentFunction);
626 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"key",
628 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"unparsed-entity-uri",
629 xsltUnparsedEntityURIFunction);
630 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"format-number",
631 xsltFormatNumberFunction);
632 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"generate-id",
633 xsltGenerateIdFunction);
634 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"system-property",
635 xsltSystemPropertyFunction);
636 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"element-available",
637 xsltElementAvailableFunction);
638 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"function-available",
639 xsltFunctionAvailableFunction);