Home | History | Annotate | Download | only in libexslt
      1 #define IN_LIBEXSLT
      2 #include "libexslt/libexslt.h"
      3 
      4 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
      5 #include <win32config.h>
      6 #else
      7 #include "config.h"
      8 #endif
      9 
     10 #include <libxml/tree.h>
     11 #include <libxml/xpath.h>
     12 #include <libxml/xpathInternals.h>
     13 #include <libxml/parser.h>
     14 #include <libxml/hash.h>
     15 
     16 #include <libxslt/xsltconfig.h>
     17 #include <libxslt/xsltutils.h>
     18 #include <libxslt/xsltInternals.h>
     19 #include <libxslt/extensions.h>
     20 
     21 #include "exslt.h"
     22 
     23 /**
     24  * exsltSaxonInit:
     25  * @ctxt: an XSLT transformation context
     26  * @URI: the namespace URI for the extension
     27  *
     28  * Initializes the SAXON module.
     29  *
     30  * Returns the data for this transformation
     31  */
     32 static xmlHashTablePtr
     33 exsltSaxonInit (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
     34 		const xmlChar *URI ATTRIBUTE_UNUSED) {
     35     return xmlHashCreate(1);
     36 }
     37 
     38 /**
     39  * exsltSaxonShutdown:
     40  * @ctxt: an XSLT transformation context
     41  * @URI: the namespace URI for the extension
     42  * @data: the module data to free up
     43  *
     44  * Shutdown the SAXON extension module
     45  */
     46 static void
     47 exsltSaxonShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
     48 		    const xmlChar *URI ATTRIBUTE_UNUSED,
     49 		    xmlHashTablePtr data) {
     50     xmlHashFree(data, (xmlHashDeallocator) xmlXPathFreeCompExpr);
     51 }
     52 
     53 
     54 /**
     55  * exsltSaxonExpressionFunction:
     56  * @ctxt: an XPath parser context
     57  * @nargs: the number of arguments
     58  *
     59  * The supplied string must contain an XPath expression. The result of
     60  * the function is a stored expression, which may be supplied as an
     61  * argument to other extension functions such as saxon:eval(),
     62  * saxon:sum() and saxon:distinct(). The result of the expression will
     63  * usually depend on the current node. The expression may contain
     64  * references to variables that are in scope at the point where
     65  * saxon:expression() is called: these variables will be replaced in
     66  * the stored expression with the values they take at the time
     67  * saxon:expression() is called, not the values of the variables at
     68  * the time the stored expression is evaluated.  Similarly, if the
     69  * expression contains namespace prefixes, these are interpreted in
     70  * terms of the namespace declarations in scope at the point where the
     71  * saxon:expression() function is called, not those in scope where the
     72  * stored expression is evaluated.
     73  *
     74  * TODO: current implementation doesn't conform to SAXON behaviour
     75  * regarding context and namespaces.
     76  */
     77 static void
     78 exsltSaxonExpressionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
     79     xmlChar *arg;
     80     xmlXPathCompExprPtr ret;
     81     xmlHashTablePtr hash;
     82     xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
     83 
     84     if (nargs != 1) {
     85 	xmlXPathSetArityError(ctxt);
     86 	return;
     87     }
     88 
     89     arg = xmlXPathPopString(ctxt);
     90     if (xmlXPathCheckError(ctxt) || (arg == NULL)) {
     91 	xmlXPathSetTypeError(ctxt);
     92 	return;
     93     }
     94 
     95     hash = (xmlHashTablePtr) xsltGetExtData(tctxt,
     96 					    ctxt->context->functionURI);
     97 
     98     ret = xmlHashLookup(hash, arg);
     99 
    100     if (ret == NULL) {
    101 	 ret = xmlXPathCompile(arg);
    102 	 if (ret == NULL) {
    103 	      xmlFree(arg);
    104 	      xsltGenericError(xsltGenericErrorContext,
    105 			"{%s}:%s: argument is not an XPath expression\n",
    106 			ctxt->context->functionURI, ctxt->context->function);
    107 	      return;
    108 	 }
    109 	 xmlHashAddEntry(hash, arg, (void *) ret);
    110     }
    111 
    112     xmlFree(arg);
    113 
    114     xmlXPathReturnExternal(ctxt, ret);
    115 }
    116 
    117 /**
    118  * exsltSaxonEvalFunction:
    119  * @ctxt:  an XPath parser context
    120  * @nargs:  number of arguments
    121  *
    122  * Implements de SAXON eval() function:
    123  *    object saxon:eval (saxon:stored-expression)
    124  * Returns the result of evaluating the supplied stored expression.
    125  * A stored expression may be obtained as the result of calling
    126  * the saxon:expression() function.
    127  * The stored expression is evaluated in the current context, that
    128  * is, the context node is the current node, and the context position
    129  * and context size are the same as the result of calling position()
    130  * or last() respectively.
    131  */
    132 static void
    133 exsltSaxonEvalFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    134      xmlXPathCompExprPtr expr;
    135      xmlXPathObjectPtr ret;
    136 
    137      if (nargs != 1) {
    138 	  xmlXPathSetArityError(ctxt);
    139 	  return;
    140      }
    141 
    142      if (!xmlXPathStackIsExternal(ctxt)) {
    143 	  xmlXPathSetTypeError(ctxt);
    144 	  return;
    145      }
    146 
    147      expr = (xmlXPathCompExprPtr) xmlXPathPopExternal(ctxt);
    148 
    149      ret = xmlXPathCompiledEval(expr, ctxt->context);
    150 
    151      valuePush(ctxt, ret);
    152 }
    153 
    154 /**
    155  * exsltSaxonEvaluateFunction:
    156  * @ctxt:  an XPath parser context
    157  * @nargs: number of arguments
    158  *
    159  * Implements the SAXON evaluate() function
    160  *     object saxon:evaluate (string)
    161  * The supplied string must contain an XPath expression. The result of
    162  * the function is the result of evaluating the XPath expression. This
    163  * is useful where an expression needs to be constructed at run-time or
    164  * passed to the stylesheet as a parameter, for example where the sort
    165  * key is determined dynamically. The context for the expression (e.g.
    166  * which variables and namespaces are available) is exactly the same as
    167  * if the expression were written explicitly at this point in the
    168  * stylesheet. The function saxon:evaluate(string) is shorthand for
    169  * saxon:eval(saxon:expression(string)).
    170  */
    171 static void
    172 exsltSaxonEvaluateFunction (xmlXPathParserContextPtr ctxt, int nargs) {
    173      if (nargs != 1) {
    174 	  xmlXPathSetArityError(ctxt);
    175 	  return;
    176      }
    177 
    178      exsltSaxonExpressionFunction(ctxt, 1);
    179      exsltSaxonEvalFunction(ctxt, 1);
    180 }
    181 
    182 /**
    183  * exsltSaxonLineNumberFunction:
    184  * @ctxt:  an XPath parser context
    185  * @nargs: number of arguments
    186  *
    187  * Implements the SAXON line-number() function
    188  *     integer saxon:line-number()
    189  *
    190  * This returns the line number of the context node in the source document
    191  * within the entity that contains it. There are no arguments. If line numbers
    192  * are not maintained for the current document, the function returns -1. (To
    193  * ensure that line numbers are maintained, use the -l option on the command
    194  * line)
    195  *
    196  * The extension has been extended to have the following form:
    197  *     integer saxon:line-number([node-set-1])
    198  * If a node-set is given, this extension will return the line number of the
    199  * node in the argument node-set that is first in document order.
    200  */
    201 static void
    202 exsltSaxonLineNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
    203     xmlNodePtr cur = NULL;
    204 
    205     if (nargs == 0) {
    206 	cur = ctxt->context->node;
    207     } else if (nargs == 1) {
    208 	xmlXPathObjectPtr obj;
    209 	xmlNodeSetPtr nodelist;
    210 	int i;
    211 
    212 	if ((ctxt->value == NULL) || (ctxt->value->type != XPATH_NODESET)) {
    213 	    xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
    214 		"saxon:line-number() : invalid arg expecting a node-set\n");
    215 	    ctxt->error = XPATH_INVALID_TYPE;
    216 	    return;
    217 	}
    218 
    219 	obj = valuePop(ctxt);
    220 	nodelist = obj->nodesetval;
    221 	if ((nodelist == NULL) || (nodelist->nodeNr <= 0)) {
    222 	    xmlXPathFreeObject(obj);
    223 	    valuePush(ctxt, xmlXPathNewFloat(-1));
    224 	    return;
    225 	}
    226 	cur = nodelist->nodeTab[0];
    227 	for (i = 1;i < nodelist->nodeNr;i++) {
    228 	    int ret = xmlXPathCmpNodes(cur, nodelist->nodeTab[i]);
    229 	    if (ret == -1)
    230 		cur = nodelist->nodeTab[i];
    231 	}
    232 	xmlXPathFreeObject(obj);
    233     } else {
    234 	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
    235 		"saxon:line-number() : invalid number of args %d\n",
    236 		nargs);
    237 	ctxt->error = XPATH_INVALID_ARITY;
    238 	return;
    239     }
    240 
    241     valuePush(ctxt, xmlXPathNewFloat(xmlGetLineNo(cur)));
    242     return;
    243 }
    244 
    245 /**
    246  * exsltSaxonRegister:
    247  *
    248  * Registers the SAXON extension module
    249  */
    250 void
    251 exsltSaxonRegister (void) {
    252      xsltRegisterExtModule (SAXON_NAMESPACE,
    253 			    (xsltExtInitFunction) exsltSaxonInit,
    254 			    (xsltExtShutdownFunction) exsltSaxonShutdown);
    255      xsltRegisterExtModuleFunction((const xmlChar *) "expression",
    256 				   SAXON_NAMESPACE,
    257 				   exsltSaxonExpressionFunction);
    258      xsltRegisterExtModuleFunction((const xmlChar *) "eval",
    259 				   SAXON_NAMESPACE,
    260 				   exsltSaxonEvalFunction);
    261      xsltRegisterExtModuleFunction((const xmlChar *) "evaluate",
    262 				   SAXON_NAMESPACE,
    263 				   exsltSaxonEvaluateFunction);
    264     xsltRegisterExtModuleFunction ((const xmlChar *) "line-number",
    265 				   SAXON_NAMESPACE,
    266 				   exsltSaxonLineNumberFunction);
    267 }
    268