ICU-13644 Minor refactoring changes in ICU4J.

X-SVN-Rev: 41129
This commit is contained in:
Shane Carr 2018-03-21 01:37:18 +00:00
parent c940df09e7
commit 369f3484d8
10 changed files with 82 additions and 71 deletions

View File

@ -16,7 +16,6 @@ import java.util.ArrayList;
import java.util.Map;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.parse.NumberParserImpl.ParseMode;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.CurrencyPluralInfo;
import com.ibm.icu.text.PluralRules;
@ -30,6 +29,40 @@ public class DecimalFormatProperties implements Cloneable, Serializable {
/** Auto-generated. */
private static final long serialVersionUID = 4095518955889349243L;
/** Controls the set of rules for parsing a string from the old DecimalFormat API. */
public static enum ParseMode {
/**
* Lenient mode should be used if you want to accept malformed user input. It will use heuristics
* to attempt to parse through typographical errors in the string.
*/
LENIENT,
/**
* Strict mode should be used if you want to require that the input is well-formed. More
* specifically, it differs from lenient mode in the following ways:
*
* <ul>
* <li>Grouping widths must match the grouping settings. For example, "12,3,45" will fail if the
* grouping width is 3, as in the pattern "#,##0".
* <li>The string must contain a complete prefix and suffix. For example, if the pattern is
* "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all fail.
* (The latter strings would be accepted in lenient mode.)
* <li>Whitespace may not appear at arbitrary places in the string. In lenient mode, whitespace
* is allowed to occur arbitrarily before and after prefixes and exponent separators.
* <li>Leading grouping separators are not allowed, as in ",123".
* <li>Minus and plus signs can only appear if specified in the pattern. In lenient mode, a plus
* or minus sign can always precede a number.
* <li>The set of characters that can be interpreted as a decimal or grouping separator is
* smaller.
* <li><strong>If currency parsing is enabled,</strong> currencies must only appear where
* specified in either the current pattern string or in a valid pattern string for the current
* locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but "1.23$" would
* fail to match.
* </ul>
*/
STRICT,
}
// The setters in this class should NOT have any side-effects or perform any validation. It is
// up to the consumer of the property bag to deal with property validation.
@ -44,7 +77,7 @@ public class DecimalFormatProperties implements Cloneable, Serializable {
/| or #equals(), but it will NOT catch if you forget to add it to #hashCode(). |/
/+--------------------------------------------------------------------------------------------*/
private transient Map<String, Map<String, String>> compactCustomData;
private transient Map<String, Map<String, String>> compactCustomData; // ICU4J-only
private transient CompactStyle compactStyle;
private transient Currency currency;
private transient CurrencyPluralInfo currencyPluralInfo;
@ -55,7 +88,7 @@ public class DecimalFormatProperties implements Cloneable, Serializable {
private transient int formatWidth;
private transient int groupingSize;
private transient int magnitudeMultiplier;
private transient MathContext mathContext;
private transient MathContext mathContext; // ICU4J-only
private transient int maximumFractionDigits;
private transient int maximumIntegerDigits;
private transient int maximumSignificantDigits;

View File

@ -11,6 +11,7 @@ import com.ibm.icu.impl.StringSegment;
import com.ibm.icu.impl.number.AffixPatternProvider;
import com.ibm.icu.impl.number.CustomSymbolCurrency;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
import com.ibm.icu.impl.number.Grouper;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
@ -31,45 +32,10 @@ import com.ibm.icu.util.ULocale;
*/
public class NumberParserImpl {
// TODO: Find a better place for this enum.
/** Controls the set of rules for parsing a string. */
public static enum ParseMode {
/**
* Lenient mode should be used if you want to accept malformed user input. It will use heuristics
* to attempt to parse through typographical errors in the string.
*/
LENIENT,
/**
* Strict mode should be used if you want to require that the input is well-formed. More
* specifically, it differs from lenient mode in the following ways:
*
* <ul>
* <li>Grouping widths must match the grouping settings. For example, "12,3,45" will fail if the
* grouping width is 3, as in the pattern "#,##0".
* <li>The string must contain a complete prefix and suffix. For example, if the pattern is
* "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all fail.
* (The latter strings would be accepted in lenient mode.)
* <li>Whitespace may not appear at arbitrary places in the string. In lenient mode, whitespace
* is allowed to occur arbitrarily before and after prefixes and exponent separators.
* <li>Leading grouping separators are not allowed, as in ",123".
* <li>Minus and plus signs can only appear if specified in the pattern. In lenient mode, a plus
* or minus sign can always precede a number.
* <li>The set of characters that can be interpreted as a decimal or grouping separator is
* smaller.
* <li><strong>If currency parsing is enabled,</strong> currencies must only appear where
* specified in either the current pattern string or in a valid pattern string for the current
* locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but "1.23$" would
* fail to match.
* </ul>
*/
STRICT,
}
public static NumberParserImpl createSimpleParser(
ULocale locale,
String pattern,
int parseFlags) {
/**
* Creates a parser with most default options. Used for testing, not production.
*/
public static NumberParserImpl createSimpleParser(ULocale locale, String pattern, int parseFlags) {
NumberParserImpl parser = new NumberParserImpl(parseFlags);
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
@ -97,12 +63,15 @@ public class NumberParserImpl {
parser.addMatcher(PaddingMatcher.getInstance("@"));
parser.addMatcher(ScientificMatcher.getInstance(symbols, grouper));
parser.addMatcher(CurrencyNamesMatcher.getInstance(locale));
parser.addMatcher(new RequireNumberMatcher());
parser.addMatcher(new RequireNumberValidator());
parser.freeze();
return parser;
}
/**
* Parses the string without returning a NumberParserImpl. Used for testing, not production.
*/
public static Number parseStatic(
String input,
ParsePosition ppos,
@ -120,6 +89,9 @@ public class NumberParserImpl {
}
}
/**
* Parses the string without returning a NumberParserImpl. Used for testing, not production.
*/
public static CurrencyAmount parseStaticCurrency(
String input,
ParsePosition ppos,
@ -130,16 +102,8 @@ public class NumberParserImpl {
parser.parse(input, true, result);
if (result.success()) {
ppos.setIndex(result.charEnd);
// TODO: Clean this up
Currency currency;
if (result.currencyCode != null) {
currency = Currency.getInstance(result.currencyCode);
} else {
assert 0 != (result.flags & ParsedNumber.FLAG_HAS_DEFAULT_CURRENCY);
currency = CustomSymbolCurrency
.resolve(properties.getCurrency(), symbols.getULocale(), symbols);
}
return new CurrencyAmount(result.getNumber(), currency);
assert result.currencyCode != null;
return new CurrencyAmount(result.getNumber(), Currency.getInstance(result.currencyCode));
} else {
ppos.setErrorIndex(result.charEnd);
return null;
@ -152,6 +116,20 @@ public class NumberParserImpl {
return createParserFromProperties(properties, symbols, false, optimize);
}
/**
* Creates a parser from the given DecimalFormatProperties. This is the endpoint used by
* DecimalFormat in production code.
*
* @param properties
* The property bag.
* @param symbols
* The locale's symbols.
* @param parseCurrency
* True to force a currency match and use monetary separators; false otherwise.
* @param optimize
* True to construct the lead-chars; false to disable.
* @return An immutable parser object.
*/
public static NumberParserImpl createParserFromProperties(
DecimalFormatProperties properties,
DecimalFormatSymbols symbols,
@ -244,20 +222,20 @@ public class NumberParserImpl {
/// VALIDATORS ///
//////////////////
parser.addMatcher(new RequireNumberMatcher());
parser.addMatcher(new RequireNumberValidator());
if (isStrict) {
parser.addMatcher(new RequireAffixMatcher());
parser.addMatcher(new RequireAffixValidator());
}
if (isStrict && properties.getMinimumExponentDigits() > 0) {
parser.addMatcher(new RequireExponentMatcher());
parser.addMatcher(new RequireExponentValidator());
}
if (parseCurrency) {
parser.addMatcher(new RequireCurrencyMatcher());
parser.addMatcher(new RequireCurrencyValidator());
}
if (properties.getDecimalPatternMatchRequired()) {
boolean patternHasDecimalSeparator = properties.getDecimalSeparatorAlwaysShown()
|| properties.getMaximumFractionDigits() != 0;
parser.addMatcher(RequireDecimalSeparatorMatcher.getInstance(patternHasDecimalSeparator));
parser.addMatcher(RequireDecimalSeparatorValidator.getInstance(patternHasDecimalSeparator));
}
if (properties.getMultiplier() != null) {
// We need to use a math context in order to prevent non-terminating decimal expansions.

View File

@ -52,7 +52,7 @@ public class ParsedNumber {
public static final int FLAG_PERCENT = 0x0002;
public static final int FLAG_PERMILLE = 0x0004;
public static final int FLAG_HAS_EXPONENT = 0x0008;
public static final int FLAG_HAS_DEFAULT_CURRENCY = 0x0010;
// public static final int FLAG_HAS_DEFAULT_CURRENCY = 0x0010; // no longer used
public static final int FLAG_HAS_DECIMAL_SEPARATOR = 0x0020;
public static final int FLAG_NAN = 0x0040;
public static final int FLAG_INFINITY = 0x0080;

View File

@ -21,7 +21,7 @@ public class ParsingUtils {
public static final int PARSE_FLAG_USE_FULL_AFFIXES = 0x0100;
public static final int PARSE_FLAG_EXACT_AFFIX = 0x0200;
public static final int PARSE_FLAG_PLUS_SIGN_ALLOWED = 0x0400;
public static final int PARSE_FLAG_OPTIMIZE = 0x1000;
public static final int PARSE_FLAG_OPTIMIZE = 0x0800;
public static void putLeadCodePoints(UnicodeSet input, UnicodeSet output) {
for (EntryRange range : input.ranges()) {

View File

@ -6,7 +6,7 @@ package com.ibm.icu.impl.number.parse;
* @author sffc
*
*/
public class RequireAffixMatcher extends ValidationMatcher {
public class RequireAffixValidator extends ValidationMatcher {
@Override
public void postProcess(ParsedNumber result) {

View File

@ -6,11 +6,11 @@ package com.ibm.icu.impl.number.parse;
* @author sffc
*
*/
public class RequireCurrencyMatcher extends ValidationMatcher {
public class RequireCurrencyValidator extends ValidationMatcher {
@Override
public void postProcess(ParsedNumber result) {
if (result.currencyCode == null && 0 == (result.flags & ParsedNumber.FLAG_HAS_DEFAULT_CURRENCY)) {
if (result.currencyCode == null) {
result.flags |= ParsedNumber.FLAG_FAIL;
}
}

View File

@ -6,18 +6,18 @@ package com.ibm.icu.impl.number.parse;
* @author sffc
*
*/
public class RequireDecimalSeparatorMatcher extends ValidationMatcher {
public class RequireDecimalSeparatorValidator extends ValidationMatcher {
private static final RequireDecimalSeparatorMatcher A = new RequireDecimalSeparatorMatcher(true);
private static final RequireDecimalSeparatorMatcher B = new RequireDecimalSeparatorMatcher(false);
private static final RequireDecimalSeparatorValidator A = new RequireDecimalSeparatorValidator(true);
private static final RequireDecimalSeparatorValidator B = new RequireDecimalSeparatorValidator(false);
private final boolean patternHasDecimalSeparator;
public static RequireDecimalSeparatorMatcher getInstance(boolean patternHasDecimalSeparator) {
public static RequireDecimalSeparatorValidator getInstance(boolean patternHasDecimalSeparator) {
return patternHasDecimalSeparator ? A : B;
}
private RequireDecimalSeparatorMatcher(boolean patternHasDecimalSeparator) {
private RequireDecimalSeparatorValidator(boolean patternHasDecimalSeparator) {
this.patternHasDecimalSeparator = patternHasDecimalSeparator;
}

View File

@ -6,7 +6,7 @@ package com.ibm.icu.impl.number.parse;
* @author sffc
*
*/
public class RequireExponentMatcher extends ValidationMatcher {
public class RequireExponentValidator extends ValidationMatcher {
@Override
public void postProcess(ParsedNumber result) {

View File

@ -6,7 +6,7 @@ package com.ibm.icu.impl.number.parse;
* @author sffc
*
*/
public class RequireNumberMatcher extends ValidationMatcher {
public class RequireNumberValidator extends ValidationMatcher {
@Override
public void postProcess(ParsedNumber result) {

View File

@ -14,11 +14,11 @@ import java.text.ParsePosition;
import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
import com.ibm.icu.impl.number.parse.NumberParserImpl;
import com.ibm.icu.impl.number.parse.NumberParserImpl.ParseMode;
import com.ibm.icu.impl.number.parse.ParsedNumber;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;