From b41da1738f0388a9599b8cf485b8b78d539acfeb Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 3 May 2001 16:01:22 +0000 Subject: [PATCH] - xsltutils.[ch] transform.c: implemented multiple levels of sorting - test/REC/test-10-2.*: added a really small test for it Daniel --- ChangeLog | 6 ++ doc/xslt.html | 30 +++++-- libxslt/transform.c | 124 +++++++---------------------- libxslt/xsltInternals.h | 5 ++ libxslt/xsltutils.c | 204 +++++++++++++++++++++++++++++++++++++++++++++--- libxslt/xsltutils.h | 7 +- tests/REC/test-10-2.out | 2 + tests/REC/test-10-2.xml | 20 +++++ tests/REC/test-10-2.xsl | 20 +++++ 9 files changed, 300 insertions(+), 118 deletions(-) create mode 100644 tests/REC/test-10-2.out create mode 100644 tests/REC/test-10-2.xml create mode 100644 tests/REC/test-10-2.xsl diff --git a/ChangeLog b/ChangeLog index 553c5a0..ecc1138 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Thu May 3 17:56:55 CEST 2001 Daniel Veillard + + * xsltutils.[ch] transform.c: implemented multiple levels of + sorting + * test/REC/test-10-2.*: added a really small test for it + Wed May 2 14:04:34 CEST 2001 Daniel Veillard * libxslt/transform.c libxslt/xslt.c: fixed xsl:text processing diff --git a/doc/xslt.html b/doc/xslt.html index 8f34494..369b74d 100644 --- a/doc/xslt.html +++ b/doc/xslt.html @@ -1,5 +1,3 @@ - The XML C library for Gnome @@ -24,7 +22,7 @@ alt="Red Hat Logo">

  • how to help
  • Downloads
  • News
  • -
  • The xsltproc command
  • +
  • The xsltproc command line tool
  • The programming API
  • @@ -238,12 +236,28 @@ for a really accurate description feature complete -

    The xsltproc command

    +

    The xsltproc command line tool tool

    -

    This program is the simplest way to use libxslt from the command line, it -takes as first argument the path or URL to an XSLT stylesheet. the next +

    This program is the simplest way to use libxslt: from the command line. It +is also used for doing the regression tests of the library.

    + +

    It takes as first argument the path or URL to an XSLT stylesheet, the next arguments are filenames or URIs of the inputs to be processed. The output of -the processing is redirected on the standard output.

    +the processing is redirected on the standard output. There is actually a few +more options available:

    +
    orchis:~ -> xsltproc
    +Usage: xsltproc [options] stylesheet file [file ...]
    +   Options:
    +      --verbose or -v: show logs of what's happening
    +      --timing: display the time used
    +      --repeat: run the transformation 20 times
    +      --debug: dump the tree of the result instead
    +      --novalid: skip the Dtd loading phase
    +      --noout: do not dump the result
    +      --maxdepth val : increase the maximum depth
    +      --html: the input document is(are) an HTML file(s)
    +      --param name value
    +orchis:~ ->

    The programming API

    @@ -251,7 +265,7 @@ the processing is redirected on the standard output.

    application should be realitively easy. First check the few steps described below, then for more detailed informations, look at the generated pages for the API and the source of -xsltproc.c .

    +libxslt/xsltproc.c .

    Basically doing an XSLT transformation can be done in a few steps:

      diff --git a/libxslt/transform.c b/libxslt/transform.c index 5162157..1727e3c 100644 --- a/libxslt/transform.c +++ b/libxslt/transform.c @@ -1179,100 +1179,20 @@ void xsltProcessOneNode(xsltTransformContextPtr ctxt, xmlNodePtr node); * @inst: the xslt sort node * @comp: precomputed informations * - * Process the xslt sort node on the source node + * function attached to xslt:sort nodes, but this should not be + * called directly */ void -xsltSort(xsltTransformContextPtr ctxt, xmlNodePtr node, - xmlNodePtr inst, xsltStylePreCompPtr comp) { - xmlXPathObjectPtr *results = NULL; - xmlNodeSetPtr list = NULL; - xmlXPathObjectPtr res; - int len = 0; - int i; - xmlNodePtr oldNode; - +xsltSort(xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED, + xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst ATTRIBUTE_UNUSED, + xsltStylePreCompPtr comp) { if (comp == NULL) { xsltGenericError(xsltGenericErrorContext, "xslt:sort : compilation had failed\n"); return; } - - if ((ctxt == NULL) || (node == NULL) || (inst == NULL) || (comp == NULL)) - return; - if (comp->select == NULL) - return; - if (comp->comp == NULL) { - comp->comp = xmlXPathCompile(comp->select); - if (comp->comp == NULL) - return; - } - - - list = ctxt->nodeList; - if ((list == NULL) || (list->nodeNr <= 1)) - goto error; /* nothing to do */ - - len = list->nodeNr; - - /* TODO: xsl:sort lang attribute */ - /* TODO: xsl:sort case-order attribute */ - - - results = xmlMalloc(len * sizeof(xmlXPathObjectPtr)); - if (results == NULL) { - xsltGenericError(xsltGenericErrorContext, - "xsltSort: memory allocation failure\n"); - goto error; - } - - oldNode = ctxt->node; - for (i = 0;i < len;i++) { - ctxt->xpathCtxt->contextSize = len; - ctxt->xpathCtxt->proximityPosition = i + 1; - ctxt->node = list->nodeTab[i]; - ctxt->xpathCtxt->node = ctxt->node; - ctxt->xpathCtxt->namespaces = comp->nsList; - ctxt->xpathCtxt->nsNr = comp->nsNr; - res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); - if (res != NULL) { - if (res->type != XPATH_STRING) - res = xmlXPathConvertString(res); - if (comp->number) - res = xmlXPathConvertNumber(res); - res->index = i; /* Save original pos for dupl resolv */ - if (comp->number) { - if (res->type == XPATH_NUMBER) { - results[i] = res; - } else { -#ifdef WITH_XSLT_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 WITH_XSLT_DEBUG_PROCESS - xsltGenericDebug(xsltGenericDebugContext, - "xsltSort: select didn't evaluate to a string\n"); -#endif - results[i] = NULL; - } - } - } - } - ctxt->node = oldNode; - - xsltSortFunction(list, &results[0], comp->descending, comp->number); - -error: - if (results != NULL) { - for (i = 0;i < len;i++) - xmlXPathFreeObject(results[i]); - xmlFree(results); - } + xsltGenericError(xsltGenericErrorContext, + "xslt:sort : improper use this should not be reached\n"); } /** @@ -2042,8 +1962,9 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodeSetPtr list = NULL, oldlist; int i, oldProximityPosition, oldContextSize; const xmlChar *oldmode, *oldmodeURI; - int have_sort=0; xsltStackElemPtr params = NULL, param, tmp, p; + int nbsorts = 0; + xmlNodePtr sorts[XSLT_MAX_SORT]; if (comp == NULL) { @@ -2179,11 +2100,11 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, params = param; } } else if (IS_XSLT_NAME(cur, "sort")) { - if (!have_sort) { - have_sort = 1; - xsltSort(ctxt, node, cur, cur->_private); + if (nbsorts >= XSLT_MAX_SORT) { + xsltGenericError(xsltGenericDebugContext, + "xslt:call-template: %s too many sort\n", node->name); } else { - TODO /* imbricated sorts */ + sorts[nbsorts++] = cur; } } else { xsltGenericError(xsltGenericDebugContext, @@ -2196,6 +2117,10 @@ xsltApplyTemplates(xsltTransformContextPtr ctxt, xmlNodePtr node, cur = cur->next; } + if (nbsorts > 0) { + xsltDoSortFunction(ctxt, sorts, nbsorts); + } + for (i = 0;i < list->nodeNr;i++) { ctxt->node = list->nodeTab[i]; ctxt->xpathCtxt->proximityPosition = i + 1; @@ -2445,8 +2370,9 @@ xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node, xmlNodePtr replacement; xmlNodeSetPtr list = NULL, oldlist; int i, oldProximityPosition, oldContextSize; - /* xmlNodePtr oldInsert = ctxt->insert; */ xmlNodePtr oldNode = ctxt->node; + int nbsorts = 0; + xmlNodePtr sorts[XSLT_MAX_SORT]; if (comp == NULL) { xsltGenericError(xsltGenericErrorContext, @@ -2505,10 +2431,20 @@ xsltForEach(xsltTransformContextPtr ctxt, xmlNodePtr node, */ replacement = inst->children; while (IS_XSLT_ELEM(replacement) && (IS_XSLT_NAME(replacement, "sort"))) { - xsltSort(ctxt, node, replacement, replacement->_private); + if (nbsorts >= XSLT_MAX_SORT) { + xsltGenericError(xsltGenericDebugContext, + "xslt:for-each: too many sort\n"); + } else { + sorts[nbsorts++] = replacement; + } replacement = replacement->next; } + if (nbsorts > 0) { + xsltDoSortFunction(ctxt, sorts, nbsorts); + } + + for (i = 0;i < list->nodeNr;i++) { ctxt->node = list->nodeTab[i]; ctxt->xpathCtxt->proximityPosition = i + 1; diff --git a/libxslt/xsltInternals.h b/libxslt/xsltInternals.h index 9031bb1..e44a006 100644 --- a/libxslt/xsltInternals.h +++ b/libxslt/xsltInternals.h @@ -21,6 +21,11 @@ extern "C" { #endif /* + * Max number of specified xsl:sort on an element + */ +#define XSLT_MAX_SORT 5 + +/* * The in-memory structure corresponding to an XSLT Variable * or Param */ diff --git a/libxslt/xsltutils.c b/libxslt/xsltutils.c index 0ae7e1a..345cd62 100644 --- a/libxslt/xsltutils.c +++ b/libxslt/xsltutils.c @@ -207,27 +207,146 @@ xsltDocumentSortFunction(xmlNodeSetPtr list) { } /** - * xsltSortFunction: - * @list: the node set - * @results: the results - * @descending: direction of order - * @number: the type of the result + * xsltComputeSortResult: + * @ctxt: a XSLT process context + * @sorts: array of sort nodes + * @nbsorts: the number of sorts in the array * - * reorder the current node list @list accordingly to the values - * present in the array of results @results + * reorder the current node list accordingly to the set of sorting + * requirement provided by the arry of nodes. + */ +static xmlXPathObjectPtr * +xsltComputeSortResult(xsltTransformContextPtr ctxt, xmlNodePtr sort) { + xmlXPathObjectPtr *results = NULL; + xmlNodeSetPtr list = NULL; + xmlXPathObjectPtr res; + int len = 0; + int i; + xmlNodePtr oldNode; + xsltStylePreCompPtr comp; + + comp = sort->_private; + if (comp == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xslt:sort : compilation had failed\n"); + return(NULL); + } + + if (comp->select == NULL) + return(NULL); + if (comp->comp == NULL) { + comp->comp = xmlXPathCompile(comp->select); + if (comp->comp == NULL) + return(NULL); + } + + + list = ctxt->nodeList; + if ((list == NULL) || (list->nodeNr <= 1)) + return(NULL); + + len = list->nodeNr; + + /* TODO: xsl:sort lang attribute */ + /* TODO: xsl:sort case-order attribute */ + + + results = xmlMalloc(len * sizeof(xmlXPathObjectPtr)); + if (results == NULL) { + xsltGenericError(xsltGenericErrorContext, + "xsltSort: memory allocation failure\n"); + return(NULL); + } + + oldNode = ctxt->node; + for (i = 0;i < len;i++) { + ctxt->xpathCtxt->contextSize = len; + ctxt->xpathCtxt->proximityPosition = i + 1; + ctxt->node = list->nodeTab[i]; + ctxt->xpathCtxt->node = ctxt->node; + ctxt->xpathCtxt->namespaces = comp->nsList; + ctxt->xpathCtxt->nsNr = comp->nsNr; + res = xmlXPathCompiledEval(comp->comp, ctxt->xpathCtxt); + if (res != NULL) { + if (res->type != XPATH_STRING) + res = xmlXPathConvertString(res); + if (comp->number) + res = xmlXPathConvertNumber(res); + res->index = i; /* Save original pos for dupl resolv */ + if (comp->number) { + if (res->type == XPATH_NUMBER) { + results[i] = res; + } else { +#ifdef WITH_XSLT_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 WITH_XSLT_DEBUG_PROCESS + xsltGenericDebug(xsltGenericDebugContext, + "xsltSort: select didn't evaluate to a string\n"); +#endif + results[i] = NULL; + } + } + } + } + ctxt->node = oldNode; + + return(results); +} + +/** + * xsltDoSortFunction: + * @ctxt: a XSLT process context + * @sorts: array of sort nodes + * @nbsorts: the number of sorts in the array + * + * reorder the current node list accordingly to the set of sorting + * requirement provided by the arry of nodes. */ void -xsltSortFunction(xmlNodeSetPtr list, xmlXPathObjectPtr *results, - int descending, int number) { +xsltDoSortFunction(xsltTransformContextPtr ctxt, xmlNodePtr *sorts, + int nbsorts) { + xmlXPathObjectPtr *resultsTab[XSLT_MAX_SORT]; + xmlXPathObjectPtr *results = NULL, *res; + xmlNodeSetPtr list = NULL; + int descending, number, desc, numb; + int len = 0; int i, j, incr; - int len, tst; + int tst; + int depth; xmlNodePtr node; xmlXPathObjectPtr tmp; + xsltStylePreCompPtr comp; - if ((list == NULL) || (results == NULL)) + if ((ctxt == NULL) || (sorts == NULL) || (nbsorts <= 0) || + (nbsorts >= XSLT_MAX_SORT)) + return; + if (sorts[0] == NULL) + return; + comp = sorts[0]->_private; + if (comp == NULL) return; + + list = ctxt->nodeList; + if ((list == NULL) || (list->nodeNr <= 1)) + return; /* nothing to do */ + len = list->nodeNr; - if (len <= 1) + + resultsTab[0] = xsltComputeSortResult(ctxt, sorts[0]); + + results = resultsTab[0]; + + descending = comp->descending; + number = comp->number; + if (results == NULL) return; /* Shell's sort of node-set */ @@ -257,6 +376,59 @@ xsltSortFunction(xmlNodeSetPtr list, xmlXPathObjectPtr *results, if (descending) tst = -tst; } + if (tst == 0) { + /* + * Okay we need to use multi level sorts + */ + depth = 1; + while (depth < nbsorts) { + if (sorts[depth] == NULL) + break; + comp = sorts[depth]->_private; + if (comp == NULL) + break; + desc = comp->descending; + numb = comp->number; + + /* + * Compute the result of the next level for the + * full set, this might be optimized ... or not + */ + if (resultsTab[depth] == NULL) + resultsTab[depth] = xsltComputeSortResult(ctxt, + sorts[depth]); + res = resultsTab[depth]; + if (res == NULL) + break; + if (res[j] == NULL) + tst = 1; + else { + if (numb) { + if (res[j]->floatval == res[j + incr]->floatval) + tst = 0; + else if (res[j]->floatval > + res[j + incr]->floatval) + tst = 1; + else tst = -1; + } else { + tst = xmlStrcmp(res[j]->stringval, + res[j + incr]->stringval); + } + if (tst == 0) + tst = res[j]->index > res[j + incr]->index; + if (desc) + tst = -tst; + } + + /* + * if we still can't differenciate at this level + * try one level deeper. + */ + if (tst != 0) + break; + depth++; + } + } if (tst > 0) { tmp = results[j]; results[j] = results[j + incr]; @@ -270,6 +442,14 @@ xsltSortFunction(xmlNodeSetPtr list, xmlXPathObjectPtr *results, } } } + + for (j = 0; j < nbsorts; j++) { + if (resultsTab[j] != NULL) { + for (i = 0;i < len;i++) + xmlXPathFreeObject(resultsTab[j][i]); + xmlFree(resultsTab[j]); + } + } } /************************************************************************ diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h index 44238e5..a4d671d 100644 --- a/libxslt/xsltutils.h +++ b/libxslt/xsltutils.h @@ -71,10 +71,9 @@ void xsltSetGenericDebugFunc (void *ctx, */ void xsltDocumentSortFunction (xmlNodeSetPtr list); -void xsltSortFunction (xmlNodeSetPtr list, - xmlXPathObjectPtr *results, - int descending, - int number); +void xsltDoSortFunction (xsltTransformContextPtr ctxt, + xmlNodePtr *sorts, + int nbsorts); /* * Output, reuse libxml I/O buffers */ diff --git a/tests/REC/test-10-2.out b/tests/REC/test-10-2.out new file mode 100644 index 0000000..2b35706 --- /dev/null +++ b/tests/REC/test-10-2.out @@ -0,0 +1,2 @@ + +
      • Joe Bar
      • Daniel Veillard
      • Joel Veillard
      diff --git a/tests/REC/test-10-2.xml b/tests/REC/test-10-2.xml new file mode 100644 index 0000000..09c8ee6 --- /dev/null +++ b/tests/REC/test-10-2.xml @@ -0,0 +1,20 @@ + + + + Joel + Veillard + + + + + Daniel + Veillard + + + + + Joe + Bar + + + diff --git a/tests/REC/test-10-2.xsl b/tests/REC/test-10-2.xsl new file mode 100644 index 0000000..613c4f3 --- /dev/null +++ b/tests/REC/test-10-2.xsl @@ -0,0 +1,20 @@ + + +
        + + + + +
      +
      + + +
    1. + + + +
    2. +
      +
      -- 2.7.4