# 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