]> granicus.if.org Git - icu/commitdiff
ICU-13060 Assorted FindBugs fixes for DecimalFormat. Moving old implementation to...
authorShane Carr <shane@unicode.org>
Fri, 24 Mar 2017 02:43:54 +0000 (02:43 +0000)
committerShane Carr <shane@unicode.org>
Fri, 24 Mar 2017 02:43:54 +0000 (02:43 +0000)
X-SVN-Rev: 39923

19 files changed:
icu4j/main/classes/core/src/com/ibm/icu/impl/number/Format.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity1.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity4.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantityBCD.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/Parse.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/Rounder.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/demo.java [deleted file]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CompactDecimalFormat.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CurrencyFormat.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/SignificantDigitsRounder.java
icu4j/main/classes/core/src/com/ibm/icu/text/NFSubstitution.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/FormatQuantityTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberStringBuilderTest.java
icu4j/main/tests/core/src/com/ibm/icu/text/DecimalFormat_ICU58.java [moved from icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat_ICU58.java with 99% similarity]
icu4j/main/tests/core/src/com/ibm/icu/text/DigitList.java [moved from icu4j/main/classes/core/src/com/ibm/icu/text/DigitList.java with 98% similarity]

index 681b663fe76b2a4f926c927baf03bef5e347ae40..c19e92461830157bfb78342120a1f75fedf9e459 100644 (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();
     }
 
index a5d376587e0074205f97862e728e906d020161ed..ec2fcd349e4ad3bcc890dfe30e3992dcbe3c702b 100644 (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);
 
index 988c71fdfa2976bed220c268caa3b8e6cf57a396..b7fe968a075ca0e16fadd442a7faa6fc7dc1b6cb 100644 (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();
index 335b78fce17dceaf3bedac7aefd70532d59c963c..3307fff7e4f093e3ca6a7492d2fb4e43d26ec394 100644 (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];
index 6d5831990ee7b334b8d7b53991348146b968cb7f..a096c2c315f1d29122d49124b8b09c74b16e1ae3 100644 (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();
     }
   }
index 4008307161cb18caf2edab4d7388332e796319c2..7ea35f2b568f80dbdf159a29785894b5fed1fd8e 100644 (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;
index 7fd26ca9e4c94859675eaff8c6408084465a1a07..81c0ab1c5c8476c5dc33b7e0b0d22d5ab7fa811d 100644 (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) {
index c5a3068ae198f7073066310be28a169e5c9307d9..ff63ccec4d4c44a1b82fc02ed88026a91265c12c 100644 (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;
 
index 19b78eef97da7a40588cb22844bf3e9116b4f18d..fb28a5f7ec516d0915c1e4f7d120168d62bdb860 100644 (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);
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/demo.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/demo.java
deleted file mode 100644 (file)
index 8a91de7..0000000
+++ /dev/null
@@ -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))));
-    }
-  }
-}
index 1136a16bc5a65b261cb62f86361b1b0d6fa665bb..5aaa34da7ea76eb8db4655916f21d748a549ace7 100644 (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) {
index f84d35a725963f689942ed41ddb074273ce9b6f6..7090ba6fc855e8c28ac0a0f8f6e4ebfb176ab7de 100644 (file)
@@ -93,8 +93,6 @@ public class CurrencyFormat {
      */
     @Deprecated
     public IProperties setCurrencyPluralInfo(CurrencyPluralInfo currencyPluralInfo);
-
-    public IProperties clone();
   }
 
   public static interface IProperties
index 4f67b4e12346ec867bdd87a15a002d6efd2ad072..767eeaf718a28909378dd7cb892e95089da7770d 100644 (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) {
index 0e0edeb46f1892b2631f4471f7f59ef6cf43ad05..0dc6d1b718d83cd52e41155d3bf072daafcd4072 100644 (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;
 
index efcdfed0762ad14289667db47bb05ea8c9e656de..24a0dce1856bfdcea6f995601d1e0f10032f94b4 100644 (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 '<';
     }
 }
index 31dc279bb80d61d1c270b658ed956aa22318a7ac..baadfdca31d60dfa6bb2243364e44a5530afcabb 100644 (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
index 2482865e9bb57b1ab787d795de3460aac91d65e5..a9e9a26750cfaf918239f9b2a3393cd9fa29c5af 100644 (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;
similarity index 99%
rename from icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat_ICU58.java
rename to icu4j/main/tests/core/src/com/ibm/icu/text/DecimalFormat_ICU58.java
index 147627495593515fab754ad564f5e99496fff162..402542e5ad8a96a5f5b9f994bcbe4dbc1c12dee9 100644 (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;
similarity index 98%
rename from icu4j/main/classes/core/src/com/ibm/icu/text/DigitList.java
rename to icu4j/main/tests/core/src/com/ibm/icu/text/DigitList.java
index 12fb19739dc1620c5bc9afd14b2479102e78953c..06b28f67253d3905648cdff36d3881ba35292101 100644 (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.");