- functions.[ch]: Bjorn Reese <breese@mail1.stofanet.dk> provided
authorDaniel Veillard <veillard@src.gnome.org>
Thu, 25 Jan 2001 11:16:26 +0000 (11:16 +0000)
committerDaniel Veillard <veillard@src.gnome.org>
Thu, 25 Jan 2001 11:16:26 +0000 (11:16 +0000)
  number formatting !!!
- acconfig.h config.h.in configure.in libxslt/Makefile.am
  tests/Makefile.am; added testing for mathematical functions,
  fixed make test(s)
- FEATURES: updated
Daniel

ChangeLog
FEATURES
acconfig.h [new file with mode: 0644]
config.h.in
configure.in
libxslt/Makefile.am
libxslt/functions.c
libxslt/functions.h
tests/Makefile.am

index f64ecb3..9c22ac3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Thu Jan 25 12:13:04 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
+
+       * functions.[ch]: Bjorn Reese <breese@mail1.stofanet.dk> provided
+         number formatting !!!
+       * acconfig.h config.h.in configure.in libxslt/Makefile.am
+         tests/Makefile.am; added testing for mathematical functions,
+         fixed make test(s)
+       * FEATURES: updated
+
 Wed Jan 24 16:59:05 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
 
        * libxslt/xsltInternals.h libxslt/pattern.c: fixed problems
index 651e570..9356fac 100644 (file)
--- a/FEATURES
+++ b/FEATURES
@@ -203,7 +203,7 @@ Functions:
 
 NO                 node-set document(object, node-set?)
 NO                 node-set key(string, object)
-NO                 string format-number(number, string, string?)
+YES                string format-number(number, string, string?)
 NO                 node-set current() 
 NO                 string unparsed-entity-uri(string)
 NO                 string generate-id(node-set?)
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..f00b312
--- /dev/null
@@ -0,0 +1,5 @@
+#undef HAVE_ISINF
+#undef HAVE_ISNAN
+#undef HAVE_POW
+#undef HAVE_FLOOR
+#undef HAVE_FABS
index be5dcb9..0c922c0 100644 (file)
@@ -5,3 +5,24 @@
 
 /* Define if you have the ANSI C header files.  */
 #undef STDC_HEADERS
+
+#undef HAVE_ISINF
+#undef HAVE_ISNAN
+#undef HAVE_POW
+#undef HAVE_FLOOR
+#undef HAVE_FABS
+
+/* Define if you have the <float.h> header file.  */
+#undef HAVE_FLOAT_H
+
+/* Define if you have the <fp_class.h> header file.  */
+#undef HAVE_FP_CLASS_H
+
+/* Define if you have the <ieeefp.h> header file.  */
+#undef HAVE_IEEEFP_H
+
+/* Define if you have the <math.h> header file.  */
+#undef HAVE_MATH_H
+
+/* Define if you have the <nan.h> header file.  */
+#undef HAVE_NAN_H
index 7b34d95..6696891 100644 (file)
@@ -8,6 +8,37 @@ AM_INIT_AUTOMAKE($PACKAGE, $VERSION, no-define)
 AM_MAINTAINER_MODE
 
 dnl
+dnl Check the environment
+dnl
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_STDC_HEADERS
+AC_ARG_PROGRAM
+AM_PROG_LIBTOOL
+
+dnl
+dnl Math detection
+dnl
+
+AC_CHECK_HEADERS(ieeefp.h nan.h math.h fp_class.h float.h)
+AC_CHECK_FUNC(isnan, , AC_CHECK_LIB(m, isnan,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_ISNAN)]))
+
+AC_CHECK_FUNC(isinf, , AC_CHECK_LIB(m, isinf,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_ISINF)]))
+
+AC_CHECK_FUNC(pow, , AC_CHECK_LIB(m, pow,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_POW)]))
+
+AC_CHECK_FUNC(floor, , AC_CHECK_LIB(m, pow,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_FLOOR)]))
+
+AC_CHECK_FUNC(fabs, , AC_CHECK_LIB(m, pow,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_FABS)]))
+
+
+dnl
 dnl Debug for DV
 dnl
 if test "${LOGNAME}" = "veillard" -a "`pwd`" = "/u/veillard/XSLT" ; then
@@ -49,16 +80,6 @@ AC_ARG_WITH(libxml-libs-prefix,
 )
 
 
