Skip to content
Snippets Groups Projects
Commit f7349cdc455a authored by Bjorn Reese's avatar Bjorn Reese
Browse files

Number formatting - FEATURES: updated - numbers.c: handles actual number

Number formatting
- FEATURES: updated
- numbers.c: handles actual number formatting for both xsl:number and
  the format-number extension function.
- function.c: formatting moved to numbers.c
- transform.c: added xsl:number
- xslt.c: minor memory leak removed
- Makefile.am: added numbers.c and numbersInternals.h
Bjorn
parent 479b59200fc3
Branches
No related tags found
No related merge requests found
......@@ -116,7 +116,7 @@
YES select = string-expression
NO disable-output-escaping = "yes" | "no"
NO xsl:number
YES xsl:number
NO level = "single" | "multiple" | "any"
NO count = pattern
NO from = pattern
......@@ -120,7 +120,7 @@
NO level = "single" | "multiple" | "any"
NO count = pattern
NO from = pattern
NO value = number-expression
NO format = { string }
YES value = number-expression
YES format = { string }
NO lang = { nmtoken }
NO letter-value = { "alphabetic" | "traditional" }
......@@ -125,7 +125,7 @@
NO lang = { nmtoken }
NO letter-value = { "alphabetic" | "traditional" }
NO grouping-separator = { char }
NO grouping-size = { number }
YES grouping-separator = { char }
YES grouping-size = { number }
YES xsl:for-each
YES select = node-set-expression
......@@ -164,18 +164,18 @@
YES name = qname
YES select = expression
NO xsl:decimal-format
NO name = qname
NO decimal-separator = char
NO grouping-separator = char
NO infinity = string
NO minus-sign = char
NO NaN = string
NO percent = char
NO per-mille = char
NO zero-digit = char
NO digit = char
NO pattern-separator = char
YES xsl:decimal-format
YES name = qname
YES decimal-separator = char
YES grouping-separator = char
YES infinity = string
YES minus-sign = char
YES NaN = string
YES percent = char
YES per-mille = char
YES zero-digit = char
YES digit = char
YES pattern-separator = char
YES xsl:message
YES terminate = "yes" | "no"
......
......@@ -11,6 +11,7 @@
pattern.h \
templates.h \
variables.h \
numbersInternals.h \
functions.h \
namespaces.h \
imports.h \
......@@ -24,6 +25,7 @@
pattern.c \
templates.c \
variables.c \
numbers.c \
functions.c \
namespaces.c \
imports.c \
......
......@@ -17,18 +17,6 @@
#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
......@@ -47,6 +35,7 @@
#include "xsltInternals.h"
#include "xsltutils.h"
#include "functions.h"
#include "numbersInternals.h"
#define DEBUG_FUNCTION
......@@ -50,42 +39,6 @@
#define DEBUG_FUNCTION
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (1 == 1)
#endif
#define DIGIT_LIST "0123456789"
#define SYMBOL_QUOTE ((xmlChar)'\'')
#define ID_STRING "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
/************************************************************************
* *
* 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
/************************************************************************
* *
......@@ -246,243 +199,6 @@
* 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 they may and may not appear
* within the format string. Nor does it tell what happens to integer
* values that does not fit into the format string.
*
* Inf and NaN not tested.
*/
#define IS_SPECIAL(self,letter) \
(((letter) == (self)->zeroDigit[0]) || \
((letter) == (self)->digit[0]) || \
((letter) == (self)->decimalPoint[0]) || \
((letter) == (self)->grouping[0]) || \
((letter) == (self)->minusSign[0]) || \
((letter) == (self)->percent[0]) || \
((letter) == (self)->permille[0]))
static xmlXPathError
xsltFormatNumberConversion(xsltDecimalFormatPtr self,
xmlChar *format,
double number,
xmlChar **result)
{
xmlXPathError status = XPATH_EXPRESSION_OK;
xmlChar *the_format;
xmlBufferPtr buffer;
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;
int is_percent = FALSE;
int is_permille = FALSE;
buffer = xmlBufferCreate();
if (buffer == NULL) {
status = XPATH_MEMORY_ERROR;
goto DECIMAL_FORMAT_END;
}
/* Find positive or negative template */
the_format = (xmlChar *)xmlStrchr(format,
self->patternSeparator[0]);
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[0]) {
if (decimal_point) {
if (fraction_zeroes > 0) {
status = XPATH_EXPR_ERROR;
goto DECIMAL_FORMAT_END;
}
fraction_digits++;
} else {
integer_digits++;
group++;
}
} else if (the_format[i] == self->zeroDigit[0]) {
if (decimal_point)
fraction_zeroes++;
else {
if (integer_digits > 0) {
status = XPATH_EXPR_ERROR;
goto DECIMAL_FORMAT_END;
}
integer_zeroes++;
group++;
}
} else if (the_format[i] == self->grouping[0]) {
if (decimal_point) {
status = XPATH_EXPR_ERROR;
goto DECIMAL_FORMAT_END;
}
group = 0;
} else if (the_format[i] == self->decimalPoint[0]) {
if (decimal_point) {
status = XPATH_EXPR_ERROR;
goto DECIMAL_FORMAT_END;
}
decimal_point = TRUE;
} else
break;
}
if (the_format[i] == self->percent[0]) {
is_percent = TRUE;
} else if (the_format[i] == self->permille[0]) {
is_permille = TRUE;
}
/* Format the number */
if (use_minus)
xmlBufferAdd(buffer, self->minusSign, 1);
number = fabs(number);
if (is_percent)
number /= 100.0;
else if (is_permille)
number /= 1000.0;
number = floor(0.5 + number * pow(10.0, (double)(fraction_digits + fraction_zeroes)));
/* Integer part */
digit_buffer[1] = (char)0;
divisor = pow(10.0, (double)(integer_digits + integer_zeroes + fraction_digits + fraction_zeroes - 1));
for (j = integer_digits + integer_zeroes; 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);
}
}
}
if (is_percent)
xmlBufferAdd(buffer, self->percent, 1);
else if (is_permille)
xmlBufferAdd(buffer, self->permille, 1);
/* Suffix */
for ( ; i < length; i++) {
if (the_format[i] == SYMBOL_QUOTE)
i++;
xmlBufferAdd(buffer, &the_format[i], 1);
}
DECIMAL_FORMAT_END:
if (status == XPATH_EXPRESSION_OK)
*result = xmlStrdup(xmlBufferContent(buffer));
xmlBufferFree(buffer);
return status;
}
void
xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
{
......@@ -518,6 +234,7 @@
numberObj->floatval,
&result) == XPATH_EXPRESSION_OK) {
valuePush(ctxt, xmlXPathNewString(result));
xmlFree(result);
}
xmlXPathFreeObject(numberObj);
......
/*
* numbers.c: Implementation of the XSLT number functions
*
* Reference:
* http://www.w3.org/TR/1999/REC-xslt-19991116
*
* See Copyright for the status of this software.
*
* Daniel.Veillard@imag.fr
* Bjorn Reese <breese@users.sourceforge.net>
*/
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <float.h>
#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif
#ifdef HAVE_NAN_H
#include <nan.h>
#endif
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include "xsltutils.h"
#include "numbersInternals.h"
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (1 == 1)
#endif
#define SYMBOL_QUOTE ((xmlChar)'\'')
static char digit_list[] = "0123456789";
static char alpha_upper_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static char alpha_lower_list[] = "abcdefghijklmnopqrstuvwxyz";
/************************************************************************
* *
* Utility functions *
* *
************************************************************************/
#define IS_SPECIAL(self,letter) \
(((letter) == (self)->zeroDigit[0]) || \
((letter) == (self)->digit[0]) || \
((letter) == (self)->decimalPoint[0]) || \
((letter) == (self)->grouping[0]) || \
((letter) == (self)->minusSign[0]) || \
((letter) == (self)->percent[0]) || \
((letter) == (self)->permille[0]))
#define IS_DIGIT_ZERO(x) xsltIsDigitZero(x)
#define IS_DIGIT_ONE(x) xsltIsDigitZero((x)-1)
static int
xsltIsDigitZero(xmlChar ch)
{
/*
* Reference: ftp://ftp.unicode.org/Public/UNIDATA/UnicodeData.txt
*/
switch (ch) {
case 0x0030: case 0x0660: case 0x06F0: case 0x0966:
case 0x09E6: case 0x0A66: case 0x0AE6: case 0x0B66:
case 0x0C66: case 0x0CE6: case 0x0D66: case 0x0E50:
case 0x0E60: case 0x0F20: case 0x1040: case 0x17E0:
case 0x1810: case 0xFF10:
return TRUE;
default:
return FALSE;
}
}
#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
/************************************************************************
*
* 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 they may and may not appear
* within the format string. Nor does it tell what happens to integer
* values that does not fit into the format string.
*
* Inf and NaN not tested.
*/
xmlXPathError
xsltFormatNumberConversion(xsltDecimalFormatPtr self,
xmlChar *format,
double number,
xmlChar **result)
{
xmlXPathError status = XPATH_EXPRESSION_OK;
xmlChar *the_format;
xmlBufferPtr buffer;
char digit_buffer[2];
int use_minus;
int i, j;
int length;
int group = 0;
int integer_digits = 0;
int integer_hash = 0;
int fraction_digits = 0;
int fraction_hash = 0;
int decimal_point;
double divisor;
int digit;
int is_percent;
int is_permille;
buffer = xmlBufferCreate();
if (buffer == NULL) {
return XPATH_MEMORY_ERROR;
}
/* Find positive or negative template */
the_format = (xmlChar *)xmlStrchr(format,
self->patternSeparator[0]);
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);
}
}
/* Handle infinity and not-a-number first */
switch (isinf(number)) {
case -1:
xmlBufferCat(buffer, self->minusSign);
/* Intentional fall-through */
case 1:
xmlBufferCat(buffer, self->infinity);
for ( ; i < length; i++) {
if (! IS_SPECIAL(self, the_format[i]))
break; /* for */
}
goto DECIMAL_FORMAT_SUFFIX;
default:
if (isnan(number)) {
xmlBufferCat(buffer, self->noNumber);
/* Skip until suffix */
for ( ; i < length; i++) {
if (! IS_SPECIAL(self, the_format[i]))
break; /* for */
}
goto DECIMAL_FORMAT_SUFFIX;
}
break;
}
/*
* Parse the number part of the format string
*/
for ( ; i < length; i++) {
if (the_format[i] == self->zeroDigit[0]) {
integer_hash++;
group++;
} else if (the_format[i] == self->grouping[0]) {
group = 0; /* Reset count */
} else
break; /* for */
}
for ( ; i < length; i++) {
if (the_format[i] == self->digit[0]) {
integer_digits++;
group++;
} else if (the_format[i] == self->grouping[0]) {
group = 0; /* Reset count */
} else
break; /* for */
}
decimal_point = (the_format[i] == self->decimalPoint[0]) ? TRUE : FALSE;
if (decimal_point) {
for ( ; i < length; i++) {
if (the_format[i] == self->digit[0]) {
fraction_digits++;
} else
break; /* for */
}
for ( ; i < length; i++) {
if (the_format[i] == self->zeroDigit[0]) {
fraction_hash++;
} else
break; /* for */
}
}
is_percent = (the_format[i] == self->percent[0]) ? TRUE : FALSE;
is_permille = (the_format[i] == self->permille[0]) ? TRUE : FALSE;
/*
* Format the number
*/
if (use_minus)
xmlBufferAdd(buffer, self->minusSign, 1);
number = fabs(number);
if (is_percent)
number *= 100.0;
else if (is_permille)
number *= 1000.0;
number = floor(0.5 + number * pow(10.0, (double)(fraction_digits + fraction_hash)));
/* Integer part */
digit_buffer[1] = (char)0;
divisor = pow(10.0, (double)(integer_digits + integer_hash + fraction_digits + fraction_hash - 1));
for (j = integer_digits + integer_hash; 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_hash; j > 0; j--) {
digit = (int)(number / divisor);
number -= (double)digit * divisor;
divisor /= 10.0;
if ((digit > 0) || (j > fraction_hash)) {
digit_buffer[0] = digit_list[digit];
xmlBufferCCat(buffer, digit_buffer);
}
}
if (is_percent)
xmlBufferAdd(buffer, self->percent, 1);
else if (is_permille)
xmlBufferAdd(buffer, self->permille, 1);
DECIMAL_FORMAT_SUFFIX:
/*
* 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 status;
}
/************************************************************************
*
* xsltNumberFormat
*
* Convert one number.
*/
static void
xsltNumberFormatDecimal(xmlBufferPtr buffer,
double number,
xmlChar digit_zero,
int width,
int digitsPerGroup,
xmlChar groupingCharacter)
{
xmlChar temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
xmlChar *pointer;
int i;
/* Build buffer from back */
pointer = &temp_string[sizeof(temp_string) - 1];
*pointer-- = 0;
for (i = 1; i < (int)sizeof(temp_string); i++) {
*pointer-- = digit_zero + (int)fmod(number, 10.0);
number /= 10.0;
width--;
if ((width <= 0) && (fabs(number) < 1.0))
break; /* for */
if ((groupingCharacter != 0) &&
((i % digitsPerGroup) == 0)) {
*pointer-- = groupingCharacter;
}
}
xmlBufferCat(buffer, pointer + 1);
}
static void
xsltNumberFormatAlpha(xmlBufferPtr buffer,
double number,
int is_upper)
{
char temp_string[sizeof(double) * CHAR_BIT * sizeof(xmlChar) + 1];
char *pointer;
int i;
char *alpha_list;
double alpha_size = (double)(sizeof(alpha_upper_list) - 1);
/* Build buffer from back */
pointer = &temp_string[sizeof(temp_string) - 1];
*pointer-- = 0;
alpha_list = (is_upper) ? alpha_upper_list : alpha_lower_list;
for (i = 1; i < (int)sizeof(buffer); i++) {
number--;
*pointer-- = alpha_list[((int)fmod(number, alpha_size))];
number /= alpha_size;
if (fabs(number) < 1.0)
break; /* for */
}
xmlBufferCCat(buffer, pointer + 1);
}
static void
xsltNumberFormatRoman(xmlBufferPtr buffer,
double number,
int is_upper)
{
/*
* Based on an example by Jim Walsh
*/
while (number >= 1000.0) {
xmlBufferCCat(buffer, (is_upper) ? "M" : "m");
number -= 1000.0;
}
if (number >= 900.0) {
xmlBufferCCat(buffer, (is_upper) ? "CM" : "cm");
number -= 900.0;
}
while (number >= 500.0) {
xmlBufferCCat(buffer, (is_upper) ? "D" : "d");
number -= 500.0;
}
if (number >= 400.0) {
xmlBufferCCat(buffer, (is_upper) ? "CD" : "cd");
number -= 400.0;
}
while (number >= 100.0) {
xmlBufferCCat(buffer, (is_upper) ? "C" : "c");
number -= 100.0;
}
if (number >= 90.0) {
xmlBufferCCat(buffer, (is_upper) ? "XC" : "xc");
number -= 90.0;
}
while (number >= 50.0) {
xmlBufferCCat(buffer, (is_upper) ? "L" : "l");
number -= 50.0;
}
if (number >= 40.0) {
xmlBufferCCat(buffer, (is_upper) ? "XL" : "xl");
number -= 40.0;
}
while (number >= 10.0) {
xmlBufferCCat(buffer, (is_upper) ? "X" : "x");
number -= 10.0;
}
if (number >= 9.0) {
xmlBufferCCat(buffer, (is_upper) ? "IX" : "ix");
number -= 9.0;
}
while (number >= 5.0) {
xmlBufferCCat(buffer, (is_upper) ? "V" : "v");
number -= 5.0;
}
if (number >= 4.0) {
xmlBufferCCat(buffer, (is_upper) ? "IV" : "iv");
number -= 4.0;
}
while (number >= 1.0) {
xmlBufferCCat(buffer, (is_upper) ? "I" : "i");
number--;
}
}
void
xsltNumberFormat(xsltTransformContextPtr ctxt,
xsltNumberDataPtr data,
xmlNodePtr node)
{
xmlXPathParserContextPtr parser = NULL;
xmlXPathObjectPtr res;
xmlBufferPtr buffer;
xmlNodePtr copy = NULL;
xmlChar *format;
int i, j;
int width;
buffer = xmlBufferCreate();
if (buffer == NULL)
goto XSLT_NUMBER_FORMAT_END;
format = data->format;
/*
* Evaluate the XPath expression to find the value(s)
*/
parser = xmlXPathNewParserContext(data->value, ctxt->xpathCtxt);
if (parser == NULL)
goto XSLT_NUMBER_FORMAT_END;
ctxt->xpathCtxt->node = node;
valuePush(parser, xmlXPathNewNodeSet(node));
xmlXPathEvalExpr(parser);
/*
* Parse format string
*/
i = 0;
for (;;) {
/*
* Workaround:
* There always seem to be a superfluos object on the stack.
* We handle this by exiting the loop if there is only one
* object back.
*/
if (parser->valueNr == 1)
break; /* for */
/* Cast to number if necessary */
if ((parser->value != NULL) &&
(parser->value->type != XPATH_NUMBER))
xmlXPathNumberFunction(parser, 1);
res = valuePop(parser);
if (res == NULL)
break; /* for */
if (res->type == XPATH_NUMBER) {
switch (isinf(res->floatval)) {
case -1:
xmlBufferCCat(buffer, "-Infinity");
break;
case 1:
xmlBufferCCat(buffer, "Infinity");
break;
default:
if (isnan(res->floatval)) {
xmlBufferCCat(buffer, "NaN");
} else {
/* Find formatting token */
if (IS_DIGIT_ONE(format[i]) || IS_DIGIT_ZERO(format[i])) {
width = 1;
while (IS_DIGIT_ZERO(format[i])) {
width++;
i++;
}
if (IS_DIGIT_ONE(format[i])) {
xsltNumberFormatDecimal(buffer,
res->floatval,
format[i] - 1,
width,
data->digitsPerGroup,
data->groupingCharacter);
i++;
}
} else if (format[i] == 'A') {
xsltNumberFormatAlpha(buffer,
res->floatval,
TRUE);
i++;
} else if (format[i] == 'a') {
xsltNumberFormatAlpha(buffer,
res->floatval,
FALSE);
i++;
} else if (format[i] == 'I') {
xsltNumberFormatRoman(buffer,
res->floatval,
TRUE);
i++;
} else if (format[i] == 'i') {
xsltNumberFormatRoman(buffer,
res->floatval,
FALSE);
i++;
}
}
break;
}
/* Insert separator */
for (j = i; !isalnum(format[i]); i++)
;
if (i > j)
xmlBufferAdd(buffer, &format[j], i - j);
}
xmlXPathFreeObject(res);
}
/* Insert number as text */
copy = xmlNewText(xmlBufferContent(buffer));
if (copy != NULL) {
xmlAddChild(ctxt->insert, copy);
}
if (parser != NULL) {
while ((res = valuePop(parser)) != NULL) {
xmlXPathFreeObject(res);
}
}
XSLT_NUMBER_FORMAT_END:
if (parser != NULL)
xmlXPathFreeParserContext(parser);
if (buffer != NULL)
xmlBufferFree(buffer);
}
/*
* numbers.h: Implementation of the XSLT number functions
*
* See Copyright for the status of this software.
*
* Daniel.Veillard@imag.fr
* Bjorn Reese <breese@users.sourceforge.net>
*/
#ifndef __XML_XSLT_NUMBERSINTERNALS_H__
#define __XML_XSLT_NUMBERSINTERNALS_H__
#include "xsltInternals.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* This data structure is just a wrapper to pass data in
*/
typedef struct _xsltNumberData {
xmlChar *value;
xmlChar *format;
int digitsPerGroup;
xmlChar groupingCharacter;
} xsltNumberData, *xsltNumberDataPtr;
xmlXPathError xsltFormatNumberConversion(xsltDecimalFormatPtr, xmlChar *,
double, xmlChar **);
void xsltNumberFormat(xsltTransformContextPtr, xsltNumberDataPtr, xmlNodePtr);
#ifdef __cplusplus
}
#endif
#endif /* __XML_XSLT_NUMBERSINTERNALS_H__ */
......@@ -30,6 +30,7 @@
#include "pattern.h"
#include "transform.h"
#include "variables.h"
#include "numbersInternals.h"
#include "namespaces.h"
#include "attributes.h"
#include "templates.h"
......@@ -41,6 +42,11 @@
* Useful macros
*/
#ifndef FALSE
# define FALSE (0 == 1)
# define TRUE (!FALSE)
#endif
#define IS_BLANK_NODE(n) \
(((n)->type == XML_TEXT_NODE) && (xsltIsBlank((n)->content)))
......@@ -799,6 +805,120 @@
xmlXPathFreeObject(res);
}
/**
* xsltNumber:
* @ctxt: a XSLT process context
* @node: the node in the source tree.
* @cur: the xslt number node
*
* Process the xslt number node on the source node
*/
void
xsltNumber(xsltTransformContextPtr ctxt,
xmlNodePtr node,
xmlNodePtr cur)
{
xmlChar *prop;
xsltNumberData numdata;
if ((ctxt == NULL) || (cur == NULL))
return;
memset(&numdata, 0, sizeof(numdata));
prop = xmlGetNsProp(cur, (const xmlChar *)"level", XSLT_NAMESPACE);
if (prop != NULL) {
if (xmlStrEqual(prop, BAD_CAST("single"))) {
TODO;
} else if (xmlStrEqual(prop, BAD_CAST("multiple"))) {
TODO;
} else if (xmlStrEqual(prop, BAD_CAST("any"))) {
TODO;
} else {
xsltGenericError(xsltGenericErrorContext,
"invalid value %s for level\n", prop);
}
xmlFree(prop);
}
prop = xmlGetNsProp(cur, (const xmlChar *)"count", XSLT_NAMESPACE);
if (prop != NULL) {
TODO;
xmlFree(prop);
}
prop = xmlGetNsProp(cur, (const xmlChar *)"from", XSLT_NAMESPACE);
if (prop != NULL) {
TODO;
xmlFree(prop);
}
prop = xmlGetNsProp(cur, (const xmlChar *)"value", XSLT_NAMESPACE);
if (prop != NULL) {
numdata.value = prop;
} else {
numdata.value = xmlStrdup(BAD_CAST("position()"));
}
prop = xmlGetNsProp(cur, (const xmlChar *)"format", XSLT_NAMESPACE);
if (prop != NULL) {
/* Unicode categories:
* Nd = Number, decimal digit
* Nl = Number, letter
* No = Number, other
* Lu = Letters, uppercase
* Ll = Letters, lowercase
* Lt = Letters, titlecase
* Lm = Letters, modifiers
* Lo = Letters, other (uncased)
*
* This corresponds to isalnum() in a Unicode locale.
*/
numdata.format = prop;
} else {
numdata.format = xmlStrdup(BAD_CAST("1"));
}
prop = xmlGetNsProp(cur, (const xmlChar *)"lang", XSLT_NAMESPACE);
if (prop != NULL) {
TODO;
xmlFree(prop);
}
prop = xmlGetNsProp(cur, (const xmlChar *)"letter-value", XSLT_NAMESPACE);
if (prop != NULL) {
if (xmlStrEqual(prop, BAD_CAST("alphabetic"))) {
TODO;
} else if (xmlStrEqual(prop, BAD_CAST("traditional"))) {
TODO;
} else {
xsltGenericError(xsltGenericErrorContext,
"invalid value %s for letter-value\n", prop);
}
xmlFree(prop);
}
prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-separator", XSLT_NAMESPACE);
if (prop != NULL) {
numdata.groupingCharacter = prop[0];
xmlFree(prop);
}
prop = xmlGetNsProp(cur, (const xmlChar *)"grouping-size", XSLT_NAMESPACE);
if (prop != NULL) {
sscanf(prop, "%d", &numdata.digitsPerGroup);
xmlFree(prop);
} else {
numdata.groupingCharacter = 0;
}
xsltNumberFormat(ctxt, &numdata, node);
if (numdata.format != NULL)
xmlFree(numdata.format);
if (numdata.value != NULL)
xmlFree(numdata.value);
}
/**
* xsltDefaultProcessOneNode:
......@@ -1335,6 +1455,10 @@
ctxt->insert = insert;
xsltComment(ctxt, node, cur);
ctxt->insert = oldInsert;
} else if (IS_XSLT_NAME(cur, "number")) {
ctxt->insert = insert;
xsltNumber(ctxt, node, cur);
ctxt->insert = oldInsert;
} else if (IS_XSLT_NAME(cur, "processing-instruction")) {
ctxt->insert = insert;
xsltProcessingInstruction(ctxt, node, cur);
......
......@@ -79,7 +79,7 @@
self = xmlMalloc(sizeof(xsltDecimalFormat));
if (self != NULL) {
self->next = NULL;
self->name = (name == NULL) ? name : xmlStrdup(name);
self->name = name;
/* Default values */
self->digit = xmlStrdup(BAD_CAST("0"));
......
......@@ -200,6 +200,8 @@
void xsltFreeStylesheet (xsltStylesheetPtr sheet);
int xsltIsBlank (xmlChar *str);
void xsltFreeStackElemList (xsltStackElemPtr elem);
xsltDecimalFormatPtr xsltDecimalFormatGetByName(xsltStylesheetPtr sheet,
xmlChar *name);
xsltStylesheetPtr xsltParseStylesheetProcess(xsltStylesheetPtr ret,
xmlDocPtr doc);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment