mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-09 00:14:10 -08:00
Add support of special symbols in DecimalFormat prefixes/suffixes
This commit is contained in:
parent
fc8200f135
commit
3df3447a38
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user