1/*
2 * dynamic.c: Implementation of the EXSLT -- Dynamic module
3 *
4 * References:
5 *   http://www.exslt.org/dyn/dyn.html
6 *
7 * See Copyright for the status of this software.
8 *
9 * Authors:
10 *   Mark Vakoc <mark_vakoc@jdedwards.com>
11 *   Thomas Broyer <tbroyer@ltgt.net>
12 *
13 * TODO:
14 * elements:
15 * functions:
16 *    min
17 *    max
18 *    sum
19 *    map
20 *    closure
21 */
22
23#define IN_LIBEXSLT
24#include "libexslt/libexslt.h"
25
26#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
27#include <win32config.h>
28#else
29#include "config.h"
30#endif
31
32#include <libxml/tree.h>
33#include <libxml/xpath.h>
34#include <libxml/xpathInternals.h>
35
36#include <libxslt/xsltconfig.h>
37#include <libxslt/xsltutils.h>
38#include <libxslt/xsltInternals.h>
39#include <libxslt/extensions.h>
40
41#include "exslt.h"
42
43/**
44 * exsltDynEvaluateFunction:
45 * @ctxt:  an XPath parser context
46 * @nargs:  the number of arguments
47 *
48 * Evaluates the string as an XPath expression and returns the result
49 * value, which may be a boolean, number, string, node set, result tree
50 * fragment or external object.
51 */
52
53static void
54exsltDynEvaluateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
55	xmlChar *str = NULL;
56	xmlXPathObjectPtr ret = NULL;
57
58	if (ctxt == NULL)
59		return;
60	if (nargs != 1) {
61		xsltPrintErrorContext(xsltXPathGetTransformContext(ctxt), NULL, NULL);
62        xsltGenericError(xsltGenericErrorContext,
63			"dyn:evalute() : invalid number of args %d\n", nargs);
64		ctxt->error = XPATH_INVALID_ARITY;
65		return;
66	}
67	str = xmlXPathPopString(ctxt);
68	/* return an empty node-set if an empty string is passed in */
69	if (!str||!xmlStrlen(str)) {
70		if (str) xmlFree(str);
71		valuePush(ctxt,xmlXPathNewNodeSet(NULL));
72		return;
73	}
74	ret = xmlXPathEval(str,ctxt->context);
75	if (ret)
76		valuePush(ctxt,ret);
77 	else {
78		xsltGenericError(xsltGenericErrorContext,
79			"dyn:evaluate() : unable to evaluate expression '%s'\n",str);
80		valuePush(ctxt,xmlXPathNewNodeSet(NULL));
81	}
82	xmlFree(str);
83	return;
84}
85
86/**
87 * exsltDynMapFunction:
88 * @ctxt:  an XPath parser context
89 * @nargs:  the number of arguments
90 *
91 * Evaluates the string as an XPath expression and returns the result
92 * value, which may be a boolean, number, string, node set, result tree
93 * fragment or external object.
94 */
95
96static void
97exsltDynMapFunction(xmlXPathParserContextPtr ctxt, int nargs)
98{
99    xmlChar *str = NULL;
100    xmlNodeSetPtr nodeset = NULL;
101    xsltTransformContextPtr tctxt;
102    xmlXPathCompExprPtr comp = NULL;
103    xmlXPathObjectPtr ret = NULL;
104    xmlDocPtr oldDoc, container = NULL;
105    xmlNodePtr oldNode;
106    int oldContextSize;
107    int oldProximityPosition;
108    int i, j;
109
110
111    if (nargs != 2) {
112        xmlXPathSetArityError(ctxt);
113        return;
114    }
115    str = xmlXPathPopString(ctxt);
116    if (xmlXPathCheckError(ctxt)) {
117        xmlXPathSetTypeError(ctxt);
118        return;
119    }
120
121    nodeset = xmlXPathPopNodeSet(ctxt);
122    if (xmlXPathCheckError(ctxt)) {
123        xmlXPathSetTypeError(ctxt);
124        return;
125    }
126    if (str == NULL || !xmlStrlen(str) || !(comp = xmlXPathCompile(str))) {
127        if (nodeset != NULL)
128            xmlXPathFreeNodeSet(nodeset);
129        if (str != NULL)
130            xmlFree(str);
131        valuePush(ctxt, xmlXPathNewNodeSet(NULL));
132        return;
133    }
134
135    ret = xmlXPathNewNodeSet(NULL);
136    if (ret == NULL) {
137        xsltGenericError(xsltGenericErrorContext,
138                         "exsltDynMapFunction: ret == NULL\n");
139        goto cleanup;
140    }
141
142    oldDoc = ctxt->context->doc;
143    oldNode = ctxt->context->node;
144    oldContextSize = ctxt->context->contextSize;
145    oldProximityPosition = ctxt->context->proximityPosition;
146
147        /**
148	 * since we really don't know we're going to be adding node(s)
149	 * down the road we create the RVT regardless
150	 */
151    tctxt = xsltXPathGetTransformContext(ctxt);
152    if (tctxt == NULL) {
153	xsltTransformError(xsltXPathGetTransformContext(ctxt), NULL, NULL,
154	      "dyn:map : internal error tctxt == NULL\n");
155	goto cleanup;
156    }
157    container = xsltCreateRVT(tctxt);
158    if (container == NULL) {
159	xsltTransformError(tctxt, NULL, NULL,
160	      "dyn:map : internal error container == NULL\n");
161	goto cleanup;
162    }
163    xsltRegisterLocalRVT(tctxt, container);
164    if (nodeset && nodeset->nodeNr > 0) {
165        xmlXPathNodeSetSort(nodeset);
166        ctxt->context->contextSize = nodeset->nodeNr;
167        ctxt->context->proximityPosition = 0;
168        for (i = 0; i < nodeset->nodeNr; i++) {
169            xmlXPathObjectPtr subResult = NULL;
170
171            ctxt->context->proximityPosition++;
172            ctxt->context->node = nodeset->nodeTab[i];
173            ctxt->context->doc = nodeset->nodeTab[i]->doc;
174
175            subResult = xmlXPathCompiledEval(comp, ctxt->context);
176            if (subResult != NULL) {
177                switch (subResult->type) {
178                    case XPATH_NODESET:
179                        if (subResult->nodesetval != NULL)
180                            for (j = 0; j < subResult->nodesetval->nodeNr;
181                                 j++)
182                                xmlXPathNodeSetAdd(ret->nodesetval,
183                                                   subResult->nodesetval->
184                                                   nodeTab[j]);
185                        break;
186                    case XPATH_BOOLEAN:
187                        if (container != NULL) {
188                            xmlNodePtr cur =
189                                xmlNewChild((xmlNodePtr) container, NULL,
190                                            BAD_CAST "boolean",
191                                            BAD_CAST (subResult->
192                                            boolval ? "true" : ""));
193                            if (cur != NULL) {
194                                cur->ns =
195                                    xmlNewNs(cur,
196                                             BAD_CAST
197                                             "http://exslt.org/common",
198                                             BAD_CAST "exsl");
199                                xmlXPathNodeSetAddUnique(ret->nodesetval,
200                                                         cur);
201                            }
202			    xsltExtensionInstructionResultRegister(tctxt, ret);
203                        }
204                        break;
205                    case XPATH_NUMBER:
206                        if (container != NULL) {
207                            xmlChar *val =
208                                xmlXPathCastNumberToString(subResult->
209                                                           floatval);
210                            xmlNodePtr cur =
211                                xmlNewChild((xmlNodePtr) container, NULL,
212                                            BAD_CAST "number", val);
213                            if (val != NULL)
214                                xmlFree(val);
215
216                            if (cur != NULL) {
217                                cur->ns =
218                                    xmlNewNs(cur,
219                                             BAD_CAST
220                                             "http://exslt.org/common",
221                                             BAD_CAST "exsl");
222                                xmlXPathNodeSetAddUnique(ret->nodesetval,
223                                                         cur);
224                            }
225			    xsltExtensionInstructionResultRegister(tctxt, ret);
226                        }
227                        break;
228                    case XPATH_STRING:
229                        if (container != NULL) {
230                            xmlNodePtr cur =
231                                xmlNewChild((xmlNodePtr) container, NULL,
232                                            BAD_CAST "string",
233                                            subResult->stringval);
234                            if (cur != NULL) {
235                                cur->ns =
236                                    xmlNewNs(cur,
237                                             BAD_CAST
238                                             "http://exslt.org/common",
239                                             BAD_CAST "exsl");
240                                xmlXPathNodeSetAddUnique(ret->nodesetval,
241                                                         cur);
242                            }
243			    xsltExtensionInstructionResultRegister(tctxt, ret);
244                        }
245                        break;
246		    default:
247                        break;
248                }
249                xmlXPathFreeObject(subResult);
250            }
251        }
252    }
253    ctxt->context->doc = oldDoc;
254    ctxt->context->node = oldNode;
255    ctxt->context->contextSize = oldContextSize;
256    ctxt->context->proximityPosition = oldProximityPosition;
257
258
259  cleanup:
260    /* restore the xpath context */
261    if (comp != NULL)
262        xmlXPathFreeCompExpr(comp);
263    if (nodeset != NULL)
264        xmlXPathFreeNodeSet(nodeset);
265    if (str != NULL)
266        xmlFree(str);
267    valuePush(ctxt, ret);
268    return;
269}
270
271
272/**
273 * exsltDynRegister:
274 *
275 * Registers the EXSLT - Dynamic module
276 */
277
278void
279exsltDynRegister (void) {
280    xsltRegisterExtModuleFunction ((const xmlChar *) "evaluate",
281				   EXSLT_DYNAMIC_NAMESPACE,
282				   exsltDynEvaluateFunction);
283  xsltRegisterExtModuleFunction ((const xmlChar *) "map",
284				   EXSLT_DYNAMIC_NAMESPACE,
285				   exsltDynMapFunction);
286
287}
288