ICU-13060 Assorted FindBugs fixes for DecimalFormat. Moving old implementation to core-tests project.

X-SVN-Rev: 39923
This commit is contained in:
Shane Carr 2017-03-24 02:43:54 +00:00
parent af55f69558
commit 3d1d6e5103
19 changed files with 266 additions and 289 deletions

View File

@ -40,7 +40,7 @@ public abstract class Format {
int length = process(inputDeque, modDeque, sb, 0);
// Resolve remaining affixes
length += modDeque.applyAll(sb, 0, length);
modDeque.applyAll(sb, 0, length);
return sb.toString();
}
@ -148,7 +148,7 @@ public abstract class Format {
ModifierHolder mods = threadLocalModifierHolder.get().clear();
NumberStringBuilder sb = threadLocalStringBuilder.get().clear();
int length = process(input, mods, sb, 0);
length += mods.applyAll(sb, 0, length);
mods.applyAll(sb, 0, length);
return sb.toString();
}

View File

@ -167,7 +167,12 @@ public interface FormatQuantity extends PluralRules.IFixedDecimal {
*/
public int getLowerDisplayMagnitude();
public FormatQuantity clone();
/**
* Like clone, but without the restrictions of the Cloneable interface clone.
*
* @return A copy of this instance which can be mutated without affecting this instance.
*/
public FormatQuantity createCopy();
public void copyFrom(FormatQuantity other);

View File

@ -247,7 +247,7 @@ public class FormatQuantity1 implements FormatQuantity {
}
@Override
public FormatQuantity clone() {
public FormatQuantity1 createCopy() {
return new FormatQuantity1(this);
}
@ -675,7 +675,7 @@ public class FormatQuantity1 implements FormatQuantity {
private int fractionCount() {
// TODO: This is temporary.
FormatQuantity1 copy = (FormatQuantity1) this.clone();
FormatQuantity1 copy = new FormatQuantity1(this);
int fractionCount = 0;
while (copy.hasNextFraction()) {
copy.nextFraction();
@ -707,7 +707,7 @@ public class FormatQuantity1 implements FormatQuantity {
@Override
public byte getDigit(int magnitude) {
// TODO: This is temporary.
FormatQuantity1 copy = (FormatQuantity1) this.clone();
FormatQuantity1 copy = new FormatQuantity1(this);
if (magnitude < 0) {
for (int p = -1; p > magnitude; p--) {
copy.nextFraction();

View File

@ -18,7 +18,7 @@ public final class FormatQuantity4 extends FormatQuantityBCD {
private long bcdLong = 0L;
private boolean usingBytes = false;;
private boolean usingBytes = false;
@Override
public int maxRepresentableDigits() {
@ -281,7 +281,8 @@ public final class FormatQuantity4 extends FormatQuantityBCD {
}
private void ensureCapacity(int capacity) {
if (bcdBytes == null && capacity > 0) {
if (capacity == 0) return;
if (bcdBytes == null) {
bcdBytes = new byte[capacity];
} else if (bcdBytes.length < capacity) {
byte[] bcd1 = new byte[capacity * 2];

View File

@ -50,13 +50,30 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
protected static final int INFINITY_FLAG = 2;
protected static final int NAN_FLAG = 4;
// The following three fields relate to the double-to-ascii fast path algorithm.
// When a double is given to FormatQuantityBCD, it is converted to using a fast algorithm. The
// fast algorithm guarantees correctness to only the first ~12 digits of the double. The process
// of rounding the number ensures that the converted digits are correct, falling back to a slow-
// path algorithm if required. Therefore, if a FormatQuantity is constructed from a double, it
// is *required* that roundToMagnitude(), roundToIncrement(), or roundToInfinity() is called. If
// you don't round, assertions will fail in certain other methods if you try calling them.
/**
* The original number provided by the user and which is represented in BCD. Used when we need to
* re-compute the BCD for an exact double representation.
*/
protected double origDouble;
/**
* The change in magnitude relative to the original double. Used when we need to re-compute the
* BCD for an exact double representation.
*/
protected int origDelta;
/**
* Whether the value in the BCD comes from the double fast path without having been rounded to
* ensure correctness
*/
protected boolean isApproximate;
// Four positions: left optional '(', left required '[', right required ']', right optional ')'.
@ -209,6 +226,10 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
@Override
public double getPluralOperand(Operand operand) {
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
// See the comment at the top of this file explaining the "isApproximate" field.
assert !isApproximate;
switch (operand) {
case i:
return toLong();
@ -241,6 +262,10 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
@Override
public int getUpperDisplayMagnitude() {
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
// See the comment at the top of this file explaining the "isApproximate" field.
assert !isApproximate;
int magnitude = scale + precision;
int result = (lReqPos > magnitude) ? lReqPos : (lOptPos < magnitude) ? lOptPos : magnitude;
return result - 1;
@ -248,6 +273,10 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
@Override
public int getLowerDisplayMagnitude() {
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
// See the comment at the top of this file explaining the "isApproximate" field.
assert !isApproximate;
int magnitude = scale;
int result = (rReqPos < magnitude) ? rReqPos : (rOptPos > magnitude) ? rOptPos : magnitude;
return result;
@ -255,6 +284,10 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
@Override
public byte getDigit(int magnitude) {
// If this assertion fails, you need to call roundToInfinity() or some other rounding method.
// See the comment at the top of this file explaining the "isApproximate" field.
assert !isApproximate;
return getDigitPos(magnitude - scale);
}
@ -287,7 +320,7 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
}
@Override
public FormatQuantity clone() {
public FormatQuantity createCopy() {
if (this instanceof FormatQuantity2) {
return new FormatQuantity2((FormatQuantity2) this);
} else if (this instanceof FormatQuantity3) {
@ -295,7 +328,7 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
} else if (this instanceof FormatQuantity4) {
return new FormatQuantity4((FormatQuantity4) this);
} else {
throw new IllegalArgumentException("Don't know how to clone " + this.getClass());
throw new IllegalArgumentException("Don't know how to copy " + this.getClass());
}
}
@ -385,13 +418,6 @@ public abstract class FormatQuantityBCD implements FormatQuantity {
flags |= INFINITY_FLAG;
} else if (n != 0) {
_setToDoubleFast(n);
// TODO: Remove this when finished testing.
// isApproximate = true;
// origDouble = n;
// origDelta = 0;
// convertToAccurateDouble();
compact();
}
}

View File

@ -29,6 +29,14 @@ public class NumberStringBuilder implements CharSequence {
length = 0;
}
public NumberStringBuilder(NumberStringBuilder source) {
this(source.chars.length);
zero = source.zero;
length = source.length;
System.arraycopy(source.chars, zero, chars, zero, length);
System.arraycopy(source.fields, zero, fields, zero, length);
}
@Override
public int length() {
return length;
@ -155,7 +163,9 @@ public class NumberStringBuilder implements CharSequence {
* NumberStringBuilder}.
*/
public int insert(int index, NumberStringBuilder other) {
assert this != other;
if (this == other) {
throw new IllegalArgumentException("Cannot call insert/append on myself");
}
int count = other.length;
if (count == 0) return 0; // nothing to insert
int position = prepareForInsert(index, count);
@ -220,7 +230,7 @@ public class NumberStringBuilder implements CharSequence {
if (start < 0 || end > length || end < start) {
throw new IndexOutOfBoundsException();
}
NumberStringBuilder other = this.clone();
NumberStringBuilder other = new NumberStringBuilder(this);
other.zero = zero + start;
other.length = end - start;
return other;
@ -393,16 +403,6 @@ public class NumberStringBuilder implements CharSequence {
return as.getIterator();
}
@Override
public NumberStringBuilder clone() {
NumberStringBuilder other = new NumberStringBuilder(chars.length);
other.zero = zero;
other.length = length;
System.arraycopy(chars, zero, other.chars, zero, length);
System.arraycopy(fields, zero, other.fields, zero, length);
return other;
}
public NumberStringBuilder clear() {
zero = chars.length / 2;
length = 0;

View File

@ -620,6 +620,7 @@ public class Parse {
// TODO(sffc): Remove this field if it is not necessary.
@SuppressWarnings("unused")
SeparatorType groupingType2;
TextTrieMap<Byte> digitTrie;
Set<AffixHolder> affixHolders = new HashSet<AffixHolder>();
@ -824,14 +825,15 @@ public class Parse {
new ConcurrentHashMap<ULocale, CurrencyAffixPatterns>();
static void addToState(ULocale uloc, ParserState state) {
if (!currencyAffixPatterns.containsKey(uloc)) {
CurrencyAffixPatterns value = currencyAffixPatterns.get(uloc);
if (value == null) {
// There can be multiple threads computing the same CurrencyAffixPatterns simultaneously,
// but that scenario is harmless.
CurrencyAffixPatterns value = new CurrencyAffixPatterns(uloc);
currencyAffixPatterns.put(uloc, value);
CurrencyAffixPatterns newValue = new CurrencyAffixPatterns(uloc);
currencyAffixPatterns.putIfAbsent(uloc, newValue);
value = currencyAffixPatterns.get(uloc);
}
CurrencyAffixPatterns instance = currencyAffixPatterns.get(uloc);
state.affixHolders.addAll(instance.set);
state.affixHolders.addAll(value.set);
}
private CurrencyAffixPatterns(ULocale uloc) {

View File

@ -780,7 +780,7 @@ public class PatternString {
result.totalIntegerDigits += 1;
result.minimumIntegerDigits += 1;
// no change to result.minimumSignificantDigits
result.maximumSignificantDigits += (seenSignificantDigitMarker ? 1 : 0);
// no change to result.maximumSignificantDigits
result.rounding.appendDigit((byte) (state.peek() - '0'), 0, true);
break;

View File

@ -201,7 +201,8 @@ public abstract class Rounder extends Format.BeforeFormat {
* @return The multiplier that was chosen to best fit the input.
*/
public int chooseMultiplierAndApply(FormatQuantity input, MultiplierGenerator mg) {
FormatQuantity copy = input.clone();
// TODO: Avoid the object creation here.
FormatQuantity copy = input.createCopy();
int magnitude = input.getMagnitude();
int multiplier = mg.getMultiplier(magnitude);

View File

@ -1,123 +0,0 @@
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.impl.number;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.List;
import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition;
import com.ibm.icu.impl.number.formatters.RangeFormat;
import com.ibm.icu.impl.number.modifiers.SimpleModifier;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.MeasureUnit;
public class demo {
public static void main(String[] args) throws ParseException {
SimpleModifier.testFormatAsPrefixSuffix();
System.out.println(new FormatQuantity1(3.14159));
System.out.println(new FormatQuantity1(3.14159, true));
System.out.println(new FormatQuantity2(3.14159));
System.out.println(
PatternString.propertiesToString(PatternString.parseToProperties("+**##,##,#00.05#%")));
ParsePosition ppos = new ParsePosition(0);
System.out.println(
Parse.parse(
"dd123",
ppos,
new Properties().setPositivePrefix("dd").setNegativePrefix("ddd"),
DecimalFormatSymbols.getInstance()));
System.out.println(ppos);
List<Format> formats = new ArrayList<Format>();
Properties properties = new Properties();
Format ndf = Endpoint.fromBTA(properties);
formats.add(ndf);
properties =
new Properties()
.setMinimumSignificantDigits(3)
.setMaximumSignificantDigits(3)
.setCompactStyle(CompactStyle.LONG);
Format cdf = Endpoint.fromBTA(properties);
formats.add(cdf);
properties =
new Properties().setFormatWidth(10).setPadPosition(PadPosition.AFTER_PREFIX);
Format pdf = Endpoint.fromBTA(properties);
formats.add(pdf);
properties =
new Properties()
.setMinimumExponentDigits(1)
.setMaximumIntegerDigits(3)
.setMaximumFractionDigits(1);
Format exf = Endpoint.fromBTA(properties);
formats.add(exf);
properties = new Properties().setRoundingIncrement(new BigDecimal("0.5"));
Format rif = Endpoint.fromBTA(properties);
formats.add(rif);
properties = new Properties().setMeasureUnit(MeasureUnit.HECTARE);
Format muf = Endpoint.fromBTA(properties);
formats.add(muf);
properties =
new Properties().setMeasureUnit(MeasureUnit.HECTARE).setCompactStyle(CompactStyle.LONG);
Format cmf = Endpoint.fromBTA(properties);
formats.add(cmf);
properties = PatternString.parseToProperties("#,##0.00 \u00a4");
Format ptf = Endpoint.fromBTA(properties);
formats.add(ptf);
RangeFormat rf = new RangeFormat(cdf, cdf, " to ");
System.out.println(rf.format(new FormatQuantity2(1234), new FormatQuantity2(2345)));
String[] cases = {
"1.0",
"2.01",
"1234.56",
"3000.0",
// "512.0000000000017",
// "4096.000000000001",
// "4096.000000000004",
// "4096.000000000005",
// "4096.000000000006",
// "4096.000000000007",
"0.00026418",
"0.01789261",
"468160.0",
"999000.0",
"999900.0",
"999990.0",
"0.0",
"12345678901.0",
// "789000000000000000000000.0",
// "789123123567853156372158.0",
"-5193.48",
};
for (String str : cases) {
System.out.println("----------");
System.out.println(str);
System.out.println(" NDF: " + ndf.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" CDF: " + cdf.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" PWD: " + pdf.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" EXF: " + exf.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" RIF: " + rif.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" MUF: " + muf.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" CMF: " + cmf.format(new FormatQuantity2(Double.parseDouble(str))));
System.out.println(" PTF: " + ptf.format(new FormatQuantity2(Double.parseDouble(str))));
}
}
}

View File

@ -355,8 +355,8 @@ public class CompactDecimalFormat extends Format.BeforeFormat {
@Override
public boolean equals(Object _other) {
if (_other == null) return false;
if (this == _other) return true;
CompactDecimalFingerprint other = (CompactDecimalFingerprint) _other;
if (this == other) return true;
if (compactStyle != other.compactStyle) return false;
if (compactType != other.compactType) return false;
if (currencySymbol != other.currencySymbol) {

View File

@ -93,8 +93,6 @@ public class CurrencyFormat {
*/
@Deprecated
public IProperties setCurrencyPluralInfo(CurrencyPluralInfo currencyPluralInfo);
public IProperties clone();
}
public static interface IProperties

View File

@ -49,9 +49,6 @@ public class ScientificFormat extends Format.BeforeFormat implements Rounder.Mul
* @return The property bag, for chaining.
*/
public IProperties setMinimumExponentDigits(int minimumExponentDigits);
@Override
public IProperties clone();
}
public static boolean useScientificNotation(IProperties properties) {

View File

@ -7,7 +7,6 @@ import java.math.RoundingMode;
import com.ibm.icu.impl.number.FormatQuantity;
import com.ibm.icu.impl.number.Properties;
import com.ibm.icu.impl.number.Rounder;
import com.ibm.icu.impl.number.Rounder.IBasicRoundingProperties;
public class SignificantDigitsRounder extends Rounder {
@ -17,7 +16,7 @@ public class SignificantDigitsRounder extends Rounder {
ENSURE_MINIMUM_SIGNIFICANT
};
public static interface IProperties extends IBasicRoundingProperties {
public static interface IProperties extends Rounder.IBasicRoundingProperties {
static int DEFAULT_MINIMUM_SIGNIFICANT_DIGITS = -1;

View File

@ -10,6 +10,8 @@ package com.ibm.icu.text;
import java.text.ParsePosition;
import com.ibm.icu.impl.number.FormatQuantity4;
//===================================================================
// NFSubstitution (abstract base class)
//===================================================================
@ -232,7 +234,8 @@ abstract class NFSubstitution {
* @param that The substitution to compare this one to
* @return true if the two substitutions are functionally equivalent
*/
public boolean equals(Object that) {
@Override
public boolean equals(Object that) {
// compare class and all of the fields all substitutions have
// in common
if (that == null) {
@ -250,8 +253,9 @@ abstract class NFSubstitution {
}
return false;
}
public int hashCode() {
@Override
public int hashCode() {
assert false : "hashCode not designed";
return 42;
}
@ -262,7 +266,8 @@ abstract class NFSubstitution {
* not be identical to the description it was created from, but
* it'll produce the same result.
*/
public String toString() {
@Override
public String toString() {
// use tokenChar() to get the character at the beginning and
// end of the substitution token. In between them will go
// either the name of the rule set it uses, or the pattern of
@ -586,7 +591,8 @@ class SameValueSubstitution extends NFSubstitution {
* Returns "number" unchanged.
* @return "number"
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return number;
}
@ -594,7 +600,8 @@ class SameValueSubstitution extends NFSubstitution {
* Returns "number" unchanged.
* @return "number"
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
return number;
}
@ -611,7 +618,8 @@ class SameValueSubstitution extends NFSubstitution {
* substitution.
* @return newRuleValue
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue;
}
@ -620,7 +628,8 @@ class SameValueSubstitution extends NFSubstitution {
* @param oldUpperBound The current upper bound.
* @return oldUpperBound
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return oldUpperBound;
}
@ -632,7 +641,8 @@ class SameValueSubstitution extends NFSubstitution {
* The token character for a SameValueSubstitution is =.
* @return '='
*/
char tokenChar() {
@Override
char tokenChar() {
return '=';
}
}
@ -691,7 +701,8 @@ class MultiplierSubstitution extends NFSubstitution {
* @param radix The radix of the divisor.
* @param exponent The exponent of the divisor.
*/
public void setDivisor(int radix, short exponent) {
@Override
public void setDivisor(int radix, short exponent) {
divisor = NFRule.power(radix, exponent);
if (divisor == 0) {
@ -708,10 +719,11 @@ class MultiplierSubstitution extends NFSubstitution {
* @param that The other substitution
* @return true if the two substitutions are functionally equal
*/
public boolean equals(Object that) {
@Override
public boolean equals(Object that) {
return super.equals(that) && divisor == ((MultiplierSubstitution) that).divisor;
}
//-----------------------------------------------------------------------
// formatting
//-----------------------------------------------------------------------
@ -721,7 +733,8 @@ class MultiplierSubstitution extends NFSubstitution {
* @param number The number being formatted.
* @return "number" divided by the rule's divisor
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return (long)Math.floor(number / divisor);
}
@ -734,7 +747,8 @@ class MultiplierSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return "number" divided by the rule's divisor
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
if (ruleSet == null) {
return number / divisor;
} else {
@ -755,7 +769,8 @@ class MultiplierSubstitution extends NFSubstitution {
* substitution
* @return newRuleValue * divisor
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue * divisor;
}
@ -764,7 +779,8 @@ class MultiplierSubstitution extends NFSubstitution {
* @param oldUpperBound Ignored.
* @return The rule's divisor.
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return divisor;
}
@ -776,7 +792,8 @@ class MultiplierSubstitution extends NFSubstitution {
* The token character for a multiplier substitution is &lt;.
* @return '&lt;'
*/
char tokenChar() {
@Override
char tokenChar() {
return '<';
}
}
@ -858,7 +875,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param radix The radix of the divisor.
* @param exponent The exponent of the divisor.
*/
public void setDivisor(int radix, short exponent) {
@Override
public void setDivisor(int radix, short exponent) {
divisor = NFRule.power(radix, exponent);
if (divisor == 0) { // this will cause recursion
@ -876,7 +894,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param that The other substitution
* @return true if the two substitutions are functionally equivalent
*/
public boolean equals(Object that) {
@Override
public boolean equals(Object that) {
if (super.equals(that)) {
ModulusSubstitution that2 = (ModulusSubstitution)that;
@ -885,7 +904,7 @@ class ModulusSubstitution extends NFSubstitution {
return false;
}
}
//-----------------------------------------------------------------------
// formatting
//-----------------------------------------------------------------------
@ -898,7 +917,8 @@ class ModulusSubstitution extends NFSubstitution {
* into
* @param position The position of the rule text in toInsertInto
*/
public void doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount) {
@Override
public void doSubstitution(long number, StringBuilder toInsertInto, int position, int recursionCount) {
// if this isn't a >>> substitution, just use the inherited version
// of this function (which uses either a rule set or a DecimalFormat
// to format its substitution value)
@ -921,7 +941,8 @@ class ModulusSubstitution extends NFSubstitution {
* into
* @param position The position of the rule text in toInsertInto
*/
public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
@Override
public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
// if this isn't a >>> substitution, just use the inherited version
// of this function (which uses either a rule set or a DecimalFormat
// to format its substitution value)
@ -943,7 +964,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return "number" mod divisor
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return number % divisor;
}
@ -953,7 +975,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return "number" mod divisor
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
return Math.floor(number % divisor);
}
@ -970,7 +993,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param baseValue The partial parse result prior to calling this
* routine.
*/
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
@Override
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
double upperBound, boolean lenientParse) {
// if this isn't a >>> substitution, we can just use the
// inherited parse() routine to do the parsing
@ -1010,7 +1034,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param oldRuleValue The base value of the rule containing the
* substitution
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
}
@ -1019,7 +1044,8 @@ class ModulusSubstitution extends NFSubstitution {
* @param oldUpperBound Ignored
* @return The owning rule's divisor
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return divisor;
}
@ -1031,7 +1057,8 @@ class ModulusSubstitution extends NFSubstitution {
* Returns true. This _is_ a ModulusSubstitution.
* @return true
*/
public boolean isModulusSubstitution() {
@Override
public boolean isModulusSubstitution() {
return true;
}
@ -1039,7 +1066,8 @@ class ModulusSubstitution extends NFSubstitution {
* The token character of a ModulusSubstitution is &gt;.
* @return '&gt;'
*/
char tokenChar() {
@Override
char tokenChar() {
return '>';
}
}
@ -1077,7 +1105,8 @@ class IntegralPartSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return "number" unchanged
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return number;
}
@ -1086,7 +1115,8 @@ class IntegralPartSubstitution extends NFSubstitution {
* @param number The integral part of the number being formatted
* @return floor(number)
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
return Math.floor(number);
}
@ -1104,7 +1134,8 @@ class IntegralPartSubstitution extends NFSubstitution {
* calling this function
* @return oldRuleValue + newRuleValue
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue + oldRuleValue;
}
@ -1114,7 +1145,8 @@ class IntegralPartSubstitution extends NFSubstitution {
* @param oldUpperBound Ignored
* @return Double.MAX_VALUE
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return Double.MAX_VALUE;
}
@ -1126,7 +1158,8 @@ class IntegralPartSubstitution extends NFSubstitution {
* An IntegralPartSubstitution's token character is &lt;
* @return '&lt;'
*/
char tokenChar() {
@Override
char tokenChar() {
return '<';
}
}
@ -1193,7 +1226,8 @@ class FractionalPartSubstitution extends NFSubstitution {
* @param position The position of the owning rule's rule text in
* toInsertInto
*/
public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
@Override
public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
if (!byDigits) {
// if we're not in "byDigits" mode, just use the inherited
// doSubstitution() routine
@ -1207,27 +1241,18 @@ class FractionalPartSubstitution extends NFSubstitution {
// (this is slower, but more accurate, than doing it from the
// other end)
// just print to string and then use that
DigitList dl = new DigitList();
dl.set(number, 20, true);
FormatQuantity4 fq = new FormatQuantity4(number);
fq.roundToInfinity(); // ensure doubles are resolved using slow path
boolean pad = false;
while (dl.count > Math.max(0, dl.decimalAt)) {
int mag = fq.getLowerDisplayMagnitude();
while (mag < 0) {
if (pad && useSpaces) {
toInsertInto.insert(position + pos, ' ');
} else {
pad = true;
}
ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, position + pos, recursionCount);
}
while (dl.decimalAt < 0) {
if (pad && useSpaces) {
toInsertInto.insert(position + pos, ' ');
} else {
pad = true;
}
ruleSet.format(0, toInsertInto, position + pos, recursionCount);
++dl.decimalAt;
ruleSet.format(fq.getDigit(mag++), toInsertInto, position + pos, recursionCount);
}
}
}
@ -1238,7 +1263,8 @@ class FractionalPartSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return 0
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return 0;
}
@ -1247,7 +1273,8 @@ class FractionalPartSubstitution extends NFSubstitution {
* @param number The number being formatted.
* @return number - floor(number)
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
return number - Math.floor(number);
}
@ -1271,7 +1298,8 @@ class FractionalPartSubstitution extends NFSubstitution {
* result; otherwise new Long(0). The result is either a Long or
* a Double.
*/
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
@Override
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
double upperBound, boolean lenientParse) {
// if we're not in byDigits mode, we can just use the inherited
// doParse()
@ -1288,7 +1316,8 @@ class FractionalPartSubstitution extends NFSubstitution {
double result;
int digit;
DigitList dl = new DigitList();
FormatQuantity4 fq = new FormatQuantity4();
int leadingZeros = 0;
while (workText.length() > 0 && workPos.getIndex() != 0) {
workPos.setIndex(0);
digit = ruleSet.parse(workText, workPos, 10).intValue();
@ -1300,7 +1329,12 @@ class FractionalPartSubstitution extends NFSubstitution {
}
if (workPos.getIndex() != 0) {
dl.append('0'+digit);
if (digit == 0) {
leadingZeros++;
} else {
fq.appendDigit((byte) digit, leadingZeros, false);
leadingZeros = 0;
}
parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
workText = workText.substring(workPos.getIndex());
@ -1310,7 +1344,7 @@ class FractionalPartSubstitution extends NFSubstitution {
}
}
}
result = dl.count == 0 ? 0 : dl.getDouble();
result = fq.toDouble();
result = composeRuleValue(result, baseValue);
return new Double(result);
@ -1324,14 +1358,16 @@ class FractionalPartSubstitution extends NFSubstitution {
* this function
* @return newRuleValue + oldRuleValue
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue + oldRuleValue;
}
/**
* Not used.
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return 0; // this value is ignored
}
@ -1343,7 +1379,8 @@ class FractionalPartSubstitution extends NFSubstitution {
* The token character for a FractionalPartSubstitution is &gt;.
* @return '&gt;'
*/
char tokenChar() {
@Override
char tokenChar() {
return '>';
}
}
@ -1380,7 +1417,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
* @param number The number being formatted.
* @return abs(number)
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return Math.abs(number);
}
@ -1389,7 +1427,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
* @param number The number being formatted.
* @return abs(number)
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
return Math.abs(number);
}
@ -1405,7 +1444,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
* this function
* @return -newRuleValue
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return -newRuleValue;
}
@ -1414,7 +1454,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
* @param oldUpperBound Ignored.
* @return Double.MAX_VALUE
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return Double.MAX_VALUE;
}
@ -1426,7 +1467,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
* The token character for an AbsoluteValueSubstitution is &gt;
* @return '&gt;'
*/
char tokenChar() {
@Override
char tokenChar() {
return '>';
}
}
@ -1476,13 +1518,13 @@ class NumeratorSubstitution extends NFSubstitution {
// Rather than keeping a backpointer to the rule, we copy its
// base value here
this.denominator = denominator;
this.withZeros = description.endsWith("<<");
}
static String fixdesc(String description) {
return description.endsWith("<<")
? description.substring(0,description.length()-1)
return description.endsWith("<<")
? description.substring(0,description.length()-1)
: description;
}
@ -1495,7 +1537,8 @@ class NumeratorSubstitution extends NFSubstitution {
* @param that The other NumeratorSubstitution
* @return true if the two objects are functionally equivalent
*/
public boolean equals(Object that) {
@Override
public boolean equals(Object that) {
if (super.equals(that)) {
NumeratorSubstitution that2 = (NumeratorSubstitution)that;
return denominator == that2.denominator && withZeros == that2.withZeros;
@ -1503,7 +1546,7 @@ class NumeratorSubstitution extends NFSubstitution {
return false;
}
}
//-----------------------------------------------------------------------
// formatting
//-----------------------------------------------------------------------
@ -1518,7 +1561,8 @@ class NumeratorSubstitution extends NFSubstitution {
* rule text begins (this value is added to this substitution's
* position to determine exactly where to insert the new text)
*/
public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
@Override
public void doSubstitution(double number, StringBuilder toInsertInto, int position, int recursionCount) {
// perform a transformation on the number being formatted that
// is dependent on the type of substitution this is
//String s = toInsertInto.toString();
@ -1557,7 +1601,8 @@ class NumeratorSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return number * denominator
*/
public long transformNumber(long number) {
@Override
public long transformNumber(long number) {
return Math.round(number * denominator);
}
@ -1566,7 +1611,8 @@ class NumeratorSubstitution extends NFSubstitution {
* @param number The number being formatted
* @return number * denominator
*/
public double transformNumber(double number) {
@Override
public double transformNumber(double number) {
return Math.round(number * denominator);
}
@ -1578,7 +1624,8 @@ class NumeratorSubstitution extends NFSubstitution {
* Dispatches to the inherited version of this function, but makes
* sure that lenientParse is off.
*/
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
@Override
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
double upperBound, boolean lenientParse) {
// we don't have to do anything special to do the parsing here,
// but we have to turn lenient parsing off-- if we leave it on,
@ -1620,7 +1667,7 @@ class NumeratorSubstitution extends NFSubstitution {
if (withZeros) {
// any base value will do in this case. is there a way to
// force this to not bother trying all the base values?
// compute the 'effective' base and prescale the value down
long n = result.longValue();
long d = 1;
@ -1646,7 +1693,8 @@ class NumeratorSubstitution extends NFSubstitution {
* @param oldRuleValue The owning rule's base value
* @return newRuleValue / oldRuleValue
*/
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
@Override
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
return newRuleValue / oldRuleValue;
}
@ -1655,7 +1703,8 @@ class NumeratorSubstitution extends NFSubstitution {
* @param oldUpperBound Ignored
* @return The base value of the rule owning this substitution
*/
public double calcUpperBound(double oldUpperBound) {
@Override
public double calcUpperBound(double oldUpperBound) {
return denominator;
}
@ -1667,7 +1716,8 @@ class NumeratorSubstitution extends NFSubstitution {
* The token character for a NumeratorSubstitution is &lt;
* @return '&lt;'
*/
char tokenChar() {
@Override
char tokenChar() {
return '<';
}
}

View File

@ -159,7 +159,7 @@ public class FormatQuantityTest extends TestFmwk {
private static void testFormatQuantityExpectedOutput(FormatQuantity rq, String expected) {
StringBuilder sb = new StringBuilder();
FormatQuantity q0 = rq.clone();
FormatQuantity q0 = rq.createCopy();
// Force an accurate double
q0.roundToInfinity();
q0.setIntegerFractionLength(1, Integer.MAX_VALUE, 1, Integer.MAX_VALUE);
@ -181,48 +181,48 @@ public class FormatQuantityTest extends TestFmwk {
new MathContext(3, RoundingMode.HALF_UP);
private static void testFormatQuantityRounding(FormatQuantity rq0, FormatQuantity rq1) {
FormatQuantity q0 = rq0.clone();
FormatQuantity q1 = rq1.clone();
FormatQuantity q0 = rq0.createCopy();
FormatQuantity q1 = rq1.createCopy();
q0.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
q1.roundToMagnitude(-1, MATH_CONTEXT_HALF_EVEN);
testFormatQuantityBehavior(q0, q1);
q0 = rq0.clone();
q1 = rq1.clone();
q0 = rq0.createCopy();
q1 = rq1.createCopy();
q0.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
q1.roundToMagnitude(-1, MATH_CONTEXT_CEILING);
testFormatQuantityBehavior(q0, q1);
q0 = rq0.clone();
q1 = rq1.clone();
q0 = rq0.createCopy();
q1 = rq1.createCopy();
q0.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
q1.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
testFormatQuantityBehavior(q0, q1);
}
private static void testFormatQuantityRoundingInterval(FormatQuantity rq0, FormatQuantity rq1) {
FormatQuantity q0 = rq0.clone();
FormatQuantity q1 = rq1.clone();
FormatQuantity q0 = rq0.createCopy();
FormatQuantity q1 = rq1.createCopy();
q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_HALF_EVEN);
testFormatQuantityBehavior(q0, q1);
q0 = rq0.clone();
q1 = rq1.clone();
q0 = rq0.createCopy();
q1 = rq1.createCopy();
q0.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
q1.roundToIncrement(new BigDecimal("0.05"), MATH_CONTEXT_CEILING);
testFormatQuantityBehavior(q0, q1);
}
private static void testFormatQuantityMath(FormatQuantity rq0, FormatQuantity rq1) {
FormatQuantity q0 = rq0.clone();
FormatQuantity q1 = rq1.clone();
FormatQuantity q0 = rq0.createCopy();
FormatQuantity q1 = rq1.createCopy();
q0.adjustMagnitude(-3);
q1.adjustMagnitude(-3);
testFormatQuantityBehavior(q0, q1);
q0 = rq0.clone();
q1 = rq1.clone();
q0 = rq0.createCopy();
q1 = rq1.createCopy();
q0.multiplyBy(new BigDecimal("3.14159"));
q1.multiplyBy(new BigDecimal("3.14159"));
testFormatQuantityBehavior(q0, q1);
@ -231,8 +231,8 @@ public class FormatQuantityTest extends TestFmwk {
private static void testFormatQuantityWithFormats(
FormatQuantity rq0, FormatQuantity rq1, List<Format> formats) {
for (Format format : formats) {
FormatQuantity q0 = rq0.clone();
FormatQuantity q1 = rq1.clone();
FormatQuantity q0 = rq0.createCopy();
FormatQuantity q1 = rq1.createCopy();
String s1 = format.format(q0);
String s2 = format.format(q1);
assertEquals("Different output from formatter (" + q0 + ", " + q1 + ")", s1, s2);
@ -240,8 +240,8 @@ public class FormatQuantityTest extends TestFmwk {
}
private static void testFormatQuantityBehavior(FormatQuantity rq0, FormatQuantity rq1) {
FormatQuantity q0 = rq0.clone();
FormatQuantity q1 = rq1.clone();
FormatQuantity q0 = rq0.createCopy();
FormatQuantity q1 = rq1.createCopy();
assertEquals("Different sign (" + q0 + ", " + q1 + ")", q0.isNegative(), q1.isNegative());
@ -250,11 +250,6 @@ public class FormatQuantityTest extends TestFmwk {
q0.getPositionFingerprint(),
q1.getPositionFingerprint());
assertEquals(
"Different upper range of digits (" + q0 + ", " + q1 + ")",
q0.getUpperDisplayMagnitude(),
q1.getUpperDisplayMagnitude());
assertDoubleEquals(
"Different double values (" + q0 + ", " + q1 + ")", q0.toDouble(), q1.toDouble());
@ -263,11 +258,19 @@ public class FormatQuantityTest extends TestFmwk {
q0.toBigDecimal(),
q1.toBigDecimal());
int equalityDigits = Math.min(q0.maxRepresentableDigits(), q1.maxRepresentableDigits());
for (int m = q0.getUpperDisplayMagnitude(), i = 0;
m >= Math.min(q0.getLowerDisplayMagnitude(), q1.getLowerDisplayMagnitude())
&& i < equalityDigits;
m--, i++) {
q0.roundToInfinity();
q1.roundToInfinity();
assertEquals(
"Different lower display magnitude",
q0.getLowerDisplayMagnitude(),
q1.getLowerDisplayMagnitude());
assertEquals(
"Different upper display magnitude",
q0.getUpperDisplayMagnitude(),
q1.getUpperDisplayMagnitude());
for (int m = q0.getUpperDisplayMagnitude(); m >= q0.getLowerDisplayMagnitude(); m--) {
assertEquals(
"Different digit at magnitude " + m + " (" + q0 + ", " + q1 + ")",
q0.getDigit(m),
@ -341,6 +344,10 @@ public class FormatQuantityTest extends TestFmwk {
assertBigDecimalEquals("Failed on append", expected.toString(), fq.toBigDecimal());
assertNull("Failed health check", fq.checkHealth());
}
fq.appendDigit((byte) 9, 2, false);
expected.append("009");
assertBigDecimalEquals("Failed on append", expected.toString(), fq.toBigDecimal());
assertNull("Failed health check", fq.checkHealth());
}
@Test

View File

@ -114,7 +114,7 @@ public class NumberStringBuilderTest {
assertEquals(fields[2], NumberFormat.Field.INTEGER);
}
sb.append(sb.clone());
sb.append(new NumberStringBuilder(sb));
sb.append(sb.toCharArray(), sb.toFieldArray());
int numNull = 0;
int numCurr = 0;

View File

@ -29,6 +29,14 @@ import com.ibm.icu.impl.Utility;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
import com.ibm.icu.text.CurrencyPluralInfo;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.UFieldPosition;
import com.ibm.icu.text.UTF16;
import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.text.NumberFormat.Field;
import com.ibm.icu.text.PluralRules.FixedDecimal;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;

View File

@ -10,6 +10,9 @@ package com.ibm.icu.text;
import java.math.BigInteger;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.NumberFormat;
/**
* <code>DigitList</code> handles the transcoding between numeric values and
* strings of characters. It only represents non-negative numbers. The
@ -43,7 +46,7 @@ import java.math.BigInteger;
* @version 1.18 08/12/98
* @author Mark Davis, Alan Liu
* */
final class DigitList {
public final class DigitList {
/**
* The maximum number of significant digits in an IEEE 754 double, that
* is, in a Java double. This must not be increased, or garbage digits
@ -114,11 +117,11 @@ final class DigitList {
ensureCapacity(count+1, count);
digits[count++] = (byte) digit;
}
public byte getDigitValue(int i) {
return (byte) (digits[i] - '0');
}
/**
* Utility routine to get the value of the digit list
* If (count == 0) this throws a NumberFormatException, which
@ -203,7 +206,7 @@ final class DigitList {
}
for (int i = n; i < text.length; ++i) {
text[i] = '0';
}
}
return new BigInteger(new String(text));
}
}
@ -253,9 +256,9 @@ final class DigitList {
long scale = (long)count - (long)decimalAt;
if (scale > 0) {
int numDigits = count;
if (scale > (long)Integer.MAX_VALUE) {
if (scale > Integer.MAX_VALUE) {
// try to reduce the scale
long numShift = scale - (long)Integer.MAX_VALUE;
long numShift = scale - Integer.MAX_VALUE;
if (numShift < count) {
numDigits -= numShift;
} else {
@ -529,9 +532,9 @@ final class DigitList {
}
}
// Value to indicate that rounding was done.
// Value to indicate that rounding was done.
private boolean didRound = false;
/**
* Indicates if last digit set was rounded or not.
* true indicates it was rounded.
@ -540,7 +543,7 @@ final class DigitList {
public boolean wasRounded() {
return didRound;
}
/**
* Utility routine to set the value of the digit list from a long
*/
@ -567,7 +570,7 @@ final class DigitList {
// be represented by DigitList.
// [NEW] Faster implementation
didRound = false;
if (source <= 0) {
if (source == Long.MIN_VALUE) {
decimalAt = count = MAX_LONG_DIGITS;
@ -580,7 +583,7 @@ final class DigitList {
int left = MAX_LONG_DIGITS;
int right;
while (source > 0) {
digits[--left] = (byte) (((long) '0') + (source % 10));
digits[--left] = (byte) (('0') + (source % 10));
source /= 10;
}
decimalAt = MAX_LONG_DIGITS-left;
@ -590,7 +593,7 @@ final class DigitList {
for (right = MAX_LONG_DIGITS - 1; digits[right] == (byte) '0'; --right) {}
count = right - left + 1;
System.arraycopy(digits, left, digits, 0, count);
}
}
if (maximumDigits > 0) round(maximumDigits);
}
@ -607,7 +610,7 @@ final class DigitList {
count = decimalAt = stringDigits.length();
didRound = false;
// Don't copy trailing zeros
while (count > 1 && stringDigits.charAt(count - 1) == '0') --count;
@ -797,7 +800,8 @@ final class DigitList {
/**
* equality test between two digit lists.
*/
public boolean equals(Object obj) {
@Override
public boolean equals(Object obj) {
if (this == obj) // quick check
return true;
if (!(obj instanceof DigitList)) // (1) same object?
@ -815,7 +819,8 @@ final class DigitList {
/**
* Generates the hash code for the digit list.
*/
public int hashCode() {
@Override
public int hashCode() {
int hashcode = decimalAt;
for (int i = 0; i < count; i++)
@ -824,7 +829,8 @@ final class DigitList {
return hashcode;
}
public String toString()
@Override
public String toString()
{
if (isZero()) return "0";
StringBuilder buf = new StringBuilder("0.");