+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
-<!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>
<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>
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:~ -> 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:~ -></pre>
<h2>The programming API</h2>
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>
* @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");
}
/**
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) {
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,
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;
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,
*/
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;
#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
*/
}
/**
- * 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 */
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];
}
}
}
+
+ for (j = 0; j < nbsorts; j++) {
+ if (resultsTab[j] != NULL) {
+ for (i = 0;i < len;i++)
+ xmlXPathFreeObject(resultsTab[j][i]);
+ xmlFree(resultsTab[j]);
+ }
+ }
}
/************************************************************************
*/
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
*/
--- /dev/null
+<?xml version="1.0"?>
+<ul><li>Joe Bar</li><li>Daniel Veillard</li><li>Joel Veillard</li></ul>
--- /dev/null
+<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>
--- /dev/null
+<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>