# HG changeset patch # User Daniel Veillard <veillard@src.gnome.org> # Date 979750056 0 # Wed Jan 17 16:47:36 2001 +0000 # Node ID 866a0e5dc96fa6bf58332592795aa7d782b00b7d # Parent 563e1383046f52590b15a669684ec4d30b0ec5b8 Continuous hacking ... - TODO: guess what, it's growing :-( - configure.in: setup hacking values when compiling in my own environment. - libxslt/transform.c libxslt/xsltutils.[hc]: added a first very rudimentary version of xsl:sort Daniel diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Wed Jan 17 17:45:20 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> + + * TODO: guess what, it's growing :-( + * configure.in: setup hacking values when compiling in my + own environment. + * libxslt/transform.c libxslt/xsltutils.[hc]: added a first + very rudimentary version of xsl:sort + Wed Jan 17 14:25:25 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr> * TODO: more stuff diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -34,3 +34,9 @@ Support Attribute value templates: -> starts to be urgent. Design it in flexible ways but try to optimize to handle most of it at the stylesheet parse time ... + +Sorting: + -> add support for imbricated sorts + -> add lang and case-order + -> add foreign sorting functions (interfaces ?). + diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -10,12 +10,13 @@ dnl dnl Debug for DV dnl -if test "${LOGNAME}" = "veillard" ; then +if test "${LOGNAME}" = "veillard" -a "`pwd`" = "/u/veillard/XSLT" ; then if test "${with_mem_debug}" = "" ; then with_mem_debug="yes" fi CFLAGS="-Wall -g -pedantic" fi + AC_ARG_WITH(mem_debug, [ --with-mem-debug Add the memory debugging module (off)]) if test "$with_mem_debug" = "yes" ; then echo Enabling memory debug support diff --git a/libxslt/transform.c b/libxslt/transform.c --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -559,7 +559,7 @@ ctxt->insert = oldInsert; } else { #ifdef DEBUG_PROCESS - xsltGenericDebug(xsltGenericDebugContext, + xsltGenericError(xsltGenericDebugContext, "xsltApplyOneTemplate: found xslt:%s\n", cur->name); #endif TODO @@ -715,6 +715,141 @@ } /** + * xsltSort: + * @ctxt: a XSLT process context + * @node: the node in the source tree. + * @inst: the xslt sort node + * + * Process the xslt sort node on the source node + */ +void +xsltSort(xsltTransformContextPtr ctxt, xmlNodePtr node, + xmlNodePtr inst) { + xmlXPathObjectPtr *results = NULL; + xmlNodeSetPtr list = NULL; + xmlXPathParserContextPtr xpathParserCtxt = NULL; + xmlChar *prop; + xmlXPathObjectPtr res, tmp; + const xmlChar *start; + int descending = 0; + int number = 0; + int len; + int i; + + if ((ctxt == NULL) || (node == NULL) || (inst == NULL)) + return; + + list = ctxt->nodeList; + if ((list == NULL) || (list->nodeNr <= 1)) + goto error; /* nothing to do */ + + len = list->nodeNr; + + prop = xmlGetNsProp(inst, (const xmlChar *)"data-type", XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "text")) + number = 0; + else if (xmlStrEqual(prop, (const xmlChar *) "number")) + number = 1; + else { + xsltGenericError(xsltGenericErrorContext, + "xsltSort: no support for data-type = %s\n", prop); + goto error; + } + xmlFree(prop); + } + prop = xmlGetNsProp(inst, (const xmlChar *)"order", XSLT_NAMESPACE); + if (prop != NULL) { + if (xmlStrEqual(prop, (const xmlChar *) "ascending")) + descending = 0; + else if (xmlStrEqual(prop, (const xmlChar *) "descending")) + descending = 1; + else { + xsltGenericError(xsltGenericErrorContext, + "xsltSort: invalid value %s for order\n", prop); + goto error; + } + xmlFree(prop); + } + /* TODO: xsl:sort lang attribute */ + /* TODO: xsl:sort order attribute */ + /* TODO: xsl:sort case-order attribute */ + + prop = xmlGetNsProp(inst, (const xmlChar *)"select", XSLT_NAMESPACE); + if (prop == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltSort: select is not defined\n"); + return; + } + + xpathParserCtxt = xmlXPathNewParserContext(prop, ctxt->xpathCtxt); + if (xpathParserCtxt == NULL) + goto error; + results = xmlMalloc(len * sizeof(xmlXPathObjectPtr)); + if (results == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltSort: memory allocation failure\n"); + goto error; + } + + start = xpathParserCtxt->cur; + for (i = 0;i < len;i++) { + xpathParserCtxt->cur = start; + node = ctxt->node = list->nodeTab[i]; + ctxt->xpathCtxt->proximityPosition = i + 1; + valuePush(xpathParserCtxt, xmlXPathNewNodeSet(node)); + xmlXPathEvalExpr(xpathParserCtxt); + xmlXPathStringFunction(xpathParserCtxt, 1); + if (number) + xmlXPathNumberFunction(xpathParserCtxt, 1); + res = valuePop(xpathParserCtxt); + do { + tmp = valuePop(xpathParserCtxt); + if (tmp != NULL) { + xmlXPathFreeObject(tmp); + } + } while (tmp != NULL); + + if (res != NULL) { + if (number) { + if (res->type == XPATH_NUMBER) { + results[i] = res; + } else { +#ifdef DEBUG_PROCESS + xsltGenericDebug(xsltGenericDebugContext, + "xsltSort: select didn't evaluate to a number\n"); +#endif + results[i] = NULL; + } + } else { + if (res->type == XPATH_STRING) { + results[i] = res; + } else { +#ifdef DEBUG_PROCESS + xsltGenericDebug(xsltGenericDebugContext, + "xsltSort: select didn't evaluate to a string\n"); +#endif + results[i] = NULL; + } + } + } + } + + xsltSortFunction(list, &results[0], descending, number); + +error: + if (xpathParserCtxt != NULL) + xmlXPathFreeParserContext(xpathParserCtxt); + if (prop != NULL) + xmlFree(prop); + if (results != NULL) { + for (i = 0;i < len;i++) + xmlXPathFreeObject(results[i]); + xmlFree(results); + } +} + +/** * xsltForEach: * @ctxt: a XSLT process context * @node: the node in the source tree. @@ -782,14 +917,22 @@ xsltGenericDebug(xsltGenericDebugContext, "xsltForEach: select evaluate to %d nodes\n", list->nodeNr); #endif - /* TODO: handle and skip the xsl:sort */ - replacement = inst->children; oldlist = ctxt->nodeList; ctxt->nodeList = list; oldContextSize = ctxt->xpathCtxt->contextSize; oldProximityPosition = ctxt->xpathCtxt->proximityPosition; ctxt->xpathCtxt->contextSize = list->nodeNr; + + /* + * handle and skip the xsl:sort + */ + replacement = inst->children; + while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) { + xsltSort(ctxt, node, replacement); + replacement = replacement->next; + } + for (i = 0;i < list->nodeNr;i++) { ctxt->node = list->nodeTab[i]; ctxt->xpathCtxt->proximityPosition = i + 1; diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c --- a/libxslt/xsltutils.c +++ b/libxslt/xsltutils.c @@ -118,3 +118,59 @@ xsltGenericDebug = xsltGenericDebugDefaultFunc; } +/************************************************************************ + * * + * Sorting * + * * + ************************************************************************/ + +/** + * xsltSortFunction: + * @list: the node set + * @results: the results + * @descending: direction of order + * @number: the type of the result + * + * reorder the current node list @list accordingly to the values + * present in the array of results @results + */ +void +xsltSortFunction(xmlNodeSetPtr list, xmlXPathObjectPtr *results, + int descending, int number) { + int i, j; + int len, tst; + xmlNodePtr node; + xmlXPathObjectPtr tmp; + + if ((list == NULL) || (results == NULL)) + return; + len = list->nodeNr; + if (len <= 1) + return; + /* TODO: sort is really not optimized, does it needs to ? */ + for (i = 0;i < len -1;i++) { + for (j = i + 1; j < len; j++) { + if (results[i] == NULL) + tst = 0; + else if (results[j] == NULL) + tst = 1; + else if (number) { + tst = (results[i]->floatval > results[j]->floatval); + if (descending) + tst = !tst; + } else { + tst = xmlStrcmp(results[i]->stringval, results[j]->stringval); + if (descending) + tst = !tst; + } + if (tst) { + tmp = results[i]; + results[i] = results[j]; + results[j] = tmp; + node = list->nodeTab[i]; + list->nodeTab[i] = list->nodeTab[j]; + list->nodeTab[j] = node; + } + } + } +} diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h --- a/libxslt/xsltutils.h +++ b/libxslt/xsltutils.h @@ -37,7 +37,8 @@ __FILE__, __LINE__); #define IS_XSLT_ELEM(n) \ - ((n)->ns != NULL) && (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE)) + (((n) != NULL) && ((n)->ns != NULL) && \ + (xmlStrEqual((n)->ns->href, XSLT_NAMESPACE))) #define IS_XSLT_NAME(n, val) \ (xmlStrEqual((n)->name, (const xmlChar *) (val))) @@ -55,6 +56,15 @@ xmlGenericErrorFunc handler); void xsltSetGenericDebugFunc (void *ctx, xmlGenericErrorFunc handler); + +/* + * Sorting ... this is definitely a temporary interface ! + */ + +void xsltSortFunction (xmlNodeSetPtr list, + xmlXPathObjectPtr *results, + int descending, + int number); #ifdef __cplusplus } #endif