Add support of special symbols in DecimalFormat prefixes/suffixes

This commit is contained in:
Alexey Andreev 2015-06-10 21:24:48 +03:00
parent fc8200f135
commit 3df3447a38
4 changed files with 186 additions and 50 deletions

View File

@ -38,10 +38,10 @@ public class TDecimalFormat extends TNumberFormat {
private static final double[] POWM10_FRAC_ARRAY = { 1E-1, 1E-2, 1E-4, 1E-8, 1E-16, 1E-32, 1E-64, 1E-128, 1E-256 };
private static final int DOUBLE_MAX_EXPONENT = 308;
TDecimalFormatSymbols symbols;
private String positivePrefix = "";
private String negativePrefix = "-";
private String positiveSuffix = "";
private String negativeSuffix = "";
FormatField[] positivePrefix = {};
FormatField[] negativePrefix = { new TextField("-") };
FormatField[] positiveSuffix = {};
FormatField[] negativeSuffix = {};
private int multiplier = 1;
private int groupingSize;
private boolean decimalSeparatorAlwaysShown;
@ -71,36 +71,51 @@ public class TDecimalFormat extends TNumberFormat {
return (DecimalFormatSymbols)symbols.clone();
}
private StringBuffer fieldsToText(FormatField[] fields, StringBuffer buffer) {
for (FormatField field : fields) {
field.render(this, buffer);
}
return buffer;
}
private String fieldsToText(FormatField[] fields) {
return fieldsToText(fields, new StringBuffer()).toString();
}
private FormatField[] textToFields(String text) {
return new FormatField[] { new TextField(text) };
}
public String getPositivePrefix() {
return positivePrefix;
return fieldsToText(positivePrefix);
}
public void setPositivePrefix(String newValue) {
positivePrefix = newValue;
positivePrefix = textToFields(newValue);
}
public String getNegativePrefix() {
return negativePrefix;
return fieldsToText(negativePrefix);
}
public void setNegativePrefix(String newValue) {
negativePrefix = newValue;
negativePrefix = textToFields(newValue);
}
public String getPositiveSuffix() {
return positiveSuffix;
return fieldsToText(positiveSuffix);
}
public void setPositiveSuffix(String newValue) {
positiveSuffix = newValue;
positiveSuffix = textToFields(newValue);
}
public String getNegativeSuffix() {
return negativeSuffix;
return fieldsToText(negativeSuffix);
}
public void setNegativeSuffix(String newValue) {
negativeSuffix = newValue;
negativeSuffix = textToFields(newValue);
}
public int getMultiplier() {
@ -221,10 +236,11 @@ public class TDecimalFormat extends TNumberFormat {
@Override
public StringBuffer format(double value, StringBuffer buffer, TFieldPosition field) {
if (Double.isNaN(value)) {
buffer.append(positivePrefix).append(symbols.getNaN()).append(positiveSuffix);
fieldsToText(positivePrefix, buffer).append(symbols.getNaN());
appendSuffix(true, buffer);
} else if (Double.isInfinite(value)) {
buffer.append(value > 0 ? positivePrefix : negativePrefix).append(symbols.getInfinity())
.append(value > 0 ? positiveSuffix : negativeSuffix);
fieldsToText(value > 0 ? positivePrefix : negativePrefix, buffer).append(symbols.getInfinity());
appendSuffix(value > 0, buffer);
} else {
MantissaAndExponent me = getMantissaAndExponent(value);
if (exponentDigits > 0) {
@ -257,7 +273,7 @@ public class TDecimalFormat extends TNumberFormat {
if (tenMultiplier == multiplier) {
exponent += multiplierDigits;
} else if (mantissa >= Long.MAX_VALUE / multiplier || mantissa <= Long.MIN_VALUE / multiplier) {
formatExponent(new BigDecimal(BigInteger.valueOf(mantissa), mantissaLength - exponent), buffer);
formatExponent(new BigDecimal(BigInteger.valueOf(mantissa), visibleExponent - exponent), buffer);
return;
} else {
mantissa *= multiplier;
@ -285,7 +301,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Append pattern prefix
buffer.append(positive ? positivePrefix : negativePrefix);
fieldsToText(positive ? positivePrefix : negativePrefix, buffer);
int exponentPos = Math.max(visibleExponent, 0);
for (int i = mantissaLength - 1; i >= exponentPos; --i) {
@ -331,11 +347,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Add suffix
if (positive) {
buffer.append(positiveSuffix != null ? positiveSuffix : "");
} else {
buffer.append(negativeSuffix != null ? negativeSuffix : positiveSuffix != null ? positiveSuffix : "");
}
appendSuffix(positive, buffer);
}
private void formatRegular(long mantissa, int exponent, StringBuffer buffer) {
@ -366,7 +378,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Append pattern prefix
buffer.append(positive ? positivePrefix : negativePrefix);
fieldsToText(positive ? positivePrefix : negativePrefix, buffer);
// Add insignificant integer zeros
int intLength = Math.max(0, exponent);
@ -445,11 +457,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Add suffix
if (positive) {
buffer.append(positiveSuffix != null ? positiveSuffix : "");
} else {
buffer.append(negativeSuffix != null ? negativeSuffix : positiveSuffix != null ? positiveSuffix : "");
}
appendSuffix(positive, buffer);
}
private void formatExponent(BigDecimal value, StringBuffer buffer) {
@ -480,7 +488,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Append pattern prefix
buffer.append(positive ? positivePrefix : negativePrefix);
fieldsToText(positive ? positivePrefix : negativePrefix, buffer);
int exponentPos = Math.max(visibleExponent, 0);
BigInteger mantissaDigitMask = pow10(BigInteger.ONE, mantissaLength - 1);
@ -529,10 +537,17 @@ public class TDecimalFormat extends TNumberFormat {
}
// Add suffix
appendSuffix(positive, buffer);
}
private void appendSuffix(boolean positive, StringBuffer buffer) {
if (positive) {
buffer.append(positiveSuffix != null ? positiveSuffix : "");
if (positiveSuffix != null) {
fieldsToText(positiveSuffix, buffer);
}
} else {
buffer.append(negativeSuffix != null ? negativeSuffix : positiveSuffix != null ? positiveSuffix : "");
fieldsToText(negativeSuffix != null ? negativeSuffix :
positiveSuffix != null ? positiveSuffix : new FormatField[0], buffer);
}
}
@ -554,7 +569,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Append pattern prefix
buffer.append(positive ? positivePrefix : negativePrefix);
fieldsToText(positive ? positivePrefix : negativePrefix, buffer);
// Add insignificant integer zeros
int intLength = Math.max(0, exponent);
@ -636,11 +651,7 @@ public class TDecimalFormat extends TNumberFormat {
}
// Add suffix
if (positive) {
buffer.append(positiveSuffix != null ? positiveSuffix : "");
} else {
buffer.append(negativeSuffix != null ? negativeSuffix : positiveSuffix != null ? positiveSuffix : "");
}
appendSuffix(positive, buffer);
}
private long applyRounding(long mantissa, int mantissaLength, int exponent) {
@ -893,4 +904,49 @@ public class TDecimalFormat extends TNumberFormat {
this.exponent = exponent;
}
}
interface FormatField {
void render(TDecimalFormat format, StringBuffer buffer);
}
static class TextField implements FormatField {
private String text;
public TextField(String text) {
this.text = text;
}
@Override
public void render(TDecimalFormat format, StringBuffer buffer) {
buffer.append(text);
}
}
static class CurrencyField implements FormatField {
@Override
public void render(TDecimalFormat format, StringBuffer buffer) {
buffer.append(format.getCurrency().getSymbol(format.symbols.getLocale()));
}
}
static class PercentField implements FormatField {
@Override
public void render(TDecimalFormat format, StringBuffer buffer) {
buffer.append(format.symbols.getPercent());
}
}
static class PerMillField implements FormatField {
@Override
public void render(TDecimalFormat format, StringBuffer buffer) {
buffer.append(format.symbols.getPerMill());
}
}
static class MinusField implements FormatField {
@Override
public void render(TDecimalFormat format, StringBuffer buffer) {
buffer.append(format.symbols.getMinusSign());
}
}
}

View File

@ -15,15 +15,19 @@
*/
package org.teavm.classlib.java.text;
import java.util.ArrayList;
import java.util.List;
import org.teavm.classlib.java.text.TDecimalFormat.FormatField;
/**
*
* @author Alexey Andreev
*/
class TDecimalFormatParser {
private String positivePrefix = "";
private String positiveSuffix = "";
private String negativePrefix;
private String negativeSuffix;
private FormatField[] positivePrefix;
private FormatField[] positiveSuffix;
private FormatField[] negativePrefix;
private FormatField[] negativeSuffix;
private int groupSize;
private int minimumIntLength;
private int intLength;
@ -33,6 +37,7 @@ class TDecimalFormatParser {
private boolean decimalSeparatorRequired;
private String string;
private int index;
private int multiplier;
public void parse(String string) {
groupSize = 0;
@ -40,6 +45,7 @@ class TDecimalFormatParser {
fracLength = 0;
exponentLength = 0;
decimalSeparatorRequired = false;
multiplier = 1;
this.string = string;
index = 0;
positivePrefix = parseText(false, false);
@ -47,6 +53,8 @@ class TDecimalFormatParser {
throw new IllegalArgumentException("Positive number pattern not found in " + string);
}
parseNumber(true);
negativePrefix = null;
negativeSuffix = null;
if (index < string.length() && string.charAt(index) != ';') {
positiveSuffix = parseText(true, false);
}
@ -61,11 +69,16 @@ class TDecimalFormatParser {
}
public void apply(TDecimalFormat format) {
format.setPositivePrefix(positivePrefix);
format.setPositiveSuffix(positiveSuffix);
format.setNegativePrefix(negativePrefix != null ? negativePrefix :
format.symbols.getMinusSign() + positivePrefix);
format.setNegativeSuffix(negativeSuffix != null ? negativeSuffix : positiveSuffix);
format.positivePrefix = positivePrefix;
format.positiveSuffix = positiveSuffix;
if (negativePrefix != null) {
format.negativePrefix = negativePrefix;
} else {
format.negativePrefix = new FormatField[positivePrefix.length + 1];
System.arraycopy(positivePrefix, 0, format.negativePrefix, 1, positivePrefix.length);
format.negativePrefix[0] = new TDecimalFormat.MinusField();
}
format.negativeSuffix = negativeSuffix != null ? negativeSuffix : positiveSuffix;
format.setGroupingSize(groupSize);
format.setGroupingUsed(groupSize > 0);
format.setMinimumIntegerDigits(!decimalSeparatorRequired ? minimumIntLength :
@ -75,9 +88,11 @@ class TDecimalFormatParser {
format.setMaximumFractionDigits(fracLength);
format.setDecimalSeparatorAlwaysShown(decimalSeparatorRequired);
format.exponentDigits = exponentLength;
format.setMultiplier(multiplier);
}
private String parseText(boolean suffix, boolean end) {
FormatField[] parseText(boolean suffix, boolean end) {
List<FormatField> fields = new ArrayList<>();
StringBuilder sb = new StringBuilder();
loop: while (index < string.length()) {
char c = string.charAt(index);
@ -114,13 +129,52 @@ class TDecimalFormatParser {
index = next + 1;
break;
}
// Currency symbol ¤
case '\u00A4':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.CurrencyField());
++index;
break;
case '%':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.PercentField());
++index;
multiplier = 100;
break;
// Per mill symbol
case '\u2030':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.PerMillField());
++index;
multiplier = 1000;
break;
case '-':
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
sb.setLength(0);
}
fields.add(new TDecimalFormat.MinusField());
++index;
break;
default:
sb.append(c);
++index;
break;
}
}
return sb.toString();
if (sb.length() > 0) {
fields.add(new TDecimalFormat.TextField(sb.toString()));
}
return fields.toArray(new FormatField[fields.size()]);
}
private void parseNumber(boolean apply) {

View File

@ -15,8 +15,10 @@
*/
package org.teavm.classlib.java.text;
import java.util.Objects;
import org.teavm.classlib.impl.unicode.CLDRHelper;
import org.teavm.classlib.java.math.TRoundingMode;
import org.teavm.classlib.java.util.TCurrency;
import org.teavm.classlib.java.util.TLocale;
/**
@ -30,6 +32,7 @@ public abstract class TNumberFormat extends TFormat {
private int maximumIntegerDigits = 40, minimumIntegerDigits = 1,
maximumFractionDigits = 3, minimumFractionDigits = 0;
private TRoundingMode roundingMode = TRoundingMode.HALF_EVEN;
TCurrency currency = TCurrency.getInstance(TLocale.getDefault());
public TNumberFormat() {
}
@ -39,6 +42,14 @@ public abstract class TNumberFormat extends TFormat {
return super.clone();
}
public TCurrency getCurrency() {
return currency;
}
public void setCurrency(TCurrency currency) {
this.currency = currency;
}
@Override
public boolean equals(Object object) {
if (object == this) {
@ -54,7 +65,8 @@ public abstract class TNumberFormat extends TFormat {
&& maximumIntegerDigits == obj.maximumIntegerDigits
&& minimumFractionDigits == obj.minimumFractionDigits
&& minimumIntegerDigits == obj.minimumIntegerDigits
&& roundingMode == obj.roundingMode;
&& roundingMode == obj.roundingMode
&& currency == obj.currency;
}
public final String format(double value) {
@ -144,7 +156,7 @@ public abstract class TNumberFormat extends TFormat {
return (groupingUsed ? 1231 : 1237) + (parseIntegerOnly ? 1231 : 1237)
+ maximumFractionDigits + maximumIntegerDigits
+ minimumFractionDigits + minimumIntegerDigits +
roundingMode.hashCode();
roundingMode.hashCode() + Objects.hashCode(currency);
}
public boolean isGroupingUsed() {

View File

@ -6,6 +6,7 @@ import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Currency;
import java.util.Locale;
import org.junit.Test;
@ -440,6 +441,19 @@ public class DecimalFormatTest {
assertEquals("23E2", format.format(BigInteger.valueOf(23)));
}
@Test
public void formatsSpecial() {
DecimalFormat format = createFormat("0%");
assertEquals("23%", format.format(0.23));
format = createFormat("0‰");
assertEquals("230‰", format.format(0.23));
format = createFormat("0.00 ¤");
format.setCurrency(Currency.getInstance("RUB"));
assertEquals("23.00 RUB", format.format(23));
}
private DecimalFormat createFormat(String format) {
return new DecimalFormat(format, symbols);
}