-dnl
-dnl Check the environment
-dnl
-
-AC_ISC_POSIX
-AC_PROG_CC
-AC_STDC_HEADERS
-AC_ARG_PROGRAM
-AM_PROG_LIBTOOL
-
 dnl No internationalization (yet ?)
 dnl 
 dnl ALL_LINGUAS="it ko fr de es no ga sv pt ja fi cs"
@@ -110,10 +131,12 @@ AC_SUBST(LIBXML_CFLAGS)
 
 XSLT_LIBDIR='-L${libdir}'
 XSLT_INCLUDEDIR='-I${includedir}'
-XSLT_LIBS="-lxslt $LIBXML_LIBS"
+EXTRA_LIBS="$LIBXML_LIBS $M_LIBS"
+XSLT_LIBS="-lxslt $LIBXML_LIBS $M_LIBS"
 
 AC_SUBST(XSLT_LIBDIR)
 AC_SUBST(XSLT_INCLUDEDIR)
+AC_SUBST(EXTRA_LIBS)
 AC_SUBST(XSLT_LIBS)
 
 AC_OUTPUT([
index 1f4724e..6036591 100644 (file)
@@ -32,7 +32,7 @@ libxslt_la_SOURCES =                  \
 bin_PROGRAMS = xsltproc
 
 DEPS = $(top_builddir)/libxslt/libxslt.la
-LDADDS = -L. $(top_builddir)/libxslt/libxslt.la $(LIBXML_LIBS)
+LDADDS = -L. $(top_builddir)/libxslt/libxslt.la $(EXTRA_LIBS)
 
 xsltproc_SOURCES = xsltproc.c
 xsltproc_LDFLAGS =
index 9eb48bf..ca041c2 100644 (file)
@@ -7,13 +7,34 @@
  * See Copyright for the status of this software.
  *
  * Daniel.Veillard@imag.fr
+ * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting
  */
 
 #include "xsltconfig.h"
 
 #include <string.h>
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
 #include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
 #include <libxml/tree.h>
 #include <libxml/valid.h>
 #include <libxml/hash.h>
 
 #define DEBUG_FUNCTION
 
+#ifndef FALSE
+# define FALSE (0 == 1)
+# define TRUE (1 == 1)
+#endif
+
+#define DIGIT_LIST "0123456789"
+#define SYMBOL_QUOTE             ((xmlChar)'\'')
+#define SYMBOL_PATTERN_SEPARATOR ((xmlChar)';')
+#define SYMBOL_ZERO_DIGIT        ((xmlChar)'#')
+#define SYMBOL_DIGIT             ((xmlChar)'0')
+#define SYMBOL_DECIMAL_POINT     ((xmlChar)'.')
+#define SYMBOL_GROUPING          ((xmlChar)',')
+#define SYMBOL_MINUS             ((xmlChar)'-')
+#define SYMBOL_PERCENT           ((xmlChar)'%')
+#define SYMBOL_PERMILLE          ((xmlChar)'?')
+
+static struct _xsltDecimalFormat globalDecimalFormat;
+
+/************************************************************************
+ *                                                                     *
+ *                     Utility functions                               *
+ *                                                                     *
+ ************************************************************************/
+
+#ifndef isnan
+static int
+isnan(volatile double number)
+{
+    return (!(number < 0.0 || number > 0.0) && (number != 0.0));
+}
+#endif
+
+#ifndef isinf
+static int
+isinf(double number)
+{
+# ifdef HUGE_VAL
+    return ((number == HUGE_VAL) ? 1 : ((number == -HUGE_VAL) ? -1 : 0));
+# else
+    return FALSE;
+# endif
+}
+#endif
+
 
 /************************************************************************
  *                                                                     *
@@ -82,9 +147,245 @@ xsltUnparsedEntityURIFunction(xmlXPathParserContextPtr ctxt, int nargs){
  * Implement the format-number() XSLT function
  *   string format-number(number, string, string?)
  */
+/*
+ * JDK 1.1 DecimalFormat class:
+ *
+ * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
+ *
+ * Structure:
+ *
+ *   pattern    := subpattern{;subpattern}
+ *   subpattern := {prefix}integer{.fraction}{suffix}
+ *   prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
+ *   suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
+ *   integer    := '#'* '0'* '0'
+ *   fraction   := '0'* '#'*
+ *
+ *   Notation:
+ *    X*       0 or more instances of X
+ *    (X | Y)  either X or Y.
+ *    X..Y     any character from X up to Y, inclusive.
+ *    S - T    characters in S, except those in T
+ *
+ * Special Characters:
+ *
+ *   Symbol Meaning
+ *   0      a digit
+ *   #      a digit, zero shows as absent
+ *   .      placeholder for decimal separator
+ *   ,      placeholder for grouping separator.
+ *   ;      separates formats.
+ *   -      default negative prefix.
+ *   %      multiply by 100 and show as percentage
+ *   ?      multiply by 1000 and show as per mille
+ *   X      any other characters can be used in the prefix or suffix
+ *   '      used to quote special characters in a prefix or suffix.
+ */
+/* TODO
+ *
+ *  The JDK description does not tell where percent and permille may
+ *  and may not appear within the format string.
+ *
+ *  Error handling.
+ *
+ *  Inf and NaN not tested.
+ */
+#define IS_SPECIAL(self,letter) \
+    ((letter == self->zeroDigit) || \
+     (letter == self->digit) || \
+     (letter == self->decimalPoint) || \
+     (letter == self->grouping) || \
+     (letter == self->minusSign) || \
+     (letter == self->percent) || \
+     (letter == self->permille)) \
+
+static xmlChar *
+PrivateDecimalFormat(xsltDecimalFormatPtr self,
+                    xmlChar *format,
+                    double number)
+{
+    xmlChar *the_format;
+    xmlBufferPtr buffer;
+    xmlChar *result;
+    char digit_buffer[2];
+    int use_minus;
+    int i, j;
+    int length;
+    int group;
+    int integer_digits = 0;
+    int integer_zeroes = 0;
+    int fraction_digits = 0;
+    int fraction_zeroes = 0;
+    int decimal_point;
+    double divisor;
+    int digit;
+
+    buffer = xmlBufferCreate();
+
+    /* Find positive or negative template */
+    the_format = (xmlChar *)xmlStrchr(format,
+                                     self->patternSeparator);
+    if ((the_format != NULL) && (number < 0.0)) {
+       /* Use negative template */
+       the_format++;
+       use_minus = FALSE;
+    } else {
+       /* Use positive template */
+       if (the_format)
+           the_format[0] = 0;
+       the_format = format;
+       use_minus = (number < 0.0) ? TRUE : FALSE;
+    }
+  
+    /* Prefix */
+    length = xmlStrlen(the_format);
+    for (i = 0; i < length; i++) {
+       if (IS_SPECIAL(self, the_format[i])) {
+           break; /* for */
+       } else {
+           if (the_format[i] == SYMBOL_QUOTE) {
+               /* Quote character */
+               i++;
+           }
+           xmlBufferAdd(buffer, &the_format[i], 1);
+       }
+    }
+
+    if (isinf(number)) {
+       xmlBufferCat(buffer, self->infinity);
+       /* Skip until suffix */
+       for ( ; i < length; i++) {
+           if (! IS_SPECIAL(self, the_format[i]))
+               break; /* for */
+       }
+    } else if (isnan(number)) {
+       xmlBufferCat(buffer, self->noNumber);
+       /* Skip until suffix */
+       for ( ; i < length; i++) {
+           if (! IS_SPECIAL(self, the_format[i]))
+               break; /* for */
+       }
+    } else {
+       
+       /* Parse the number part of the format string */
+       decimal_point = FALSE;
+       group = 0;
+       for ( ; i < length; i++) {
+           
+           if (the_format[i] == self->digit) {
+               if (decimal_point) {
+                   if (fraction_zeroes > 0)
+                       ; /* Error in format */
+                   fraction_digits++;
+               } else {
+                   integer_digits++;
+                   group++;
+               }
+               
+           } else if (the_format[i] == self->zeroDigit) {
+               if (decimal_point)
+                   fraction_zeroes++;
+               else {
+                   if (integer_digits > 0)
+                       ; /* Error in format */
+                   integer_zeroes++;
+                   group++;
+               }
+               
+           } else if (the_format[i] == self->grouping) {
+               if (decimal_point)
+                   ; /* Error in format */
+               group = 0;
+               
+           } else if (the_format[i] == self->decimalPoint) {
+               if (decimal_point)
+                   ; /* Error in format */
+               decimal_point = TRUE;
+               
+           } else
+               break;
+       }
+       
+       /* Format the number */
+
+       if (use_minus)
+           xmlBufferAdd(buffer, &(self->minusSign), 1);
+
+       number = fabs(number);
+       number = floor(0.5 + number * pow(10.0, (double)(fraction_digits + fraction_zeroes)));
+       
+       /* Integer part */
+       digit_buffer[1] = (char)0;
+       j = integer_digits + integer_zeroes;
+       divisor = pow(10.0, (double)(integer_digits + integer_zeroes + fraction_digits + fraction_zeroes - 1));
+       for ( ; j > 0; j--) {
+           digit = (int)(number / divisor);
+           number -= (double)digit * divisor;
+           divisor /= 10.0;
+           if ((digit > 0) || (j <= integer_digits)) {
+               digit_buffer[0] = DIGIT_LIST[digit];
+               xmlBufferCCat(buffer, digit_buffer);
+           }
+       }
+       
+       if (decimal_point)
+           xmlBufferAdd(buffer, &(self->decimalPoint), 1);
+
+       /* Fraction part */
+       for (j = fraction_digits + fraction_zeroes; j > 0; j--) {
+           digit = (int)(number / divisor);
+           number -= (double)digit * divisor;
+           divisor /= 10.0;
+           if ((digit > 0) || (j > fraction_zeroes)) {
+               digit_buffer[0] = DIGIT_LIST[digit];
+               xmlBufferCCat(buffer, digit_buffer);
+           }
+       }
+    }
+
+    /* Suffix */
+    for ( ; i < length; i++) {
+       if (the_format[i] == SYMBOL_QUOTE)
+           i++;
+       xmlBufferAdd(buffer, &the_format[i], 1);
+    }
+    
+    result = xmlStrdup(xmlBufferContent(buffer));
+    xmlBufferFree(buffer);
+    return result;
+}
+
 void
-xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs){
-    TODO /* function */
+xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+    xmlXPathObjectPtr numberObj = NULL;
+    xmlXPathObjectPtr formatObj = NULL;
+    xmlXPathObjectPtr decimalObj = NULL;
+    
+    switch (nargs) {
+    case 3:
+       CAST_TO_STRING;
+       decimalObj = valuePop(ctxt);
+       globalDecimalFormat.decimalPoint = decimalObj->stringval[0]; /* hack */
+       /* Intentional fall-through */
+    case 2:
+       CAST_TO_STRING;
+       formatObj = valuePop(ctxt);
+       CAST_TO_NUMBER;
+       numberObj = valuePop(ctxt);
+       break;
+    default:
+       XP_ERROR(XPATH_INVALID_ARITY);
+    }
+    
+    valuePush(ctxt,
+             xmlXPathNewString(PrivateDecimalFormat(&globalDecimalFormat,
+                                                    formatObj->stringval,
+                                                    numberObj->floatval)));
+    
+    xmlXPathFreeObject(numberObj);
+    xmlXPathFreeObject(formatObj);
+    xmlXPathFreeObject(decimalObj);
 }
 
 /**
index 67d3208..b043451 100644 (file)
@@ -4,6 +4,7 @@
  * See Copyright for the status of this software.
  *
  * Daniel.Veillard@imag.fr
+ * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting
  */
 
 #ifndef __XML_XSLT_FUNCTIONS_H__
@@ -18,6 +19,25 @@ extern "C" {
 #endif
 
 /*
+ * Data structure of decimal-format
+ */
+typedef struct _xsltDecimalFormat {
+    /* Used for interpretation of pattern */
+    xmlChar digit;
+    xmlChar patternSeparator;
+    /* May appear in result */
+    xmlChar minusSign;
+    xmlChar *infinity;
+    xmlChar *noNumber;
+    /* Used for interpretation of pattern and may appear in result */
+    xmlChar decimalPoint;
+    xmlChar grouping;
+    xmlChar percent;
+    xmlChar permille;
+    xmlChar zeroDigit;
+} xsltDecimalFormat, *xsltDecimalFormatPtr;
+
+/*
  * Interfaces for the functions implementations
  */
 
index b463578..26fe50a 100644 (file)
@@ -2,3 +2,4 @@
 
 SUBDIRS=REC1 REC2
 
+test tests: all