- xsltutils.[ch] transform.c: implemented multiple levels of
authorDaniel Veillard <veillard@src.gnome.org>
Thu, 3 May 2001 16:01:22 +0000 (16:01 +0000)
committerDaniel Veillard <veillard@src.gnome.org>
Thu, 3 May 2001 16:01:22 +0000 (16:01 +0000)
  sorting
- test/REC/test-10-2.*: added a really small test for it
Daniel

ChangeLog
doc/xslt.html
libxslt/transform.c
libxslt/xsltInternals.h
libxslt/xsltutils.c
libxslt/xsltutils.h
tests/REC/test-10-2.out [new file with mode: 0644]
tests/REC/test-10-2.xml [new file with mode: 0644]
tests/REC/test-10-2.xsl [new file with mode: 0644]

index 553c5a0..ecc1138 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Thu May  3 17:56:55 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
+
+       * 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 <Daniel.Veillard@imag.fr>
 
        * libxslt/transform.c libxslt/xslt.c: fixed xsl:text processing
index 8f34494..369b74d 100644 (file)
@@ -1,5 +1,3 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-    "http://www.w3.org/TR/html4/loose.dtd">
 <html>
 <head>
   <title>The XML C library for Gnome</title>
@@ -24,7 +22,7 @@ alt="Red Hat Logo"></a></p>
   <li><a href="#help">how to help</a></li>
   <li><a href="#Downloads">Downloads</a></li>
   <li><a href="#News">News</a></li>
-  <li><a href="#XML">The xsltproc command</a></li>
+  <li><a href="#XML">The xsltproc command line tool</a></li>
   <li><a href="#XSLT">The programming API</a></li>
 </ul>
 
@@ -238,12 +236,28 @@ for a really accurate description</h3>
     feature complete</li>
 </ul>
 
-<h2>The xsltproc command</h2>
+<h2>The xsltproc command line tool tool</h2>
 
-<p>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
+<p>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.</p>
+
+<p>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.</p>
+the processing is redirected on the standard output. There is actually a few
+more options available:</p>
+<pre>orchis:~ -&gt; 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:~ -&gt;</pre>
 
 <h2>The programming API</h2>
 
@@ -251,7 +265,7 @@ the processing is redirected on the standard output.</p>
 application should be realitively easy. First check the few steps described
 below, then for more detailed informations, look at the<a
 href="libxslt-lib.html"> generated pages</a> for the API and the source of
-xsltproc.c .</p>
+libxslt/xsltproc.c .</p>
 
 <p>Basically doing an XSLT transformation can be done in a few steps:</p>
 <ol>
index 5162157..1727e3c 100644 (file)
@@ -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;
index 9031bb1..e44a006 100644 (file)
@@ -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
  */
index 0ae7e1a..345cd62 100644 (file)
@@ -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]);
+       }
+    }
 }
 
 /************************************************************************
index 44238e5..a4d671d 100644 (file)
@@ -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 (file)
index 0000000..2b35706
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<ul><li>Joe Bar</li><li>Daniel Veillard</li><li>Joel Veillard</li></ul>
diff --git a/tests/REC/test-10-2.xml b/tests/REC/test-10-2.xml
new file mode 100644 (file)
index 0000000..09c8ee6
--- /dev/null
@@ -0,0 +1,20 @@
+<employees>
+  <employee>
+    <name>
+      <given>Joel</given>
+      <family>Veillard</family>
+    </name>
+  </employee>
+  <employee>
+    <name>
+      <given>Daniel</given>
+      <family>Veillard</family>
+    </name>
+  </employee>
+  <employee>
+    <name>
+      <given>Joe</given>
+      <family>Bar</family>
+    </name>
+  </employee>
+</employees>
diff --git a/tests/REC/test-10-2.xsl b/tests/REC/test-10-2.xsl
new file mode 100644 (file)
index 0000000..613c4f3
--- /dev/null
@@ -0,0 +1,20 @@
+<xsl:stylesheet version="1.0"
+      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+      xmlns:fo="http://www.w3.org/1999/XSL/Format">
+<xsl:template match="employees">
+  <ul>
+    <xsl:apply-templates select="employee">
+      <xsl:sort select="name/family"/>
+      <xsl:sort select="name/given"/>
+    </xsl:apply-templates>
+  </ul>
+</xsl:template>
+
+<xsl:template match="employee">
+  <li>
+    <xsl:value-of select="name/given"/>
+    <xsl:text> </xsl:text>
+    <xsl:value-of select="name/family"/>
+  </li>
+</xsl:template>
+</xsl:stylesheet>