summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Thierer <tobiast@google.com>2017-11-21 16:33:42 +0000
committerFredrik Roubert <roubert@google.com>2018-05-31 16:40:01 -0700
commit926c458b0803cea4a71e771db29648246b679565 (patch)
tree6642bcb896c6dfdb1c744ddda25e873c4e5e2ed3
parented1b87a3342117df58d428a5ad02bd42161e958e (diff)
downloadicu-926c458b0803cea4a71e771db29648246b679565.tar.gz
Android patch: Drop ICU test classes in libcore packages.
This change was introduced in Android for ICU 59: https://android.googlesource.com/platform/external/icu/+/7caa07d Change-Id: I848d3d9bcef938e86ecc1da4cbc32131bbff0c7a
-rw-r--r--icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java21
-rw-r--r--icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java6
-rw-r--r--icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java6286
-rw-r--r--icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java842
-rw-r--r--icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitListTest.java47
5 files changed, 18 insertions, 7184 deletions
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java
index 400ab06c4..204d73d1e 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java
@@ -9,7 +9,6 @@ import java.text.ParsePosition;
import org.junit.Test;
import com.ibm.icu.dev.test.TestUtil;
-import com.ibm.icu.dev.text.DecimalFormat_ICU58;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.PatternStringParser;
@@ -39,6 +38,13 @@ public class NumberFormatDataDrivenTest {
return new BigDecimal(s);
}
+ // Android patch: Android can't access DecimalFormat_ICU58 for testing (b/33448125).
+ // That class lived in a package under test and relied on package access, but
+ // 1.) Android Compatibility Test Suite (CTS) run tests with a different ClassLoader,
+ // preventing package access, and
+ // 2.) By default, the OpenJDK 9 toolchain won't compile non-libcore code that in
+ // libcore packages (see http://b/68224249).
+ /*
private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU58 =
new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
@Override
@@ -128,7 +134,7 @@ public class NumberFormatDataDrivenTest {
/**
* @param tuple
* @return
- */
+ *
private DecimalFormat_ICU58 createDecimalFormat(DataDrivenNumberFormatTestData tuple) {
DecimalFormat_ICU58 fmt =
@@ -141,7 +147,7 @@ public class NumberFormatDataDrivenTest {
/**
* @param tuple
* @param fmt
- */
+ *
private void adjustDecimalFormat(
DataDrivenNumberFormatTestData tuple, DecimalFormat_ICU58 fmt) {
if (tuple.minIntegerDigits != null) {
@@ -247,6 +253,8 @@ public class NumberFormatDataDrivenTest {
}
}
};
+ */
+ // Android patch end.
private DataDrivenNumberFormatTestUtility.CodeUnderTest JDK =
new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
@@ -747,14 +755,15 @@ public class NumberFormatDataDrivenTest {
}
};
+ // Android patch: Android can't access DecimalFormat_ICU58 for testing (b/33448125).
+ /*
@Test
public void TestDataDrivenICU58() {
- // Android can't access DecimalFormat_ICU58 for testing (ticket #13283).
- if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android) return;
-
DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
"numberformattestspecification.txt", ICU58);
}
+ */
+ // Android patch end.
// Note: This test case is really questionable. Depending on Java version,
// something may or may not work. However the test data assumes a specific
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
index 8906629ec..e3f690f4e 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java
@@ -43,7 +43,6 @@ import org.junit.runners.JUnit4;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.dev.test.format.IntlTestDecimalFormatAPIC.FieldContainer;
-import com.ibm.icu.dev.text.DecimalFormat_ICU58;
import com.ibm.icu.impl.ICUConfig;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.impl.data.ResourceReader;
@@ -1661,8 +1660,7 @@ public class NumberFormatTest extends TestFmwk {
localizedPattern, df1.toLocalizedPattern());
// Android can't access DecimalFormat_ICU58 for testing (ticket #13283).
- if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android) continue;
-
+ /*
// Note: ICU 58 does not support plus signs in patterns
// Note: ICU 58 always prints the negative part of scientific notation patterns,
// even when the negative part is not necessary
@@ -1675,6 +1673,8 @@ public class NumberFormatTest extends TestFmwk {
standardPattern58, df4.toPattern());
assertEquals("toLocalizedPattern should match on ICU58 standardPattern instance",
localizedPattern58, df3.toLocalizedPattern());
+ */
+ // Android patch end.
}
}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java
deleted file mode 100644
index 0fa9d8896..000000000
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java
+++ /dev/null
@@ -1,6286 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-/*
- *******************************************************************************
- * Copyright (C) 1996-2016, International Business Machines Corporation and
- * others. All Rights Reserved.
- *******************************************************************************
- */
-package com.ibm.icu.dev.text;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.math.BigInteger;
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedString;
-import java.text.ChoiceFormat;
-import java.text.FieldPosition;
-import java.text.Format;
-import java.text.ParsePosition;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import com.ibm.icu.impl.ICUConfig;
-import com.ibm.icu.impl.PatternProps;
-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.FixedDecimal;
-import com.ibm.icu.text.UFieldPosition;
-import com.ibm.icu.text.UTF16;
-import com.ibm.icu.text.UnicodeSet;
-import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.Currency.CurrencyUsage;
-import com.ibm.icu.util.CurrencyAmount;
-import com.ibm.icu.util.ULocale;
-import com.ibm.icu.util.ULocale.Category;
-
-/**
- * {@icuenhanced java.text.DecimalFormat}.{@icu _usage_}
- *
- * <code>DecimalFormat</code> is a concrete subclass of {@link NumberFormat} that formats
- * decimal numbers. It has a variety of features designed to make it possible to parse and
- * format numbers in any locale, including support for Western, Arabic, or Indic digits.
- * It also supports different flavors of numbers, including integers ("123"), fixed-point
- * numbers ("123.4"), scientific notation ("1.23E4"), percentages ("12%"), and currency
- * amounts ("$123.00", "USD123.00", "123.00 US dollars"). All of these flavors can be
- * easily localized.
- *
- * <p>To obtain a {@link NumberFormat} for a specific locale (including the default
- * locale) call one of <code>NumberFormat</code>'s factory methods such as {@link
- * NumberFormat#getInstance}. Do not call the <code>DecimalFormat</code> constructors
- * directly, unless you know what you are doing, since the {@link NumberFormat} factory
- * methods may return subclasses other than <code>DecimalFormat</code>. If you need to
- * customize the format object, do something like this:
- *
- * <blockquote><pre>
- * NumberFormat f = NumberFormat.getInstance(loc);
- * if (f instanceof DecimalFormat) {
- * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true);
- * }</pre></blockquote>
- *
- * <p><strong>Example Usage</strong>
- *
- * Print out a number using the localized number, currency, and percent
- * format for each locale.
- *
- * <blockquote><pre>
- * Locale[] locales = NumberFormat.getAvailableLocales();
- * double myNumber = -1234.56;
- * NumberFormat format;
- * for (int j=0; j&lt;3; ++j) {
- * System.out.println("FORMAT");
- * for (int i = 0; i &lt; locales.length; ++i) {
- * if (locales[i].getCountry().length() == 0) {
- * // Skip language-only locales
- * continue;
- * }
- * System.out.print(locales[i].getDisplayName());
- * switch (j) {
- * case 0:
- * format = NumberFormat.getInstance(locales[i]); break;
- * case 1:
- * format = NumberFormat.getCurrencyInstance(locales[i]); break;
- * default:
- * format = NumberFormat.getPercentInstance(locales[i]); break;
- * }
- * try {
- * // Assume format is a DecimalFormat
- * System.out.print(": " + ((DecimalFormat) format).toPattern()
- * + " -&gt; " + form.format(myNumber));
- * } catch (Exception e) {}
- * try {
- * System.out.println(" -&gt; " + format.parse(form.format(myNumber)));
- * } catch (ParseException e) {}
- * }
- * }</pre></blockquote>
- *
- * <p>Another example use getInstance(style).<br>
- * Print out a number using the localized number, currency, percent,
- * scientific, integer, iso currency, and plural currency format for each locale.
- *
- * <blockquote><pre>
- * ULocale locale = new ULocale("en_US");
- * double myNumber = 1234.56;
- * for (int j=NumberFormat.NUMBERSTYLE; j&lt;=NumberFormat.PLURALCURRENCYSTYLE; ++j) {
- * NumberFormat format = NumberFormat.getInstance(locale, j);
- * try {
- * // Assume format is a DecimalFormat
- * System.out.print(": " + ((DecimalFormat) format).toPattern()
- * + " -&gt; " + form.format(myNumber));
- * } catch (Exception e) {}
- * try {
- * System.out.println(" -&gt; " + format.parse(form.format(myNumber)));
- * } catch (ParseException e) {}
- * }</pre></blockquote>
- *
- * <h3>Patterns</h3>
- *
- * <p>A <code>DecimalFormat</code> consists of a <em>pattern</em> and a set of
- * <em>symbols</em>. The pattern may be set directly using {@link #applyPattern}, or
- * indirectly using other API methods which manipulate aspects of the pattern, such as the
- * minimum number of integer digits. The symbols are stored in a {@link
- * DecimalFormatSymbols} object. When using the {@link NumberFormat} factory methods, the
- * pattern and symbols are read from ICU's locale data.
- *
- * <h4>Special Pattern Characters</h4>
- *
- * <p>Many characters in a pattern are taken literally; they are matched during parsing
- * and output unchanged during formatting. Special characters, on the other hand, stand
- * for other characters, strings, or classes of characters. For example, the '#'
- * character is replaced by a localized digit. Often the replacement character is the
- * same as the pattern character; in the U.S. locale, the ',' grouping character is
- * replaced by ','. However, the replacement is still happening, and if the symbols are
- * modified, the grouping character changes. Some special characters affect the behavior
- * of the formatter by their presence; for example, if the percent character is seen, then
- * the value is multiplied by 100 before being displayed.
- *
- * <p>To insert a special character in a pattern as a literal, that is, without any
- * special meaning, the character must be quoted. There are some exceptions to this which
- * are noted below.
- *
- * <p>The characters listed here are used in non-localized patterns. Localized patterns
- * use the corresponding characters taken from this formatter's {@link
- * DecimalFormatSymbols} object instead, and these characters lose their special status.
- * Two exceptions are the currency sign and quote, which are not localized.
- *
- * <blockquote>
- * <table border=0 cellspacing=3 cellpadding=0 summary="Chart showing symbol,
- * location, localized, and meaning.">
- * <tr style="background-color: #ccccff">
- * <th align=left>Symbol
- * <th align=left>Location
- * <th align=left>Localized?
- * <th align=left>Meaning
- * <tr style="vertical-align: top;">
- * <td><code>0</code>
- * <td>Number
- * <td>Yes
- * <td>Digit
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>1-9</code>
- * <td>Number
- * <td>Yes
- * <td>'1' through '9' indicate rounding.
- * <tr style="vertical-align: top;">
- * <td><code>@</code>
- * <td>Number
- * <td>No
- * <td>Significant digit
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>#</code>
- * <td>Number
- * <td>Yes
- * <td>Digit, zero shows as absent
- * <tr style="vertical-align: top;">
- * <td><code>.</code>
- * <td>Number
- * <td>Yes
- * <td>Decimal separator or monetary decimal separator
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>-</code>
- * <td>Number
- * <td>Yes
- * <td>Minus sign
- * <tr style="vertical-align: top;">
- * <td><code>,</code>
- * <td>Number
- * <td>Yes
- * <td>Grouping separator
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>E</code>
- * <td>Number
- * <td>Yes
- * <td>Separates mantissa and exponent in scientific notation.
- * <em>Need not be quoted in prefix or suffix.</em>
- * <tr style="vertical-align: top;">
- * <td><code>+</code>
- * <td>Exponent
- * <td>Yes
- * <td>Prefix positive exponents with localized plus sign.
- * <em>Need not be quoted in prefix or suffix.</em>
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>;</code>
- * <td>Subpattern boundary
- * <td>Yes
- * <td>Separates positive and negative subpatterns
- * <tr style="vertical-align: top;">
- * <td><code>%</code>
- * <td>Prefix or suffix
- * <td>Yes
- * <td>Multiply by 100 and show as percentage
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>&#92;u2030</code>
- * <td>Prefix or suffix
- * <td>Yes
- * <td>Multiply by 1000 and show as per mille
- * <tr style="vertical-align: top;">
- * <td><code>&#164;</code> (<code>&#92;u00A4</code>)
- * <td>Prefix or suffix
- * <td>No
- * <td>Currency sign, replaced by currency symbol. If
- * doubled, replaced by international currency symbol.
- * If tripled, replaced by currency plural names, for example,
- * "US dollar" or "US dollars" for America.
- * If present in a pattern, the monetary decimal separator
- * is used instead of the decimal separator.
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>'</code>
- * <td>Prefix or suffix
- * <td>No
- * <td>Used to quote special characters in a prefix or suffix,
- * for example, <code>"'#'#"</code> formats 123 to
- * <code>"#123"</code>. To create a single quote
- * itself, use two in a row: <code>"# o''clock"</code>.
- * <tr style="vertical-align: top;">
- * <td><code>*</code>
- * <td>Prefix or suffix boundary
- * <td>Yes
- * <td>Pad escape, precedes pad character
- * </table>
- * </blockquote>
- *
- * <p>A <code>DecimalFormat</code> pattern contains a postive and negative subpattern, for
- * example, "#,##0.00;(#,##0.00)". Each subpattern has a prefix, a numeric part, and a
- * suffix. If there is no explicit negative subpattern, the negative subpattern is the
- * localized minus sign prefixed to the positive subpattern. That is, "0.00" alone is
- * equivalent to "0.00;-0.00". If there is an explicit negative subpattern, it serves
- * only to specify the negative prefix and suffix; the number of digits, minimal digits,
- * and other characteristics are ignored in the negative subpattern. That means that
- * "#,##0.0#;(#)" has precisely the same result as "#,##0.0#;(#,##0.0#)".
- *
- * <p>The prefixes, suffixes, and various symbols used for infinity, digits, thousands
- * separators, decimal separators, etc. may be set to arbitrary values, and they will
- * appear properly during formatting. However, care must be taken that the symbols and
- * strings do not conflict, or parsing will be unreliable. For example, either the
- * positive and negative prefixes or the suffixes must be distinct for {@link #parse} to
- * be able to distinguish positive from negative values. Another example is that the
- * decimal separator and thousands separator should be distinct characters, or parsing
- * will be impossible.
- *
- * <p>The <em>grouping separator</em> is a character that separates clusters of integer
- * digits to make large numbers more legible. It commonly used for thousands, but in some
- * locales it separates ten-thousands. The <em>grouping size</em> is the number of digits
- * between the grouping separators, such as 3 for "100,000,000" or 4 for "1 0000
- * 0000". There are actually two different grouping sizes: One used for the least
- * significant integer digits, the <em>primary grouping size</em>, and one used for all
- * others, the <em>secondary grouping size</em>. In most locales these are the same, but
- * sometimes they are different. For example, if the primary grouping interval is 3, and
- * the secondary is 2, then this corresponds to the pattern "#,##,##0", and the number
- * 123456789 is formatted as "12,34,56,789". If a pattern contains multiple grouping
- * separators, the interval between the last one and the end of the integer defines the
- * primary grouping size, and the interval between the last two defines the secondary
- * grouping size. All others are ignored, so "#,##,###,####" == "###,###,####" ==
- * "##,#,###,####".
- *
- * <p>Illegal patterns, such as "#.#.#" or "#.###,###", will cause
- * <code>DecimalFormat</code> to throw an {@link IllegalArgumentException} with a message
- * that describes the problem.
- *
- * <h4>Pattern BNF</h4>
- *
- * <pre>
- * pattern := subpattern (';' subpattern)?
- * subpattern := prefix? number exponent? suffix?
- * number := (integer ('.' fraction)?) | sigDigits
- * prefix := '&#92;u0000'..'&#92;uFFFD' - specialCharacters
- * suffix := '&#92;u0000'..'&#92;uFFFD' - specialCharacters
- * integer := '#'* '0'* '0'
- * fraction := '0'* '#'*
- * sigDigits := '#'* '@' '@'* '#'*
- * exponent := 'E' '+'? '0'* '0'
- * padSpec := '*' padChar
- * padChar := '&#92;u0000'..'&#92;uFFFD' - quote
- * &#32;
- * Notation:
- * X* 0 or more instances of X
- * X? 0 or 1 instances of X
- * X|Y either X or Y
- * C..D any character from C up to D, inclusive
- * S-T characters in S, except those in T
- * </pre>
- * The first subpattern is for positive numbers. The second (optional)
- * subpattern is for negative numbers.
- *
- * <p>Not indicated in the BNF syntax above:
- *
- * <ul>
- *
- * <li>The grouping separator ',' can occur inside the integer and sigDigits
- * elements, between any two pattern characters of that element, as long as the integer or
- * sigDigits element is not followed by the exponent element.
- *
- * <li>Two grouping intervals are recognized: That between the decimal point and the first
- * grouping symbol, and that between the first and second grouping symbols. These
- * intervals are identical in most locales, but in some locales they differ. For example,
- * the pattern &quot;#,##,###&quot; formats the number 123456789 as
- * &quot;12,34,56,789&quot;.
- *
- * <li>The pad specifier <code>padSpec</code> may appear before the prefix, after the
- * prefix, before the suffix, after the suffix, or not at all.
- *
- * <li>In place of '0', the digits '1' through '9' may be used to indicate a rounding
- * increment.
- *
- * </ul>
- *
- * <h4>Parsing</h4>
- *
- * <p><code>DecimalFormat</code> parses all Unicode characters that represent decimal
- * digits, as defined by {@link UCharacter#digit}. In addition,
- * <code>DecimalFormat</code> also recognizes as digits the ten consecutive characters
- * starting with the localized zero digit defined in the {@link DecimalFormatSymbols}
- * object. During formatting, the {@link DecimalFormatSymbols}-based digits are output.
- *
- * <p>During parsing, grouping separators are ignored.
- *
- * <p>For currency parsing, the formatter is able to parse every currency style formats no
- * matter which style the formatter is constructed with. For example, a formatter
- * instance gotten from NumberFormat.getInstance(ULocale, NumberFormat.CURRENCYSTYLE) can
- * parse formats such as "USD1.00" and "3.00 US dollars".
- *
- * <p>If {@link #parse(String, ParsePosition)} fails to parse a string, it returns
- * <code>null</code> and leaves the parse position unchanged. The convenience method
- * {@link #parse(String)} indicates parse failure by throwing a {@link
- * java.text.ParseException}.
- *
- * <p>Parsing an extremely large or small absolute value (such as 1.0E10000 or 1.0E-10000)
- * requires huge memory allocation for representing the parsed number. Such input may expose
- * a risk of DoS attacks. To prevent huge memory allocation triggered by such inputs,
- * <code>DecimalFormat</code> internally limits of maximum decimal digits to be 1000. Thus,
- * an input string resulting more than 1000 digits in plain decimal representation (non-exponent)
- * will be treated as either overflow (positive/negative infinite) or underflow (+0.0/-0.0).
- *
- * <h4>Formatting</h4>
- *
- * <p>Formatting is guided by several parameters, all of which can be specified either
- * using a pattern or using the API. The following description applies to formats that do
- * not use <a href="#sci">scientific notation</a> or <a href="#sigdig">significant
- * digits</a>.
- *
- * <ul><li>If the number of actual integer digits exceeds the <em>maximum integer
- * digits</em>, then only the least significant digits are shown. For example, 1997 is
- * formatted as "97" if the maximum integer digits is set to 2.
- *
- * <li>If the number of actual integer digits is less than the <em>minimum integer
- * digits</em>, then leading zeros are added. For example, 1997 is formatted as "01997"
- * if the minimum integer digits is set to 5.
- *
- * <li>If the number of actual fraction digits exceeds the <em>maximum fraction
- * digits</em>, then half-even rounding it performed to the maximum fraction digits. For
- * example, 0.125 is formatted as "0.12" if the maximum fraction digits is 2. This
- * behavior can be changed by specifying a rounding increment and a rounding mode.
- *
- * <li>If the number of actual fraction digits is less than the <em>minimum fraction
- * digits</em>, then trailing zeros are added. For example, 0.125 is formatted as
- * "0.1250" if the mimimum fraction digits is set to 4.
- *
- * <li>Trailing fractional zeros are not displayed if they occur <em>j</em> positions
- * after the decimal, where <em>j</em> is less than the maximum fraction digits. For
- * example, 0.10004 is formatted as "0.1" if the maximum fraction digits is four or less.
- * </ul>
- *
- * <p><strong>Special Values</strong>
- *
- * <p><code>NaN</code> is represented as a single character, typically
- * <code>&#92;uFFFD</code>. This character is determined by the {@link
- * DecimalFormatSymbols} object. This is the only value for which the prefixes and
- * suffixes are not used.
- *
- * <p>Infinity is represented as a single character, typically <code>&#92;u221E</code>,
- * with the positive or negative prefixes and suffixes applied. The infinity character is
- * determined by the {@link DecimalFormatSymbols} object.
- *
- * <h4><a name="sci">Scientific Notation</a></h4>
- *
- * <p>Numbers in scientific notation are expressed as the product of a mantissa and a
- * power of ten, for example, 1234 can be expressed as 1.234 x 10<sup>3</sup>. The
- * mantissa is typically in the half-open interval [1.0, 10.0) or sometimes [0.0, 1.0),
- * but it need not be. <code>DecimalFormat</code> supports arbitrary mantissas.
- * <code>DecimalFormat</code> can be instructed to use scientific notation through the API
- * or through the pattern. In a pattern, the exponent character immediately followed by
- * one or more digit characters indicates scientific notation. Example: "0.###E0" formats
- * the number 1234 as "1.234E3".
- *
- * <ul>
- *
- * <li>The number of digit characters after the exponent character gives the minimum
- * exponent digit count. There is no maximum. Negative exponents are formatted using the
- * localized minus sign, <em>not</em> the prefix and suffix from the pattern. This allows
- * patterns such as "0.###E0 m/s". To prefix positive exponents with a localized plus
- * sign, specify '+' between the exponent and the digits: "0.###E+0" will produce formats
- * "1E+1", "1E+0", "1E-1", etc. (In localized patterns, use the localized plus sign
- * rather than '+'.)
- *
- * <li>The minimum number of integer digits is achieved by adjusting the exponent.
- * Example: 0.00123 formatted with "00.###E0" yields "12.3E-4". This only happens if
- * there is no maximum number of integer digits. If there is a maximum, then the minimum
- * number of integer digits is fixed at one.
- *
- * <li>The maximum number of integer digits, if present, specifies the exponent grouping.
- * The most common use of this is to generate <em>engineering notation</em>, in which the
- * exponent is a multiple of three, e.g., "##0.###E0". The number 12345 is formatted
- * using "##0.####E0" as "12.345E3".
- *
- * <li>When using scientific notation, the formatter controls the digit counts using
- * significant digits logic. The maximum number of significant digits limits the total
- * number of integer and fraction digits that will be shown in the mantissa; it does not
- * affect parsing. For example, 12345 formatted with "##0.##E0" is "12.3E3". See the
- * section on significant digits for more details.
- *
- * <li>The number of significant digits shown is determined as follows: If
- * areSignificantDigitsUsed() returns false, then the minimum number of significant digits
- * shown is one, and the maximum number of significant digits shown is the sum of the
- * <em>minimum integer</em> and <em>maximum fraction</em> digits, and is unaffected by the
- * maximum integer digits. If this sum is zero, then all significant digits are shown.
- * If areSignificantDigitsUsed() returns true, then the significant digit counts are
- * specified by getMinimumSignificantDigits() and getMaximumSignificantDigits(). In this
- * case, the number of integer digits is fixed at one, and there is no exponent grouping.
- *
- * <li>Exponential patterns may not contain grouping separators.
- *
- * </ul>
- *
- * <h4><a name="sigdig">Significant Digits</a></h4>
- *
- * <code>DecimalFormat</code> has two ways of controlling how many digits are shows: (a)
- * significant digits counts, or (b) integer and fraction digit counts. Integer and
- * fraction digit counts are described above. When a formatter is using significant
- * digits counts, the number of integer and fraction digits is not specified directly, and
- * the formatter settings for these counts are ignored. Instead, the formatter uses
- * however many integer and fraction digits are required to display the specified number
- * of significant digits. Examples:
- *
- * <blockquote>
- * <table border=0 cellspacing=3 cellpadding=0>
- * <tr style="background-color: #ccccff">
- * <th align=left>Pattern
- * <th align=left>Minimum significant digits
- * <th align=left>Maximum significant digits
- * <th align=left>Number
- * <th align=left>Output of format()
- * <tr style="vertical-align: top;">
- * <td><code>@@@</code>
- * <td>3
- * <td>3
- * <td>12345
- * <td><code>12300</code>
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>@@@</code>
- * <td>3
- * <td>3
- * <td>0.12345
- * <td><code>0.123</code>
- * <tr style="vertical-align: top;">
- * <td><code>@@##</code>
- * <td>2
- * <td>4
- * <td>3.14159
- * <td><code>3.142</code>
- * <tr style="vertical-align: top; background-color: #eeeeff;">
- * <td><code>@@##</code>
- * <td>2
- * <td>4
- * <td>1.23004
- * <td><code>1.23</code>
- * </table>
- * </blockquote>
- *
- * <ul>
- *
- * <li>Significant digit counts may be expressed using patterns that specify a minimum and
- * maximum number of significant digits. These are indicated by the <code>'@'</code> and
- * <code>'#'</code> characters. The minimum number of significant digits is the number of
- * <code>'@'</code> characters. The maximum number of significant digits is the number of
- * <code>'@'</code> characters plus the number of <code>'#'</code> characters following on
- * the right. For example, the pattern <code>"@@@"</code> indicates exactly 3 significant
- * digits. The pattern <code>"@##"</code> indicates from 1 to 3 significant digits.
- * Trailing zero digits to the right of the decimal separator are suppressed after the
- * minimum number of significant digits have been shown. For example, the pattern
- * <code>"@##"</code> formats the number 0.1203 as <code>"0.12"</code>.
- *
- * <li>If a pattern uses significant digits, it may not contain a decimal separator, nor
- * the <code>'0'</code> pattern character. Patterns such as <code>"@00"</code> or
- * <code>"@.###"</code> are disallowed.
- *
- * <li>Any number of <code>'#'</code> characters may be prepended to the left of the
- * leftmost <code>'@'</code> character. These have no effect on the minimum and maximum
- * significant digits counts, but may be used to position grouping separators. For
- * example, <code>"#,#@#"</code> indicates a minimum of one significant digits, a maximum
- * of two significant digits, and a grouping size of three.
- *
- * <li>In order to enable significant digits formatting, use a pattern containing the
- * <code>'@'</code> pattern character. Alternatively, call {@link
- * #setSignificantDigitsUsed setSignificantDigitsUsed(true)}.
- *
- * <li>In order to disable significant digits formatting, use a pattern that does not
- * contain the <code>'@'</code> pattern character. Alternatively, call {@link
- * #setSignificantDigitsUsed setSignificantDigitsUsed(false)}.
- *
- * <li>The number of significant digits has no effect on parsing.
- *
- * <li>Significant digits may be used together with exponential notation. Such patterns
- * are equivalent to a normal exponential pattern with a minimum and maximum integer digit
- * count of one, a minimum fraction digit count of <code>getMinimumSignificantDigits() -
- * 1</code>, and a maximum fraction digit count of <code>getMaximumSignificantDigits() -
- * 1</code>. For example, the pattern <code>"@@###E0"</code> is equivalent to
- * <code>"0.0###E0"</code>.
- *
- * <li>If signficant digits are in use, then the integer and fraction digit counts, as set
- * via the API, are ignored. If significant digits are not in use, then the signficant
- * digit counts, as set via the API, are ignored.
- *
- * </ul>
- *
- * <h4>Padding</h4>
- *
- * <p><code>DecimalFormat</code> supports padding the result of {@link #format} to a
- * specific width. Padding may be specified either through the API or through the pattern
- * syntax. In a pattern the pad escape character, followed by a single pad character,
- * causes padding to be parsed and formatted. The pad escape character is '*' in
- * unlocalized patterns, and can be localized using {@link
- * DecimalFormatSymbols#setPadEscape}. For example, <code>"$*x#,##0.00"</code> formats
- * 123 to <code>"$xx123.00"</code>, and 1234 to <code>"$1,234.00"</code>.
- *
- * <ul>
- *
- * <li>When padding is in effect, the width of the positive subpattern, including prefix
- * and suffix, determines the format width. For example, in the pattern <code>"* #0
- * o''clock"</code>, the format width is 10.
- *
- * <li>The width is counted in 16-bit code units (Java <code>char</code>s).
- *
- * <li>Some parameters which usually do not matter have meaning when padding is used,
- * because the pattern width is significant with padding. In the pattern "*
- * ##,##,#,##0.##", the format width is 14. The initial characters "##,##," do not affect
- * the grouping size or maximum integer digits, but they do affect the format width.
- *
- * <li>Padding may be inserted at one of four locations: before the prefix, after the
- * prefix, before the suffix, or after the suffix. If padding is specified in any other
- * location, {@link #applyPattern} throws an {@link IllegalArgumentException}. If there
- * is no prefix, before the prefix and after the prefix are equivalent, likewise for the
- * suffix.
- *
- * <li>When specified in a pattern, the 16-bit <code>char</code> immediately following the
- * pad escape is the pad character. This may be any character, including a special pattern
- * character. That is, the pad escape <em>escapes</em> the following character. If there
- * is no character after the pad escape, then the pattern is illegal.
- *
- * </ul>
- *
- * <p>
- * <strong>Rounding</strong>
- *
- * <p><code>DecimalFormat</code> supports rounding to a specific increment. For example,
- * 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the nearest 0.65 is 1.3. The
- * rounding increment may be specified through the API or in a pattern. To specify a
- * rounding increment in a pattern, include the increment in the pattern itself. "#,#50"
- * specifies a rounding increment of 50. "#,##0.05" specifies a rounding increment of
- * 0.05.
- *
- * <ul>
- *
- * <li>Rounding only affects the string produced by formatting. It does not affect
- * parsing or change any numerical values.
- *
- * <li>A <em>rounding mode</em> determines how values are rounded; see the {@link
- * com.ibm.icu.math.BigDecimal} documentation for a description of the modes. Rounding
- * increments specified in patterns use the default mode, {@link
- * com.ibm.icu.math.BigDecimal#ROUND_HALF_EVEN}.
- *
- * <li>Some locales use rounding in their currency formats to reflect the smallest
- * currency denomination.
- *
- * <li>In a pattern, digits '1' through '9' specify rounding, but otherwise behave
- * identically to digit '0'.
- *
- * </ul>
- *
- * <h4>Synchronization</h4>
- *
- * <p><code>DecimalFormat</code> objects are not synchronized. Multiple threads should
- * not access one formatter concurrently.
- *
- * @see java.text.Format
- * @see NumberFormat
- * @author Mark Davis
- * @author Alan Liu
- * @deprecated DecimalFormat was overhauled in ICU 59. This is the old implementation, provided
- * temporarily to ease the transition. This class will be removed from ICU 60.
- */
-@Deprecated
-public class DecimalFormat_ICU58 extends NumberFormat {
-
- /**
- * Creates a DecimalFormat using the default pattern and symbols for the default
- * <code>FORMAT</code> locale. This is a convenient way to obtain a DecimalFormat when
- * internationalization is not the main concern.
- *
- * <p>To obtain standard formats for a given locale, use the factory methods on
- * NumberFormat such as getNumberInstance. These factories will return the most
- * appropriate sub-class of NumberFormat for a given locale.
- *
- * @see NumberFormat#getInstance
- * @see NumberFormat#getNumberInstance
- * @see NumberFormat#getCurrencyInstance
- * @see NumberFormat#getPercentInstance
- * @see Category#FORMAT
- * @stable ICU 2.0
- */
- public DecimalFormat_ICU58() {
- ULocale def = ULocale.getDefault(Category.FORMAT);
- String pattern = getPattern(def, 0);
- // Always applyPattern after the symbols are set
- this.symbols = new DecimalFormatSymbols(def);
- setCurrency(Currency.getInstance(def));
- applyPatternWithoutExpandAffix(pattern, false);
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- currencyPluralInfo = new CurrencyPluralInfo(def);
- // the exact pattern is not known until the plural count is known.
- // so, no need to expand affix now.
- } else {
- expandAffixAdjustWidth(null);
- }
- }
-
- /**
- * Creates a DecimalFormat from the given pattern and the symbols for the default
- * <code>FORMAT</code> locale. This is a convenient way to obtain a DecimalFormat when
- * internationalization is not the main concern.
- *
- * <p>To obtain standard formats for a given locale, use the factory methods on
- * NumberFormat such as getNumberInstance. These factories will return the most
- * appropriate sub-class of NumberFormat for a given locale.
- *
- * @param pattern A non-localized pattern string.
- * @throws IllegalArgumentException if the given pattern is invalid.
- * @see NumberFormat#getInstance
- * @see NumberFormat#getNumberInstance
- * @see NumberFormat#getCurrencyInstance
- * @see NumberFormat#getPercentInstance
- * @see Category#FORMAT
- * @stable ICU 2.0
- */
- public DecimalFormat_ICU58(String pattern) {
- // Always applyPattern after the symbols are set
- ULocale def = ULocale.getDefault(Category.FORMAT);
- this.symbols = new DecimalFormatSymbols(def);
- setCurrency(Currency.getInstance(def));
- applyPatternWithoutExpandAffix(pattern, false);
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- currencyPluralInfo = new CurrencyPluralInfo(def);
- } else {
- expandAffixAdjustWidth(null);
- }
- }
-
- /**
- * Creates a DecimalFormat from the given pattern and symbols. Use this constructor
- * when you need to completely customize the behavior of the format.
- *
- * <p>To obtain standard formats for a given locale, use the factory methods on
- * NumberFormat such as getInstance or getCurrencyInstance. If you need only minor
- * adjustments to a standard format, you can modify the format returned by a
- * NumberFormat factory method.
- *
- * @param pattern a non-localized pattern string
- * @param symbols the set of symbols to be used
- * @exception IllegalArgumentException if the given pattern is invalid
- * @see NumberFormat#getInstance
- * @see NumberFormat#getNumberInstance
- * @see NumberFormat#getCurrencyInstance
- * @see NumberFormat#getPercentInstance
- * @see DecimalFormatSymbols
- * @stable ICU 2.0
- */
- public DecimalFormat_ICU58(String pattern, DecimalFormatSymbols symbols) {
- createFromPatternAndSymbols(pattern, symbols);
- }
-
- private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) {
- // Always applyPattern after the symbols are set
- symbols = (DecimalFormatSymbols) inputSymbols.clone();
- if (pattern.indexOf(CURRENCY_SIGN) >= 0) {
- // Only spend time with currency symbols when we're going to display it.
- // Also set some defaults before the apply pattern.
- setCurrencyForSymbols();
- }
- applyPatternWithoutExpandAffix(pattern, false);
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale());
- } else {
- expandAffixAdjustWidth(null);
- }
- }
-
- /**
- * Creates a DecimalFormat from the given pattern, symbols, information used for
- * currency plural format, and format style. Use this constructor when you need to
- * completely customize the behavior of the format.
- *
- * <p>To obtain standard formats for a given locale, use the factory methods on
- * NumberFormat such as getInstance or getCurrencyInstance.
- *
- * <p>If you need only minor adjustments to a standard format, you can modify the
- * format returned by a NumberFormat factory method using the setters.
- *
- * <p>If you want to completely customize a decimal format, using your own
- * DecimalFormatSymbols (such as group separators) and your own information for
- * currency plural formatting (such as plural rule and currency plural patterns), you
- * can use this constructor.
- *
- * @param pattern a non-localized pattern string
- * @param symbols the set of symbols to be used
- * @param infoInput the information used for currency plural format, including
- * currency plural patterns and plural rules.
- * @param style the decimal formatting style, it is one of the following values:
- * NumberFormat.NUMBERSTYLE; NumberFormat.CURRENCYSTYLE; NumberFormat.PERCENTSTYLE;
- * NumberFormat.SCIENTIFICSTYLE; NumberFormat.INTEGERSTYLE;
- * NumberFormat.ISOCURRENCYSTYLE; NumberFormat.PLURALCURRENCYSTYLE;
- * @stable ICU 4.2
- */
- public DecimalFormat_ICU58(String pattern, DecimalFormatSymbols symbols, CurrencyPluralInfo infoInput,
- int style) {
- CurrencyPluralInfo info = infoInput;
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- info = (CurrencyPluralInfo) infoInput.clone();
- }
- create(pattern, symbols, info, style);
- }
-
- private void create(String pattern, DecimalFormatSymbols inputSymbols, CurrencyPluralInfo info,
- int inputStyle) {
- if (inputStyle != NumberFormat.PLURALCURRENCYSTYLE) {
- createFromPatternAndSymbols(pattern, inputSymbols);
- } else {
- // Always applyPattern after the symbols are set
- symbols = (DecimalFormatSymbols) inputSymbols.clone();
- currencyPluralInfo = info;
- // the pattern used in format is not fixed until formatting, in which, the
- // number is known and will be used to pick the right pattern based on plural
- // count. Here, set the pattern as the pattern of plural count == "other".
- // For most locale, the patterns are probably the same for all plural
- // count. If not, the right pattern need to be re-applied during format.
- String currencyPluralPatternForOther =
- currencyPluralInfo.getCurrencyPluralPattern("other");
- applyPatternWithoutExpandAffix(currencyPluralPatternForOther, false);
- setCurrencyForSymbols();
- }
- style = inputStyle;
- }
-
- /**
- * Creates a DecimalFormat for currency plural format from the given pattern, symbols,
- * and style.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Deprecated
- public DecimalFormat_ICU58(String pattern, DecimalFormatSymbols inputSymbols, int style) {
- CurrencyPluralInfo info = null;
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- info = new CurrencyPluralInfo(inputSymbols.getULocale());
- }
- create(pattern, inputSymbols, info, style);
- }
-
- /**
- * {@inheritDoc}
- * @stable ICU 2.0
- */
- @Override
- public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- // See if number is negative.
- // usage: isNegative(multiply(numberToBeFormatted));
- private boolean isNegative(double number) {
- // Detecting whether a double is negative is easy with the exception of the value
- // -0.0. This is a double which has a zero mantissa (and exponent), but a negative
- // sign bit. It is semantically distinct from a zero with a positive sign bit, and
- // this distinction is important to certain kinds of computations. However, it's a
- // little tricky to detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you
- // may ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
- // -Infinity. Proper detection of -0.0 is needed to deal with the issues raised by
- // bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
- return (number < 0.0) || (number == 0.0 && 1 / number < 0.0);
- }
-
- // Rounds the number and strips of the negative sign.
- // usage: round(multiply(numberToBeFormatted))
- private double round(double number) {
- boolean isNegative = isNegative(number);
- if (isNegative)
- number = -number;
-
- // Apply rounding after multiplier
- if (roundingDouble > 0.0) {
- // number = roundingDouble
- // * round(number / roundingDouble, roundingMode, isNegative);
- return round(
- number, roundingDouble, roundingDoubleReciprocal, roundingMode,
- isNegative);
- }
- return number;
- }
-
- // Multiplies given number by multipler (if there is one) returning the new
- // number. If there is no multiplier, returns the number passed in unchanged.
- private double multiply(double number) {
- if (multiplier != 1) {
- return number * multiplier;
- }
- return number;
- }
-
- // [Spark/CDL] The actual method to format number. If boolean value
- // parseAttr == true, then attribute information will be recorded.
- private StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition,
- boolean parseAttr) {
- fieldPosition.setBeginIndex(0);
- fieldPosition.setEndIndex(0);
-
- if (Double.isNaN(number)) {
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(result.length());
- }
-
- result.append(symbols.getNaN());
- // TODO: Combine setting a single FieldPosition or adding to an AttributedCharacterIterator
- // into a function like recordAttribute(FieldAttribute, begin, end).
-
- // [Spark/CDL] Add attribute for NaN here.
- // result.append(symbols.getNaN());
- if (parseAttr) {
- addAttribute(Field.INTEGER, result.length() - symbols.getNaN().length(),
- result.length());
- }
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setEndIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setEndIndex(result.length());
- }
-
- addPadding(result, fieldPosition, 0, 0);
- return result;
- }
-
- // Do this BEFORE checking to see if value is negative or infinite and
- // before rounding.
- number = multiply(number);
- boolean isNegative = isNegative(number);
- number = round(number);
-
- if (Double.isInfinite(number)) {
- int prefixLen = appendAffix(result, isNegative, true, fieldPosition, parseAttr);
-
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(result.length());
- }
-
- // [Spark/CDL] Add attribute for infinity here.
- result.append(symbols.getInfinity());
- if (parseAttr) {
- addAttribute(Field.INTEGER, result.length() - symbols.getInfinity().length(),
- result.length());
- }
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setEndIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setEndIndex(result.length());
- }
-
- int suffixLen = appendAffix(result, isNegative, false, fieldPosition, parseAttr);
-
- addPadding(result, fieldPosition, prefixLen, suffixLen);
- return result;
- }
-
- int precision = precision(false);
-
- // This is to fix rounding for scientific notation. See ticket:10542.
- // This code should go away when a permanent fix is done for ticket:9931.
- //
- // This block of code only executes for scientific notation so it will not interfere with the
- // previous fix in {@link #resetActualRounding} for fixed decimal numbers.
- // Moreover this code only runs when there is rounding to be done (precision > 0) and when the
- // rounding mode is something other than ROUND_HALF_EVEN.
- // This block of code does the correct rounding of number in advance so that it will fit into
- // the number of digits indicated by precision. In this way, we avoid using the default
- // ROUND_HALF_EVEN behavior of DigitList. For example, if number = 0.003016 and roundingMode =
- // ROUND_DOWN and precision = 3 then after this code executes, number = 0.00301 (3 significant digits)
- if (useExponentialNotation && precision > 0 && number != 0.0 && roundingMode != BigDecimal.ROUND_HALF_EVEN) {
- int log10RoundingIncr = 1 - precision + (int) Math.floor(Math.log10(Math.abs(number)));
- double roundingIncReciprocal = 0.0;
- double roundingInc = 0.0;
- if (log10RoundingIncr < 0) {
- roundingIncReciprocal =
- BigDecimal.ONE.movePointRight(-log10RoundingIncr).doubleValue();
- } else {
- roundingInc =
- BigDecimal.ONE.movePointRight(log10RoundingIncr).doubleValue();
- }
- number = DecimalFormat_ICU58.round(number, roundingInc, roundingIncReciprocal, roundingMode, isNegative);
- }
- // End fix for ticket:10542
-
- // At this point we are guaranteed a nonnegative finite
- // number.
- synchronized (digitList) {
- digitList.set(number, precision, !useExponentialNotation &&
- !areSignificantDigitsUsed());
- return subformat(number, result, fieldPosition, isNegative, false, parseAttr);
- }
- }
-
- /**
- * This is a special function used by the CompactDecimalFormat subclass.
- * It completes only the rounding portion of the formatting and returns
- * the resulting double. CompactDecimalFormat uses the result to compute
- * the plural form to use.
- *
- * @param number The number to format.
- * @return The number rounded to the correct number of significant digits
- * with negative sign stripped off.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Deprecated
- double adjustNumberAsInFormatting(double number) {
- if (Double.isNaN(number)) {
- return number;
- }
- number = round(multiply(number));
- if (Double.isInfinite(number)) {
- return number;
- }
- return toDigitList(number).getDouble();
- }
-
- @Deprecated
- DigitList toDigitList(double number) {
- DigitList result = new DigitList();
- result.set(number, precision(false), false);
- return result;
- }
-
- /**
- * This is a special function used by the CompactDecimalFormat subclass
- * to determine if the number to be formatted is negative.
- *
- * @param number The number to format.
- * @return True if number is negative.
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Deprecated
- boolean isNumberNegative(double number) {
- if (Double.isNaN(number)) {
- return false;
- }
- return isNegative(multiply(number));
- }
-
- /**
- * Round a double value to the nearest multiple of the given rounding increment,
- * according to the given mode. This is equivalent to rounding value/roundingInc to
- * the nearest integer, according to the given mode, and returning that integer *
- * roundingInc. Note this is changed from the version in 2.4, since division of
- * doubles have inaccuracies. jitterbug 1871.
- *
- * @param number
- * the absolute value of the number to be rounded
- * @param roundingInc
- * the rounding increment
- * @param roundingIncReciprocal
- * if non-zero, is the reciprocal of rounding inc.
- * @param mode
- * a BigDecimal rounding mode
- * @param isNegative
- * true if the number to be rounded is negative
- * @return the absolute value of the rounded result
- */
- private static double round(double number, double roundingInc, double roundingIncReciprocal,
- int mode, boolean isNegative) {
-
- double div = roundingIncReciprocal == 0.0 ? number / roundingInc : number *
- roundingIncReciprocal;
-
- // do the absolute cases first
-
- switch (mode) {
- case BigDecimal.ROUND_CEILING:
- div = (isNegative ? Math.floor(div + epsilon) : Math.ceil(div - epsilon));
- break;
- case BigDecimal.ROUND_FLOOR:
- div = (isNegative ? Math.ceil(div - epsilon) : Math.floor(div + epsilon));
- break;
- case BigDecimal.ROUND_DOWN:
- div = (Math.floor(div + epsilon));
- break;
- case BigDecimal.ROUND_UP:
- div = (Math.ceil(div - epsilon));
- break;
- case BigDecimal.ROUND_UNNECESSARY:
- if (div != Math.floor(div)) {
- throw new ArithmeticException("Rounding necessary");
- }
- return number;
- default:
-
- // Handle complex cases, where the choice depends on the closer value.
-
- // We figure out the distances to the two possible values, ceiling and floor.
- // We then go for the diff that is smaller. Only if they are equal does the
- // mode matter.
-
- double ceil = Math.ceil(div);
- double ceildiff = ceil - div; // (ceil * roundingInc) - number;
- double floor = Math.floor(div);
- double floordiff = div - floor; // number - (floor * roundingInc);
-
- // Note that the diff values were those mapped back to the "normal" space by
- // using the roundingInc. I don't have access to the original author of the
- // code but suspect that that was to produce better result in edge cases
- // because of machine precision, rather than simply using the difference
- // between, say, ceil and div. However, it didn't work in all cases. Am
- // trying instead using an epsilon value.
-
- switch (mode) {
- case BigDecimal.ROUND_HALF_EVEN:
- // We should be able to just return Math.rint(a), but this
- // doesn't work in some VMs.
- // if one is smaller than the other, take the corresponding side
- if (floordiff + epsilon < ceildiff) {
- div = floor;
- } else if (ceildiff + epsilon < floordiff) {
- div = ceil;
- } else { // they are equal, so we want to round to whichever is even
- double testFloor = floor / 2;
- div = (testFloor == Math.floor(testFloor)) ? floor : ceil;
- }
- break;
- case BigDecimal.ROUND_HALF_DOWN:
- div = ((floordiff <= ceildiff + epsilon) ? floor : ceil);
- break;
- case BigDecimal.ROUND_HALF_UP:
- div = ((ceildiff <= floordiff + epsilon) ? ceil : floor);
- break;
- default:
- throw new IllegalArgumentException("Invalid rounding mode: " + mode);
- }
- }
- number = roundingIncReciprocal == 0.0 ? div * roundingInc : div / roundingIncReciprocal;
- return number;
- }
-
- private static double epsilon = 0.00000000001;
-
- /**
- * @stable ICU 2.0
- */
- // [Spark/CDL] Delegate to format_long_StringBuffer_FieldPosition_boolean
- @Override
- public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- private StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition,
- boolean parseAttr) {
- fieldPosition.setBeginIndex(0);
- fieldPosition.setEndIndex(0);
-
- // If we are to do rounding, we need to move into the BigDecimal
- // domain in order to do divide/multiply correctly.
- if (actualRoundingIncrementICU != null) {
- return format(BigDecimal.valueOf(number), result, fieldPosition);
- }
-
- boolean isNegative = (number < 0);
- if (isNegative)
- number = -number;
-
- // In general, long values always represent real finite numbers, so we don't have
- // to check for +/- Infinity or NaN. However, there is one case we have to be
- // careful of: The multiplier can push a number near MIN_VALUE or MAX_VALUE
- // outside the legal range. We check for this before multiplying, and if it
- // happens we use BigInteger instead.
- if (multiplier != 1) {
- boolean tooBig = false;
- if (number < 0) { // This can only happen if number == Long.MIN_VALUE
- long cutoff = Long.MIN_VALUE / multiplier;
- tooBig = (number <= cutoff); // number == cutoff can only happen if multiplier == -1
- } else {
- long cutoff = Long.MAX_VALUE / multiplier;
- tooBig = (number > cutoff);
- }
- if (tooBig) {
- // [Spark/CDL] Use
- // format_BigInteger_StringBuffer_FieldPosition_boolean instead
- // parseAttr is used to judge whether to synthesize attributes.
- return format(BigInteger.valueOf(isNegative ? -number : number), result,
- fieldPosition, parseAttr);
- }
- }
-
- number *= multiplier;
- synchronized (digitList) {
- digitList.set(number, precision(true));
- // Issue 11808
- if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
- throw new ArithmeticException("Rounding necessary");
- }
- return subformat(number, result, fieldPosition, isNegative, true, parseAttr);
- }
- }
-
- /**
- * Formats a BigInteger number.
- *
- * @stable ICU 2.0
- */
- @Override
- public StringBuffer format(BigInteger number, StringBuffer result,
- FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- private StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition,
- boolean parseAttr) {
- // If we are to do rounding, we need to move into the BigDecimal
- // domain in order to do divide/multiply correctly.
- if (actualRoundingIncrementICU != null) {
- return format(new BigDecimal(number), result, fieldPosition);
- }
-
- if (multiplier != 1) {
- number = number.multiply(BigInteger.valueOf(multiplier));
- }
-
- // At this point we are guaranteed a nonnegative finite
- // number.
- synchronized (digitList) {
- digitList.set(number, precision(true));
- // For issue 11808.
- if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
- throw new ArithmeticException("Rounding necessary");
- }
- return subformat(number.intValue(), result, fieldPosition, number.signum() < 0, true,
- parseAttr);
- }
- }
-
- /**
- * Formats a BigDecimal number.
- *
- * @stable ICU 2.0
- */
- @Override
- public StringBuffer format(java.math.BigDecimal number, StringBuffer result,
- FieldPosition fieldPosition) {
- return format(number, result, fieldPosition, false);
- }
-
- private StringBuffer format(java.math.BigDecimal number, StringBuffer result,
- FieldPosition fieldPosition,
- boolean parseAttr) {
- if (multiplier != 1) {
- number = number.multiply(java.math.BigDecimal.valueOf(multiplier));
- }
-
- if (actualRoundingIncrement != null) {
- number = number.divide(actualRoundingIncrement, 0, roundingMode).multiply(actualRoundingIncrement);
- }
-
- synchronized (digitList) {
- digitList.set(number, precision(false), !useExponentialNotation &&
- !areSignificantDigitsUsed());
- // For issue 11808.
- if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
- throw new ArithmeticException("Rounding necessary");
- }
- return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0,
- false, parseAttr);
- }
- }
-
- /**
- * Formats a BigDecimal number.
- *
- * @stable ICU 2.0
- */
- @Override
- public StringBuffer format(BigDecimal number, StringBuffer result,
- FieldPosition fieldPosition) {
- // This method is just a copy of the corresponding java.math.BigDecimal method
- // for now. It isn't very efficient since it must create a conversion object to
- // do math on the rounding increment. In the future we may try to clean this up,
- // or even better, limit our support to just one flavor of BigDecimal.
- if (multiplier != 1) {
- number = number.multiply(BigDecimal.valueOf(multiplier), mathContext);
- }
-
- if (actualRoundingIncrementICU != null) {
- number = number.divide(actualRoundingIncrementICU, 0, roundingMode)
- .multiply(actualRoundingIncrementICU, mathContext);
- }
-
- synchronized (digitList) {
- digitList.set(number, precision(false), !useExponentialNotation &&
- !areSignificantDigitsUsed());
- // For issue 11808.
- if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
- throw new ArithmeticException("Rounding necessary");
- }
- return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0,
- false, false);
- }
- }
-
- /**
- * Returns true if a grouping separator belongs at the given position, based on whether
- * grouping is in use and the values of the primary and secondary grouping interval.
- *
- * @param pos the number of integer digits to the right of the current position. Zero
- * indicates the position after the rightmost integer digit.
- * @return true if a grouping character belongs at the current position.
- */
- private boolean isGroupingPosition(int pos) {
- boolean result = false;
- if (isGroupingUsed() && (pos > 0) && (groupingSize > 0)) {
- if ((groupingSize2 > 0) && (pos > groupingSize)) {
- result = ((pos - groupingSize) % groupingSize2) == 0;
- } else {
- result = pos % groupingSize == 0;
- }
- }
- return result;
- }
-
- /**
- * Return the number of fraction digits to display, or the total
- * number of digits for significant digit formats and exponential
- * formats.
- */
- private int precision(boolean isIntegral) {
- if (areSignificantDigitsUsed()) {
- return getMaximumSignificantDigits();
- } else if (useExponentialNotation) {
- return getMinimumIntegerDigits() + getMaximumFractionDigits();
- } else {
- return isIntegral ? 0 : getMaximumFractionDigits();
- }
- }
-
- private StringBuffer subformat(int number, StringBuffer result, FieldPosition fieldPosition,
- boolean isNegative, boolean isInteger, boolean parseAttr) {
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- // compute the plural category from the digitList plus other settings
- return subformat(currencyPluralInfo.select(getFixedDecimal(number)),
- result, fieldPosition, isNegative,
- isInteger, parseAttr);
- } else {
- return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
- }
- }
-
- /**
- * This is ugly, but don't see a better way to do it without major restructuring of the code.
- */
- /*package*/ FixedDecimal getFixedDecimal(double number) {
- // get the visible fractions and the number of fraction digits.
- return getFixedDecimal(number, digitList);
- }
-
- FixedDecimal getFixedDecimal(double number, DigitList dl) {
- int fractionalDigitsInDigitList = dl.count - dl.decimalAt;
- int v;
- long f;
- int maxFractionalDigits;
- int minFractionalDigits;
- if (useSignificantDigits) {
- maxFractionalDigits = maxSignificantDigits - dl.decimalAt;
- minFractionalDigits = minSignificantDigits - dl.decimalAt;
- if (minFractionalDigits < 0) {
- minFractionalDigits = 0;
- }
- if (maxFractionalDigits < 0) {
- maxFractionalDigits = 0;
- }
- } else {
- maxFractionalDigits = getMaximumFractionDigits();
- minFractionalDigits = getMinimumFractionDigits();
- }
- v = fractionalDigitsInDigitList;
- if (v < minFractionalDigits) {
- v = minFractionalDigits;
- } else if (v > maxFractionalDigits) {
- v = maxFractionalDigits;
- }
- f = 0;
- if (v > 0) {
- for (int i = Math.max(0, dl.decimalAt); i < dl.count; ++i) {
- f *= 10;
- f += (dl.digits[i] - '0');
- }
- for (int i = v; i < fractionalDigitsInDigitList; ++i) {
- f *= 10;
- }
- }
- return new FixedDecimal(number, v, f);
- }
-
- private StringBuffer subformat(double number, StringBuffer result, FieldPosition fieldPosition,
- boolean isNegative,
- boolean isInteger, boolean parseAttr) {
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- // compute the plural category from the digitList plus other settings
- return subformat(currencyPluralInfo.select(getFixedDecimal(number)),
- result, fieldPosition, isNegative,
- isInteger, parseAttr);
- } else {
- return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
- }
- }
-
- private StringBuffer subformat(String pluralCount, StringBuffer result, FieldPosition fieldPosition,
- boolean isNegative, boolean isInteger, boolean parseAttr) {
- // There are 2 ways to activate currency plural format: by applying a pattern with
- // 3 currency sign directly, or by instantiate a decimal formatter using
- // PLURALCURRENCYSTYLE. For both cases, the number of currency sign in the
- // pattern is 3. Even if the number of currency sign in the pattern is 3, it does
- // not mean we need to reset the pattern. For 1st case, we do not need to reset
- // pattern. For 2nd case, we might need to reset pattern, if the default pattern
- // (corresponding to plural count 'other') we use is different from the pattern
- // based on 'pluralCount'.
- //
- // style is only valid when decimal formatter is constructed through
- // DecimalFormat(pattern, symbol, style)
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- // May need to reset pattern if the style is PLURALCURRENCYSTYLE.
- String currencyPluralPattern = currencyPluralInfo.getCurrencyPluralPattern(pluralCount);
- if (formatPattern.equals(currencyPluralPattern) == false) {
- applyPatternWithoutExpandAffix(currencyPluralPattern, false);
- }
- }
- // Expand the affix to the right name according to the plural rule. This is only
- // used for currency plural formatting. Currency plural name is not a fixed
- // static one, it is a dynamic name based on the currency plural count. So, the
- // affixes need to be expanded here. For other cases, the affix is a static one
- // based on pattern alone, and it is already expanded during applying pattern, or
- // setDecimalFormatSymbols, or setCurrency.
- expandAffixAdjustWidth(pluralCount);
- return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
- }
-
- /**
- * Complete the formatting of a finite number. On entry, the
- * digitList must be filled in with the correct digits.
- */
- private StringBuffer subformat(StringBuffer result, FieldPosition fieldPosition,
- boolean isNegative, boolean isInteger, boolean parseAttr) {
- // NOTE: This isn't required anymore because DigitList takes care of this.
- //
- // // The negative of the exponent represents the number of leading // zeros
- // between the decimal and the first non-zero digit, for // a value < 0.1 (e.g.,
- // for 0.00123, -fExponent == 2). If this // is more than the maximum fraction
- // digits, then we have an underflow // for the printed representation. We
- // recognize this here and set // the DigitList representation to zero in this
- // situation.
- //
- // if (-digitList.decimalAt >= getMaximumFractionDigits())
- // {
- // digitList.count = 0;
- // }
-
-
-
- // Per bug 4147706, DecimalFormat must respect the sign of numbers which format as
- // zero. This allows sensible computations and preserves relations such as
- // signum(1/x) = signum(x), where x is +Infinity or -Infinity. Prior to this fix,
- // we always formatted zero values as if they were positive. Liu 7/6/98.
- if (digitList.isZero()) {
- digitList.decimalAt = 0; // Normalize
- }
-
- int prefixLen = appendAffix(result, isNegative, true, fieldPosition, parseAttr);
-
- if (useExponentialNotation) {
- subformatExponential(result, fieldPosition, parseAttr);
- } else {
- subformatFixed(result, fieldPosition, isInteger, parseAttr);
- }
-
- int suffixLen = appendAffix(result, isNegative, false, fieldPosition, parseAttr);
- addPadding(result, fieldPosition, prefixLen, suffixLen);
- return result;
- }
-
- private void subformatFixed(StringBuffer result,
- FieldPosition fieldPosition,
- boolean isInteger,
- boolean parseAttr) {
- String[] digits = symbols.getDigitStrings();
-
- String grouping = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
- symbols.getGroupingSeparatorString(): symbols.getMonetaryGroupingSeparatorString();
- String decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
- symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString();
- boolean useSigDig = areSignificantDigitsUsed();
- int maxIntDig = getMaximumIntegerDigits();
- int minIntDig = getMinimumIntegerDigits();
- int i;
- // [Spark/CDL] Record the integer start index.
- int intBegin = result.length();
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD ||
- fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(intBegin);
- }
- long fractionalDigits = 0;
- int fractionalDigitsCount = 0;
- boolean recordFractionDigits = false;
-
- int sigCount = 0;
- int minSigDig = getMinimumSignificantDigits();
- int maxSigDig = getMaximumSignificantDigits();
- if (!useSigDig) {
- minSigDig = 0;
- maxSigDig = Integer.MAX_VALUE;
- }
-
- // Output the integer portion. Here 'count' is the total number of integer
- // digits we will display, including both leading zeros required to satisfy
- // getMinimumIntegerDigits, and actual digits present in the number.
- int count = useSigDig ? Math.max(1, digitList.decimalAt) : minIntDig;
- if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
- count = digitList.decimalAt;
- }
-
- // Handle the case where getMaximumIntegerDigits() is smaller than the real
- // number of integer digits. If this is so, we output the least significant
- // max integer digits. For example, the value 1997 printed with 2 max integer
- // digits is just "97".
-
- int digitIndex = 0; // Index into digitList.fDigits[]
- if (count > maxIntDig && maxIntDig >= 0) {
- count = maxIntDig;
- digitIndex = digitList.decimalAt - count;
- }
-
- int sizeBeforeIntegerPart = result.length();
- for (i = count - 1; i >= 0; --i) {
- if (i < digitList.decimalAt && digitIndex < digitList.count
- && sigCount < maxSigDig) {
- // Output a real digit
- result.append(digits[digitList.getDigitValue(digitIndex++)]);
- ++sigCount;
- } else {
- // Output a zero (leading or trailing)
- result.append(digits[0]);
- if (sigCount > 0) {
- ++sigCount;
- }
- }
-
- // Output grouping separator if necessary.
- if (isGroupingPosition(i)) {
- result.append(grouping);
- // [Spark/CDL] Add grouping separator attribute here.
- // Set only for the first instance.
- // Length of grouping separator is 1.
- if (fieldPosition.getFieldAttribute() == Field.GROUPING_SEPARATOR &&
- fieldPosition.getBeginIndex() == 0 && fieldPosition.getEndIndex() == 0) {
- fieldPosition.setBeginIndex(result.length()-1);
- fieldPosition.setEndIndex(result.length());
- }
- if (parseAttr) {
- addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length());
- }
- }
- }
-
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD ||
- fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setEndIndex(result.length());
- }
-
- // This handles the special case of formatting 0. For zero only, we count the
- // zero to the left of the decimal point as one signficant digit. Ordinarily we
- // do not count any leading 0's as significant. If the number we are formatting
- // is not zero, then either sigCount or digits.getCount() will be non-zero.
- if (sigCount == 0 && digitList.count == 0) {
- sigCount = 1;
- }
-
- // Determine whether or not there are any printable fractional digits. If
- // we've used up the digits we know there aren't.
- boolean fractionPresent = (!isInteger && digitIndex < digitList.count)
- || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0));
-
- // If there is no fraction present, and we haven't printed any integer digits,
- // then print a zero. Otherwise we won't print _any_ digits, and we won't be
- // able to parse this string.
- if (!fractionPresent && result.length() == sizeBeforeIntegerPart)
- result.append(digits[0]);
- // [Spark/CDL] Add attribute for integer part.
- if (parseAttr) {
- addAttribute(Field.INTEGER, intBegin, result.length());
- }
- // Output the decimal separator if we always do so.
- if (decimalSeparatorAlwaysShown || fractionPresent) {
- if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
- fieldPosition.setBeginIndex(result.length());
- }
- result.append(decimal);
- if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
- fieldPosition.setEndIndex(result.length());
- }
- // [Spark/CDL] Add attribute for decimal separator
- if (parseAttr) {
- addAttribute(Field.DECIMAL_SEPARATOR, result.length() - 1, result.length());
- }
- }
-
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
- fieldPosition.setBeginIndex(result.length());
- }
-
- // [Spark/CDL] Record the begin index of fraction part.
- int fracBegin = result.length();
- recordFractionDigits = fieldPosition instanceof UFieldPosition;
-
- count = useSigDig ? Integer.MAX_VALUE : getMaximumFractionDigits();
- if (useSigDig && (sigCount == maxSigDig ||
- (sigCount >= minSigDig && digitIndex == digitList.count))) {
- count = 0;
- }
- for (i = 0; i < count; ++i) {
- // Here is where we escape from the loop. We escape if we've output the
- // maximum fraction digits (specified in the for expression above). We
- // also stop when we've output the minimum digits and either: we have an
- // integer, so there is no fractional stuff to display, or we're out of
- // significant digits.
- if (!useSigDig && i >= getMinimumFractionDigits() &&
- (isInteger || digitIndex >= digitList.count)) {
- break;
- }
-
- // Output leading fractional zeros. These are zeros that come after the
- // decimal but before any significant digits. These are only output if
- // abs(number being formatted) < 1.0.
- if (-1 - i > (digitList.decimalAt - 1)) {
- result.append(digits[0]);
- if (recordFractionDigits) {
- ++fractionalDigitsCount;
- fractionalDigits *= 10;
- }
- continue;
- }
-
- // Output a digit, if we have any precision left, or a zero if we
- // don't. We don't want to output noise digits.
- if (!isInteger && digitIndex < digitList.count) {
- byte digit = digitList.getDigitValue(digitIndex++);
- result.append(digits[digit]);
- if (recordFractionDigits) {
- ++fractionalDigitsCount;
- fractionalDigits *= 10;
- fractionalDigits += digit;
- }
- } else {
- result.append(digits[0]);
- if (recordFractionDigits) {
- ++fractionalDigitsCount;
- fractionalDigits *= 10;
- }
- }
-
- // If we reach the maximum number of significant digits, or if we output
- // all the real digits and reach the minimum, then we are done.
- ++sigCount;
- if (useSigDig && (sigCount == maxSigDig ||
- (digitIndex == digitList.count && sigCount >= minSigDig))) {
- break;
- }
- }
-
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
- fieldPosition.setEndIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
- fieldPosition.setEndIndex(result.length());
- }
- if (recordFractionDigits) {
- ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits);
- }
-
- // [Spark/CDL] Add attribute information if necessary.
- if (parseAttr && (decimalSeparatorAlwaysShown || fractionPresent)) {
- addAttribute(Field.FRACTION, fracBegin, result.length());
- }
- }
-
- private void subformatExponential(StringBuffer result,
- FieldPosition fieldPosition,
- boolean parseAttr) {
- String[] digits = symbols.getDigitStringsLocal();
- String decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
- symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString();
- boolean useSigDig = areSignificantDigitsUsed();
- int maxIntDig = getMaximumIntegerDigits();
- int minIntDig = getMinimumIntegerDigits();
- int i;
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- fieldPosition.setEndIndex(-1);
- } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
- fieldPosition.setBeginIndex(-1);
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setBeginIndex(result.length());
- fieldPosition.setEndIndex(-1);
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
- fieldPosition.setBeginIndex(-1);
- }
-
- // [Spark/CDL]
- // the begin index of integer part
- // the end index of integer part
- // the begin index of fractional part
- int intBegin = result.length();
- int intEnd = -1;
- int fracBegin = -1;
- int minFracDig = 0;
- if (useSigDig) {
- maxIntDig = minIntDig = 1;
- minFracDig = getMinimumSignificantDigits() - 1;
- } else {
- minFracDig = getMinimumFractionDigits();
- if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
- maxIntDig = 1;
- if (maxIntDig < minIntDig) {
- maxIntDig = minIntDig;
- }
- }
- if (maxIntDig > minIntDig) {
- minIntDig = 1;
- }
- }
- long fractionalDigits = 0;
- int fractionalDigitsCount = 0;
- boolean recordFractionDigits = false;
-
- // Minimum integer digits are handled in exponential format by adjusting the
- // exponent. For example, 0.01234 with 3 minimum integer digits is "123.4E-4".
-
- // Maximum integer digits are interpreted as indicating the repeating
- // range. This is useful for engineering notation, in which the exponent is
- // restricted to a multiple of 3. For example, 0.01234 with 3 maximum integer
- // digits is "12.34e-3". If maximum integer digits are defined and are larger
- // than minimum integer digits, then minimum integer digits are ignored.
-
- int exponent = digitList.decimalAt;
- if (maxIntDig > 1 && maxIntDig != minIntDig) {
- // A exponent increment is defined; adjust to it.
- exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1;
- exponent *= maxIntDig;
- } else {
- // No exponent increment is defined; use minimum integer digits.
- // If none is specified, as in "#E0", generate 1 integer digit.
- exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1;
- }
-
- // We now output a minimum number of digits, and more if there are more
- // digits, up to the maximum number of digits. We place the decimal point
- // after the "integer" digits, which are the first (decimalAt - exponent)
- // digits.
- int minimumDigits = minIntDig + minFracDig;
- // The number of integer digits is handled specially if the number
- // is zero, since then there may be no digits.
- int integerDigits = digitList.isZero() ? minIntDig : digitList.decimalAt - exponent;
- int totalDigits = digitList.count;
- if (minimumDigits > totalDigits)
- totalDigits = minimumDigits;
- if (integerDigits > totalDigits)
- totalDigits = integerDigits;
-
- for (i = 0; i < totalDigits; ++i) {
- if (i == integerDigits) {
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- fieldPosition.setEndIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- fieldPosition.setEndIndex(result.length());
- }
-
- // [Spark/CDL] Add attribute for integer part
- if (parseAttr) {
- intEnd = result.length();
- addAttribute(Field.INTEGER, intBegin, result.length());
- }
- if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
- fieldPosition.setBeginIndex(result.length());
- }
- result.append(decimal);
- if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
- fieldPosition.setEndIndex(result.length());
- }
- // [Spark/CDL] Add attribute for decimal separator
- fracBegin = result.length();
- if (parseAttr) {
- // Length of decimal separator is 1.
- int decimalSeparatorBegin = result.length() - 1;
- addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin,
- result.length());
- }
- // Record field information for caller.
- if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
- fieldPosition.setBeginIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
- fieldPosition.setBeginIndex(result.length());
- }
- recordFractionDigits = fieldPosition instanceof UFieldPosition;
-
- }
- byte digit = (i < digitList.count) ? digitList.getDigitValue(i) : (byte)0;
- result.append(digits[digit]);
- if (recordFractionDigits) {
- ++fractionalDigitsCount;
- fractionalDigits *= 10;
- fractionalDigits += digit;
- }
- }
-
- // For ICU compatibility and format 0 to 0E0 with pattern "#E0" [Richard/GCL]
- if (digitList.isZero() && (totalDigits == 0)) {
- result.append(digits[0]);
- }
-
- // add the decimal separator if it is to be always shown AND there are no decimal digits
- if ((fracBegin == -1) && this.decimalSeparatorAlwaysShown) {
- if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
- fieldPosition.setBeginIndex(result.length());
- }
- result.append(decimal);
- if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
- fieldPosition.setEndIndex(result.length());
- }
- if (parseAttr) {
- // Length of decimal separator is 1.
- int decimalSeparatorBegin = result.length() - 1;
- addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin, result.length());
- }
- }
-
- // Record field information
- if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
- if (fieldPosition.getEndIndex() < 0) {
- fieldPosition.setEndIndex(result.length());
- }
- } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
- if (fieldPosition.getBeginIndex() < 0) {
- fieldPosition.setBeginIndex(result.length());
- }
- fieldPosition.setEndIndex(result.length());
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
- if (fieldPosition.getEndIndex() < 0) {
- fieldPosition.setEndIndex(result.length());
- }
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
- if (fieldPosition.getBeginIndex() < 0) {
- fieldPosition.setBeginIndex(result.length());
- }
- fieldPosition.setEndIndex(result.length());
- }
- if (recordFractionDigits) {
- ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits);
- }
-
- // [Spark/CDL] Calculate the end index of integer part and fractional
- // part if they are not properly processed yet.
- if (parseAttr) {
- if (intEnd < 0) {
- addAttribute(Field.INTEGER, intBegin, result.length());
- }
- if (fracBegin > 0) {
- addAttribute(Field.FRACTION, fracBegin, result.length());
- }
- }
-
- // The exponent is output using the pattern-specified minimum exponent
- // digits. There is no maximum limit to the exponent digits, since truncating
- // the exponent would result in an unacceptable inaccuracy.
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SYMBOL) {
- fieldPosition.setBeginIndex(result.length());
- }
-
- result.append(symbols.getExponentSeparator());
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SYMBOL) {
- fieldPosition.setEndIndex(result.length());
- }
- // [Spark/CDL] For exponent symbol, add an attribute.
- if (parseAttr) {
- addAttribute(Field.EXPONENT_SYMBOL, result.length() -
- symbols.getExponentSeparator().length(), result.length());
- }
- // For zero values, we force the exponent to zero. We must do this here, and
- // not earlier, because the value is used to determine integer digit count
- // above.
- if (digitList.isZero())
- exponent = 0;
-
- boolean negativeExponent = exponent < 0;
- if (negativeExponent) {
- exponent = -exponent;
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
- fieldPosition.setBeginIndex(result.length());
- }
- result.append(symbols.getMinusSignString());
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
- fieldPosition.setEndIndex(result.length());
- }
- // [Spark/CDL] If exponent has sign, then add an exponent sign
- // attribute.
- if (parseAttr) {
- // Length of exponent sign is 1.
- addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length());
- }
- } else if (exponentSignAlwaysShown) {
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
- fieldPosition.setBeginIndex(result.length());
- }
- result.append(symbols.getPlusSignString());
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
- fieldPosition.setEndIndex(result.length());
- }
- // [Spark/CDL] Add an plus sign attribute.
- if (parseAttr) {
- // Length of exponent sign is 1.
- int expSignBegin = result.length() - 1;
- addAttribute(Field.EXPONENT_SIGN, expSignBegin, result.length());
- }
- }
- int expBegin = result.length();
- digitList.set(exponent);
- {
- int expDig = minExponentDigits;
- if (useExponentialNotation && expDig < 1) {
- expDig = 1;
- }
- for (i = digitList.decimalAt; i < expDig; ++i)
- result.append(digits[0]);
- }
- for (i = 0; i < digitList.decimalAt; ++i) {
- result.append((i < digitList.count) ? digits[digitList.getDigitValue(i)]
- : digits[0]);
- }
- // [Spark/CDL] Add attribute for exponent part.
- if (fieldPosition.getFieldAttribute() == Field.EXPONENT) {
- fieldPosition.setBeginIndex(expBegin);
- fieldPosition.setEndIndex(result.length());
- }
- if (parseAttr) {
- addAttribute(Field.EXPONENT, expBegin, result.length());
- }
- }
-
- private final void addPadding(StringBuffer result, FieldPosition fieldPosition, int prefixLen,
- int suffixLen) {
- if (formatWidth > 0) {
- int len = formatWidth - result.length();
- if (len > 0) {
- char[] padding = new char[len];
- for (int i = 0; i < len; ++i) {
- padding[i] = pad;
- }
- switch (padPosition) {
- case PAD_AFTER_PREFIX:
- result.insert(prefixLen, padding);
- break;
- case PAD_BEFORE_PREFIX:
- result.insert(0, padding);
- break;
- case PAD_BEFORE_SUFFIX:
- result.insert(result.length() - suffixLen, padding);
- break;
- case PAD_AFTER_SUFFIX:
- result.append(padding);
- break;
- }
- if (padPosition == PAD_BEFORE_PREFIX || padPosition == PAD_AFTER_PREFIX) {
- fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + len);
- fieldPosition.setEndIndex(fieldPosition.getEndIndex() + len);
- }
- }
- }
- }
-
- /**
- * Parses the given string, returning a <code>Number</code> object to represent the
- * parsed value. <code>Double</code> objects are returned to represent non-integral
- * values which cannot be stored in a <code>BigDecimal</code>. These are
- * <code>NaN</code>, infinity, -infinity, and -0.0. If {@link #isParseBigDecimal()} is
- * false (the default), all other values are returned as <code>Long</code>,
- * <code>BigInteger</code>, or <code>BigDecimal</code> values, in that order of
- * preference. If {@link #isParseBigDecimal()} is true, all other values are returned
- * as <code>BigDecimal</code> valuse. If the parse fails, null is returned.
- *
- * @param text the string to be parsed
- * @param parsePosition defines the position where parsing is to begin, and upon
- * return, the position where parsing left off. If the position has not changed upon
- * return, then parsing failed.
- * @return a <code>Number</code> object with the parsed value or
- * <code>null</code> if the parse failed
- * @stable ICU 2.0
- */
- @Override
- public Number parse(String text, ParsePosition parsePosition) {
- return (Number) parse(text, parsePosition, null);
- }
-
- /**
- * Parses text from the given string as a CurrencyAmount. Unlike the parse() method,
- * this method will attempt to parse a generic currency name, searching for a match of
- * this object's locale's currency display names, or for a 3-letter ISO currency
- * code. This method will fail if this format is not a currency format, that is, if it
- * does not contain the currency pattern symbol (U+00A4) in its prefix or suffix.
- *
- * @param text the text to parse
- * @param pos input-output position; on input, the position within text to match; must
- * have 0 &lt;= pos.getIndex() &lt; text.length(); on output, the position after the last
- * matched character. If the parse fails, the position in unchanged upon output.
- * @return a CurrencyAmount, or null upon failure
- * @stable ICU 49
- */
- @Override
- public CurrencyAmount parseCurrency(CharSequence text, ParsePosition pos) {
- Currency[] currency = new Currency[1];
- return (CurrencyAmount) parse(text.toString(), pos, currency);
- }
-
- /**
- * Parses the given text as either a Number or a CurrencyAmount.
- *
- * @param text the string to parse
- * @param parsePosition input-output position; on input, the position within text to
- * match; must have 0 <= pos.getIndex() < text.length(); on output, the position after
- * the last matched character. If the parse fails, the position in unchanged upon
- * output.
- * @param currency if non-null, a CurrencyAmount is parsed and returned; otherwise a
- * Number is parsed and returned
- * @return a Number or CurrencyAmount or null
- */
- private Object parse(String text, ParsePosition parsePosition, Currency[] currency) {
- int backup;
- int i = backup = parsePosition.getIndex();
-
- // Handle NaN as a special case:
-
- // Skip padding characters, if around prefix
- if (formatWidth > 0 &&
- (padPosition == PAD_BEFORE_PREFIX || padPosition == PAD_AFTER_PREFIX)) {
- i = skipPadding(text, i);
- }
- if (text.regionMatches(i, symbols.getNaN(), 0, symbols.getNaN().length())) {
- i += symbols.getNaN().length();
- // Skip padding characters, if around suffix
- if (formatWidth > 0 && (padPosition == PAD_BEFORE_SUFFIX ||
- padPosition == PAD_AFTER_SUFFIX)) {
- i = skipPadding(text, i);
- }
- parsePosition.setIndex(i);
- return new Double(Double.NaN);
- }
-
- // NaN parse failed; start over
- i = backup;
-
- boolean[] status = new boolean[STATUS_LENGTH];
- if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) {
- if (!parseForCurrency(text, parsePosition, currency, status)) {
- return null;
- }
- } else if (currency != null) {
- return null;
- } else {
- if (!subparse(text, parsePosition, digitList, status, currency, negPrefixPattern,
- negSuffixPattern, posPrefixPattern, posSuffixPattern,
- false, Currency.SYMBOL_NAME)) {
- parsePosition.setIndex(backup);
- return null;
- }
- }
-
- Number n = null;
-
- // Handle infinity
- if (status[STATUS_INFINITE]) {
- n = new Double(status[STATUS_POSITIVE] ? Double.POSITIVE_INFINITY :
- Double.NEGATIVE_INFINITY);
- }
-
- // Handle underflow
- else if (status[STATUS_UNDERFLOW]) {
- n = status[STATUS_POSITIVE] ? new Double("0.0") : new Double("-0.0");
- }
-
- // Handle -0.0
- else if (!status[STATUS_POSITIVE] && digitList.isZero()) {
- n = new Double("-0.0");
- }
-
- else {
- // Do as much of the multiplier conversion as possible without
- // losing accuracy.
- int mult = multiplier; // Don't modify this.multiplier
- while (mult % 10 == 0) {
- --digitList.decimalAt;
- mult /= 10;
- }
-
- // Handle integral values
- if (!parseBigDecimal && mult == 1 && digitList.isIntegral()) {
- // hack quick long
- if (digitList.decimalAt < 12) { // quick check for long
- long l = 0;
- if (digitList.count > 0) {
- int nx = 0;
- while (nx < digitList.count) {
- l = l * 10 + (char) digitList.digits[nx++] - '0';
- }
- while (nx++ < digitList.decimalAt) {
- l *= 10;
- }
- if (!status[STATUS_POSITIVE]) {
- l = -l;
- }
- }
- n = Long.valueOf(l);
- } else {
- BigInteger big = digitList.getBigInteger(status[STATUS_POSITIVE]);
- n = (big.bitLength() < 64) ? (Number) Long.valueOf(big.longValue()) : (Number) big;
- }
- }
- // Handle non-integral values or the case where parseBigDecimal is set
- else {
- BigDecimal big = digitList.getBigDecimalICU(status[STATUS_POSITIVE]);
- n = big;
- if (mult != 1) {
- n = big.divide(BigDecimal.valueOf(mult), mathContext);
- }
- }
- }
-
- // Assemble into CurrencyAmount if necessary
- return (currency != null) ? (Object) new CurrencyAmount(n, currency[0]) : (Object) n;
- }
-
- private boolean parseForCurrency(String text, ParsePosition parsePosition,
- Currency[] currency, boolean[] status) {
- int origPos = parsePosition.getIndex();
- if (!isReadyForParsing) {
- int savedCurrencySignCount = currencySignCount;
- setupCurrencyAffixForAllPatterns();
- // reset pattern back
- if (savedCurrencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- applyPatternWithoutExpandAffix(formatPattern, false);
- } else {
- applyPattern(formatPattern, false);
- }
- isReadyForParsing = true;
- }
- int maxPosIndex = origPos;
- int maxErrorPos = -1;
- boolean[] savedStatus = null;
- // First, parse against current pattern.
- // Since current pattern could be set by applyPattern(),
- // it could be an arbitrary pattern, and it may not be the one
- // defined in current locale.
- boolean[] tmpStatus = new boolean[STATUS_LENGTH];
- ParsePosition tmpPos = new ParsePosition(origPos);
- DigitList tmpDigitList = new DigitList();
- boolean found;
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- found = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
- negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern,
- true, Currency.LONG_NAME);
- } else {
- found = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
- negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern,
- true, Currency.SYMBOL_NAME);
- }
- if (found) {
- if (tmpPos.getIndex() > maxPosIndex) {
- maxPosIndex = tmpPos.getIndex();
- savedStatus = tmpStatus;
- digitList = tmpDigitList;
- }
- } else {
- maxErrorPos = tmpPos.getErrorIndex();
- }
- // Then, parse against affix patterns. Those are currency patterns and currency
- // plural patterns defined in the locale.
- for (AffixForCurrency affix : affixPatternsForCurrency) {
- tmpStatus = new boolean[STATUS_LENGTH];
- tmpPos = new ParsePosition(origPos);
- tmpDigitList = new DigitList();
- boolean result = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
- affix.getNegPrefix(), affix.getNegSuffix(),
- affix.getPosPrefix(), affix.getPosSuffix(),
- true, affix.getPatternType());
- if (result) {
- found = true;
- if (tmpPos.getIndex() > maxPosIndex) {
- maxPosIndex = tmpPos.getIndex();
- savedStatus = tmpStatus;
- digitList = tmpDigitList;
- }
- } else {
- maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? tmpPos.getErrorIndex()
- : maxErrorPos;
- }
- }
- // Finally, parse against simple affix to find the match. For example, in
- // TestMonster suite, if the to-be-parsed text is "-\u00A40,00".
- // complexAffixCompare will not find match, since there is no ISO code matches
- // "\u00A4", and the parse stops at "\u00A4". We will just use simple affix
- // comparison (look for exact match) to pass it.
- //
- // TODO: We should parse against simple affix first when
- // output currency is not requested. After the complex currency
- // parsing implementation was introduced, the default currency
- // instance parsing slowed down because of the new code flow.
- // I filed #10312 - Yoshito
- tmpStatus = new boolean[STATUS_LENGTH];
- tmpPos = new ParsePosition(origPos);
- tmpDigitList = new DigitList();
-
- // Disable complex currency parsing and try it again.
- boolean result = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency,
- negativePrefix, negativeSuffix, positivePrefix, positiveSuffix,
- false /* disable complex currency parsing */, Currency.SYMBOL_NAME);
- if (result) {
- if (tmpPos.getIndex() > maxPosIndex) {
- maxPosIndex = tmpPos.getIndex();
- savedStatus = tmpStatus;
- digitList = tmpDigitList;
- }
- found = true;
- } else {
- maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? tmpPos.getErrorIndex() :
- maxErrorPos;
- }
-
- if (!found) {
- // parsePosition.setIndex(origPos);
- parsePosition.setErrorIndex(maxErrorPos);
- } else {
- parsePosition.setIndex(maxPosIndex);
- parsePosition.setErrorIndex(-1);
- for (int index = 0; index < STATUS_LENGTH; ++index) {
- status[index] = savedStatus[index];
- }
- }
- return found;
- }
-
- // Get affix patterns used in locale's currency pattern (NumberPatterns[1]) and
- // currency plural pattern (CurrencyUnitPatterns).
- private void setupCurrencyAffixForAllPatterns() {
- if (currencyPluralInfo == null) {
- currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale());
- }
- affixPatternsForCurrency = new HashSet<AffixForCurrency>();
-
- // save the current pattern, since it will be changed by
- // applyPatternWithoutExpandAffix
- String savedFormatPattern = formatPattern;
-
- // CURRENCYSTYLE and ISOCURRENCYSTYLE should have the same prefix and suffix, so,
- // only need to save one of them. Here, chose onlyApplyPatternWithoutExpandAffix
- // without saving the actualy pattern in 'pattern' data member. TODO: is it uloc?
- applyPatternWithoutExpandAffix(getPattern(symbols.getULocale(), NumberFormat.CURRENCYSTYLE),
- false);
- AffixForCurrency affixes = new AffixForCurrency(
- negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern,
- Currency.SYMBOL_NAME);
- affixPatternsForCurrency.add(affixes);
-
- // add plural pattern
- Iterator<String> iter = currencyPluralInfo.pluralPatternIterator();
- Set<String> currencyUnitPatternSet = new HashSet<String>();
- while (iter.hasNext()) {
- String pluralCount = iter.next();
- String currencyPattern = currencyPluralInfo.getCurrencyPluralPattern(pluralCount);
- if (currencyPattern != null &&
- currencyUnitPatternSet.contains(currencyPattern) == false) {
- currencyUnitPatternSet.add(currencyPattern);
- applyPatternWithoutExpandAffix(currencyPattern, false);
- affixes = new AffixForCurrency(negPrefixPattern, negSuffixPattern, posPrefixPattern,
- posSuffixPattern, Currency.LONG_NAME);
- affixPatternsForCurrency.add(affixes);
- }
- }
- // reset pattern back
- formatPattern = savedFormatPattern;
- }
-
- // currency formatting style options
- private static final int CURRENCY_SIGN_COUNT_ZERO = 0;
- private static final int CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT = 1;
- private static final int CURRENCY_SIGN_COUNT_IN_ISO_FORMAT = 2;
- private static final int CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT = 3;
-
- private static final int STATUS_INFINITE = 0;
- private static final int STATUS_POSITIVE = 1;
- private static final int STATUS_UNDERFLOW = 2;
- private static final int STATUS_LENGTH = 3;
-
- private static final UnicodeSet dotEquivalents = new UnicodeSet(
- //"[.\u2024\u3002\uFE12\uFE52\uFF0E\uFF61]"
- 0x002E, 0x002E,
- 0x2024, 0x2024,
- 0x3002, 0x3002,
- 0xFE12, 0xFE12,
- 0xFE52, 0xFE52,
- 0xFF0E, 0xFF0E,
- 0xFF61, 0xFF61).freeze();
-
- private static final UnicodeSet commaEquivalents = new UnicodeSet(
- //"[,\u060C\u066B\u3001\uFE10\uFE11\uFE50\uFE51\uFF0C\uFF64]"
- 0x002C, 0x002C,
- 0x060C, 0x060C,
- 0x066B, 0x066B,
- 0x3001, 0x3001,
- 0xFE10, 0xFE11,
- 0xFE50, 0xFE51,
- 0xFF0C, 0xFF0C,
- 0xFF64, 0xFF64).freeze();
-
-// private static final UnicodeSet otherGroupingSeparators = new UnicodeSet(
-// //"[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]"
-// 0x0020, 0x0020,
-// 0x0027, 0x0027,
-// 0x00A0, 0x00A0,
-// 0x066C, 0x066C,
-// 0x2000, 0x200A,
-// 0x2018, 0x2019,
-// 0x202F, 0x202F,
-// 0x205F, 0x205F,
-// 0x3000, 0x3000,
-// 0xFF07, 0xFF07).freeze();
-
- private static final UnicodeSet strictDotEquivalents = new UnicodeSet(
- //"[.\u2024\uFE52\uFF0E\uFF61]"
- 0x002E, 0x002E,
- 0x2024, 0x2024,
- 0xFE52, 0xFE52,
- 0xFF0E, 0xFF0E,
- 0xFF61, 0xFF61).freeze();
-
- private static final UnicodeSet strictCommaEquivalents = new UnicodeSet(
- //"[,\u066B\uFE10\uFE50\uFF0C]"
- 0x002C, 0x002C,
- 0x066B, 0x066B,
- 0xFE10, 0xFE10,
- 0xFE50, 0xFE50,
- 0xFF0C, 0xFF0C).freeze();
-
-// private static final UnicodeSet strictOtherGroupingSeparators = new UnicodeSet(
-// //"[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]"
-// 0x0020, 0x0020,
-// 0x0027, 0x0027,
-// 0x00A0, 0x00A0,
-// 0x066C, 0x066C,
-// 0x2000, 0x200A,
-// 0x2018, 0x2019,
-// 0x202F, 0x202F,
-// 0x205F, 0x205F,
-// 0x3000, 0x3000,
-// 0xFF07, 0xFF07).freeze();
-
- private static final UnicodeSet defaultGroupingSeparators =
- // new UnicodeSet(dotEquivalents).addAll(commaEquivalents)
- // .addAll(otherGroupingSeparators).freeze();
- new UnicodeSet(
- 0x0020, 0x0020,
- 0x0027, 0x0027,
- 0x002C, 0x002C,
- 0x002E, 0x002E,
- 0x00A0, 0x00A0,
- 0x060C, 0x060C,
- 0x066B, 0x066C,
- 0x2000, 0x200A,
- 0x2018, 0x2019,
- 0x2024, 0x2024,
- 0x202F, 0x202F,
- 0x205F, 0x205F,
- 0x3000, 0x3002,
- 0xFE10, 0xFE12,
- 0xFE50, 0xFE52,
- 0xFF07, 0xFF07,
- 0xFF0C, 0xFF0C,
- 0xFF0E, 0xFF0E,
- 0xFF61, 0xFF61,
- 0xFF64, 0xFF64).freeze();
-
- private static final UnicodeSet strictDefaultGroupingSeparators =
- // new UnicodeSet(strictDotEquivalents).addAll(strictCommaEquivalents)
- // .addAll(strictOtherGroupingSeparators).freeze();
- new UnicodeSet(
- 0x0020, 0x0020,
- 0x0027, 0x0027,
- 0x002C, 0x002C,
- 0x002E, 0x002E,
- 0x00A0, 0x00A0,
- 0x066B, 0x066C,
- 0x2000, 0x200A,
- 0x2018, 0x2019,
- 0x2024, 0x2024,
- 0x202F, 0x202F,
- 0x205F, 0x205F,
- 0x3000, 0x3000,
- 0xFE10, 0xFE10,
- 0xFE50, 0xFE50,
- 0xFE52, 0xFE52,
- 0xFF07, 0xFF07,
- 0xFF0C, 0xFF0C,
- 0xFF0E, 0xFF0E,
- 0xFF61, 0xFF61).freeze();
-
- static final UnicodeSet minusSigns =
- new UnicodeSet(
- 0x002D, 0x002D,
- 0x207B, 0x207B,
- 0x208B, 0x208B,
- 0x2212, 0x2212,
- 0x2796, 0x2796,
- 0xFE63, 0xFE63,
- 0xFF0D, 0xFF0D).freeze();
-
- static final UnicodeSet plusSigns =
- new UnicodeSet(
- 0x002B, 0x002B,
- 0x207A, 0x207A,
- 0x208A, 0x208A,
- 0x2795, 0x2795,
- 0xFB29, 0xFB29,
- 0xFE62, 0xFE62,
- 0xFF0B, 0xFF0B).freeze();
-
- // equivalent grouping and decimal support
- static final boolean skipExtendedSeparatorParsing = ICUConfig.get(
- "com.ibm.icu.text.DecimalFormat.SkipExtendedSeparatorParsing", "false")
- .equals("true");
-
- // allow control of requiring a matching decimal point when parsing
- boolean parseRequireDecimalPoint = false;
-
- // When parsing a number with big exponential value, it requires to transform the
- // value into a string representation to construct BigInteger instance. We want to
- // set the maximum size because it can easily trigger OutOfMemoryException.
- // PARSE_MAX_EXPONENT is currently set to 1000 (See getParseMaxDigits()),
- // which is much bigger than MAX_VALUE of Double ( See the problem reported by ticket#5698
- private int PARSE_MAX_EXPONENT = 1000;
-
- /**
- * Parses the given text into a number. The text is parsed beginning at parsePosition,
- * until an unparseable character is seen.
- *
- * @param text the string to parse.
- * @param parsePosition the position at which to being parsing. Upon return, the first
- * unparseable character.
- * @param digits the DigitList to set to the parsed value.
- * @param status Upon return contains boolean status flags indicating whether the
- * value was infinite and whether it was positive.
- * @param currency return value for parsed currency, for generic currency parsing
- * mode, or null for normal parsing. In generic currency parsing mode, any currency is
- * parsed, not just the currency that this formatter is set to.
- * @param negPrefix negative prefix pattern
- * @param negSuffix negative suffix pattern
- * @param posPrefix positive prefix pattern
- * @param negSuffix negative suffix pattern
- * @param parseComplexCurrency whether it is complex currency parsing or not.
- * @param type type of currency to parse against, LONG_NAME only or not.
- */
- private final boolean subparse(
- String text, ParsePosition parsePosition, DigitList digits,
- boolean status[], Currency currency[], String negPrefix, String negSuffix, String posPrefix,
- String posSuffix, boolean parseComplexCurrency, int type) {
-
- int position = parsePosition.getIndex();
- int oldStart = parsePosition.getIndex();
-
- // Match padding before prefix
- if (formatWidth > 0 && padPosition == PAD_BEFORE_PREFIX) {
- position = skipPadding(text, position);
- }
-
- // Match positive and negative prefixes; prefer longest match.
- int posMatch = compareAffix(text, position, false, true, posPrefix, parseComplexCurrency, type, currency);
- int negMatch = compareAffix(text, position, true, true, negPrefix, parseComplexCurrency, type, currency);
- if (posMatch >= 0 && negMatch >= 0) {
- if (posMatch > negMatch) {
- negMatch = -1;
- } else if (negMatch > posMatch) {
- posMatch = -1;
- }
- }
- if (posMatch >= 0) {
- position += posMatch;
- } else if (negMatch >= 0) {
- position += negMatch;
- } else {
- parsePosition.setErrorIndex(position);
- return false;
- }
-
- // Match padding after prefix
- if (formatWidth > 0 && padPosition == PAD_AFTER_PREFIX) {
- position = skipPadding(text, position);
- }
-
- // process digits or Inf, find decimal position
- status[STATUS_INFINITE] = false;
- if (text.regionMatches(position, symbols.getInfinity(), 0,
- symbols.getInfinity().length())) {
- position += symbols.getInfinity().length();
- status[STATUS_INFINITE] = true;
- } else {
- // We now have a string of digits, possibly with grouping symbols, and decimal
- // points. We want to process these into a DigitList. We don't want to put a
- // bunch of leading zeros into the DigitList though, so we keep track of the
- // location of the decimal point, put only significant digits into the
- // DigitList, and adjust the exponent as needed.
-
- digits.decimalAt = digits.count = 0;
- String decimal = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
- symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString();
- String grouping = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
- symbols.getGroupingSeparatorString() : symbols.getMonetaryGroupingSeparatorString();
-
- String exponentSep = symbols.getExponentSeparator();
- boolean sawDecimal = false;
- boolean sawGrouping = false;
- boolean sawDigit = false;
- long exponent = 0; // Set to the exponent value, if any
-
- // strict parsing
- boolean strictParse = isParseStrict();
- boolean strictFail = false; // did we exit with a strict parse failure?
- int lastGroup = -1; // where did we last see a grouping separator?
- int groupedDigitCount = 0; // tracking count of digits delimited by grouping separator
- int gs2 = groupingSize2 == 0 ? groupingSize : groupingSize2;
-
- UnicodeSet decimalEquiv = skipExtendedSeparatorParsing ? UnicodeSet.EMPTY :
- getEquivalentDecimals(decimal, strictParse);
- UnicodeSet groupEquiv = skipExtendedSeparatorParsing ? UnicodeSet.EMPTY :
- (strictParse ? strictDefaultGroupingSeparators : defaultGroupingSeparators);
-
- // We have to track digitCount ourselves, because digits.count will pin when
- // the maximum allowable digits is reached.
- int digitCount = 0;
-
- int backup = -1; // used for preserving the last confirmed position
- int[] parsedDigit = {-1}; // allocates int[1] for parsing a single digit
-
- while (position < text.length()) {
- // Check if the sequence at the current position matches a decimal digit
- int matchLen = matchesDigit(text, position, parsedDigit);
- if (matchLen > 0) {
- // matched a digit
- // Cancel out backup setting (see grouping handler below)
- if (backup != -1) {
- if (strictParse) {
- // comma followed by digit, so group before comma is a secondary
- // group. If there was a group separator before that, the group
- // must == the secondary group length, else it can be <= the the
- // secondary group length.
- if ((lastGroup != -1 && groupedDigitCount != gs2)
- || (lastGroup == -1 && groupedDigitCount > gs2)) {
- strictFail = true;
- break;
- }
- }
- lastGroup = backup;
- groupedDigitCount = 0;
- }
-
- groupedDigitCount++;
- position += matchLen;
- backup = -1;
- sawDigit = true;
- if (parsedDigit[0] == 0 && digits.count == 0) {
- // Handle leading zeros
- if (!sawDecimal) {
- // Ignore leading zeros in integer part of number.
- continue;
- }
- // If we have seen the decimal, but no significant digits yet,
- // then we account for leading zeros by decrementing the
- // digits.decimalAt into negative values.
- --digits.decimalAt;
- } else {
- ++digitCount;
- digits.append((char) (parsedDigit[0] + '0'));
- }
- continue;
- }
-
- // Check if the sequence at the current position matches locale's decimal separator
- int decimalStrLen = decimal.length();
- if (text.regionMatches(position, decimal, 0, decimalStrLen)) {
- // matched a decimal separator
- if (strictParse) {
- if (backup != -1 ||
- (lastGroup != -1 && groupedDigitCount != groupingSize)) {
- strictFail = true;
- break;
- }
- }
-
- // If we're only parsing integers, or if we ALREADY saw the decimal,
- // then don't parse this one.
- if (isParseIntegerOnly() || sawDecimal) {
- break;
- }
-
- digits.decimalAt = digitCount; // Not digits.count!
- sawDecimal = true;
- position += decimalStrLen;
- continue;
- }
-
- if (isGroupingUsed()) {
- // Check if the sequence at the current position matches locale's grouping separator
- int groupingStrLen = grouping.length();
- if (text.regionMatches(position, grouping, 0, groupingStrLen)) {
- if (sawDecimal) {
- break;
- }
-
- if (strictParse) {
- if ((!sawDigit || backup != -1)) {
- // leading group, or two group separators in a row
- strictFail = true;
- break;
- }
- }
-
- // Ignore grouping characters, if we are using them, but require that
- // they be followed by a digit. Otherwise we backup and reprocess
- // them.
- backup = position;
- position += groupingStrLen;
- sawGrouping = true;
- continue;
- }
- }
-
- // Check if the code point at the current position matches one of decimal/grouping equivalent group chars
- int cp = text.codePointAt(position);
- if (!sawDecimal && decimalEquiv.contains(cp)) {
- // matched a decimal separator
- if (strictParse) {
- if (backup != -1 ||
- (lastGroup != -1 && groupedDigitCount != groupingSize)) {
- strictFail = true;
- break;
- }
- }
-
- // If we're only parsing integers, or if we ALREADY saw the decimal,
- // then don't parse this one.
- if (isParseIntegerOnly()) {
- break;
- }
-
- digits.decimalAt = digitCount; // Not digits.count!
-
- // Once we see a decimal separator character, we only accept that
- // decimal separator character from then on.
- decimal = String.valueOf(Character.toChars(cp));
-
- sawDecimal = true;
- position += Character.charCount(cp);
- continue;
- }
-
- if (isGroupingUsed() && !sawGrouping && groupEquiv.contains(cp)) {
- // matched a grouping separator
- if (sawDecimal) {
- break;
- }
-
- if (strictParse) {
- if ((!sawDigit || backup != -1)) {
- // leading group, or two group separators in a row
- strictFail = true;
- break;
- }
- }
-
- // Once we see a grouping character, we only accept that grouping
- // character from then on.
- grouping = String.valueOf(Character.toChars(cp));
-
- // Ignore grouping characters, if we are using them, but require that
- // they be followed by a digit. Otherwise we backup and reprocess
- // them.
- backup = position;
- position += Character.charCount(cp);
- sawGrouping = true;
- continue;
- }
-
- // Check if the sequence at the current position matches locale's exponent separator
- int exponentSepStrLen = exponentSep.length();
- if (text.regionMatches(true, position, exponentSep, 0, exponentSepStrLen)) {
- // parse sign, if present
- boolean negExp = false;
- int pos = position + exponentSep.length();
- if (pos < text.length()) {
- String plusSign = symbols.getPlusSignString();
- String minusSign = symbols.getMinusSignString();
- if (text.regionMatches(pos, plusSign, 0, plusSign.length())) {
- pos += plusSign.length();
- } else if (text.regionMatches(pos, minusSign, 0, minusSign.length())) {
- pos += minusSign.length();
- negExp = true;
- }
- }
-
- DigitList exponentDigits = new DigitList();
- exponentDigits.count = 0;
- while (pos < text.length()) {
- int digitMatchLen = matchesDigit(text, pos, parsedDigit);
- if (digitMatchLen > 0) {
- exponentDigits.append((char) (parsedDigit[0] + '0'));
- pos += digitMatchLen;
- } else {
- break;
- }
- }
-
- if (exponentDigits.count > 0) {
- // defer strict parse until we know we have a bona-fide exponent
- if (strictParse && sawGrouping) {
- strictFail = true;
- break;
- }
-
- // Quick overflow check for exponential part. Actual limit check
- // will be done later in this code.
- if (exponentDigits.count > 10 /* maximum decimal digits for int */) {
- if (negExp) {
- // set underflow flag
- status[STATUS_UNDERFLOW] = true;
- } else {
- // set infinite flag
- status[STATUS_INFINITE] = true;
- }
- } else {
- exponentDigits.decimalAt = exponentDigits.count;
- exponent = exponentDigits.getLong();
- if (negExp) {
- exponent = -exponent;
- }
- }
- position = pos; // Advance past the exponent
- }
-
- break; // Whether we fail or succeed, we exit this loop
- }
-
- // All other cases, stop parsing
- break;
- }
-
- if (digits.decimalAt == 0 && isDecimalPatternMatchRequired()) {
- if (this.formatPattern.indexOf(decimal) != -1) {
- parsePosition.setIndex(oldStart);
- parsePosition.setErrorIndex(position);
- return false;
- }
- }
-
- if (backup != -1)
- position = backup;
-
- // If there was no decimal point we have an integer
- if (!sawDecimal) {
- digits.decimalAt = digitCount; // Not digits.count!
- }
-
- // check for strict parse errors
- if (strictParse && !sawDecimal) {
- if (lastGroup != -1 && groupedDigitCount != groupingSize) {
- strictFail = true;
- }
- }
- if (strictFail) {
- // only set with strictParse and a leading zero error leading zeros are an
- // error with strict parsing except immediately before nondigit (except
- // group separator followed by digit), or end of text.
-
- parsePosition.setIndex(oldStart);
- parsePosition.setErrorIndex(position);
- return false;
- }
-
- // Adjust for exponent, if any
- exponent += digits.decimalAt;
- if (exponent < -getParseMaxDigits()) {
- status[STATUS_UNDERFLOW] = true;
- } else if (exponent > getParseMaxDigits()) {
- status[STATUS_INFINITE] = true;
- } else {
- digits.decimalAt = (int) exponent;
- }
-
- // If none of the text string was recognized. For example, parse "x" with
- // pattern "#0.00" (return index and error index both 0) parse "$" with
- // pattern "$#0.00". (return index 0 and error index 1).
- if (!sawDigit && digitCount == 0) {
- parsePosition.setIndex(oldStart);
- parsePosition.setErrorIndex(oldStart);
- return false;
- }
- }
-
- // Match padding before suffix
- if (formatWidth > 0 && padPosition == PAD_BEFORE_SUFFIX) {
- position = skipPadding(text, position);
- }
-
- // Match positive and negative suffixes; prefer longest match.
- if (posMatch >= 0) {
- posMatch = compareAffix(text, position, false, false, posSuffix, parseComplexCurrency, type, currency);
- }
- if (negMatch >= 0) {
- negMatch = compareAffix(text, position, true, false, negSuffix, parseComplexCurrency, type, currency);
- }
- if (posMatch >= 0 && negMatch >= 0) {
- if (posMatch > negMatch) {
- negMatch = -1;
- } else if (negMatch > posMatch) {
- posMatch = -1;
- }
- }
-
- // Fail if neither or both
- if ((posMatch >= 0) == (negMatch >= 0)) {
- parsePosition.setErrorIndex(position);
- return false;
- }
-
- position += (posMatch >= 0 ? posMatch : negMatch);
-
- // Match padding after suffix
- if (formatWidth > 0 && padPosition == PAD_AFTER_SUFFIX) {
- position = skipPadding(text, position);
- }
-
- parsePosition.setIndex(position);
-
- status[STATUS_POSITIVE] = (posMatch >= 0);
-
- if (parsePosition.getIndex() == oldStart) {
- parsePosition.setErrorIndex(position);
- return false;
- }
- return true;
- }
-
- /**
- * Check if the substring at the specified position matches a decimal digit.
- * If matched, this method sets the decimal value to <code>decVal</code> and
- * returns matched length.
- *
- * @param str The input string
- * @param start The start index
- * @param decVal Receives decimal value
- * @return Length of match, or 0 if the sequence at the position is not
- * a decimal digit.
- */
- private int matchesDigit(String str, int start, int[] decVal) {
- String[] localeDigits = symbols.getDigitStringsLocal();
-
- // Check if the sequence at the current position matches locale digits.
- for (int i = 0; i < 10; i++) {
- int digitStrLen = localeDigits[i].length();
- if (str.regionMatches(start, localeDigits[i], 0, digitStrLen)) {
- decVal[0] = i;
- return digitStrLen;
- }
- }
-
- // If no locale digit match, then check if this is a Unicode digit
- int cp = str.codePointAt(start);
- decVal[0] = UCharacter.digit(cp, 10);
- if (decVal[0] >= 0) {
- return Character.charCount(cp);
- }
-
- return 0;
- }
-
- /**
- * Returns a set of characters equivalent to the given desimal separator used for
- * parsing number. This method may return an empty set.
- */
- private UnicodeSet getEquivalentDecimals(String decimal, boolean strictParse) {
- UnicodeSet equivSet = UnicodeSet.EMPTY;
- if (strictParse) {
- if (strictDotEquivalents.contains(decimal)) {
- equivSet = strictDotEquivalents;
- } else if (strictCommaEquivalents.contains(decimal)) {
- equivSet = strictCommaEquivalents;
- }
- } else {
- if (dotEquivalents.contains(decimal)) {
- equivSet = dotEquivalents;
- } else if (commaEquivalents.contains(decimal)) {
- equivSet = commaEquivalents;
- }
- }
- return equivSet;
- }
-
- /**
- * Starting at position, advance past a run of pad characters, if any. Return the
- * index of the first character after position that is not a pad character. Result is
- * >= position.
- */
- private final int skipPadding(String text, int position) {
- while (position < text.length() && text.charAt(position) == pad) {
- ++position;
- }
- return position;
- }
-
- /**
- * Returns the length matched by the given affix, or -1 if none. Runs of white space
- * in the affix, match runs of white space in the input. Pattern white space and input
- * white space are determined differently; see code.
- *
- * @param text input text
- * @param pos offset into input at which to begin matching
- * @param isNegative
- * @param isPrefix
- * @param affixPat affix pattern used for currency affix comparison
- * @param complexCurrencyParsing whether it is currency parsing or not
- * @param type compare against currency type, LONG_NAME only or not.
- * @param currency return value for parsed currency, for generic currency parsing
- * mode, or null for normal parsing. In generic currency parsing mode, any currency
- * is parsed, not just the currency that this formatter is set to.
- * @return length of input that matches, or -1 if match failure
- */
- private int compareAffix(String text, int pos, boolean isNegative, boolean isPrefix,
- String affixPat, boolean complexCurrencyParsing, int type, Currency[] currency) {
- if (currency != null || currencyChoice != null || (currencySignCount != CURRENCY_SIGN_COUNT_ZERO && complexCurrencyParsing)) {
- return compareComplexAffix(affixPat, text, pos, type, currency);
- }
- if (isPrefix) {
- return compareSimpleAffix(isNegative ? negativePrefix : positivePrefix, text, pos);
- } else {
- return compareSimpleAffix(isNegative ? negativeSuffix : positiveSuffix, text, pos);
- }
-
- }
-
- /**
- * Check for bidi marks: LRM, RLM, ALM
- */
- private static boolean isBidiMark(int c) {
- return (c==0x200E || c==0x200F || c==0x061C);
- }
-
- /**
- * Remove bidi marks from affix
- */
- private static String trimMarksFromAffix(String affix) {
- boolean hasBidiMark = false;
- int idx = 0;
- for (; idx < affix.length(); idx++) {
- if (isBidiMark(affix.charAt(idx))) {
- hasBidiMark = true;
- break;
- }
- }
- if (!hasBidiMark) {
- return affix;
- }
-
- StringBuilder buf = new StringBuilder();
- buf.append(affix, 0, idx);
- idx++; // skip the first Bidi mark
- for (; idx < affix.length(); idx++) {
- char c = affix.charAt(idx);
- if (!isBidiMark(c)) {
- buf.append(c);
- }
- }
-
- return buf.toString();
- }
-
- /**
- * Return the length matched by the given affix, or -1 if none. Runs of white space in
- * the affix, match runs of white space in the input. Pattern white space and input
- * white space are determined differently; see code.
- *
- * @param affix pattern string, taken as a literal
- * @param input input text
- * @param pos offset into input at which to begin matching
- * @return length of input that matches, or -1 if match failure
- */
- private static int compareSimpleAffix(String affix, String input, int pos) {
- int start = pos;
- // Affixes here might consist of sign, currency symbol and related spacing, etc.
- // For more efficiency we should keep lazily-created trimmed affixes around in
- // instance variables instead of trimming each time they are used (the next step).
- String trimmedAffix = (affix.length() > 1)? trimMarksFromAffix(affix): affix;
- for (int i = 0; i < trimmedAffix.length();) {
- int c = UTF16.charAt(trimmedAffix, i);
- int len = UTF16.getCharCount(c);
- if (PatternProps.isWhiteSpace(c)) {
- // We may have a pattern like: \u200F and input text like: \u200F Note
- // that U+200F and U+0020 are Pattern_White_Space but only U+0020 is
- // UWhiteSpace. So we have to first do a direct match of the run of RULE
- // whitespace in the pattern, then match any extra characters.
- boolean literalMatch = false;
- while (pos < input.length()) {
- int ic = UTF16.charAt(input, pos);
- if (ic == c) {
- literalMatch = true;
- i += len;
- pos += len;
- if (i == trimmedAffix.length()) {
- break;
- }
- c = UTF16.charAt(trimmedAffix, i);
- len = UTF16.getCharCount(c);
- if (!PatternProps.isWhiteSpace(c)) {
- break;
- }
- } else if (isBidiMark(ic)) {
- pos++; // just skip over this input text
- } else {
- break;
- }
- }
-
- // Advance over run in trimmedAffix
- i = skipPatternWhiteSpace(trimmedAffix, i);
-
- // Advance over run in input text. Must see at least one white space char
- // in input, unless we've already matched some characters literally.
- int s = pos;
- pos = skipUWhiteSpace(input, pos);
- if (pos == s && !literalMatch) {
- return -1;
- }
- // If we skip UWhiteSpace in the input text, we need to skip it in the
- // pattern. Otherwise, the previous lines may have skipped over text
- // (such as U+00A0) that is also in the trimmedAffix.
- i = skipUWhiteSpace(trimmedAffix, i);
- } else {
- boolean match = false;
- while (pos < input.length()) {
- int ic = UTF16.charAt(input, pos);
- if (!match && equalWithSignCompatibility(ic, c)) {
- i += len;
- pos += len;
- match = true;
- } else if (isBidiMark(ic)) {
- pos++; // just skip over this input text
- } else {
- break;
- }
- }
- if (!match) {
- return -1;
- }
- }
- }
- return pos - start;
- }
-
- private static boolean equalWithSignCompatibility(int lhs, int rhs) {
- return lhs == rhs
- || (minusSigns.contains(lhs) && minusSigns.contains(rhs))
- || (plusSigns.contains(lhs) && plusSigns.contains(rhs));
- }
-
- /**
- * Skips over a run of zero or more Pattern_White_Space characters at pos in text.
- */
- private static int skipPatternWhiteSpace(String text, int pos) {
- while (pos < text.length()) {
- int c = UTF16.charAt(text, pos);
- if (!PatternProps.isWhiteSpace(c)) {
- break;
- }
- pos += UTF16.getCharCount(c);
- }
- return pos;
- }
-
- /**
- * Skips over a run of zero or more isUWhiteSpace() characters at pos in text.
- */
- private static int skipUWhiteSpace(String text, int pos) {
- while (pos < text.length()) {
- int c = UTF16.charAt(text, pos);
- if (!UCharacter.isUWhiteSpace(c)) {
- break;
- }
- pos += UTF16.getCharCount(c);
- }
- return pos;
- }
-
- /**
- * Skips over a run of zero or more bidi marks at pos in text.
- */
- private static int skipBidiMarks(String text, int pos) {
- while (pos < text.length()) {
- int c = UTF16.charAt(text, pos);
- if (!isBidiMark(c)) {
- break;
- }
- pos += UTF16.getCharCount(c);
- }
- return pos;
- }
-
- /**
- * Returns the length matched by the given affix, or -1 if none.
- *
- * @param affixPat pattern string
- * @param text input text
- * @param pos offset into input at which to begin matching
- * @param type parse against currency type, LONG_NAME only or not.
- * @param currency return value for parsed currency, for generic
- * currency parsing mode, or null for normal parsing. In generic
- * currency parsing mode, any currency is parsed, not just the
- * currency that this formatter is set to.
- * @return position after the matched text, or -1 if match failure
- */
- private int compareComplexAffix(String affixPat, String text, int pos, int type,
- Currency[] currency) {
- int start = pos;
- for (int i = 0; i < affixPat.length() && pos >= 0;) {
- char c = affixPat.charAt(i++);
- if (c == QUOTE) {
- for (;;) {
- int j = affixPat.indexOf(QUOTE, i);
- if (j == i) {
- pos = match(text, pos, QUOTE);
- i = j + 1;
- break;
- } else if (j > i) {
- pos = match(text, pos, affixPat.substring(i, j));
- i = j + 1;
- if (i < affixPat.length() && affixPat.charAt(i) == QUOTE) {
- pos = match(text, pos, QUOTE);
- ++i;
- // loop again
- } else {
- break;
- }
- } else {
- // Unterminated quote; should be caught by apply
- // pattern.
- throw new RuntimeException();
- }
- }
- continue;
- }
-
- String affix = null;
-
- switch (c) {
- case CURRENCY_SIGN:
- // since the currency names in choice format is saved the same way as
- // other currency names, do not need to do currency choice parsing here.
- // the general currency parsing parse against all names, including names
- // in choice format. assert(currency != null || (getCurrency() != null &&
- // currencyChoice != null));
- boolean intl = i < affixPat.length() && affixPat.charAt(i) == CURRENCY_SIGN;
- if (intl) {
- ++i;
- }
- boolean plural = i < affixPat.length() && affixPat.charAt(i) == CURRENCY_SIGN;
- if (plural) {
- ++i;
- intl = false;
- }
- // Parse generic currency -- anything for which we have a display name, or
- // any 3-letter ISO code. Try to parse display name for our locale; first
- // determine our locale. TODO: use locale in CurrencyPluralInfo
- ULocale uloc = getLocale(ULocale.VALID_LOCALE);
- if (uloc == null) {
- // applyPattern has been called; use the symbols
- uloc = symbols.getLocale(ULocale.VALID_LOCALE);
- }
- // Delegate parse of display name => ISO code to Currency
- ParsePosition ppos = new ParsePosition(pos);
- // using Currency.parse to handle mixed style parsing.
- String iso = Currency.parse(uloc, text, type, ppos);
-
- // If parse succeeds, populate currency[0]
- if (iso != null) {
- if (currency != null) {
- currency[0] = Currency.getInstance(iso);
- } else {
- // The formatter is currency-style but the client has not requested
- // the value of the parsed currency. In this case, if that value does
- // not match the formatter's current value, then the parse fails.
- Currency effectiveCurr = getEffectiveCurrency();
- if (iso.compareTo(effectiveCurr.getCurrencyCode()) != 0) {
- pos = -1;
- continue;
- }
- }
- pos = ppos.getIndex();
- } else {
- pos = -1;
- }
- continue;
- case PATTERN_PERCENT:
- affix = symbols.getPercentString();
- break;
- case PATTERN_PER_MILLE:
- affix = symbols.getPerMillString();
- break;
- case PATTERN_PLUS_SIGN:
- affix = symbols.getPlusSignString();
- break;
- case PATTERN_MINUS_SIGN:
- affix = symbols.getMinusSignString();
- break;
- default:
- // fall through to affix != null test, which will fail
- break;
- }
-
- if (affix != null) {
- pos = match(text, pos, affix);
- continue;
- }
-
- pos = match(text, pos, c);
- if (PatternProps.isWhiteSpace(c)) {
- i = skipPatternWhiteSpace(affixPat, i);
- }
- }
-
- return pos - start;
- }
-
- /**
- * Matches a single character at text[pos] and return the index of the next character
- * upon success. Return -1 on failure. If ch is a Pattern_White_Space then match a run of
- * white space in text.
- */
- static final int match(String text, int pos, int ch) {
- if (pos < 0 || pos >= text.length()) {
- return -1;
- }
- pos = skipBidiMarks(text, pos);
- if (PatternProps.isWhiteSpace(ch)) {
- // Advance over run of white space in input text
- // Must see at least one white space char in input
- int s = pos;
- pos = skipPatternWhiteSpace(text, pos);
- if (pos == s) {
- return -1;
- }
- return pos;
- }
- if (pos >= text.length() || UTF16.charAt(text, pos) != ch) {
- return -1;
- }
- pos = skipBidiMarks(text, pos + UTF16.getCharCount(ch));
- return pos;
- }
-
- /**
- * Matches a string at text[pos] and return the index of the next character upon
- * success. Return -1 on failure. Match a run of white space in str with a run of
- * white space in text.
- */
- static final int match(String text, int pos, String str) {
- for (int i = 0; i < str.length() && pos >= 0;) {
- int ch = UTF16.charAt(str, i);
- i += UTF16.getCharCount(ch);
- if (isBidiMark(ch)) {
- continue;
- }
- pos = match(text, pos, ch);
- if (PatternProps.isWhiteSpace(ch)) {
- i = skipPatternWhiteSpace(str, i);
- }
- }
- return pos;
- }
-
- /**
- * Returns a copy of the decimal format symbols used by this format.
- *
- * @return desired DecimalFormatSymbols
- * @see DecimalFormatSymbols
- * @stable ICU 2.0
- */
- public DecimalFormatSymbols getDecimalFormatSymbols() {
- try {
- // don't allow multiple references
- return (DecimalFormatSymbols) symbols.clone();
- } catch (Exception foo) {
- return null; // should never happen
- }
- }
-
- /**
- * Sets the decimal format symbols used by this format. The format uses a copy of the
- * provided symbols.
- *
- * @param newSymbols desired DecimalFormatSymbols
- * @see DecimalFormatSymbols
- * @stable ICU 2.0
- */
- public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
- symbols = (DecimalFormatSymbols) newSymbols.clone();
- setCurrencyForSymbols();
- expandAffixes(null);
- }
-
- /**
- * Update the currency object to match the symbols. This method is used only when the
- * caller has passed in a symbols object that may not be the default object for its
- * locale.
- */
- private void setCurrencyForSymbols() {
-
- // Bug 4212072 Update the affix strings according to symbols in order to keep the
- // affix strings up to date. [Richard/GCL]
-
- // With the introduction of the Currency object, the currency symbols in the DFS
- // object are ignored. For backward compatibility, we check any explicitly set DFS
- // object. If it is a default symbols object for its locale, we change the
- // currency object to one for that locale. If it is custom, we set the currency to
- // null.
- DecimalFormatSymbols def = new DecimalFormatSymbols(symbols.getULocale());
-
- if (symbols.getCurrencySymbol().equals(def.getCurrencySymbol())
- && symbols.getInternationalCurrencySymbol()
- .equals(def.getInternationalCurrencySymbol())) {
- setCurrency(Currency.getInstance(symbols.getULocale()));
- } else {
- setCurrency(null);
- }
- }
-
- /**
- * Returns the positive prefix.
- *
- * <p>Examples: +123, $123, sFr123
- * @return the prefix
- * @stable ICU 2.0
- */
- public String getPositivePrefix() {
- return positivePrefix;
- }
-
- /**
- * Sets the positive prefix.
- *
- * <p>Examples: +123, $123, sFr123
- * @param newValue the prefix
- * @stable ICU 2.0
- */
- public void setPositivePrefix(String newValue) {
- positivePrefix = newValue;
- posPrefixPattern = null;
- }
-
- /**
- * Returns the negative prefix.
- *
- * <p>Examples: -123, ($123) (with negative suffix), sFr-123
- *
- * @return the prefix
- * @stable ICU 2.0
- */
- public String getNegativePrefix() {
- return negativePrefix;
- }
-
- /**
- * Sets the negative prefix.
- *
- * <p>Examples: -123, ($123) (with negative suffix), sFr-123
- * @param newValue the prefix
- * @stable ICU 2.0
- */
- public void setNegativePrefix(String newValue) {
- negativePrefix = newValue;
- negPrefixPattern = null;
- }
-
- /**
- * Returns the positive suffix.
- *
- * <p>Example: 123%
- *
- * @return the suffix
- * @stable ICU 2.0
- */
- public String getPositiveSuffix() {
- return positiveSuffix;
- }
-
- /**
- * Sets the positive suffix.
- *
- * <p>Example: 123%
- * @param newValue the suffix
- * @stable ICU 2.0
- */
- public void setPositiveSuffix(String newValue) {
- positiveSuffix = newValue;
- posSuffixPattern = null;
- }
-
- /**
- * Returns the negative suffix.
- *
- * <p>Examples: -123%, ($123) (with positive suffixes)
- *
- * @return the suffix
- * @stable ICU 2.0
- */
- public String getNegativeSuffix() {
- return negativeSuffix;
- }
-
- /**
- * Sets the positive suffix.
- *
- * <p>Examples: 123%
- * @param newValue the suffix
- * @stable ICU 2.0
- */
- public void setNegativeSuffix(String newValue) {
- negativeSuffix = newValue;
- negSuffixPattern = null;
- }
-
- /**
- * Returns the multiplier for use in percent, permill, etc. For a percentage, set the
- * suffixes to have "%" and the multiplier to be 100. (For Arabic, use arabic percent
- * symbol). For a permill, set the suffixes to have "\u2031" and the multiplier to be
- * 1000.
- *
- * <p>Examples: with 100, 1.23 -&gt; "123", and "123" -&gt; 1.23
- *
- * @return the multiplier
- * @stable ICU 2.0
- */
- public int getMultiplier() {
- return multiplier;
- }
-
- /**
- * Sets the multiplier for use in percent, permill, etc. For a percentage, set the
- * suffixes to have "%" and the multiplier to be 100. (For Arabic, use arabic percent
- * symbol). For a permill, set the suffixes to have "\u2031" and the multiplier to be
- * 1000.
- *
- * <p>Examples: with 100, 1.23 -&gt; "123", and "123" -&gt; 1.23
- *
- * @param newValue the multiplier
- * @stable ICU 2.0
- */
- public void setMultiplier(int newValue) {
- if (newValue == 0) {
- throw new IllegalArgumentException("Bad multiplier: " + newValue);
- }
- multiplier = newValue;
- }
-
- /**
- * {@icu} Returns the rounding increment.
- *
- * @return A positive rounding increment, or <code>null</code> if a custom rounding
- * increment is not in effect.
- * @see #setRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 2.0
- */
- public java.math.BigDecimal getRoundingIncrement() {
- if (roundingIncrementICU == null)
- return null;
- return roundingIncrementICU.toBigDecimal();
- }
-
- /**
- * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers
- * will be rounded to the number of digits displayed.
- *
- * @param newValue A positive rounding increment, or <code>null</code> or
- * <code>BigDecimal(0.0)</code> to use the default rounding increment.
- * @throws IllegalArgumentException if <code>newValue</code> is &lt; 0.0
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 2.0
- */
- public void setRoundingIncrement(java.math.BigDecimal newValue) {
- if (newValue == null) {
- setRoundingIncrement((BigDecimal) null);
- } else {
- setRoundingIncrement(new BigDecimal(newValue));
- }
- }
-
- /**
- * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers
- * will be rounded to the number of digits displayed.
- *
- * @param newValue A positive rounding increment, or <code>null</code> or
- * <code>BigDecimal(0.0)</code> to use the default rounding increment.
- * @throws IllegalArgumentException if <code>newValue</code> is &lt; 0.0
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 3.6
- */
- public void setRoundingIncrement(BigDecimal newValue) {
- int i = newValue == null ? 0 : newValue.compareTo(BigDecimal.ZERO);
- if (i < 0) {
- throw new IllegalArgumentException("Illegal rounding increment");
- }
- if (i == 0) {
- setInternalRoundingIncrement(null);
- } else {
- setInternalRoundingIncrement(newValue);
- }
- resetActualRounding();
- }
-
- /**
- * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers
- * will be rounded to the number of digits displayed.
- *
- * @param newValue A positive rounding increment, or 0.0 to use the default
- * rounding increment.
- * @throws IllegalArgumentException if <code>newValue</code> is &lt; 0.0
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see #setRoundingMode
- * @stable ICU 2.0
- */
- public void setRoundingIncrement(double newValue) {
- if (newValue < 0.0) {
- throw new IllegalArgumentException("Illegal rounding increment");
- }
- if (newValue == 0.0d) {
- setInternalRoundingIncrement((BigDecimal) null);
- } else {
- // Should use BigDecimal#valueOf(double) instead of constructor
- // to avoid the double precision problem.
- setInternalRoundingIncrement(BigDecimal.valueOf(newValue));
- }
- resetActualRounding();
- }
-
- /**
- * Returns the rounding mode.
- *
- * @return A rounding mode, between <code>BigDecimal.ROUND_UP</code> and
- * <code>BigDecimal.ROUND_UNNECESSARY</code>.
- * @see #setRoundingIncrement
- * @see #getRoundingIncrement
- * @see #setRoundingMode
- * @see java.math.BigDecimal
- * @stable ICU 2.0
- */
- @Override
- public int getRoundingMode() {
- return roundingMode;
- }
-
- /**
- * Sets the rounding mode. This has no effect unless the rounding increment is greater
- * than zero.
- *
- * @param roundingMode A rounding mode, between <code>BigDecimal.ROUND_UP</code> and
- * <code>BigDecimal.ROUND_UNNECESSARY</code>.
- * @exception IllegalArgumentException if <code>roundingMode</code> is unrecognized.
- * @see #setRoundingIncrement
- * @see #getRoundingIncrement
- * @see #getRoundingMode
- * @see java.math.BigDecimal
- * @stable ICU 2.0
- */
- @Override
- public void setRoundingMode(int roundingMode) {
- if (roundingMode < BigDecimal.ROUND_UP || roundingMode > BigDecimal.ROUND_UNNECESSARY) {
- throw new IllegalArgumentException("Invalid rounding mode: " + roundingMode);
- }
-
- this.roundingMode = roundingMode;
- resetActualRounding();
- }
-
- /**
- * Returns the width to which the output of <code>format()</code> is padded. The width is
- * counted in 16-bit code units.
- *
- * @return the format width, or zero if no padding is in effect
- * @see #setFormatWidth
- * @see #getPadCharacter
- * @see #setPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public int getFormatWidth() {
- return formatWidth;
- }
-
- /**
- * Sets the width to which the output of <code>format()</code> is
- * padded. The width is counted in 16-bit code units. This method
- * also controls whether padding is enabled.
- *
- * @param width the width to which to pad the result of
- * <code>format()</code>, or zero to disable padding
- * @exception IllegalArgumentException if <code>width</code> is &lt; 0
- * @see #getFormatWidth
- * @see #getPadCharacter
- * @see #setPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public void setFormatWidth(int width) {
- if (width < 0) {
- throw new IllegalArgumentException("Illegal format width");
- }
- formatWidth = width;
- }
-
- /**
- * {@icu} Returns the character used to pad to the format width. The default is ' '.
- *
- * @return the pad character
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #setPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public char getPadCharacter() {
- return pad;
- }
-
- /**
- * {@icu} Sets the character used to pad to the format width. If padding is not
- * enabled, then this will take effect if padding is later enabled.
- *
- * @param padChar the pad character
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #getPadCharacter
- * @see #getPadPosition
- * @see #setPadPosition
- * @stable ICU 2.0
- */
- public void setPadCharacter(char padChar) {
- pad = padChar;
- }
-
- /**
- * {@icu} Returns the position at which padding will take place. This is the location at
- * which padding will be inserted if the result of <code>format()</code> is shorter
- * than the format width.
- *
- * @return the pad position, one of <code>PAD_BEFORE_PREFIX</code>,
- * <code>PAD_AFTER_PREFIX</code>, <code>PAD_BEFORE_SUFFIX</code>, or
- * <code>PAD_AFTER_SUFFIX</code>.
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #setPadCharacter
- * @see #getPadCharacter
- * @see #setPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public int getPadPosition() {
- return padPosition;
- }
-
- /**
- * {@icu} Sets the position at which padding will take place. This is the location at
- * which padding will be inserted if the result of <code>format()</code> is shorter
- * than the format width. This has no effect unless padding is enabled.
- *
- * @param padPos the pad position, one of <code>PAD_BEFORE_PREFIX</code>,
- * <code>PAD_AFTER_PREFIX</code>, <code>PAD_BEFORE_SUFFIX</code>, or
- * <code>PAD_AFTER_SUFFIX</code>.
- * @exception IllegalArgumentException if the pad position in unrecognized
- * @see #setFormatWidth
- * @see #getFormatWidth
- * @see #setPadCharacter
- * @see #getPadCharacter
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public void setPadPosition(int padPos) {
- if (padPos < PAD_BEFORE_PREFIX || padPos > PAD_AFTER_SUFFIX) {
- throw new IllegalArgumentException("Illegal pad position");
- }
- padPosition = padPos;
- }
-
- /**
- * {@icu} Returns whether or not scientific notation is used.
- *
- * @return true if this object formats and parses scientific notation
- * @see #setScientificNotation
- * @see #getMinimumExponentDigits
- * @see #setMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public boolean isScientificNotation() {
- return useExponentialNotation;
- }
-
- /**
- * {@icu} Sets whether or not scientific notation is used. When scientific notation is
- * used, the effective maximum number of integer digits is &lt;= 8. If the maximum number
- * of integer digits is set to more than 8, the effective maximum will be 1. This
- * allows this call to generate a 'default' scientific number format without
- * additional changes.
- *
- * @param useScientific true if this object formats and parses scientific notation
- * @see #isScientificNotation
- * @see #getMinimumExponentDigits
- * @see #setMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public void setScientificNotation(boolean useScientific) {
- useExponentialNotation = useScientific;
- }
-
- /**
- * {@icu} Returns the minimum exponent digits that will be shown.
- *
- * @return the minimum exponent digits that will be shown
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #setMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public byte getMinimumExponentDigits() {
- return minExponentDigits;
- }
-
- /**
- * {@icu} Sets the minimum exponent digits that will be shown. This has no effect
- * unless scientific notation is in use.
- *
- * @param minExpDig a value &gt;= 1 indicating the fewest exponent
- * digits that will be shown
- * @exception IllegalArgumentException if <code>minExpDig</code> &lt; 1
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #getMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public void setMinimumExponentDigits(byte minExpDig) {
- if (minExpDig < 1) {
- throw new IllegalArgumentException("Exponent digits must be >= 1");
- }
- minExponentDigits = minExpDig;
- }
-
- /**
- * {@icu} Returns whether the exponent sign is always shown.
- *
- * @return true if the exponent is always prefixed with either the localized minus
- * sign or the localized plus sign, false if only negative exponents are prefixed with
- * the localized minus sign.
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #setMinimumExponentDigits
- * @see #getMinimumExponentDigits
- * @see #setExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public boolean isExponentSignAlwaysShown() {
- return exponentSignAlwaysShown;
- }
-
- /**
- * {@icu} Sets whether the exponent sign is always shown. This has no effect unless
- * scientific notation is in use.
- *
- * @param expSignAlways true if the exponent is always prefixed with either the
- * localized minus sign or the localized plus sign, false if only negative exponents
- * are prefixed with the localized minus sign.
- * @see #setScientificNotation
- * @see #isScientificNotation
- * @see #setMinimumExponentDigits
- * @see #getMinimumExponentDigits
- * @see #isExponentSignAlwaysShown
- * @stable ICU 2.0
- */
- public void setExponentSignAlwaysShown(boolean expSignAlways) {
- exponentSignAlwaysShown = expSignAlways;
- }
-
- /**
- * Returns the grouping size. Grouping size is the number of digits between grouping
- * separators in the integer portion of a number. For example, in the number
- * "123,456.78", the grouping size is 3.
- *
- * @see #setGroupingSize
- * @see NumberFormat#isGroupingUsed
- * @see DecimalFormatSymbols#getGroupingSeparator
- * @stable ICU 2.0
- */
- public int getGroupingSize() {
- return groupingSize;
- }
-
- /**
- * Sets the grouping size. Grouping size is the number of digits between grouping
- * separators in the integer portion of a number. For example, in the number
- * "123,456.78", the grouping size is 3.
- *
- * @see #getGroupingSize
- * @see NumberFormat#setGroupingUsed
- * @see DecimalFormatSymbols#setGroupingSeparator
- * @stable ICU 2.0
- */
- public void setGroupingSize(int newValue) {
- groupingSize = (byte) newValue;
- }
-
- /**
- * {@icu} Returns the secondary grouping size. In some locales one grouping interval
- * is used for the least significant integer digits (the primary grouping size), and
- * another is used for all others (the secondary grouping size). A formatter
- * supporting a secondary grouping size will return a positive integer unequal to the
- * primary grouping size returned by <code>getGroupingSize()</code>. For example, if
- * the primary grouping size is 4, and the secondary grouping size is 2, then the
- * number 123456789 formats as "1,23,45,6789", and the pattern appears as "#,##,###0".
- *
- * @return the secondary grouping size, or a value less than one if there is none
- * @see #setSecondaryGroupingSize
- * @see NumberFormat#isGroupingUsed
- * @see DecimalFormatSymbols#getGroupingSeparator
- * @stable ICU 2.0
- */
- public int getSecondaryGroupingSize() {
- return groupingSize2;
- }
-
- /**
- * {@icu} Sets the secondary grouping size. If set to a value less than 1, then
- * secondary grouping is turned off, and the primary grouping size is used for all
- * intervals, not just the least significant.
- *
- * @see #getSecondaryGroupingSize
- * @see NumberFormat#setGroupingUsed
- * @see DecimalFormatSymbols#setGroupingSeparator
- * @stable ICU 2.0
- */
- public void setSecondaryGroupingSize(int newValue) {
- groupingSize2 = (byte) newValue;
- }
-
- /**
- * {@icu} Returns the MathContext used by this format.
- *
- * @return desired MathContext
- * @see #getMathContext
- * @stable ICU 4.2
- */
- public MathContext getMathContextICU() {
- return mathContext;
- }
-
- /**
- * {@icu} Returns the MathContext used by this format.
- *
- * @return desired MathContext
- * @see #getMathContext
- * @stable ICU 4.2
- */
- public java.math.MathContext getMathContext() {
- try {
- // don't allow multiple references
- return mathContext == null ? null : new java.math.MathContext(mathContext.getDigits(),
- java.math.RoundingMode.valueOf(mathContext.getRoundingMode()));
- } catch (Exception foo) {
- return null; // should never happen
- }
- }
-
- /**
- * {@icu} Sets the MathContext used by this format.
- *
- * @param newValue desired MathContext
- * @see #getMathContext
- * @stable ICU 4.2
- */
- public void setMathContextICU(MathContext newValue) {
- mathContext = newValue;
- }
-
- /**
- * {@icu} Sets the MathContext used by this format.
- *
- * @param newValue desired MathContext
- * @see #getMathContext
- * @stable ICU 4.2
- */
- public void setMathContext(java.math.MathContext newValue) {
- mathContext = new MathContext(newValue.getPrecision(), MathContext.SCIENTIFIC, false,
- (newValue.getRoundingMode()).ordinal());
- }
-
- /**
- * Returns the behavior of the decimal separator with integers. (The decimal
- * separator will always appear with decimals.) <p> Example: Decimal ON: 12345 -&gt;
- * 12345.; OFF: 12345 -&gt; 12345
- *
- * @stable ICU 2.0
- */
- public boolean isDecimalSeparatorAlwaysShown() {
- return decimalSeparatorAlwaysShown;
- }
-
- /**
- * When decimal match is not required, the input does not have to
- * contain a decimal mark when there is a decimal mark specified in the
- * pattern.
- * @param value true if input must contain a match to decimal mark in pattern
- * Default is false.
- * @stable ICU 54
- */
- public void setDecimalPatternMatchRequired(boolean value) {
- parseRequireDecimalPoint = value;
- }
-
- /**
- * {@icu} Returns whether the input to parsing must contain a decimal mark if there
- * is a decimal mark in the pattern.
- * @return true if input must contain a match to decimal mark in pattern
- * @stable ICU 54
- */
- public boolean isDecimalPatternMatchRequired() {
- return parseRequireDecimalPoint;
- }
-
-
- /**
- * Sets the behavior of the decimal separator with integers. (The decimal separator
- * will always appear with decimals.)
- *
- * <p>This only affects formatting, and only where there might be no digits after the
- * decimal point, e.g., if true, 3456.00 -&gt; "3,456." if false, 3456.00 -&gt; "3456" This
- * is independent of parsing. If you want parsing to stop at the decimal point, use
- * setParseIntegerOnly.
- *
- * <p>
- * Example: Decimal ON: 12345 -&gt; 12345.; OFF: 12345 -&gt; 12345
- *
- * @stable ICU 2.0
- */
- public void setDecimalSeparatorAlwaysShown(boolean newValue) {
- decimalSeparatorAlwaysShown = newValue;
- }
-
- /**
- * {@icu} Returns a copy of the CurrencyPluralInfo used by this format. It might
- * return null if the decimal format is not a plural type currency decimal
- * format. Plural type currency decimal format means either the pattern in the decimal
- * format contains 3 currency signs, or the decimal format is initialized with
- * PLURALCURRENCYSTYLE.
- *
- * @return desired CurrencyPluralInfo
- * @see CurrencyPluralInfo
- * @stable ICU 4.2
- */
- public CurrencyPluralInfo getCurrencyPluralInfo() {
- try {
- // don't allow multiple references
- return currencyPluralInfo == null ? null :
- (CurrencyPluralInfo) currencyPluralInfo.clone();
- } catch (Exception foo) {
- return null; // should never happen
- }
- }
-
- /**
- * {@icu} Sets the CurrencyPluralInfo used by this format. The format uses a copy of
- * the provided information.
- *
- * @param newInfo desired CurrencyPluralInfo
- * @see CurrencyPluralInfo
- * @stable ICU 4.2
- */
- public void setCurrencyPluralInfo(CurrencyPluralInfo newInfo) {
- currencyPluralInfo = (CurrencyPluralInfo) newInfo.clone();
- isReadyForParsing = false;
- }
-
- /**
- * Overrides clone.
- * @stable ICU 2.0
- */
- @Override
- public Object clone() {
- try {
- DecimalFormat_ICU58 other = (DecimalFormat_ICU58) super.clone();
- other.symbols = (DecimalFormatSymbols) symbols.clone();
- other.digitList = new DigitList(); // fix for JB#5358
- if (currencyPluralInfo != null) {
- other.currencyPluralInfo = (CurrencyPluralInfo) currencyPluralInfo.clone();
- }
- other.attributes = new ArrayList<FieldPosition>(); // #9240
- other.currencyUsage = currencyUsage;
-
- // TODO: We need to figure out whether we share a single copy of DigitList by
- // multiple cloned copies. format/subformat are designed to use a single
- // instance, but parse/subparse implementation is not.
- return other;
- } catch (Exception e) {
- throw new IllegalStateException();
- }
- }
-
- /**
- * Overrides equals.
- * @stable ICU 2.0
- */
- @Override
- public boolean equals(Object obj) {
- if (obj == null)
- return false;
- if (!super.equals(obj))
- return false; // super does class check
-
- DecimalFormat_ICU58 other = (DecimalFormat_ICU58) obj;
- // Add the comparison of the four new added fields ,they are posPrefixPattern,
- // posSuffixPattern, negPrefixPattern, negSuffixPattern. [Richard/GCL]
- // following are added to accomodate changes for currency plural format.
- return currencySignCount == other.currencySignCount
- && (style != NumberFormat.PLURALCURRENCYSTYLE ||
- equals(posPrefixPattern, other.posPrefixPattern)
- && equals(posSuffixPattern, other.posSuffixPattern)
- && equals(negPrefixPattern, other.negPrefixPattern)
- && equals(negSuffixPattern, other.negSuffixPattern))
- && multiplier == other.multiplier
- && groupingSize == other.groupingSize
- && groupingSize2 == other.groupingSize2
- && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown
- && useExponentialNotation == other.useExponentialNotation
- && (!useExponentialNotation || minExponentDigits == other.minExponentDigits)
- && useSignificantDigits == other.useSignificantDigits
- && (!useSignificantDigits || minSignificantDigits == other.minSignificantDigits
- && maxSignificantDigits == other.maxSignificantDigits)
- && symbols.equals(other.symbols)
- && Utility.objectEquals(currencyPluralInfo, other.currencyPluralInfo)
- && currencyUsage.equals(other.currencyUsage);
- }
-
- // method to unquote the strings and compare
- private boolean equals(String pat1, String pat2) {
- if (pat1 == null || pat2 == null) {
- return (pat1 == null && pat2 == null);
- }
- // fast path
- if (pat1.equals(pat2)) {
- return true;
- }
- return unquote(pat1).equals(unquote(pat2));
- }
-
- private String unquote(String pat) {
- StringBuilder buf = new StringBuilder(pat.length());
- int i = 0;
- while (i < pat.length()) {
- char ch = pat.charAt(i++);
- if (ch != QUOTE) {
- buf.append(ch);
- }
- }
- return buf.toString();
- }
-
- // protected void handleToString(StringBuffer buf) {
- // buf.append("\nposPrefixPattern: '" + posPrefixPattern + "'\n");
- // buf.append("positivePrefix: '" + positivePrefix + "'\n");
- // buf.append("posSuffixPattern: '" + posSuffixPattern + "'\n");
- // buf.append("positiveSuffix: '" + positiveSuffix + "'\n");
- // buf.append("negPrefixPattern: '" +
- // com.ibm.icu.impl.Utility.format1ForSource(negPrefixPattern) + "'\n");
- // buf.append("negativePrefix: '" +
- // com.ibm.icu.impl.Utility.format1ForSource(negativePrefix) + "'\n");
- // buf.append("negSuffixPattern: '" + negSuffixPattern + "'\n");
- // buf.append("negativeSuffix: '" + negativeSuffix + "'\n");
- // buf.append("multiplier: '" + multiplier + "'\n");
- // buf.append("groupingSize: '" + groupingSize + "'\n");
- // buf.append("groupingSize2: '" + groupingSize2 + "'\n");
- // buf.append("decimalSeparatorAlwaysShown: '" + decimalSeparatorAlwaysShown + "'\n");
- // buf.append("useExponentialNotation: '" + useExponentialNotation + "'\n");
- // buf.append("minExponentDigits: '" + minExponentDigits + "'\n");
- // buf.append("useSignificantDigits: '" + useSignificantDigits + "'\n");
- // buf.append("minSignificantDigits: '" + minSignificantDigits + "'\n");
- // buf.append("maxSignificantDigits: '" + maxSignificantDigits + "'\n");
- // buf.append("symbols: '" + symbols + "'");
- // }
-
- /**
- * Overrides hashCode.
- * @stable ICU 2.0
- */
- @Override
- public int hashCode() {
- return super.hashCode() * 37 + positivePrefix.hashCode();
- // just enough fields for a reasonable distribution
- }
-
- /**
- * Synthesizes a pattern string that represents the current state of this Format
- * object.
- *
- * @see #applyPattern
- * @stable ICU 2.0
- */
- public String toPattern() {
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- // the prefix or suffix pattern might not be defined yet, so they can not be
- // synthesized, instead, get them directly. but it might not be the actual
- // pattern used in formatting. the actual pattern used in formatting depends
- // on the formatted number's plural count.
- return formatPattern;
- }
- return toPattern(false);
- }
-
- /**
- * Synthesizes a localized pattern string that represents the current state of this
- * Format object.
- *
- * @see #applyPattern
- * @stable ICU 2.0
- */
- public String toLocalizedPattern() {
- if (style == NumberFormat.PLURALCURRENCYSTYLE) {
- return formatPattern;
- }
- return toPattern(true);
- }
-
- /**
- * Expands the affix pattern strings into the expanded affix strings. If any affix
- * pattern string is null, do not expand it. This method should be called any time the
- * symbols or the affix patterns change in order to keep the expanded affix strings up
- * to date. This method also will be called before formatting if format currency
- * plural names, since the plural name is not a static one, it is based on the
- * currency plural count, the affix will be known only after the currency plural count
- * is know. In which case, the parameter 'pluralCount' will be a non-null currency
- * plural count. In all other cases, the 'pluralCount' is null, which means it is not
- * needed.
- */
- // Bug 4212072 [Richard/GCL]
- private void expandAffixes(String pluralCount) {
- // expandAffix() will set currencyChoice to a non-null value if
- // appropriate AND if it is null.
- currencyChoice = null;
-
- // Reuse one StringBuffer for better performance
- StringBuffer buffer = new StringBuffer();
- if (posPrefixPattern != null) {
- expandAffix(posPrefixPattern, pluralCount, buffer);
- positivePrefix = buffer.toString();
- }
- if (posSuffixPattern != null) {
- expandAffix(posSuffixPattern, pluralCount, buffer);
- positiveSuffix = buffer.toString();
- }
- if (negPrefixPattern != null) {
- expandAffix(negPrefixPattern, pluralCount, buffer);
- negativePrefix = buffer.toString();
- }
- if (negSuffixPattern != null) {
- expandAffix(negSuffixPattern, pluralCount, buffer);
- negativeSuffix = buffer.toString();
- }
- }
-
- /**
- * Expands an affix pattern into an affix string. All characters in the pattern are
- * literal unless bracketed by QUOTEs. The following characters outside QUOTE are
- * recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, PATTERN_MINUS, and
- * CURRENCY_SIGN. If CURRENCY_SIGN is doubled, it is interpreted as an international
- * currency sign. If CURRENCY_SIGN is tripled, it is interpreted as currency plural
- * long names, such as "US Dollars". Any other character outside QUOTE represents
- * itself. Quoted text must be well-formed.
- *
- * This method is used in two distinct ways. First, it is used to expand the stored
- * affix patterns into actual affixes. For this usage, doFormat must be false. Second,
- * it is used to expand the stored affix patterns given a specific number (doFormat ==
- * true), for those rare cases in which a currency format references a ChoiceFormat
- * (e.g., en_IN display name for INR). The number itself is taken from digitList.
- * TODO: There are no currency ChoiceFormat patterns, figure out what is still relevant here.
- *
- * When used in the first way, this method has a side effect: It sets currencyChoice
- * to a ChoiceFormat object, if the currency's display name in this locale is a
- * ChoiceFormat pattern (very rare). It only does this if currencyChoice is null to
- * start with.
- *
- * @param pattern the non-null, possibly empty pattern
- * @param pluralCount the plural count. It is only used for currency plural format. In
- * which case, it is the plural count of the currency amount. For example, in en_US,
- * it is the singular "one", or the plural "other". For all other cases, it is null,
- * and is not being used.
- * @param buffer a scratch StringBuffer; its contents will be lost
- */
- // Bug 4212072 [Richard/GCL]
- private void expandAffix(String pattern, String pluralCount, StringBuffer buffer) {
- buffer.setLength(0);
- for (int i = 0; i < pattern.length();) {
- char c = pattern.charAt(i++);
- if (c == QUOTE) {
- for (;;) {
- int j = pattern.indexOf(QUOTE, i);
- if (j == i) {
- buffer.append(QUOTE);
- i = j + 1;
- break;
- } else if (j > i) {
- buffer.append(pattern.substring(i, j));
- i = j + 1;
- if (i < pattern.length() && pattern.charAt(i) == QUOTE) {
- buffer.append(QUOTE);
- ++i;
- // loop again
- } else {
- break;
- }
- } else {
- // Unterminated quote; should be caught by apply
- // pattern.
- throw new RuntimeException();
- }
- }
- continue;
- }
-
- switch (c) {
- case CURRENCY_SIGN:
- // As of ICU 2.2 we use the currency object, and ignore the currency
- // symbols in the DFS, unless we have a null currency object. This occurs
- // if resurrecting a pre-2.2 object or if the user sets a custom DFS.
- boolean intl = i < pattern.length() && pattern.charAt(i) == CURRENCY_SIGN;
- boolean plural = false;
- if (intl) {
- ++i;
- if (i < pattern.length() && pattern.charAt(i) == CURRENCY_SIGN) {
- plural = true;
- intl = false;
- ++i;
- }
- }
- String s = null;
- Currency currency = getCurrency();
- if (currency != null) {
- // plural name is only needed when pluralCount != null, which means
- // when formatting currency plural names. For other cases,
- // pluralCount == null, and plural names are not needed.
- if (plural && pluralCount != null) {
- s = currency.getName(symbols.getULocale(), Currency.PLURAL_LONG_NAME,
- pluralCount, null);
- } else if (!intl) {
- s = currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null);
- } else {
- s = currency.getCurrencyCode();
- }
- } else {
- s = intl ? symbols.getInternationalCurrencySymbol() :
- symbols.getCurrencySymbol();
- }
- // Here is where FieldPosition could be set for CURRENCY PLURAL.
- buffer.append(s);
- break;
- case PATTERN_PERCENT:
- buffer.append(symbols.getPercentString());
- break;
- case PATTERN_PER_MILLE:
- buffer.append(symbols.getPerMillString());
- break;
- case PATTERN_MINUS_SIGN:
- buffer.append(symbols.getMinusSignString());
- break;
- default:
- buffer.append(c);
- break;
- }
- }
- }
-
- /**
- * Append an affix to the given StringBuffer.
- *
- * @param buf
- * buffer to append to
- * @param isNegative
- * @param isPrefix
- * @param fieldPosition
- * @param parseAttr
- */
- private int appendAffix(StringBuffer buf, boolean isNegative, boolean isPrefix,
- FieldPosition fieldPosition,
- boolean parseAttr) {
- if (currencyChoice != null) {
- String affixPat = null;
- if (isPrefix) {
- affixPat = isNegative ? negPrefixPattern : posPrefixPattern;
- } else {
- affixPat = isNegative ? negSuffixPattern : posSuffixPattern;
- }
- StringBuffer affixBuf = new StringBuffer();
- expandAffix(affixPat, null, affixBuf);
- buf.append(affixBuf);
- return affixBuf.length();
- }
-
- String affix = null;
- String pattern;
- if (isPrefix) {
- affix = isNegative ? negativePrefix : positivePrefix;
- pattern = isNegative ? negPrefixPattern : posPrefixPattern;
- } else {
- affix = isNegative ? negativeSuffix : positiveSuffix;
- pattern = isNegative ? negSuffixPattern : posSuffixPattern;
- }
- // [Spark/CDL] Invoke formatAffix2Attribute to add attributes for affix
- if (parseAttr) {
- // Updates for Ticket 11805.
- int offset = affix.indexOf(symbols.getCurrencySymbol());
- if (offset > -1) {
- formatAffix2Attribute(isPrefix, Field.CURRENCY, buf, offset,
- symbols.getCurrencySymbol().length());
- }
- offset = affix.indexOf(symbols.getMinusSignString());
- if (offset > -1) {
- formatAffix2Attribute(isPrefix, Field.SIGN, buf, offset,
- symbols.getMinusSignString().length());
- }
- offset = affix.indexOf(symbols.getPercentString());
- if (offset > -1) {
- formatAffix2Attribute(isPrefix, Field.PERCENT, buf, offset,
- symbols.getPercentString().length());
- }
- offset = affix.indexOf(symbols.getPerMillString());
- if (offset > -1) {
- formatAffix2Attribute(isPrefix, Field.PERMILLE, buf, offset,
- symbols.getPerMillString().length());
- }
- offset = pattern.indexOf("¤¤¤");
- if (offset > -1) {
- formatAffix2Attribute(isPrefix, Field.CURRENCY, buf, offset,
- affix.length() - offset);
- }
- }
-
- // Look for SIGN, PERCENT, PERMILLE in the formatted affix.
- if (fieldPosition.getFieldAttribute() == NumberFormat.Field.SIGN) {
- String sign = isNegative ? symbols.getMinusSignString() : symbols.getPlusSignString();
- int firstPos = affix.indexOf(sign);
- if (firstPos > -1) {
- int startPos = buf.length() + firstPos;
- fieldPosition.setBeginIndex(startPos);
- fieldPosition.setEndIndex(startPos + sign.length());
- }
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERCENT) {
- int firstPos = affix.indexOf(symbols.getPercentString());
- if (firstPos > -1) {
- int startPos = buf.length() + firstPos;
- fieldPosition.setBeginIndex(startPos);
- fieldPosition.setEndIndex(startPos + symbols.getPercentString().length());
- }
- } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERMILLE) {
- int firstPos = affix.indexOf(symbols.getPerMillString());
- if (firstPos > -1) {
- int startPos = buf.length() + firstPos;
- fieldPosition.setBeginIndex(startPos);
- fieldPosition.setEndIndex(startPos + symbols.getPerMillString().length());
- }
- } else
- // If CurrencySymbol or InternationalCurrencySymbol is in the affix, check for currency symbol.
- // Get spelled out name if "¤¤¤" is in the pattern.
- if (fieldPosition.getFieldAttribute() == NumberFormat.Field.CURRENCY) {
- if (affix.indexOf(symbols.getCurrencySymbol()) > -1) {
- String aff = symbols.getCurrencySymbol();
- int firstPos = affix.indexOf(aff);
- int start = buf.length() + firstPos;
- int end = start + aff.length();
- fieldPosition.setBeginIndex(start);
- fieldPosition.setEndIndex(end);
- } else if (affix.indexOf(symbols.getInternationalCurrencySymbol()) > -1) {
- String aff = symbols.getInternationalCurrencySymbol();
- int firstPos = affix.indexOf(aff);
- int start = buf.length() + firstPos;
- int end = start + aff.length();
- fieldPosition.setBeginIndex(start);
- fieldPosition.setEndIndex(end);
- } else if (pattern.indexOf("¤¤¤") > -1) {
- // It's a plural, and we know where it is in the pattern.
- int firstPos = pattern.indexOf("¤¤¤");
- int start = buf.length() + firstPos;
- int end = buf.length() + affix.length(); // This seems clunky and wrong.
- fieldPosition.setBeginIndex(start);
- fieldPosition.setEndIndex(end);
- }
- }
-
- buf.append(affix);
- return affix.length();
- }
-
- // Fix for prefix and suffix in Ticket 11805.
- private void formatAffix2Attribute(boolean isPrefix, Field fieldType,
- StringBuffer buf, int offset, int symbolSize) {
- int begin;
- begin = offset;
- if (!isPrefix) {
- begin += buf.length();
- }
-
- addAttribute(fieldType, begin, begin + symbolSize);
- }
-
- /**
- * [Spark/CDL] Use this method to add attribute.
- */
- private void addAttribute(Field field, int begin, int end) {
- FieldPosition pos = new FieldPosition(field);
- pos.setBeginIndex(begin);
- pos.setEndIndex(end);
- attributes.add(pos);
- }
-
- /**
- * Formats the object to an attributed string, and return the corresponding iterator.
- *
- * @stable ICU 3.6
- */
- @Override
- public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
- return formatToCharacterIterator(obj, NULL_UNIT);
- }
-
- AttributedCharacterIterator formatToCharacterIterator(Object obj, Unit unit) {
- if (!(obj instanceof Number))
- throw new IllegalArgumentException();
- Number number = (Number) obj;
- StringBuffer text = new StringBuffer();
- unit.writePrefix(text);
- attributes.clear();
- if (obj instanceof BigInteger) {
- format((BigInteger) number, text, new FieldPosition(0), true);
- } else if (obj instanceof java.math.BigDecimal) {
- format((java.math.BigDecimal) number, text, new FieldPosition(0)
- , true);
- } else if (obj instanceof Double) {
- format(number.doubleValue(), text, new FieldPosition(0), true);
- } else if (obj instanceof Integer || obj instanceof Long) {
- format(number.longValue(), text, new FieldPosition(0), true);
- } else {
- throw new IllegalArgumentException();
- }
- unit.writeSuffix(text);
- AttributedString as = new AttributedString(text.toString());
-
- // add NumberFormat field attributes to the AttributedString
- for (int i = 0; i < attributes.size(); i++) {
- FieldPosition pos = attributes.get(i);
- Format.Field attribute = pos.getFieldAttribute();
- as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos.getEndIndex());
- }
-
- // return the CharacterIterator from AttributedString
- return as.getIterator();
- }
-
- /**
- * Appends an affix pattern to the given StringBuffer. Localize unquoted specials.
- * <p>
- * <b>Note:</b> This implementation does not support new String localized symbols.
- */
- private void appendAffixPattern(StringBuffer buffer, boolean isNegative, boolean isPrefix,
- boolean localized) {
- String affixPat = null;
- if (isPrefix) {
- affixPat = isNegative ? negPrefixPattern : posPrefixPattern;
- } else {
- affixPat = isNegative ? negSuffixPattern : posSuffixPattern;
- }
-
- // When there is a null affix pattern, we use the affix itself.
- if (affixPat == null) {
- String affix = null;
- if (isPrefix) {
- affix = isNegative ? negativePrefix : positivePrefix;
- } else {
- affix = isNegative ? negativeSuffix : positiveSuffix;
- }
- // Do this crudely for now: Wrap everything in quotes.
- buffer.append(QUOTE);
- for (int i = 0; i < affix.length(); ++i) {
- char ch = affix.charAt(i);
- if (ch == QUOTE) {
- buffer.append(ch);
- }
- buffer.append(ch);
- }
- buffer.append(QUOTE);
- return;
- }
-
- if (!localized) {
- buffer.append(affixPat);
- } else {
- int i, j;
- for (i = 0; i < affixPat.length(); ++i) {
- char ch = affixPat.charAt(i);
- switch (ch) {
- case QUOTE:
- j = affixPat.indexOf(QUOTE, i + 1);
- if (j < 0) {
- throw new IllegalArgumentException("Malformed affix pattern: " + affixPat);
- }
- buffer.append(affixPat.substring(i, j + 1));
- i = j;
- continue;
- case PATTERN_PER_MILLE:
- ch = symbols.getPerMill();
- break;
- case PATTERN_PERCENT:
- ch = symbols.getPercent();
- break;
- case PATTERN_MINUS_SIGN:
- ch = symbols.getMinusSign();
- break;
- }
- // check if char is same as any other symbol
- if (ch == symbols.getDecimalSeparator() || ch == symbols.getGroupingSeparator()) {
- buffer.append(QUOTE);
- buffer.append(ch);
- buffer.append(QUOTE);
- } else {
- buffer.append(ch);
- }
- }
- }
- }
-
- /**
- * Does the real work of generating a pattern.
- * <p>
- * <b>Note:</b> This implementation does not support new String localized symbols.
- */
- private String toPattern(boolean localized) {
- StringBuffer result = new StringBuffer();
- char zero = localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT;
- char digit = localized ? symbols.getDigit() : PATTERN_DIGIT;
- char sigDigit = 0;
- boolean useSigDig = areSignificantDigitsUsed();
- if (useSigDig) {
- sigDigit = localized ? symbols.getSignificantDigit() : PATTERN_SIGNIFICANT_DIGIT;
- }
- char group = localized ? symbols.getGroupingSeparator() : PATTERN_GROUPING_SEPARATOR;
- int i;
- int roundingDecimalPos = 0; // Pos of decimal in roundingDigits
- String roundingDigits = null;
- int padPos = (formatWidth > 0) ? padPosition : -1;
- String padSpec = (formatWidth > 0)
- ? new StringBuffer(2).append(localized
- ? symbols.getPadEscape()
- : PATTERN_PAD_ESCAPE).append(pad).toString()
- : null;
- if (roundingIncrementICU != null) {
- i = roundingIncrementICU.scale();
- roundingDigits = roundingIncrementICU.movePointRight(i).toString();
- roundingDecimalPos = roundingDigits.length() - i;
- }
- for (int part = 0; part < 2; ++part) {
- // variable not used int partStart = result.length();
- if (padPos == PAD_BEFORE_PREFIX) {
- result.append(padSpec);
- }
-
- // Use original symbols read from resources in pattern eg. use "\u00A4"
- // instead of "$" in Locale.US [Richard/GCL]
- appendAffixPattern(result, part != 0, true, localized);
- if (padPos == PAD_AFTER_PREFIX) {
- result.append(padSpec);
- }
- int sub0Start = result.length();
- int g = isGroupingUsed() ? Math.max(0, groupingSize) : 0;
- if (g > 0 && groupingSize2 > 0 && groupingSize2 != groupingSize) {
- g += groupingSize2;
- }
- int maxDig = 0, minDig = 0, maxSigDig = 0;
- if (useSigDig) {
- minDig = getMinimumSignificantDigits();
- maxDig = maxSigDig = getMaximumSignificantDigits();
- } else {
- minDig = getMinimumIntegerDigits();
- maxDig = getMaximumIntegerDigits();
- }
- if (useExponentialNotation) {
- if (maxDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
- maxDig = 1;
- }
- } else if (useSigDig) {
- maxDig = Math.max(maxDig, g + 1);
- } else {
- maxDig = Math.max(Math.max(g, getMinimumIntegerDigits()), roundingDecimalPos) + 1;
- }
- for (i = maxDig; i > 0; --i) {
- if (!useExponentialNotation && i < maxDig && isGroupingPosition(i)) {
- result.append(group);
- }
- if (useSigDig) {
- // #@,@### (maxSigDig == 5, minSigDig == 2) 65 4321 (1-based pos,
- // count from the right) Use # if pos > maxSigDig or 1 <= pos <=
- // (maxSigDig - minSigDig) Use @ if (maxSigDig - minSigDig) < pos <=
- // maxSigDig
- result.append((maxSigDig >= i && i > (maxSigDig - minDig)) ? sigDigit : digit);
- } else {
- if (roundingDigits != null) {
- int pos = roundingDecimalPos - i;
- if (pos >= 0 && pos < roundingDigits.length()) {
- result.append((char) (roundingDigits.charAt(pos) - '0' + zero));
- continue;
- }
- }
- result.append(i <= minDig ? zero : digit);
- }
- }
- if (!useSigDig) {
- if (getMaximumFractionDigits() > 0 || decimalSeparatorAlwaysShown) {
- result.append(localized ? symbols.getDecimalSeparator() :
- PATTERN_DECIMAL_SEPARATOR);
- }
- int pos = roundingDecimalPos;
- for (i = 0; i < getMaximumFractionDigits(); ++i) {
- if (roundingDigits != null && pos < roundingDigits.length()) {
- result.append(pos < 0 ? zero :
- (char) (roundingDigits.charAt(pos) - '0' + zero));
- ++pos;
- continue;
- }
- result.append(i < getMinimumFractionDigits() ? zero : digit);
- }
- }
- if (useExponentialNotation) {
- if (localized) {
- result.append(symbols.getExponentSeparator());
- } else {
- result.append(PATTERN_EXPONENT);
- }
- if (exponentSignAlwaysShown) {
- result.append(localized ? symbols.getPlusSign() : PATTERN_PLUS_SIGN);
- }
- for (i = 0; i < minExponentDigits; ++i) {
- result.append(zero);
- }
- }
- if (padSpec != null && !useExponentialNotation) {
- int add = formatWidth
- - result.length()
- + sub0Start
- - ((part == 0)
- ? positivePrefix.length() + positiveSuffix.length()
- : negativePrefix.length() + negativeSuffix.length());
- while (add > 0) {
- result.insert(sub0Start, digit);
- ++maxDig;
- --add;
- // Only add a grouping separator if we have at least 2 additional
- // characters to be added, so we don't end up with ",###".
- if (add > 1 && isGroupingPosition(maxDig)) {
- result.insert(sub0Start, group);
- --add;
- }
- }
- }
- if (padPos == PAD_BEFORE_SUFFIX) {
- result.append(padSpec);
- }
- // Use original symbols read from resources in pattern eg. use "\u00A4"
- // instead of "$" in Locale.US [Richard/GCL]
- appendAffixPattern(result, part != 0, false, localized);
- if (padPos == PAD_AFTER_SUFFIX) {
- result.append(padSpec);
- }
- if (part == 0) {
- if (negativeSuffix.equals(positiveSuffix) &&
- negativePrefix.equals(PATTERN_MINUS_SIGN + positivePrefix)) {
- break;
- } else {
- result.append(localized ? symbols.getPatternSeparator() : PATTERN_SEPARATOR);
- }
- }
- }
- return result.toString();
- }
-
- /**
- * Applies the given pattern to this Format object. A pattern is a short-hand
- * specification for the various formatting properties. These properties can also be
- * changed individually through the various setter methods.
- *
- * <p>There is no limit to integer digits are set by this routine, since that is the
- * typical end-user desire; use setMaximumInteger if you want to set a real value. For
- * negative numbers, use a second pattern, separated by a semicolon
- *
- * <p>Example "#,#00.0#" -&gt; 1,234.56
- *
- * <p>This means a minimum of 2 integer digits, 1 fraction digit, and a maximum of 2
- * fraction digits.
- *
- * <p>Example: "#,#00.0#;(#,#00.0#)" for negatives in parentheses.
- *
- * <p>In negative patterns, the minimum and maximum counts are ignored; these are
- * presumed to be set in the positive pattern.
- *
- * @stable ICU 2.0
- */
- public void applyPattern(String pattern) {
- applyPattern(pattern, false);
- }
-
- /**
- * Applies the given pattern to this Format object. The pattern is assumed to be in a
- * localized notation. A pattern is a short-hand specification for the various
- * formatting properties. These properties can also be changed individually through
- * the various setter methods.
- *
- * <p>There is no limit to integer digits are set by this routine, since that is the
- * typical end-user desire; use setMaximumInteger if you want to set a real value. For
- * negative numbers, use a second pattern, separated by a semicolon
- *
- * <p>Example "#,#00.0#" -&gt; 1,234.56
- *
- * <p>This means a minimum of 2 integer digits, 1 fraction digit, and a maximum of 2
- * fraction digits.
- *
- * <p>Example: "#,#00.0#;(#,#00.0#)" for negatives in parantheses.
- *
- * <p>In negative patterns, the minimum and maximum counts are ignored; these are
- * presumed to be set in the positive pattern.
- *
- * @stable ICU 2.0
- */
- public void applyLocalizedPattern(String pattern) {
- applyPattern(pattern, true);
- }
-
- /**
- * Does the real work of applying a pattern.
- */
- private void applyPattern(String pattern, boolean localized) {
- applyPatternWithoutExpandAffix(pattern, localized);
- expandAffixAdjustWidth(null);
- }
-
- private void expandAffixAdjustWidth(String pluralCount) {
- // Bug 4212072 Update the affix strings according to symbols in order to keep the
- // affix strings up to date. [Richard/GCL]
- expandAffixes(pluralCount);
-
- // Now that we have the actual prefix and suffix, fix up formatWidth
- if (formatWidth > 0) {
- formatWidth += positivePrefix.length() + positiveSuffix.length();
- }
- }
-
- private void applyPatternWithoutExpandAffix(String pattern, boolean localized) {
- char zeroDigit = PATTERN_ZERO_DIGIT; // '0'
- char sigDigit = PATTERN_SIGNIFICANT_DIGIT; // '@'
- char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
- char decimalSeparator = PATTERN_DECIMAL_SEPARATOR;
- char percent = PATTERN_PERCENT;
- char perMill = PATTERN_PER_MILLE;
- char digit = PATTERN_DIGIT; // '#'
- char separator = PATTERN_SEPARATOR;
- String exponent = String.valueOf(PATTERN_EXPONENT);
- char plus = PATTERN_PLUS_SIGN;
- char padEscape = PATTERN_PAD_ESCAPE;
- char minus = PATTERN_MINUS_SIGN; // Bug 4212072 [Richard/GCL]
- if (localized) {
- zeroDigit = symbols.getZeroDigit();
- sigDigit = symbols.getSignificantDigit();
- groupingSeparator = symbols.getGroupingSeparator();
- decimalSeparator = symbols.getDecimalSeparator();
- percent = symbols.getPercent();
- perMill = symbols.getPerMill();
- digit = symbols.getDigit();
- separator = symbols.getPatternSeparator();
- exponent = symbols.getExponentSeparator();
- plus = symbols.getPlusSign();
- padEscape = symbols.getPadEscape();
- minus = symbols.getMinusSign(); // Bug 4212072 [Richard/GCL]
- }
- char nineDigit = (char) (zeroDigit + 9);
-
- boolean gotNegative = false;
-
- int pos = 0;
- // Part 0 is the positive pattern. Part 1, if present, is the negative
- // pattern.
- for (int part = 0; part < 2 && pos < pattern.length(); ++part) {
- // The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix, 2=suffix,
- // 3=prefix in quote, 4=suffix in quote. Subpart 0 is between the prefix and
- // suffix, and consists of pattern characters. In the prefix and suffix,
- // percent, permille, and currency symbols are recognized and translated.
- int subpart = 1, sub0Start = 0, sub0Limit = 0, sub2Limit = 0;
-
- // It's important that we don't change any fields of this object
- // prematurely. We set the following variables for the multiplier, grouping,
- // etc., and then only change the actual object fields if everything parses
- // correctly. This also lets us register the data from part 0 and ignore the
- // part 1, except for the prefix and suffix.
- StringBuilder prefix = new StringBuilder();
- StringBuilder suffix = new StringBuilder();
- int decimalPos = -1;
- int multpl = 1;
- int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0, sigDigitCount = 0;
- byte groupingCount = -1;
- byte groupingCount2 = -1;
- int padPos = -1;
- char padChar = 0;
- int incrementPos = -1;
- long incrementVal = 0;
- byte expDigits = -1;
- boolean expSignAlways = false;
- int currencySignCnt = 0;
-
- // The affix is either the prefix or the suffix.
- StringBuilder affix = prefix;
-
- int start = pos;
-
- PARTLOOP: for (; pos < pattern.length(); ++pos) {
- char ch = pattern.charAt(pos);
- switch (subpart) {
- case 0: // Pattern proper subpart (between prefix & suffix)
- // Process the digits, decimal, and grouping characters. We record
- // five pieces of information. We expect the digits to occur in the
- // pattern ####00.00####, and we record the number of left digits,
- // zero (central) digits, and right digits. The position of the last
- // grouping character is recorded (should be somewhere within the
- // first two blocks of characters), as is the position of the decimal
- // point, if any (should be in the zero digits). If there is no
- // decimal point, then there should be no right digits.
- if (ch == digit) {
- if (zeroDigitCount > 0 || sigDigitCount > 0) {
- ++digitRightCount;
- } else {
- ++digitLeftCount;
- }
- if (groupingCount >= 0 && decimalPos < 0) {
- ++groupingCount;
- }
- } else if ((ch >= zeroDigit && ch <= nineDigit) || ch == sigDigit) {
- if (digitRightCount > 0) {
- patternError("Unexpected '" + ch + '\'', pattern);
- }
- if (ch == sigDigit) {
- ++sigDigitCount;
- } else {
- ++zeroDigitCount;
- if (ch != zeroDigit) {
- int p = digitLeftCount + zeroDigitCount + digitRightCount;
- if (incrementPos >= 0) {
- while (incrementPos < p) {
- incrementVal *= 10;
- ++incrementPos;
- }
- } else {
- incrementPos = p;
- }
- incrementVal += ch - zeroDigit;
- }
- }
- if (groupingCount >= 0 && decimalPos < 0) {
- ++groupingCount;
- }
- } else if (ch == groupingSeparator) {
- // Bug 4212072 process the Localized pattern like
- // "'Fr. '#'##0.05;'Fr.-'#'##0.05" (Locale="CH", groupingSeparator
- // == QUOTE) [Richard/GCL]
- if (ch == QUOTE && (pos + 1) < pattern.length()) {
- char after = pattern.charAt(pos + 1);
- if (!(after == digit || (after >= zeroDigit && after <= nineDigit))) {
- // A quote outside quotes indicates either the opening
- // quote or two quotes, which is a quote literal. That is,
- // we have the first quote in 'do' or o''clock.
- if (after == QUOTE) {
- ++pos;
- // Fall through to append(ch)
- } else {
- if (groupingCount < 0) {
- subpart = 3; // quoted prefix subpart
- } else {
- // Transition to suffix subpart
- subpart = 2; // suffix subpart
- affix = suffix;
- sub0Limit = pos--;
- }
- continue;
- }
- }
- }
-
- if (decimalPos >= 0) {
- patternError("Grouping separator after decimal", pattern);
- }
- groupingCount2 = groupingCount;
- groupingCount = 0;
- } else if (ch == decimalSeparator) {
- if (decimalPos >= 0) {
- patternError("Multiple decimal separators", pattern);
- }
- // Intentionally incorporate the digitRightCount, even though it
- // is illegal for this to be > 0 at this point. We check pattern
- // syntax below.
- decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
- } else {
- if (pattern.regionMatches(pos, exponent, 0, exponent.length())) {
- if (expDigits >= 0) {
- patternError("Multiple exponential symbols", pattern);
- }
- if (groupingCount >= 0) {
- patternError("Grouping separator in exponential", pattern);
- }
- pos += exponent.length();
- // Check for positive prefix
- if (pos < pattern.length() && pattern.charAt(pos) == plus) {
- expSignAlways = true;
- ++pos;
- }
- // Use lookahead to parse out the exponential part of the
- // pattern, then jump into suffix subpart.
- expDigits = 0;
- while (pos < pattern.length() && pattern.charAt(pos) == zeroDigit) {
- ++expDigits;
- ++pos;
- }
-
- // 1. Require at least one mantissa pattern digit
- // 2. Disallow "#+ @" in mantissa
- // 3. Require at least one exponent pattern digit
- if (((digitLeftCount + zeroDigitCount) < 1 &&
- (sigDigitCount + digitRightCount) < 1)
- || (sigDigitCount > 0 && digitLeftCount > 0) || expDigits < 1) {
- patternError("Malformed exponential", pattern);
- }
- }
- // Transition to suffix subpart
- subpart = 2; // suffix subpart
- affix = suffix;
- sub0Limit = pos--; // backup: for() will increment
- continue;
- }
- break;
- case 1: // Prefix subpart
- case 2: // Suffix subpart
- // Process the prefix / suffix characters Process unquoted characters
- // seen in prefix or suffix subpart.
-
- // Several syntax characters implicitly begins the next subpart if we
- // are in the prefix; otherwise they are illegal if unquoted.
- if (ch == digit || ch == groupingSeparator || ch == decimalSeparator
- || (ch >= zeroDigit && ch <= nineDigit) || ch == sigDigit) {
- // Any of these characters implicitly begins the
- // next subpart if we are in the prefix
- if (subpart == 1) { // prefix subpart
- subpart = 0; // pattern proper subpart
- sub0Start = pos--; // Reprocess this character
- continue;
- } else if (ch == QUOTE) {
- // Bug 4212072 process the Localized pattern like
- // "'Fr. '#'##0.05;'Fr.-'#'##0.05" (Locale="CH",
- // groupingSeparator == QUOTE) [Richard/GCL]
-
- // A quote outside quotes indicates either the opening quote
- // or two quotes, which is a quote literal. That is, we have
- // the first quote in 'do' or o''clock.
- if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) {
- ++pos;
- affix.append(ch);
- } else {
- subpart += 2; // open quote
- }
- continue;
- }
- patternError("Unquoted special character '" + ch + '\'', pattern);
- } else if (ch == CURRENCY_SIGN) {
- // Use lookahead to determine if the currency sign is
- // doubled or not.
- boolean doubled = (pos + 1) < pattern.length() &&
- pattern.charAt(pos + 1) == CURRENCY_SIGN;
-
- // Bug 4212072 To meet the need of expandAffix(String,
- // StirngBuffer) [Richard/GCL]
- if (doubled) {
- ++pos; // Skip over the doubled character
- affix.append(ch); // append two: one here, one below
- if ((pos + 1) < pattern.length() &&
- pattern.charAt(pos + 1) == CURRENCY_SIGN) {
- ++pos; // Skip over the tripled character
- affix.append(ch); // append again
- currencySignCnt = CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT;
- } else {
- currencySignCnt = CURRENCY_SIGN_COUNT_IN_ISO_FORMAT;
- }
- } else {
- currencySignCnt = CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT;
- }
- // Fall through to append(ch)
- } else if (ch == QUOTE) {
- // A quote outside quotes indicates either the opening quote or
- // two quotes, which is a quote literal. That is, we have the
- // first quote in 'do' or o''clock.
- if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) {
- ++pos;
- affix.append(ch); // append two: one here, one below
- } else {
- subpart += 2; // open quote
- }
- // Fall through to append(ch)
- } else if (ch == separator) {
- // Don't allow separators in the prefix, and don't allow
- // separators in the second pattern (part == 1).
- if (subpart == 1 || part == 1) {
- patternError("Unquoted special character '" + ch + '\'', pattern);
- }
- sub2Limit = pos++;
- break PARTLOOP; // Go to next part
- } else if (ch == percent || ch == perMill) {
- // Next handle characters which are appended directly.
- if (multpl != 1) {
- patternError("Too many percent/permille characters", pattern);
- }
- multpl = (ch == percent) ? 100 : 1000;
- // Convert to non-localized pattern
- ch = (ch == percent) ? PATTERN_PERCENT : PATTERN_PER_MILLE;
- // Fall through to append(ch)
- } else if (ch == minus) {
- // Convert to non-localized pattern
- ch = PATTERN_MINUS_SIGN;
- // Fall through to append(ch)
- } else if (ch == padEscape) {
- if (padPos >= 0) {
- patternError("Multiple pad specifiers", pattern);
- }
- if ((pos + 1) == pattern.length()) {
- patternError("Invalid pad specifier", pattern);
- }
- padPos = pos++; // Advance past pad char
- padChar = pattern.charAt(pos);
- continue;
- }
- affix.append(ch);
- break;
- case 3: // Prefix subpart, in quote
- case 4: // Suffix subpart, in quote
- // A quote within quotes indicates either the closing quote or two
- // quotes, which is a quote literal. That is, we have the second quote
- // in 'do' or 'don''t'.
- if (ch == QUOTE) {
- if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) {
- ++pos;
- affix.append(ch);
- } else {
- subpart -= 2; // close quote
- }
- // Fall through to append(ch)
- }
- // NOTE: In ICU 2.2 there was code here to parse quoted percent and
- // permille characters _within quotes_ and give them special
- // meaning. This is incorrect, since quoted characters are literals
- // without special meaning.
- affix.append(ch);
- break;
- }
- }
-
- if (subpart == 3 || subpart == 4) {
- patternError("Unterminated quote", pattern);
- }
-
- if (sub0Limit == 0) {
- sub0Limit = pattern.length();
- }
-
- if (sub2Limit == 0) {
- sub2Limit = pattern.length();
- }
-
- // Handle patterns with no '0' pattern character. These patterns are legal,
- // but must be recodified to make sense. "##.###" -> "#0.###". ".###" ->
- // ".0##".
- //
- // We allow patterns of the form "####" to produce a zeroDigitCount of zero
- // (got that?); although this seems like it might make it possible for
- // format() to produce empty strings, format() checks for this condition and
- // outputs a zero digit in this situation. Having a zeroDigitCount of zero
- // yields a minimum integer digits of zero, which allows proper round-trip
- // patterns. We don't want "#" to become "#0" when toPattern() is called (even
- // though that's what it really is, semantically).
- if (zeroDigitCount == 0 && sigDigitCount == 0 &&
- digitLeftCount > 0 && decimalPos >= 0) {
- // Handle "###.###" and "###." and ".###"
- int n = decimalPos;
- if (n == 0)
- ++n; // Handle ".###"
- digitRightCount = digitLeftCount - n;
- digitLeftCount = n - 1;
- zeroDigitCount = 1;
- }
-
- // Do syntax checking on the digits, decimal points, and quotes.
- if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0)
- || (decimalPos >= 0
- && (sigDigitCount > 0
- || decimalPos < digitLeftCount
- || decimalPos > (digitLeftCount + zeroDigitCount)))
- || groupingCount == 0
- || groupingCount2 == 0
- || (sigDigitCount > 0 && zeroDigitCount > 0)
- || subpart > 2) { // subpart > 2 == unmatched quote
- patternError("Malformed pattern", pattern);
- }
-
- // Make sure pad is at legal position before or after affix.
- if (padPos >= 0) {
- if (padPos == start) {
- padPos = PAD_BEFORE_PREFIX;
- } else if (padPos + 2 == sub0Start) {
- padPos = PAD_AFTER_PREFIX;
- } else if (padPos == sub0Limit) {
- padPos = PAD_BEFORE_SUFFIX;
- } else if (padPos + 2 == sub2Limit) {
- padPos = PAD_AFTER_SUFFIX;
- } else {
- patternError("Illegal pad position", pattern);
- }
- }
-
- if (part == 0) {
- // Set negative affixes temporarily to match the positive
- // affixes. Fix this up later after processing both parts.
-
- // Bug 4212072 To meet the need of expandAffix(String, StirngBuffer)
- // [Richard/GCL]
- posPrefixPattern = negPrefixPattern = prefix.toString();
- posSuffixPattern = negSuffixPattern = suffix.toString();
-
- useExponentialNotation = (expDigits >= 0);
- if (useExponentialNotation) {
- minExponentDigits = expDigits;
- exponentSignAlwaysShown = expSignAlways;
- }
- int digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount;
- // The effectiveDecimalPos is the position the decimal is at or would be
- // at if there is no decimal. Note that if decimalPos<0, then
- // digitTotalCount == digitLeftCount + zeroDigitCount.
- int effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount;
- boolean useSigDig = (sigDigitCount > 0);
- setSignificantDigitsUsed(useSigDig);
- if (useSigDig) {
- setMinimumSignificantDigits(sigDigitCount);
- setMaximumSignificantDigits(sigDigitCount + digitRightCount);
- } else {
- int minInt = effectiveDecimalPos - digitLeftCount;
- setMinimumIntegerDigits(minInt);
-
- // Upper limit on integer and fraction digits for a Java double
- // [Richard/GCL]
- setMaximumIntegerDigits(useExponentialNotation ? digitLeftCount + minInt :
- DOUBLE_INTEGER_DIGITS);
- _setMaximumFractionDigits(decimalPos >= 0 ?
- (digitTotalCount - decimalPos) : 0);
- setMinimumFractionDigits(decimalPos >= 0 ?
- (digitLeftCount + zeroDigitCount - decimalPos) : 0);
- }
- setGroupingUsed(groupingCount > 0);
- this.groupingSize = (groupingCount > 0) ? groupingCount : 0;
- this.groupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount)
- ? groupingCount2 : 0;
- this.multiplier = multpl;
- setDecimalSeparatorAlwaysShown(decimalPos == 0 || decimalPos == digitTotalCount);
- if (padPos >= 0) {
- padPosition = padPos;
- formatWidth = sub0Limit - sub0Start; // to be fixed up below
- pad = padChar;
- } else {
- formatWidth = 0;
- }
- if (incrementVal != 0) {
- // BigDecimal scale cannot be negative (even though this makes perfect
- // sense), so we need to handle this.
- int scale = incrementPos - effectiveDecimalPos;
- roundingIncrementICU = BigDecimal.valueOf(incrementVal, scale > 0 ? scale : 0);
- if (scale < 0) {
- roundingIncrementICU = roundingIncrementICU.movePointRight(-scale);
- }
- roundingMode = BigDecimal.ROUND_HALF_EVEN;
- } else {
- setRoundingIncrement((BigDecimal) null);
- }
-
- // Update currency sign count for the new pattern
- currencySignCount = currencySignCnt;
- } else {
- // Bug 4212072 To meet the need of expandAffix(String, StirngBuffer)
- // [Richard/GCL]
- negPrefixPattern = prefix.toString();
- negSuffixPattern = suffix.toString();
- gotNegative = true;
- }
- }
-
-
- // Bug 4140009 Process the empty pattern [Richard/GCL]
- if (pattern.length() == 0) {
- posPrefixPattern = posSuffixPattern = "";
- setMinimumIntegerDigits(0);
- setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
- setMinimumFractionDigits(0);
- _setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
- }
-
- // If there was no negative pattern, or if the negative pattern is identical to
- // the positive pattern, then prepend the minus sign to the positive pattern to
- // form the negative pattern.
-
- // Bug 4212072 To meet the need of expandAffix(String, StirngBuffer) [Richard/GCL]
-
- if (!gotNegative ||
- (negPrefixPattern.equals(posPrefixPattern)
- && negSuffixPattern.equals(posSuffixPattern))) {
- negSuffixPattern = posSuffixPattern;
- negPrefixPattern = PATTERN_MINUS_SIGN + posPrefixPattern;
- }
-
- // Can't call setLocale when not in the right package:
- //setLocale(null, null);
-
- // save the pattern
- formatPattern = pattern;
-
- // special handlings for currency instance
- if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) {
- // reset rounding increment and max/min fractional digits
- // by the currency
- Currency theCurrency = getCurrency();
- if (theCurrency != null) {
- setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage));
- int d = theCurrency.getDefaultFractionDigits(currencyUsage);
- setMinimumFractionDigits(d);
- _setMaximumFractionDigits(d);
- }
-
- // initialize currencyPluralInfo if needed
- if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT
- && currencyPluralInfo == null) {
- currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale());
- }
- }
- resetActualRounding();
- }
-
-
- private void patternError(String msg, String pattern) {
- throw new IllegalArgumentException(msg + " in pattern \"" + pattern + '"');
- }
-
-
- // Rewrite the following 4 "set" methods Upper limit on integer and fraction digits
- // for a Java double [Richard/GCL]
-
- /**
- * Sets the maximum number of digits allowed in the integer portion of a number. This
- * override limits the integer digit count to 309.
- *
- * @see NumberFormat#setMaximumIntegerDigits
- * @stable ICU 2.0
- */
- @Override
- public void setMaximumIntegerDigits(int newValue) {
- super.setMaximumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS));
- }
-
- /**
- * Sets the minimum number of digits allowed in the integer portion of a number. This
- * override limits the integer digit count to 309.
- *
- * @see NumberFormat#setMinimumIntegerDigits
- * @stable ICU 2.0
- */
- @Override
- public void setMinimumIntegerDigits(int newValue) {
- super.setMinimumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS));
- }
-
- /**
- * {@icu} Returns the minimum number of significant digits that will be
- * displayed. This value has no effect unless {@link #areSignificantDigitsUsed()}
- * returns true.
- *
- * @return the fewest significant digits that will be shown
- * @stable ICU 3.0
- */
- public int getMinimumSignificantDigits() {
- return minSignificantDigits;
- }
-
- /**
- * {@icu} Returns the maximum number of significant digits that will be
- * displayed. This value has no effect unless {@link #areSignificantDigitsUsed()}
- * returns true.
- *
- * @return the most significant digits that will be shown
- * @stable ICU 3.0
- */
- public int getMaximumSignificantDigits() {
- return maxSignificantDigits;
- }
-
- /**
- * {@icu} Sets the minimum number of significant digits that will be displayed. If
- * <code>min</code> is less than one then it is set to one. If the maximum significant
- * digits count is less than <code>min</code>, then it is set to <code>min</code>.
- * This function also enables the use of significant digits by this formatter -
- * {@link #areSignificantDigitsUsed()} will return true.
- *
- * @param min the fewest significant digits to be shown
- * @stable ICU 3.0
- */
- public void setMinimumSignificantDigits(int min) {
- if (min < 1) {
- min = 1;
- }
- // pin max sig dig to >= min
- int max = Math.max(maxSignificantDigits, min);
- minSignificantDigits = min;
- maxSignificantDigits = max;
- setSignificantDigitsUsed(true);
- }
-
- /**
- * {@icu} Sets the maximum number of significant digits that will be displayed. If
- * <code>max</code> is less than one then it is set to one. If the minimum significant
- * digits count is greater than <code>max</code>, then it is set to <code>max</code>.
- * This function also enables the use of significant digits by this formatter -
- * {@link #areSignificantDigitsUsed()} will return true.
- *
- * @param max the most significant digits to be shown
- * @stable ICU 3.0
- */
- public void setMaximumSignificantDigits(int max) {
- if (max < 1) {
- max = 1;
- }
- // pin min sig dig to 1..max
- int min = Math.min(minSignificantDigits, max);
- minSignificantDigits = min;
- maxSignificantDigits = max;
- setSignificantDigitsUsed(true);
- }
-
- /**
- * {@icu} Returns true if significant digits are in use or false if integer and
- * fraction digit counts are in use.
- *
- * @return true if significant digits are in use
- * @stable ICU 3.0
- */
- public boolean areSignificantDigitsUsed() {
- return useSignificantDigits;
- }
-
- /**
- * {@icu} Sets whether significant digits are in use, or integer and fraction digit
- * counts are in use.
- *
- * @param useSignificantDigits true to use significant digits, or false to use integer
- * and fraction digit counts
- * @stable ICU 3.0
- */
- public void setSignificantDigitsUsed(boolean useSignificantDigits) {
- this.useSignificantDigits = useSignificantDigits;
- }
-
- /**
- * Sets the <tt>Currency</tt> object used to display currency amounts. This takes
- * effect immediately, if this format is a currency format. If this format is not a
- * currency format, then the currency object is used if and when this object becomes a
- * currency format through the application of a new pattern.
- *
- * @param theCurrency new currency object to use. Must not be null.
- * @stable ICU 2.2
- */
- @Override
- public void setCurrency(Currency theCurrency) {
- // If we are a currency format, then modify our affixes to
- // encode the currency symbol for the given currency in our
- // locale, and adjust the decimal digits and rounding for the
- // given currency.
-
- super.setCurrency(theCurrency);
- if (theCurrency != null) {
- String s = theCurrency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null);
- symbols.setCurrency(theCurrency);
- symbols.setCurrencySymbol(s);
- }
-
- if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) {
- if (theCurrency != null) {
- setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage));
- int d = theCurrency.getDefaultFractionDigits(currencyUsage);
- setMinimumFractionDigits(d);
- setMaximumFractionDigits(d);
- }
- if (currencySignCount != CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
- // This is not necessary for plural format type
- // because affixes will be resolved in subformat
- expandAffixes(null);
- }
- }
- }
-
- /**
- * Sets the <tt>Currency Usage</tt> object used to display currency.
- * This takes effect immediately, if this format is a
- * currency format.
- * @param newUsage new currency context object to use.
- * @stable ICU 54
- */
- public void setCurrencyUsage(CurrencyUsage newUsage) {
- if (newUsage == null) {
- throw new NullPointerException("return value is null at method AAA");
- }
- currencyUsage = newUsage;
- Currency theCurrency = this.getCurrency();
-
- // We set rounding/digit based on currency context
- if (theCurrency != null) {
- setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage));
- int d = theCurrency.getDefaultFractionDigits(currencyUsage);
- setMinimumFractionDigits(d);
- _setMaximumFractionDigits(d);
- }
- }
-
- /**
- * Returns the <tt>Currency Usage</tt> object used to display currency
- * @stable ICU 54
- */
- public CurrencyUsage getCurrencyUsage() {
- return currencyUsage;
- }
-
- /**
- * Returns the currency in effect for this formatter. Subclasses should override this
- * method as needed. Unlike getCurrency(), this method should never return null.
- *
- * @internal
- * @deprecated This API is ICU internal only.
- */
- @Deprecated
- @Override
- protected Currency getEffectiveCurrency() {
- Currency c = getCurrency();
- if (c == null) {
- c = Currency.getInstance(symbols.getInternationalCurrencySymbol());
- }
- return c;
- }
-
- /**
- * Sets the maximum number of digits allowed in the fraction portion of a number. This
- * override limits the fraction digit count to 340.
- *
- * @see NumberFormat#setMaximumFractionDigits
- * @stable ICU 2.0
- */
- @Override
- public void setMaximumFractionDigits(int newValue) {
- _setMaximumFractionDigits(newValue);
- resetActualRounding();
- }
-
- /*
- * Internal method for DecimalFormat, setting maximum fractional digits
- * without triggering actual rounding recalculated.
- */
- private void _setMaximumFractionDigits(int newValue) {
- super.setMaximumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
- }
-
- /**
- * Sets the minimum number of digits allowed in the fraction portion of a number. This
- * override limits the fraction digit count to 340.
- *
- * @see NumberFormat#setMinimumFractionDigits
- * @stable ICU 2.0
- */
- @Override
- public void setMinimumFractionDigits(int newValue) {
- super.setMinimumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS));
- }
-
- /**
- * Sets whether {@link #parse(String, ParsePosition)} returns BigDecimal. The
- * default value is false.
- *
- * @param value true if {@link #parse(String, ParsePosition)}
- * returns BigDecimal.
- * @stable ICU 3.6
- */
- public void setParseBigDecimal(boolean value) {
- parseBigDecimal = value;
- }
-
- /**
- * Returns whether {@link #parse(String, ParsePosition)} returns BigDecimal.
- *
- * @return true if {@link #parse(String, ParsePosition)} returns BigDecimal.
- * @stable ICU 3.6
- */
- public boolean isParseBigDecimal() {
- return parseBigDecimal;
- }
-
- /**
- * Set the maximum number of exponent digits when parsing a number.
- * If the limit is set too high, an OutOfMemoryException may be triggered.
- * The default value is 1000.
- * @param newValue the new limit
- * @stable ICU 51
- */
- public void setParseMaxDigits(int newValue) {
- if (newValue > 0) {
- PARSE_MAX_EXPONENT = newValue;
- }
- }
-
- /**
- * Get the current maximum number of exponent digits when parsing a
- * number.
- * @return the maximum number of exponent digits for parsing
- * @stable ICU 51
- */
- public int getParseMaxDigits() {
- return PARSE_MAX_EXPONENT;
- }
-
- private void writeObject(ObjectOutputStream stream) throws IOException {
- // Ticket#6449 Format.Field instances are not serializable. When
- // formatToCharacterIterator is called, attributes (ArrayList) stores
- // FieldPosition instances with NumberFormat.Field. Because NumberFormat.Field is
- // not serializable, we need to clear the contents of the list when writeObject is
- // called. We could remove the field or make it transient, but it will break
- // serialization compatibility.
- attributes.clear();
-
- stream.defaultWriteObject();
- }
-
- /**
- * First, read the default serializable fields from the stream. Then if
- * <code>serialVersionOnStream</code> is less than 1, indicating that the stream was
- * written by JDK 1.1, initialize <code>useExponentialNotation</code> to false, since
- * it was not present in JDK 1.1. Finally, set serialVersionOnStream back to the
- * maximum allowed value so that default serialization will work properly if this
- * object is streamed out again.
- */
- private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
- stream.defaultReadObject();
-
- // Bug 4185761 validate fields [Richard/GCL]
-
- // We only need to check the maximum counts because NumberFormat .readObject has
- // already ensured that the maximum is greater than the minimum count.
-
- // Commented for compatibility with previous version, and reserved for further use
- // if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS ||
- // getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) { throw new
- // InvalidObjectException("Digit count out of range"); }
-
-
- // Truncate the maximumIntegerDigits to DOUBLE_INTEGER_DIGITS and
- // maximumFractionDigits to DOUBLE_FRACTION_DIGITS
-
- if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS) {
- setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS);
- }
- if (getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
- _setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS);
- }
- if (serialVersionOnStream < 2) {
- exponentSignAlwaysShown = false;
- setInternalRoundingIncrement(null);
- roundingMode = BigDecimal.ROUND_HALF_EVEN;
- formatWidth = 0;
- pad = ' ';
- padPosition = PAD_BEFORE_PREFIX;
- if (serialVersionOnStream < 1) {
- // Didn't have exponential fields
- useExponentialNotation = false;
- }
- }
- if (serialVersionOnStream < 3) {
- // Versions prior to 3 do not store a currency object. Create one to match
- // the DecimalFormatSymbols object.
- setCurrencyForSymbols();
- }
- if (serialVersionOnStream < 4) {
- currencyUsage = CurrencyUsage.STANDARD;
- }
- serialVersionOnStream = currentSerialVersion;
- digitList = new DigitList();
-
- if (roundingIncrement != null) {
- setInternalRoundingIncrement(new BigDecimal(roundingIncrement));
- }
- resetActualRounding();
- }
-
- private void setInternalRoundingIncrement(BigDecimal value) {
- roundingIncrementICU = value;
- roundingIncrement = value == null ? null : value.toBigDecimal();
- }
-
- // ----------------------------------------------------------------------
- // INSTANCE VARIABLES
- // ----------------------------------------------------------------------
-
- private transient DigitList digitList = new DigitList();
-
- /**
- * The symbol used as a prefix when formatting positive numbers, e.g. "+".
- *
- * @serial
- * @see #getPositivePrefix
- */
- private String positivePrefix = "";
-
- /**
- * The symbol used as a suffix when formatting positive numbers. This is often an
- * empty string.
- *
- * @serial
- * @see #getPositiveSuffix
- */
- private String positiveSuffix = "";
-
- /**
- * The symbol used as a prefix when formatting negative numbers, e.g. "-".
- *
- * @serial
- * @see #getNegativePrefix
- */
- private String negativePrefix = "-";
-
- /**
- * The symbol used as a suffix when formatting negative numbers. This is often an
- * empty string.
- *
- * @serial
- * @see #getNegativeSuffix
- */
- private String negativeSuffix = "";
-
- /**
- * The prefix pattern for non-negative numbers. This variable corresponds to
- * <code>positivePrefix</code>.
- *
- * <p>This pattern is expanded by the method <code>expandAffix()</code> to
- * <code>positivePrefix</code> to update the latter to reflect changes in
- * <code>symbols</code>. If this variable is <code>null</code> then
- * <code>positivePrefix</code> is taken as a literal value that does not change when
- * <code>symbols</code> changes. This variable is always <code>null</code> for
- * <code>DecimalFormat</code> objects older than stream version 2 restored from
- * stream.
- *
- * @serial
- */
- // [Richard/GCL]
- private String posPrefixPattern;
-
- /**
- * The suffix pattern for non-negative numbers. This variable corresponds to
- * <code>positiveSuffix</code>. This variable is analogous to
- * <code>posPrefixPattern</code>; see that variable for further documentation.
- *
- * @serial
- */
- // [Richard/GCL]
- private String posSuffixPattern;
-
- /**
- * The prefix pattern for negative numbers. This variable corresponds to
- * <code>negativePrefix</code>. This variable is analogous to
- * <code>posPrefixPattern</code>; see that variable for further documentation.
- *
- * @serial
- */
- // [Richard/GCL]
- private String negPrefixPattern;
-
- /**
- * The suffix pattern for negative numbers. This variable corresponds to
- * <code>negativeSuffix</code>. This variable is analogous to
- * <code>posPrefixPattern</code>; see that variable for further documentation.
- *
- * @serial
- */
- // [Richard/GCL]
- private String negSuffixPattern;
-
- /**
- * Formatter for ChoiceFormat-based currency names. If this field is not null, then
- * delegate to it to format currency symbols.
- * TODO: This is obsolete: Remove, and design extensible serialization. ICU ticket #12090.
- *
- * @since ICU 2.6
- */
- private ChoiceFormat currencyChoice;
-
- /**
- * The multiplier for use in percent, permill, etc.
- *
- * @serial
- * @see #getMultiplier
- */
- private int multiplier = 1;
-
- /**
- * The number of digits between grouping separators in the integer portion of a
- * number. Must be greater than 0 if <code>NumberFormat.groupingUsed</code> is true.
- *
- * @serial
- * @see #getGroupingSize
- * @see NumberFormat#isGroupingUsed
- */
- private byte groupingSize = 3; // invariant, > 0 if useThousands
-
- /**
- * The secondary grouping size. This is only used for Hindi numerals, which use a
- * primary grouping of 3 and a secondary grouping of 2, e.g., "12,34,567". If this
- * value is less than 1, then secondary grouping is equal to the primary grouping.
- *
- */
- private byte groupingSize2 = 0;
-
- /**
- * If true, forces the decimal separator to always appear in a formatted number, even
- * if the fractional part of the number is zero.
- *
- * @serial
- * @see #isDecimalSeparatorAlwaysShown
- */
- private boolean decimalSeparatorAlwaysShown = false;
-
- /**
- * The <code>DecimalFormatSymbols</code> object used by this format. It contains the
- * symbols used to format numbers, e.g. the grouping separator, decimal separator, and
- * so on.
- *
- * @serial
- * @see #setDecimalFormatSymbols
- * @see DecimalFormatSymbols
- */
- private DecimalFormatSymbols symbols = null; // LIU new DecimalFormatSymbols();
-
- /**
- * True to use significant digits rather than integer and fraction digit counts.
- *
- * @serial
- * @since ICU 3.0
- */
- private boolean useSignificantDigits = false;
-
- /**
- * The minimum number of significant digits to show. Must be &gt;= 1 and &lt;=
- * maxSignificantDigits. Ignored unless useSignificantDigits == true.
- *
- * @serial
- * @since ICU 3.0
- */
- private int minSignificantDigits = 1;
-
- /**
- * The maximum number of significant digits to show. Must be &gt;=
- * minSignficantDigits. Ignored unless useSignificantDigits == true.
- *
- * @serial
- * @since ICU 3.0
- */
- private int maxSignificantDigits = 6;
-
- /**
- * True to force the use of exponential (i.e. scientific) notation
- * when formatting numbers.
- *
- *<p> Note that the JDK 1.2 public API provides no way to set this
- * field, even though it is supported by the implementation and
- * the stream format. The intent is that this will be added to the
- * API in the future.
- *
- * @serial
- */
- private boolean useExponentialNotation; // Newly persistent in JDK 1.2
-
- /**
- * The minimum number of digits used to display the exponent when a number is
- * formatted in exponential notation. This field is ignored if
- * <code>useExponentialNotation</code> is not true.
- *
- * <p>Note that the JDK 1.2 public API provides no way to set this field, even though
- * it is supported by the implementation and the stream format. The intent is that
- * this will be added to the API in the future.
- *
- * @serial
- */
- private byte minExponentDigits; // Newly persistent in JDK 1.2
-
- /**
- * If true, the exponent is always prefixed with either the plus sign or the minus
- * sign. Otherwise, only negative exponents are prefixed with the minus sign. This has
- * no effect unless <code>useExponentialNotation</code> is true.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private boolean exponentSignAlwaysShown = false;
-
- /**
- * The value to which numbers are rounded during formatting. For example, if the
- * rounding increment is 0.05, then 13.371 would be formatted as 13.350, assuming 3
- * fraction digits. Has the value <code>null</code> if rounding is not in effect, or a
- * positive value if rounding is in effect. Default value <code>null</code>.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- // Note: this is kept in sync with roundingIncrementICU.
- // it is only kept around to avoid a conversion when formatting a java.math.BigDecimal
- private java.math.BigDecimal roundingIncrement = null;
-
- /**
- * The value to which numbers are rounded during formatting. For example, if the
- * rounding increment is 0.05, then 13.371 would be formatted as 13.350, assuming 3
- * fraction digits. Has the value <code>null</code> if rounding is not in effect, or a
- * positive value if rounding is in effect. Default value <code>null</code>. WARNING:
- * the roundingIncrement value is the one serialized.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private transient BigDecimal roundingIncrementICU = null;
-
- /**
- * The rounding mode. This value controls any rounding operations which occur when
- * applying a rounding increment or when reducing the number of fraction digits to
- * satisfy a maximum fraction digits limit. The value may assume any of the
- * <code>BigDecimal</code> rounding mode values. Default value
- * <code>BigDecimal.ROUND_HALF_EVEN</code>.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private int roundingMode = BigDecimal.ROUND_HALF_EVEN;
-
- /**
- * Operations on <code>BigDecimal</code> numbers are controlled by a {@link
- * MathContext} object, which provides the context (precision and other information)
- * for the operation. The default <code>MathContext</code> settings are
- * <code>digits=0, form=PLAIN, lostDigits=false, roundingMode=ROUND_HALF_UP</code>;
- * these settings perform fixed point arithmetic with unlimited precision, as defined
- * for the original BigDecimal class in Java 1.1 and Java 1.2
- */
- // context for plain unlimited math
- private MathContext mathContext = new MathContext(0, MathContext.PLAIN);
-
- /**
- * The padded format width, or zero if there is no padding. Must be &gt;= 0. Default
- * value zero.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private int formatWidth = 0;
-
- /**
- * The character used to pad the result of format to <code>formatWidth</code>, if
- * padding is in effect. Default value ' '.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private char pad = ' ';
-
- /**
- * The position in the string at which the <code>pad</code> character will be
- * inserted, if padding is in effect. Must have a value from
- * <code>PAD_BEFORE_PREFIX</code> to <code>PAD_AFTER_SUFFIX</code>. Default value
- * <code>PAD_BEFORE_PREFIX</code>.
- *
- * @serial
- * @since AlphaWorks NumberFormat
- */
- private int padPosition = PAD_BEFORE_PREFIX;
-
- /**
- * True if {@link #parse(String, ParsePosition)} to return BigDecimal rather than
- * Long, Double or BigDecimal except special values. This property is introduced for
- * J2SE 5 compatibility support.
- *
- * @serial
- * @since ICU 3.6
- * @see #setParseBigDecimal(boolean)
- * @see #isParseBigDecimal()
- */
- private boolean parseBigDecimal = false;
-
- /**
- * The currency usage for the NumberFormat(standard or cash usage).
- * It is used as STANDARD by default
- * @since ICU 54
- */
- private CurrencyUsage currencyUsage = CurrencyUsage.STANDARD;
-
- // ----------------------------------------------------------------------
-
- static final int currentSerialVersion = 4;
-
- /**
- * The internal serial version which says which version was written Possible values
- * are:
- *
- * <ul>
- *
- * <li><b>0</b> (default): versions before JDK 1.2
- *
- * <li><b>1</b>: version from JDK 1.2 and later, which includes the two new fields
- * <code>useExponentialNotation</code> and <code>minExponentDigits</code>.
- *
- * <li><b>2</b>: version on AlphaWorks, which adds roundingMode, formatWidth, pad,
- * padPosition, exponentSignAlwaysShown, roundingIncrement.
- *
- * <li><b>3</b>: ICU 2.2. Adds currency object.
- *
- * <li><b>4</b>: ICU 54. Adds currency usage(standard vs cash)
- *
- * </ul>
- *
- * @serial
- */
- private int serialVersionOnStream = currentSerialVersion;
-
- // ----------------------------------------------------------------------
- // CONSTANTS
- // ----------------------------------------------------------------------
-
- /**
- * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to
- * specify pad characters inserted before the prefix.
- *
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_BEFORE_PREFIX = 0;
-
- /**
- * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to
- * specify pad characters inserted after the prefix.
- *
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_AFTER_PREFIX = 1;
-
- /**
- * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to
- * specify pad characters inserted before the suffix.
- *
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_AFTER_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_BEFORE_SUFFIX = 2;
-
- /**
- * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to
- * specify pad characters inserted after the suffix.
- *
- * @see #setPadPosition
- * @see #getPadPosition
- * @see #PAD_BEFORE_PREFIX
- * @see #PAD_AFTER_PREFIX
- * @see #PAD_BEFORE_SUFFIX
- * @stable ICU 2.0
- */
- public static final int PAD_AFTER_SUFFIX = 3;
-
- // Constants for characters used in programmatic (unlocalized) patterns.
- static final char PATTERN_ZERO_DIGIT = '0';
- static final char PATTERN_ONE_DIGIT = '1';
- static final char PATTERN_TWO_DIGIT = '2';
- static final char PATTERN_THREE_DIGIT = '3';
- static final char PATTERN_FOUR_DIGIT = '4';
- static final char PATTERN_FIVE_DIGIT = '5';
- static final char PATTERN_SIX_DIGIT = '6';
- static final char PATTERN_SEVEN_DIGIT = '7';
- static final char PATTERN_EIGHT_DIGIT = '8';
- static final char PATTERN_NINE_DIGIT = '9';
- static final char PATTERN_GROUPING_SEPARATOR = ',';
- static final char PATTERN_DECIMAL_SEPARATOR = '.';
- static final char PATTERN_DIGIT = '#';
- static final char PATTERN_SIGNIFICANT_DIGIT = '@';
- static final char PATTERN_EXPONENT = 'E';
- static final char PATTERN_PLUS_SIGN = '+';
- static final char PATTERN_MINUS_SIGN = '-';
-
- // Affix
- private static final char PATTERN_PER_MILLE = '\u2030';
- private static final char PATTERN_PERCENT = '%';
- static final char PATTERN_PAD_ESCAPE = '*';
-
- // Other
- private static final char PATTERN_SEPARATOR = ';';
-
- // Pad escape is package private to allow access by DecimalFormatSymbols.
- // Also plus sign. Also exponent.
-
- /**
- * The CURRENCY_SIGN is the standard Unicode symbol for currency. It is used in
- * patterns and substitued with either the currency symbol, or if it is doubled, with
- * the international currency symbol. If the CURRENCY_SIGN is seen in a pattern, then
- * the decimal separator is replaced with the monetary decimal separator.
- *
- * The CURRENCY_SIGN is not localized.
- */
- private static final char CURRENCY_SIGN = '\u00A4';
-
- private static final char QUOTE = '\'';
-
- /**
- * Upper limit on integer and fraction digits for a Java double [Richard/GCL]
- */
- static final int DOUBLE_INTEGER_DIGITS = 309;
- static final int DOUBLE_FRACTION_DIGITS = 340;
-
- /**
- * When someone turns on scientific mode, we assume that more than this number of
- * digits is due to flipping from some other mode that didn't restrict the maximum,
- * and so we force 1 integer digit. We don't bother to track and see if someone is
- * using exponential notation with more than this number, it wouldn't make sense
- * anyway, and this is just to make sure that someone turning on scientific mode with
- * default settings doesn't end up with lots of zeroes.
- */
- static final int MAX_SCIENTIFIC_INTEGER_DIGITS = 8;
-
- // Proclaim JDK 1.1 serial compatibility.
- private static final long serialVersionUID = 864413376551465018L;
-
- private ArrayList<FieldPosition> attributes = new ArrayList<FieldPosition>();
-
- // The following are used in currency format
-
- // -- triple currency sign char array
- // private static final char[] tripleCurrencySign = {0xA4, 0xA4, 0xA4};
- // -- triple currency sign string
- // private static final String tripleCurrencyStr = new String(tripleCurrencySign);
- //
- // -- default currency plural pattern char array
- // private static final char[] defaultCurrencyPluralPatternChar =
- // {0, '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4};
- // -- default currency plural pattern string
- // private static final String defaultCurrencyPluralPattern =
- // new String(defaultCurrencyPluralPatternChar);
-
- // pattern used in this formatter
- private String formatPattern = "";
- // style is only valid when decimal formatter is constructed by
- // DecimalFormat(pattern, decimalFormatSymbol, style)
- private int style = NumberFormat.NUMBERSTYLE;
- /**
- * Represents whether this is a currency format, and which currency format style. 0:
- * not currency format type; 1: currency style -- symbol name, such as "$" for US
- * dollar. 2: currency style -- ISO name, such as USD for US dollar. 3: currency style
- * -- plural long name, such as "US Dollar" for "1.00 US Dollar", or "US Dollars" for
- * "3.00 US Dollars".
- */
- private int currencySignCount = CURRENCY_SIGN_COUNT_ZERO;
-
- /**
- * For parsing purposes, we need to remember all prefix patterns and suffix patterns
- * of every currency format pattern, including the pattern of the default currency
- * style, ISO currency style, and plural currency style. The patterns are set through
- * applyPattern. The following are used to represent the affix patterns in currency
- * plural formats.
- */
- private static final class AffixForCurrency {
- // negative prefix pattern
- private String negPrefixPatternForCurrency = null;
- // negative suffix pattern
- private String negSuffixPatternForCurrency = null;
- // positive prefix pattern
- private String posPrefixPatternForCurrency = null;
- // positive suffix pattern
- private String posSuffixPatternForCurrency = null;
- private final int patternType;
-
- public AffixForCurrency(String negPrefix, String negSuffix, String posPrefix,
- String posSuffix, int type) {
- negPrefixPatternForCurrency = negPrefix;
- negSuffixPatternForCurrency = negSuffix;
- posPrefixPatternForCurrency = posPrefix;
- posSuffixPatternForCurrency = posSuffix;
- patternType = type;
- }
-
- public String getNegPrefix() {
- return negPrefixPatternForCurrency;
- }
-
- public String getNegSuffix() {
- return negSuffixPatternForCurrency;
- }
-
- public String getPosPrefix() {
- return posPrefixPatternForCurrency;
- }
-
- public String getPosSuffix() {
- return posSuffixPatternForCurrency;
- }
-
- public int getPatternType() {
- return patternType;
- }
- }
-
- // Affix pattern set for currency. It is a set of AffixForCurrency, each element of
- // the set saves the negative prefix, negative suffix, positive prefix, and positive
- // suffix of a pattern.
- private transient Set<AffixForCurrency> affixPatternsForCurrency = null;
-
- // For currency parsing. Since currency parsing needs to parse against all currency
- // patterns, before the parsing, we need to set up the affix patterns for all currencies.
- private transient boolean isReadyForParsing = false;
-
- // Information needed for DecimalFormat to format/parse currency plural.
- private CurrencyPluralInfo currencyPluralInfo = null;
-
- /**
- * Unit is an immutable class for the textual representation of a unit, in
- * particular its prefix and suffix.
- *
- * @author rocketman
- *
- */
- static class Unit {
- private final String prefix;
- private final String suffix;
-
- public Unit(String prefix, String suffix) {
- this.prefix = prefix;
- this.suffix = suffix;
- }
-
- public void writeSuffix(StringBuffer toAppendTo) {
- toAppendTo.append(suffix);
- }
-
- public void writePrefix(StringBuffer toAppendTo) {
- toAppendTo.append(prefix);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof Unit)) {
- return false;
- }
- Unit other = (Unit) obj;
- return prefix.equals(other.prefix) && suffix.equals(other.suffix);
- }
- @Override
- public String toString() {
- return prefix + "/" + suffix;
- }
- }
-
- static final Unit NULL_UNIT = new Unit("", "");
-
- // Note about rounding implementation
- //
- // The original design intended to skip rounding operation when roundingIncrement is not
- // set. However, rounding may need to occur when fractional digits exceed the width of
- // fractional part of pattern.
- //
- // DigitList class has built-in rounding mechanism, using ROUND_HALF_EVEN. This implementation
- // forces non-null roundingIncrement if the setting is other than ROUND_HALF_EVEN, otherwise,
- // when rounding occurs in DigitList by pattern's fractional digits' width, the result
- // does not match the rounding mode.
- //
- // Ideally, all rounding operation should be done in one place like ICU4C trunk does
- // (ICU4C rounding implementation was rewritten recently). This is intrim implemetation
- // to fix various issues. In the future, we should entire implementation of rounding
- // in this class, like ICU4C did.
- //
- // Once we fully implement rounding logic in DigitList, then following fields and methods
- // should be gone.
-
- private transient BigDecimal actualRoundingIncrementICU = null;
- private transient java.math.BigDecimal actualRoundingIncrement = null;
-
- /*
- * The actual rounding increment as a double.
- */
- private transient double roundingDouble = 0.0;
-
- /*
- * If the roundingDouble is the reciprocal of an integer (the most common case!), this
- * is set to be that integer. Otherwise it is 0.0.
- */
- private transient double roundingDoubleReciprocal = 0.0;
-
- /*
- * Set roundingDouble, roundingDoubleReciprocal and actualRoundingIncrement
- * based on rounding mode and width of fractional digits. Whenever setting affecting
- * rounding mode, rounding increment and maximum width of fractional digits, then
- * this method must be called.
- *
- * roundingIncrementICU is the field storing the custom rounding increment value,
- * while actual rounding increment could be larger.
- */
- private void resetActualRounding() {
- if (roundingIncrementICU != null) {
- BigDecimal byWidth = getMaximumFractionDigits() > 0 ?
- BigDecimal.ONE.movePointLeft(getMaximumFractionDigits()) : BigDecimal.ONE;
- if (roundingIncrementICU.compareTo(byWidth) >= 0) {
- actualRoundingIncrementICU = roundingIncrementICU;
- } else {
- actualRoundingIncrementICU = byWidth.equals(BigDecimal.ONE) ? null : byWidth;
- }
- } else {
- if (roundingMode == BigDecimal.ROUND_HALF_EVEN || isScientificNotation()) {
- // This rounding fix is irrelevant if mode is ROUND_HALF_EVEN as DigitList
- // does ROUND_HALF_EVEN for us. This rounding fix won't work at all for
- // scientific notation.
- actualRoundingIncrementICU = null;
- } else {
- if (getMaximumFractionDigits() > 0) {
- actualRoundingIncrementICU = BigDecimal.ONE.movePointLeft(getMaximumFractionDigits());
- } else {
- actualRoundingIncrementICU = BigDecimal.ONE;
- }
- }
- }
-
- if (actualRoundingIncrementICU == null) {
- setRoundingDouble(0.0d);
- actualRoundingIncrement = null;
- } else {
- setRoundingDouble(actualRoundingIncrementICU.doubleValue());
- actualRoundingIncrement = actualRoundingIncrementICU.toBigDecimal();
- }
- }
-
- static final double roundingIncrementEpsilon = 0.000000001;
-
- private void setRoundingDouble(double newValue) {
- roundingDouble = newValue;
- if (roundingDouble > 0.0d) {
- double rawRoundedReciprocal = 1.0d / roundingDouble;
- roundingDoubleReciprocal = Math.rint(rawRoundedReciprocal);
- if (Math.abs(rawRoundedReciprocal - roundingDoubleReciprocal) > roundingIncrementEpsilon) {
- roundingDoubleReciprocal = 0.0d;
- }
- } else {
- roundingDoubleReciprocal = 0.0d;
- }
- }
-}
-
-// eof
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java
deleted file mode 100644
index 1d80c5b08..000000000
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java
+++ /dev/null
@@ -1,842 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-/*
- *******************************************************************************
- * Copyright (C) 1996-2015, International Business Machines Corporation and *
- * others. All Rights Reserved. *
- *******************************************************************************
- */
-package com.ibm.icu.dev.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
- * division of labor between <code>DigitList</code> and
- * <code>DecimalFormat</code> is that <code>DigitList</code> handles the radix
- * 10 representation issues and numeric conversion, including rounding;
- * <code>DecimalFormat</code> handles the locale-specific issues such as
- * positive and negative representation, digit grouping, decimal point,
- * currency, and so on.
- *
- * <p>A <code>DigitList</code> is a representation of a finite numeric value.
- * <code>DigitList</code> objects do not represent <code>NaN</code> or infinite
- * values. A <code>DigitList</code> value can be converted to a
- * <code>BigDecimal</code> without loss of precision. Conversion to other
- * numeric formats may involve loss of precision, depending on the specific
- * value.
- *
- * <p>The <code>DigitList</code> representation consists of a string of
- * characters, which are the digits radix 10, from '0' to '9'. It also has a
- * base 10 exponent associated with it. The value represented by a
- * <code>DigitList</code> object can be computed by mulitplying the fraction
- * <em>f</em>, where 0 <= <em>f</em> < 1, derived by placing all the digits of
- * the list to the right of the decimal point, by 10^exponent.
- *
- * @see java.util.Locale
- * @see java.text.Format
- * @see NumberFormat
- * @see DecimalFormat
- * @see java.text.ChoiceFormat
- * @see java.text.MessageFormat
- * @version 1.18 08/12/98
- * @author Mark Davis, Alan Liu
- * */
-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
- * will be generated, and should not be decreased, or accuracy will be lost.
- */
- public static final int MAX_LONG_DIGITS = 19; // == Long.toString(Long.MAX_VALUE).length()
- public static final int DBL_DIG = 17;
-
- /**
- * These data members are intentionally public and can be set directly.
- *
- * The value represented is given by placing the decimal point before
- * digits[decimalAt]. If decimalAt is < 0, then leading zeros between
- * the decimal point and the first nonzero digit are implied. If decimalAt
- * is > count, then trailing zeros between the digits[count-1] and the
- * decimal point are implied.
- *
- * Equivalently, the represented value is given by f * 10^decimalAt. Here
- * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to
- * the right of the decimal.
- *
- * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We
- * don't allow denormalized numbers because our exponent is effectively of
- * unlimited magnitude. The count value contains the number of significant
- * digits present in digits[].
- *
- * Zero is represented by any DigitList with count == 0 or with each digits[i]
- * for all i <= count == '0'.
- */
- public int decimalAt = 0;
- public int count = 0;
- public byte[] digits = new byte[MAX_LONG_DIGITS];
-
- private final void ensureCapacity(int digitCapacity, int digitsToCopy) {
- if (digitCapacity > digits.length) {
- byte[] newDigits = new byte[digitCapacity * 2];
- System.arraycopy(digits, 0, newDigits, 0, digitsToCopy);
- digits = newDigits;
- }
- }
-
- /**
- * Return true if the represented number is zero.
- */
- boolean isZero()
- {
- for (int i=0; i<count; ++i) if (digits[i] != '0') return false;
- return true;
- }
-
-// Unused as of ICU 2.6 - alan
-// /**
-// * Clears out the digits.
-// * Use before appending them.
-// * Typically, you set a series of digits with append, then at the point
-// * you hit the decimal point, you set myDigitList.decimalAt = myDigitList.count;
-// * then go on appending digits.
-// */
-// public void clear () {
-// decimalAt = 0;
-// count = 0;
-// }
-
- /**
- * Appends digits to the list.
- */
- public void append (int digit) {
- 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
- * mimics Long.parseLong().
- */
- public final double getDouble() {
- if (count == 0) return 0.0;
- StringBuilder temp = new StringBuilder(count);
- temp.append('.');
- for (int i = 0; i < count; ++i) temp.append((char)(digits[i]));
- temp.append('E');
- temp.append(Integer.toString(decimalAt));
- return Double.valueOf(temp.toString()).doubleValue();
- // long value = Long.parseLong(temp.toString());
- // return (value * Math.pow(10, decimalAt - count));
- }
-
- /**
- * Utility routine to get the value of the digit list.
- * If (count == 0) this returns 0, unlike Long.parseLong().
- */
- public final long getLong() {
- // for now, simple implementation; later, do proper IEEE native stuff
-
- if (count == 0) return 0;
-
- // We have to check for this, because this is the one NEGATIVE value
- // we represent. If we tried to just pass the digits off to parseLong,
- // we'd get a parse failure.
- if (isLongMIN_VALUE()) return Long.MIN_VALUE;
-
- StringBuilder temp = new StringBuilder(count);
- for (int i = 0; i < decimalAt; ++i)
- {
- temp.append((i < count) ? (char)(digits[i]) : '0');
- }
- return Long.parseLong(temp.toString());
- }
-
- /**
- * Return a <code>BigInteger</code> representing the value stored in this
- * <code>DigitList</code>. This method assumes that this object contains
- * an integral value; if not, it will return an incorrect value.
- * [bnf]
- * @param isPositive determines the sign of the returned result
- * @return the value of this object as a <code>BigInteger</code>
- */
- public BigInteger getBigInteger(boolean isPositive) {
- if (isZero()) return BigInteger.valueOf(0);
- //Eclipse stated the following is "dead code"
- /*if (false) {
- StringBuilder stringRep = new StringBuilder(count);
- if (!isPositive) {
- stringRep.append('-');
- }
- for (int i=0; i<count; ++i) {
- stringRep.append((char) digits[i]);
- }
- int d = decimalAt;
- while (d-- > count) {
- stringRep.append('0');
- }
- return new BigInteger(stringRep.toString());
- } else*/ {
- int len = decimalAt > count ? decimalAt : count;
- if (!isPositive) {
- len += 1;
- }
- char[] text = new char[len];
- int n = 0;
- if (!isPositive) {
- text[0] = '-';
- for (int i = 0; i < count; ++i) {
- text[i+1] = (char)digits[i];
- }
- n = count+1;
- } else {
- for (int i = 0; i < count; ++i) {
- text[i] = (char)digits[i];
- }
- n = count;
- }
- for (int i = n; i < text.length; ++i) {
- text[i] = '0';
- }
- return new BigInteger(new String(text));
- }
- }
-
- private String getStringRep(boolean isPositive) {
- if (isZero()) return "0";
- StringBuilder stringRep = new StringBuilder(count+1);
- if (!isPositive) {
- stringRep.append('-');
- }
- int d = decimalAt;
- if (d < 0) {
- stringRep.append('.');
- while (d < 0) {
- stringRep.append('0');
- ++d;
- }
- d = -1;
- }
- for (int i=0; i<count; ++i) {
- if (d == i) {
- stringRep.append('.');
- }
- stringRep.append((char) digits[i]);
- }
- while (d-- > count) {
- stringRep.append('0');
- }
- return stringRep.toString();
- }
-
- /**
- * Return an <code>ICU BigDecimal</code> representing the value stored in this
- * <code>DigitList</code>.
- * [bnf]
- * @param isPositive determines the sign of the returned result
- * @return the value of this object as a <code>BigDecimal</code>
- */
- public com.ibm.icu.math.BigDecimal getBigDecimalICU(boolean isPositive) {
- if (isZero()) {
- return com.ibm.icu.math.BigDecimal.valueOf(0);
- }
- // if exponential notion is negative,
- // we prefer to use BigDecimal constructor with scale,
- // because it works better when extremely small value
- // is used. See #5698.
- long scale = (long)count - (long)decimalAt;
- if (scale > 0) {
- int numDigits = count;
- if (scale > Integer.MAX_VALUE) {
- // try to reduce the scale
- long numShift = scale - Integer.MAX_VALUE;
- if (numShift < count) {
- numDigits -= numShift;
- } else {
- // fallback to 0
- return new com.ibm.icu.math.BigDecimal(0);
- }
- }
- StringBuilder significantDigits = new StringBuilder(numDigits + 1);
- if (!isPositive) {
- significantDigits.append('-');
- }
- for (int i = 0; i < numDigits; i++) {
- significantDigits.append((char)digits[i]);
- }
- BigInteger unscaledVal = new BigInteger(significantDigits.toString());
- return new com.ibm.icu.math.BigDecimal(unscaledVal, (int)scale);
- } else {
- return new com.ibm.icu.math.BigDecimal(getStringRep(isPositive));
- }
- }
-
- /**
- * Return whether or not this objects represented value is an integer.
- * [bnf]
- * @return true if the represented value of this object is an integer
- */
- boolean isIntegral() {
- // Trim trailing zeros. This does not change the represented value.
- while (count > 0 && digits[count - 1] == (byte)'0') --count;
- return count == 0 || decimalAt >= count;
- }
-
-// Unused as of ICU 2.6 - alan
-// /**
-// * Return true if the number represented by this object can fit into
-// * a long.
-// */
-// boolean fitsIntoLong(boolean isPositive)
-// {
-// // Figure out if the result will fit in a long. We have to
-// // first look for nonzero digits after the decimal point;
-// // then check the size. If the digit count is 18 or less, then
-// // the value can definitely be represented as a long. If it is 19
-// // then it may be too large.
-//
-// // Trim trailing zeros. This does not change the represented value.
-// while (count > 0 && digits[count - 1] == (byte)'0') --count;
-//
-// if (count == 0) {
-// // Positive zero fits into a long, but negative zero can only
-// // be represented as a double. - bug 4162852
-// return isPositive;
-// }
-//
-// if (decimalAt < count || decimalAt > MAX_LONG_DIGITS) return false;
-//
-// if (decimalAt < MAX_LONG_DIGITS) return true;
-//
-// // At this point we have decimalAt == count, and count == MAX_LONG_DIGITS.
-// // The number will overflow if it is larger than 9223372036854775807
-// // or smaller than -9223372036854775808.
-// for (int i=0; i<count; ++i)
-// {
-// byte dig = digits[i], max = LONG_MIN_REP[i];
-// if (dig > max) return false;
-// if (dig < max) return true;
-// }
-//
-// // At this point the first count digits match. If decimalAt is less
-// // than count, then the remaining digits are zero, and we return true.
-// if (count < decimalAt) return true;
-//
-// // Now we have a representation of Long.MIN_VALUE, without the leading
-// // negative sign. If this represents a positive value, then it does
-// // not fit; otherwise it fits.
-// return !isPositive;
-// }
-
-// Unused as of ICU 2.6 - alan
-// /**
-// * Set the digit list to a representation of the given double value.
-// * This method supports fixed-point notation.
-// * @param source Value to be converted; must not be Inf, -Inf, Nan,
-// * or a value <= 0.
-// * @param maximumFractionDigits The most fractional digits which should
-// * be converted.
-// */
-// public final void set(double source, int maximumFractionDigits)
-// {
-// set(source, maximumFractionDigits, true);
-// }
-
- /**
- * Set the digit list to a representation of the given double value.
- * This method supports both fixed-point and exponential notation.
- * @param source Value to be converted; must not be Inf, -Inf, Nan,
- * or a value <= 0.
- * @param maximumDigits The most fractional or total digits which should
- * be converted.
- * @param fixedPoint If true, then maximumDigits is the maximum
- * fractional digits to be converted. If false, total digits.
- */
- final void set(double source, int maximumDigits, boolean fixedPoint)
- {
- if (source == 0) source = 0;
- // Generate a representation of the form DDDDD, DDDDD.DDDDD, or
- // DDDDDE+/-DDDDD.
- String rep = Double.toString(source);
-
- didRound = false;
-
- set(rep, MAX_LONG_DIGITS);
-
- if (fixedPoint) {
- // The negative of the exponent represents the number of leading
- // zeros between the decimal and the first non-zero digit, for
- // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
- // is more than the maximum fraction digits, then we have an underflow
- // for the printed representation.
- if (-decimalAt > maximumDigits) {
- count = 0;
- return;
- } else if (-decimalAt == maximumDigits) {
- if (shouldRoundUp(0)) {
- count = 1;
- ++decimalAt;
- digits[0] = (byte)'1';
- } else {
- count = 0;
- }
- return;
- }
- // else fall through
- }
-
- // Eliminate trailing zeros.
- while (count > 1 && digits[count - 1] == '0')
- --count;
-
- // Eliminate digits beyond maximum digits to be displayed.
- // Round up if appropriate.
- round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits);
- }
-
- /**
- * Given a string representation of the form DDDDD, DDDDD.DDDDD,
- * or DDDDDE+/-DDDDD, set this object's value to it. Ignore
- * any leading '-'.
- */
- private void set(String rep, int maxCount) {
- decimalAt = -1;
- count = 0;
- int exponent = 0;
- // Number of zeros between decimal point and first non-zero digit after
- // decimal point, for numbers < 1.
- int leadingZerosAfterDecimal = 0;
- boolean nonZeroDigitSeen = false;
- // Skip over leading '-'
- int i=0;
- if (rep.charAt(i) == '-') {
- ++i;
- }
- for (; i < rep.length(); ++i) {
- char c = rep.charAt(i);
- if (c == '.') {
- decimalAt = count;
- } else if (c == 'e' || c == 'E') {
- ++i;
- // Integer.parseInt doesn't handle leading '+' signs
- if (rep.charAt(i) == '+') {
- ++i;
- }
- exponent = Integer.valueOf(rep.substring(i)).intValue();
- break;
- } else if (count < maxCount) {
- if (!nonZeroDigitSeen) {
- nonZeroDigitSeen = (c != '0');
- if (!nonZeroDigitSeen && decimalAt != -1) {
- ++leadingZerosAfterDecimal;
- }
- }
-
- if (nonZeroDigitSeen) {
- ensureCapacity(count+1, count);
- digits[count++] = (byte)c;
- }
- }
- }
- if (decimalAt == -1) {
- decimalAt = count;
- }
- decimalAt += exponent - leadingZerosAfterDecimal;
- }
-
- /**
- * Return true if truncating the representation to the given number
- * of digits will result in an increment to the last digit. This
- * method implements half-even rounding, the default rounding mode.
- * [bnf]
- * @param maximumDigits the number of digits to keep, from 0 to
- * <code>count-1</code>. If 0, then all digits are rounded away, and
- * this method returns true if a one should be generated (e.g., formatting
- * 0.09 with "#.#").
- * @return true if digit <code>maximumDigits-1</code> should be
- * incremented
- */
- private boolean shouldRoundUp(int maximumDigits) {
- // variable not used boolean increment = false;
- // Implement IEEE half-even rounding
- /*Bug 4243108
- format(0.0) gives "0.1" if preceded by parse("99.99") [Richard/GCL]
- */
- if (maximumDigits < count) {
- if (digits[maximumDigits] > '5') {
- return true;
- } else if (digits[maximumDigits] == '5' ) {
- for (int i=maximumDigits+1; i<count; ++i) {
- if (digits[i] != '0') {
- return true;
- }
- }
- return maximumDigits > 0 && (digits[maximumDigits-1] % 2 != 0);
- }
- }
- return false;
- }
-
- /**
- * Round the representation to the given number of digits.
- * @param maximumDigits The maximum number of digits to be shown.
- * Upon return, count will be less than or equal to maximumDigits.
- * This now performs rounding when maximumDigits is 0, formerly it did not.
- */
- public final void round(int maximumDigits) {
- // Eliminate digits beyond maximum digits to be displayed.
- // Round up if appropriate.
- // [bnf] rewritten to fix 4179818
- if (maximumDigits >= 0 && maximumDigits < count) {
- if (shouldRoundUp(maximumDigits)) {
- // Rounding up involves incrementing digits from LSD to MSD.
- // In most cases this is simple, but in a worst case situation
- // (9999..99) we have to adjust the decimalAt value.
- for (;;)
- {
- --maximumDigits;
- if (maximumDigits < 0)
- {
- // We have all 9's, so we increment to a single digit
- // of one and adjust the exponent.
- digits[0] = (byte) '1';
- ++decimalAt;
- maximumDigits = 0; // Adjust the count
- didRound = true;
- break;
- }
-
- ++digits[maximumDigits];
- didRound = true;
- if (digits[maximumDigits] <= '9') break;
- // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
- }
- ++maximumDigits; // Increment for use as count
- }
- count = maximumDigits;
- }
- // Bug 4217661 DecimalFormat formats 1.001 to "1.00" instead of "1"
- // Eliminate trailing zeros. [Richard/GCL]
- // [dlf] moved outside if block, see ticket #6408
- while (count > 1 && digits[count-1] == '0') {
- --count;
- }
- }
-
- // 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.
- * false indicates rounding has not been done.
- */
- public boolean wasRounded() {
- return didRound;
- }
-
- /**
- * Utility routine to set the value of the digit list from a long
- */
- public final void set(long source)
- {
- set(source, 0);
- }
-
- /**
- * Set the digit list to a representation of the given long value.
- * @param source Value to be converted; must be >= 0 or ==
- * Long.MIN_VALUE.
- * @param maximumDigits The most digits which should be converted.
- * If maximumDigits is lower than the number of significant digits
- * in source, the representation will be rounded. Ignored if <= 0.
- */
- public final void set(long source, int maximumDigits)
- {
- // This method does not expect a negative number. However,
- // "source" can be a Long.MIN_VALUE (-9223372036854775808),
- // if the number being formatted is a Long.MIN_VALUE. In that
- // case, it will be formatted as -Long.MIN_VALUE, a number
- // which is outside the legal range of a long, but which can
- // be represented by DigitList.
- // [NEW] Faster implementation
- didRound = false;
-
- if (source <= 0) {
- if (source == Long.MIN_VALUE) {
- decimalAt = count = MAX_LONG_DIGITS;
- System.arraycopy(LONG_MIN_REP, 0, digits, 0, count);
- } else {
- count = 0;
- decimalAt = 0;
- }
- } else {
- int left = MAX_LONG_DIGITS;
- int right;
- while (source > 0) {
- digits[--left] = (byte) (('0') + (source % 10));
- source /= 10;
- }
- decimalAt = MAX_LONG_DIGITS-left;
- // Don't copy trailing zeros
- // we are guaranteed that there is at least one non-zero digit,
- // so we don't have to check lower bounds
- 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);
- }
-
- /**
- * Set the digit list to a representation of the given BigInteger value.
- * [bnf]
- * @param source Value to be converted
- * @param maximumDigits The most digits which should be converted.
- * If maximumDigits is lower than the number of significant digits
- * in source, the representation will be rounded. Ignored if <= 0.
- */
- public final void set(BigInteger source, int maximumDigits) {
- String stringDigits = source.toString();
-
- count = decimalAt = stringDigits.length();
- didRound = false;
-
- // Don't copy trailing zeros
- while (count > 1 && stringDigits.charAt(count - 1) == '0') --count;
-
- int offset = 0;
- if (stringDigits.charAt(0) == '-') {
- ++offset;
- --count;
- --decimalAt;
- }
-
- ensureCapacity(count, 0);
- for (int i = 0; i < count; ++i) {
- digits[i] = (byte) stringDigits.charAt(i + offset);
- }
-
- if (maximumDigits > 0) round(maximumDigits);
- }
-
- /**
- * Internal method that sets this digit list to represent the
- * given value. The value is given as a String of the format
- * returned by BigDecimal.
- * @param stringDigits value to be represented with the following
- * syntax, expressed as a regular expression: -?\d*.?\d*
- * Must not be an empty string.
- * @param maximumDigits The most digits which should be converted.
- * If maximumDigits is lower than the number of significant digits
- * in source, the representation will be rounded. Ignored if <= 0.
- * @param fixedPoint If true, then maximumDigits is the maximum
- * fractional digits to be converted. If false, total digits.
- */
- private void setBigDecimalDigits(String stringDigits,
- int maximumDigits, boolean fixedPoint) {
-//| // Find the first non-zero digit, the decimal, and the last non-zero digit.
-//| int first=-1, last=stringDigits.length()-1, decimal=-1;
-//| for (int i=0; (first<0 || decimal<0) && i<=last; ++i) {
-//| char c = stringDigits.charAt(i);
-//| if (c == '.') {
-//| decimal = i;
-//| } else if (first < 0 && (c >= '1' && c <= '9')) {
-//| first = i;
-//| }
-//| }
-//|
-//| if (first < 0) {
-//| clear();
-//| return;
-//| }
-//|
-//| // At this point we know there is at least one non-zero digit, so the
-//| // following loop is safe.
-//| for (;;) {
-//| char c = stringDigits.charAt(last);
-//| if (c != '0' && c != '.') {
-//| break;
-//| }
-//| --last;
-//| }
-//|
-//| if (decimal < 0) {
-//| decimal = stringDigits.length();
-//| }
-//|
-//| count = last - first;
-//| if (decimal < first || decimal > last) {
-//| ++count;
-//| }
-//| decimalAt = decimal - first;
-//| if (decimalAt < 0) {
-//| ++decimalAt;
-//| }
-//|
-//| ensureCapacity(count, 0);
-//| for (int i = 0; i < count; ++i) {
-//| digits[i] = (byte) stringDigits.charAt(first++);
-//| if (first == decimal) {
-//| ++first;
-//| }
-//| }
-
- didRound = false;
-
- // The maxDigits here could also be Integer.MAX_VALUE
- set(stringDigits, stringDigits.length());
-
- // Eliminate digits beyond maximum digits to be displayed.
- // Round up if appropriate.
- // {dlf} Some callers depend on passing '0' to round to mean 'don't round', but
- // rather than pass that information explicitly, we rely on some magic with maximumDigits
- // and decimalAt. Unfortunately, this is no good, because there are cases where maximumDigits
- // is zero and we do want to round, e.g. BigDecimal values -1 < x < 1. So since round
- // changed to perform rounding when the argument is 0, we now force the argument
- // to -1 in the situations where it matters.
- round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits);
- }
-
- /**
- * Set the digit list to a representation of the given BigDecimal value.
- * [bnf]
- * @param source Value to be converted
- * @param maximumDigits The most digits which should be converted.
- * If maximumDigits is lower than the number of significant digits
- * in source, the representation will be rounded. Ignored if <= 0.
- * @param fixedPoint If true, then maximumDigits is the maximum
- * fractional digits to be converted. If false, total digits.
- */
- public final void set(java.math.BigDecimal source,
- int maximumDigits, boolean fixedPoint) {
- setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint);
- }
-
- /*
- * Set the digit list to a representation of the given BigDecimal value.
- * [bnf]
- * @param source Value to be converted
- * @param maximumDigits The most digits which should be converted.
- * If maximumDigits is lower than the number of significant digits
- * in source, the representation will be rounded. Ignored if <= 0.
- * @param fixedPoint If true, then maximumDigits is the maximum
- * fractional digits to be converted. If false, total digits.
- */
- public final void set(com.ibm.icu.math.BigDecimal source,
- int maximumDigits, boolean fixedPoint) {
- setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint);
- }
-
- /**
- * Returns true if this DigitList represents Long.MIN_VALUE;
- * false, otherwise. This is required so that getLong() works.
- */
- private boolean isLongMIN_VALUE()
- {
- if (decimalAt != count || count != MAX_LONG_DIGITS)
- return false;
-
- for (int i = 0; i < count; ++i)
- {
- if (digits[i] != LONG_MIN_REP[i]) return false;
- }
-
- return true;
- }
-
- private static byte[] LONG_MIN_REP;
-
- static
- {
- // Store the representation of LONG_MIN without the leading '-'
- String s = Long.toString(Long.MIN_VALUE);
- LONG_MIN_REP = new byte[MAX_LONG_DIGITS];
- for (int i=0; i < MAX_LONG_DIGITS; ++i)
- {
- LONG_MIN_REP[i] = (byte)s.charAt(i + 1);
- }
- }
-
-// Unused -- Alan 2003-05
-// /**
-// * Return the floor of the log base 10 of a given double.
-// * This method compensates for inaccuracies which arise naturally when
-// * computing logs, and always give the correct value. The parameter
-// * must be positive and finite.
-// */
-// private static final int log10(double d)
-// {
-// // The reason this routine is needed is that simply taking the
-// // log and dividing by log10 yields a result which may be off
-// // by 1 due to rounding errors. For example, the naive log10
-// // of 1.0e300 taken this way is 299, rather than 300.
-// double log10 = Math.log(d) / LOG10;
-// int ilog10 = (int)Math.floor(log10);
-// // Positive logs could be too small, e.g. 0.99 instead of 1.0
-// if (log10 > 0 && d >= Math.pow(10, ilog10 + 1))
-// {
-// ++ilog10;
-// }
-// // Negative logs could be too big, e.g. -0.99 instead of -1.0
-// else if (log10 < 0 && d < Math.pow(10, ilog10))
-// {
-// --ilog10;
-// }
-// return ilog10;
-// }
-//
-// private static final double LOG10 = Math.log(10.0);
-
- /**
- * equality test between two digit lists.
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) // quick check
- return true;
- if (!(obj instanceof DigitList)) // (1) same object?
- return false;
- DigitList other = (DigitList) obj;
- if (count != other.count ||
- decimalAt != other.decimalAt)
- return false;
- for (int i = 0; i < count; i++)
- if (digits[i] != other.digits[i])
- return false;
- return true;
- }
-
- /**
- * Generates the hash code for the digit list.
- */
- @Override
- public int hashCode() {
- int hashcode = decimalAt;
-
- for (int i = 0; i < count; i++)
- hashcode = hashcode * 37 + digits[i];
-
- return hashcode;
- }
-
- @Override
- public String toString()
- {
- if (isZero()) return "0";
- StringBuilder buf = new StringBuilder("0.");
- for (int i=0; i<count; ++i) buf.append((char)digits[i]);
- buf.append("x10^");
- buf.append(decimalAt);
- return buf.toString();
- }
-}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitListTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitListTest.java
deleted file mode 100644
index ac2153065..000000000
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitListTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-
-package com.ibm.icu.dev.text;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import com.ibm.icu.dev.test.TestFmwk;
-
-
-@RunWith(JUnit4.class)
-public class DigitListTest extends TestFmwk {
-
- private static DigitList digitList = new DigitList();
- private static long testdata = 1414213562;
-
- @Before
- public void init() {
- digitList.set(testdata);
- }
-
- @Test
- public void TestToString() {
- String digitListStr = digitList.toString();
- assertEquals("DigitList incorrect", "0.1414213562x10^10", digitListStr);
- }
- @Test
- public void TestHashCode() {
- int dlHashcode = digitList.hashCode();
- assertEquals("DigitList hash code incorrect", -616183837, dlHashcode);
- }
-
- @Test
- public void TestEquals() {
- DigitList digitList2 = new DigitList();
-
- // Test for success
- digitList2.set(testdata);
- assertTrue("DigitList objects with same values found unequal", digitList.equals(digitList2));
- // Test for failure
- digitList2.set(testdata+1);
- assertFalse("DigitList objects with different values found equal", digitList.equals(digitList2));
- }
-}