New files for bug 65983, implement format-number(). Not part of build yet.

This commit is contained in:
sicking%bigfoot.com 2001-10-15 12:29:22 +00:00
Родитель d11eac45cc
Коммит 4078291d09
1 изменённых файлов: 484 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,484 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the TransforMiiX XSLT processor.
*
* The Initial Developer of the Original Code is
* Jonas Sicking.
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonas Sicking <sicking@bigfoot.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "XSLTFunctions.h"
#include "primitives.h"
#include "Names.h"
#include <math.h>
#ifndef TX_EXE
#include "prdtoa.h"
#else
#include <stdio.h>
#endif
const UNICODE_CHAR txFormatNumberFunctionCall::FORMAT_QUOTE = '\'';
/*
* FormatNumberFunctionCall
* A representation of the XSLT additional function: format-number()
*/
/*
* Creates a new format-number function call
*/
txFormatNumberFunctionCall::txFormatNumberFunctionCall(ProcessorState* aPs) :
FunctionCall(FORMAT_NUMBER_FN)
{
mPs = aPs;
}
/*
* Evaluates this Expr based on the given context node and processor state
* @param context the context node for evaluation of this Expr
* @param cs the ContextState containing the stack information needed
* for evaluation
* @return the result of the evaluation
*/
ExprResult* txFormatNumberFunctionCall::evaluate(Node* aContext,
ContextState* aCs)
{
if (!requireParams(2, 3, aCs))
return new StringResult();
// Get number and format
ListIterator iter(&params);
double value;
String formatStr;
String formatName;
value = evaluateToNumber((Expr*)iter.next(), aContext, aCs);
evaluateToString((Expr*)iter.next(), aContext, aCs, formatStr);
if (iter.hasNext())
evaluateToString((Expr*)iter.next(), aContext, aCs, formatName);
txDecimalFormat* format = mPs->getDecimalFormat(formatName);
if (!format) {
String err("unknown decimal format for: ");
toString(err);
aCs->recieveError(err);
return new StringResult(err);
}
// Special cases
if (Double::isNaN(value))
return new StringResult(format->mNaN);
if (value == Double::POSITIVE_INFINITY)
return new StringResult(format->mInfinity);
if (value == Double::NEGATIVE_INFINITY) {
String res;
res.append(format->mMinusSign);
res.append(format->mInfinity);
return new StringResult(res);
}
// Value is a normal finite number
String prefix;
String suffix;
int minIntegerSize=0;
int minFractionSize=0;
int maxFractionSize=0;
int multiplier=1;
int groupSize=-1;
UNICODE_CHAR c;
int pos=0;
int formatLen = formatStr.length();
MBool inQuote;
// Get right subexpression
inQuote = MB_FALSE;
if (Double::isNeg(value)) {
while (pos < formatLen &&
(inQuote ||
formatStr.charAt(pos) != format->mPatternSeparator)) {
if (formatStr.charAt(pos) == FORMAT_QUOTE)
inQuote = !inQuote;
pos++;
}
if (pos == formatLen) {
pos = 0;
prefix.append(format->mMinusSign);
value = value * -1;
}
else
pos++;
}
// Parse the format string
FormatParseState pState = Prefix;
inQuote = MB_FALSE;
while (pos < formatLen && pState != Finished) {
c=formatStr.charAt(pos++);
switch (pState) {
case Prefix:
case Suffix:
if (!inQuote) {
if (c == format->mPercent) {
if (multiplier == 1)
multiplier = 100;
else {
String err(INVALID_PARAM_VALUE);
toString(err);
aCs->recieveError(err);
return new StringResult(err);
}
}
else if (c == format->mPerMille) {
if (multiplier == 1)
multiplier = 1000;
else {
String err(INVALID_PARAM_VALUE);
toString(err);
aCs->recieveError(err);
return new StringResult(err);
}
}
else if (c == format->mMinusSign) {
if (Double::isNeg(value))
value=-1 * value;
else {
String err(INVALID_PARAM_VALUE);
toString(err);
aCs->recieveError(err);
return new StringResult(err);
}
}
else if (c == format->mDecimalSeparator ||
c == format->mGroupingSeparator ||
c == format->mZeroDigit ||
c == format->mDigit ||
c == format->mPatternSeparator) {
pState = pState == Prefix ? IntDigit : Finished;
pos--;
break;
}
}
if (c == FORMAT_QUOTE)
inQuote = !inQuote;
else if (pState == Prefix)
prefix.append(c);
else
suffix.append(c);
break;
case IntDigit:
if (c == format->mGroupingSeparator)
groupSize=0;
else if (c == format->mDigit) {
if (groupSize >= 0)
groupSize++;
}
else {
pState = IntZero;
pos--;
}
break;
case IntZero:
if (c == format->mGroupingSeparator)
groupSize = 0;
else if (c == format->mZeroDigit) {
if (groupSize >= 0)
groupSize++;
minIntegerSize++;
}
else if (c == format->mDecimalSeparator) {
pState = FracZero;
}
else {
pState = Suffix;
pos--;
}
break;
case FracZero:
if (c == format->mZeroDigit) {
maxFractionSize++;
minFractionSize++;
}
else {
pState = FracDigit;
pos--;
}
break;
case FracDigit:
if (c == format->mDigit)
maxFractionSize++;
else {
pState = Suffix;
pos--;
}
break;
case Finished:
break;
}
}
// Did we manage to parse the entire formatstring and was it valid
if ((c != format->mPatternSeparator && pos < formatLen) ||
minIntegerSize == 0 ||
inQuote ||
groupSize == 0) {
String err(INVALID_PARAM_VALUE);
toString(err);
aCs->recieveError(err);
return new StringResult(err);
}
/*
* FINALLY we're done with the parsing
* now build the result string
*/
value = fabs(value) * multiplier;
// Prefix
String res(prefix);
#ifdef TX_EXE
int bufsize;
if (value > 1)
bufsize = (int)log10(value) + maxFractionSize + 5;
else
bufsize = 1 + maxFractionSize + 5;
char* buf = new char[bufsize];
if (!buf) {
//XXX ErrorReport: out of memory
return new StringResult;
}
char formatBuf[30];
sprintf(formatBuf, "%%0%d.%df", minIntegerSize, maxFractionSize);
sprintf(buf, formatBuf, value);
// Find decimalseparator for grouping
int intDigits;
for (intDigits = 0; buf[intDigits] && buf[intDigits] != '.'; intDigits++);
if (groupSize < 0)
groupSize = intDigits * 2; //to simplify grouping
// Integer digits
int i;
for (i = 0; i < intDigits; ++i) {
res.append((UNICODE_CHAR)(buf[i] - '0' + format->mZeroDigit));
if ((intDigits-i)%groupSize == 0 && intDigits-i != 0)
res.append(format->mGroupingSeparator);
}
// Fractions
MBool printDeci = MB_TRUE;
int extraZeros = 0;
if (buf[i])
i++; // skip decimal separator
for (; buf[i]; i++) {
if (i-intDigits-1 > minFractionSize && buf[i] == '0') {
extraZeros++;
}
else {
if (printDeci) {
res.append(format->mDecimalSeparator);
printDeci = MB_FALSE;
}
while (extraZeros) {
res.append(format->mZeroDigit);
extraZeros--;
}
res.append((UNICODE_CHAR)(buf[i] - '0' + format->mZeroDigit));
}
}
delete [] buf;
#else // TX_EXE
int bufsize;
if (value > 1)
bufsize = (int)log10(value) + 30;
else
bufsize = 1 + 30;
char* buf = new char[bufsize];
if (!buf) {
//XXX ErrorReport: out of memory
return new StringResult;
}
PRIntn bufIntDigits, sign;
char* endp;
PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1);
int buflen = endp - buf;
int intDigits;
intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize;
if (groupSize < 0)
groupSize = intDigits * 2; //to simplify grouping
res.setLength(res.length() +
intDigits + // integer digits
1 + // decimal separator
maxFractionSize + // fractions
(intDigits-1)/groupSize); // group separators
PRInt32 i = bufIntDigits + maxFractionSize - 1;
MBool carry = (i+1 < buflen) && (buf[i+1] >= '5');
MBool hasFraction = MB_FALSE;
PRInt32 resPos = res.length()-1;
// Fractions
for (; i >= bufIntDigits; --i) {
int digit;
if (i >= buflen) {
digit = 0;
}
else if (carry) {
digit = (buf[i] - '0' + 1) % 10;
carry = digit == 0;
}
else {
digit = buf[i] - '0';
}
if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) {
hasFraction = MB_TRUE;
res.replace(resPos--,
(UNICODE_CHAR)(digit + format->mZeroDigit));
}
else {
res.setLength(resPos--);
}
}
// Decimal separator
if (hasFraction)
res.replace(resPos--, format->mDecimalSeparator);
else
res.setLength(resPos--);
// Integer digits
for (i = 0; i < intDigits; ++i) {
int digit;
if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) {
digit = 0;
}
else {
digit = buf[bufIntDigits-i-1] - '0';
}
if (carry) {
digit = digit % 10;
carry = digit == 0;
}
if (i != 0 && i%groupSize == 0)
res.replace(resPos--, format->mGroupingSeparator);
res.replace(resPos--, (UNICODE_CHAR)(digit + format->mZeroDigit));
}
if (carry) {
if (i%groupSize == 0)
res.insert(resPos + 1, format->mGroupingSeparator);
res.insert(resPos + 1, (UNICODE_CHAR)(1 + format->mZeroDigit));
}
delete [] buf;
#endif // TX_EXE
// Build suffix
res.append(suffix);
return new StringResult(res);
} //-- evaluate
/*
* txDecimalFormat
* A representation of the XSLT element <xsl:decimal-format>
*/
txDecimalFormat::txDecimalFormat()
{
mDecimalSeparator = '.';
mGroupingSeparator = ',';
mInfinity = "Infinity";
mMinusSign = '-';
mNaN = "NaN";
mPercent = '%';
mPerMille = 0x2030;
mZeroDigit = '0';
mDigit = '#';
mPatternSeparator = ';';
}
MBool txDecimalFormat::isEqual(txDecimalFormat* other)
{
return mDecimalSeparator == other->mDecimalSeparator &&
mGroupingSeparator == other->mGroupingSeparator &&
mInfinity.isEqual(other->mInfinity) &&
mMinusSign == other->mMinusSign &&
mNaN.isEqual(other->mNaN) &&
mPercent == other->mPercent &&
mPerMille == other->mPerMille &&
mZeroDigit == other->mZeroDigit &&
mDigit == other->mDigit &&
mPatternSeparator == other->mPatternSeparator;
}