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