ICU-13513 Starting to tie in with existing code. Working through the data-driven test file first.
X-SVN-Rev: 40726
This commit is contained in:
parent
47d7ebe968
commit
a0de8d89c5
@ -0,0 +1,56 @@
|
||||
// © 2017 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.impl.number;
|
||||
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
|
||||
import com.ibm.icu.text.CurrencyPluralInfo;
|
||||
|
||||
public class CurrencyPluralInfoAffixProvider implements AffixPatternProvider {
|
||||
private final AffixPatternProvider[] affixesByPlural;
|
||||
|
||||
public CurrencyPluralInfoAffixProvider(CurrencyPluralInfo cpi) {
|
||||
affixesByPlural = new ParsedPatternInfo[StandardPlural.COUNT];
|
||||
for (StandardPlural plural : StandardPlural.VALUES) {
|
||||
affixesByPlural[plural.ordinal()] = PatternStringParser
|
||||
.parseToPatternInfo(cpi.getCurrencyPluralPattern(plural.getKeyword()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int flags, int i) {
|
||||
int pluralOrdinal = (flags & Flags.PLURAL_MASK);
|
||||
return affixesByPlural[pluralOrdinal].charAt(flags, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length(int flags) {
|
||||
int pluralOrdinal = (flags & Flags.PLURAL_MASK);
|
||||
return affixesByPlural[pluralOrdinal].length(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean positiveHasPlusSign() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].positiveHasPlusSign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNegativeSubpattern() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].hasNegativeSubpattern();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean negativeHasMinusSign() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].negativeHasMinusSign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrencySign() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].hasCurrencySign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSymbolType(int type) {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].containsSymbolType(type);
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
// © 2017 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.impl.number;
|
||||
|
||||
public class PropertiesAffixPatternProvider implements AffixPatternProvider {
|
||||
private final String posPrefix;
|
||||
private final String posSuffix;
|
||||
private final String negPrefix;
|
||||
private final String negSuffix;
|
||||
|
||||
public PropertiesAffixPatternProvider(DecimalFormatProperties properties) {
|
||||
// There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the
|
||||
// explicit setters (setPositivePrefix and friends). The way to resolve the settings is as follows:
|
||||
//
|
||||
// 1) If the explicit setting is present for the field, use it.
|
||||
// 2) Otherwise, follows UTS 35 rules based on the pattern string.
|
||||
//
|
||||
// Importantly, the explicit setters affect only the one field they override. If you set the positive
|
||||
// prefix, that should not affect the negative prefix. Since it is impossible for the user of this class
|
||||
// to know whether the origin for a string was the override or the pattern, we have to say that we always
|
||||
// have a negative subpattern and perform all resolution logic here.
|
||||
|
||||
// Convenience: Extract the properties into local variables.
|
||||
// Variables are named with three chars: [p/n][p/s][o/p]
|
||||
// [p/n] => p for positive, n for negative
|
||||
// [p/s] => p for prefix, s for suffix
|
||||
// [o/p] => o for escaped custom override string, p for pattern string
|
||||
String ppo = AffixUtils.escape(properties.getPositivePrefix());
|
||||
String pso = AffixUtils.escape(properties.getPositiveSuffix());
|
||||
String npo = AffixUtils.escape(properties.getNegativePrefix());
|
||||
String nso = AffixUtils.escape(properties.getNegativeSuffix());
|
||||
String ppp = properties.getPositivePrefixPattern();
|
||||
String psp = properties.getPositiveSuffixPattern();
|
||||
String npp = properties.getNegativePrefixPattern();
|
||||
String nsp = properties.getNegativeSuffixPattern();
|
||||
|
||||
if (ppo != null) {
|
||||
posPrefix = ppo;
|
||||
} else if (ppp != null) {
|
||||
posPrefix = ppp;
|
||||
} else {
|
||||
// UTS 35: Default positive prefix is empty string.
|
||||
posPrefix = "";
|
||||
}
|
||||
|
||||
if (pso != null) {
|
||||
posSuffix = pso;
|
||||
} else if (psp != null) {
|
||||
posSuffix = psp;
|
||||
} else {
|
||||
// UTS 35: Default positive suffix is empty string.
|
||||
posSuffix = "";
|
||||
}
|
||||
|
||||
if (npo != null) {
|
||||
negPrefix = npo;
|
||||
} else if (npp != null) {
|
||||
negPrefix = npp;
|
||||
} else {
|
||||
// UTS 35: Default negative prefix is "-" with positive prefix.
|
||||
// Important: We prepend the "-" to the pattern, not the override!
|
||||
negPrefix = ppp == null ? "-" : "-" + ppp;
|
||||
}
|
||||
|
||||
if (nso != null) {
|
||||
negSuffix = nso;
|
||||
} else if (nsp != null) {
|
||||
negSuffix = nsp;
|
||||
} else {
|
||||
// UTS 35: Default negative prefix is the positive prefix.
|
||||
negSuffix = psp == null ? "" : psp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int flags, int i) {
|
||||
return getStringForFlags(flags).charAt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length(int flags) {
|
||||
return getStringForFlags(flags).length();
|
||||
}
|
||||
|
||||
private String getStringForFlags(int flags) {
|
||||
boolean prefix = (flags & Flags.PREFIX) != 0;
|
||||
boolean negative = (flags & Flags.NEGATIVE_SUBPATTERN) != 0;
|
||||
if (prefix && negative) {
|
||||
return negPrefix;
|
||||
} else if (prefix) {
|
||||
return posPrefix;
|
||||
} else if (negative) {
|
||||
return negSuffix;
|
||||
} else {
|
||||
return posSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean positiveHasPlusSign() {
|
||||
return AffixUtils.containsType(posPrefix, AffixUtils.TYPE_PLUS_SIGN)
|
||||
|| AffixUtils.containsType(posSuffix, AffixUtils.TYPE_PLUS_SIGN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNegativeSubpattern() {
|
||||
// See comments in the constructor for more information on why this is always true.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean negativeHasMinusSign() {
|
||||
return AffixUtils.containsType(negPrefix, AffixUtils.TYPE_MINUS_SIGN)
|
||||
|| AffixUtils.containsType(negSuffix, AffixUtils.TYPE_MINUS_SIGN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrencySign() {
|
||||
return AffixUtils.hasCurrencySymbols(posPrefix) || AffixUtils.hasCurrencySymbols(posSuffix)
|
||||
|| AffixUtils.hasCurrencySymbols(negPrefix) || AffixUtils.hasCurrencySymbols(negSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSymbolType(int type) {
|
||||
return AffixUtils.containsType(posPrefix, type) || AffixUtils.containsType(posSuffix, type)
|
||||
|| AffixUtils.containsType(negPrefix, type) || AffixUtils.containsType(negSuffix, type);
|
||||
}
|
||||
}
|
@ -16,22 +16,37 @@ import com.ibm.icu.text.UnicodeSet;
|
||||
*/
|
||||
public class DecimalMatcher implements NumberParseMatcher {
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
// TODO: Re-generate these sets from the database. They probably haven't been updated in a while.
|
||||
private static final UnicodeSet UNISET_PERIOD_LIKE = new UnicodeSet("[.\\u2024\\u3002\\uFE12\\uFE52\\uFF0E\\uFF61]")
|
||||
.freeze();
|
||||
private static final UnicodeSet UNISET_STRICT_PERIOD_LIKE = new UnicodeSet("[.\\u2024\\uFE52\\uFF0E\\uFF61]")
|
||||
.freeze();
|
||||
private static final UnicodeSet UNISET_COMMA_LIKE = new UnicodeSet(
|
||||
"[,\\u060C\\u066B\\u3001\\uFE10\\uFE11\\uFE50\\uFE51\\uFF0C\\uFF64]").freeze();
|
||||
private static final UnicodeSet UNISET_STRICT_COMMA_LIKE = new UnicodeSet("[,\\u066B\\uFE10\\uFE50\\uFF0C]")
|
||||
.freeze();
|
||||
private static final UnicodeSet UNISET_OTHER_GROUPING_SEPARATORS = new UnicodeSet(
|
||||
"[\\ '\\u00A0\\u066C\\u2000-\\u200A\\u2018\\u2019\\u202F\\u205F\\u3000\\uFF07]").freeze();
|
||||
|
||||
public static DecimalMatcher getInstance(DecimalFormatSymbols symbols) {
|
||||
// TODO(sffc): Auto-generated method stub
|
||||
return new DecimalMatcher(symbols.getDigitStrings(),
|
||||
new UnicodeSet("[,]").freeze(),
|
||||
new UnicodeSet("[.]").freeze(),
|
||||
false);
|
||||
String groupingSeparator = symbols.getGroupingSeparatorString();
|
||||
UnicodeSet groupingSet = UNISET_COMMA_LIKE.contains(groupingSeparator) ? UNISET_COMMA_LIKE.cloneAsThawed().addAll(UNISET_OTHER_GROUPING_SEPARATORS).freeze()
|
||||
: UNISET_PERIOD_LIKE.contains(groupingSeparator) ? UNISET_PERIOD_LIKE.cloneAsThawed().addAll(UNISET_OTHER_GROUPING_SEPARATORS).freeze()
|
||||
: UNISET_OTHER_GROUPING_SEPARATORS.contains(groupingSeparator)
|
||||
? UNISET_OTHER_GROUPING_SEPARATORS
|
||||
: new UnicodeSet().addAll(groupingSeparator).freeze();
|
||||
|
||||
String decimalSeparator = symbols.getDecimalSeparatorString();
|
||||
UnicodeSet decimalSet = UNISET_COMMA_LIKE.contains(decimalSeparator) ? UNISET_COMMA_LIKE
|
||||
: UNISET_PERIOD_LIKE.contains(decimalSeparator) ? UNISET_PERIOD_LIKE
|
||||
: new UnicodeSet().addAll(decimalSeparator).freeze();
|
||||
|
||||
return new DecimalMatcher(symbols.getDigitStrings(), groupingSet, decimalSet, false);
|
||||
}
|
||||
|
||||
public static DecimalMatcher getExponentInstance(DecimalFormatSymbols symbols) {
|
||||
return new DecimalMatcher(symbols.getDigitStrings(),
|
||||
new UnicodeSet("[,]").freeze(),
|
||||
new UnicodeSet("[.]").freeze(),
|
||||
true);
|
||||
return new DecimalMatcher(symbols
|
||||
.getDigitStrings(), new UnicodeSet("[,]").freeze(), new UnicodeSet("[.]").freeze(), true);
|
||||
}
|
||||
|
||||
private final String[] digitStrings;
|
||||
@ -39,6 +54,7 @@ public class DecimalMatcher implements NumberParseMatcher {
|
||||
private final UnicodeSet decimalUniSet;
|
||||
private final UnicodeSet separatorSet;
|
||||
public boolean requireGroupingMatch = false;
|
||||
public boolean groupingEnabled = true;
|
||||
private final int grouping1 = 3;
|
||||
private final int grouping2 = 3;
|
||||
private final boolean isScientific;
|
||||
@ -51,7 +67,11 @@ public class DecimalMatcher implements NumberParseMatcher {
|
||||
this.digitStrings = digitStrings;
|
||||
this.groupingUniSet = groupingUniSet;
|
||||
this.decimalUniSet = decimalUniSet;
|
||||
separatorSet = groupingUniSet.cloneAsThawed().addAll(decimalUniSet).freeze();
|
||||
if (groupingEnabled) {
|
||||
separatorSet = groupingUniSet.cloneAsThawed().addAll(decimalUniSet).freeze();
|
||||
} else {
|
||||
separatorSet = decimalUniSet;
|
||||
}
|
||||
this.isScientific = isScientific;
|
||||
}
|
||||
|
||||
@ -65,6 +85,7 @@ public class DecimalMatcher implements NumberParseMatcher {
|
||||
int currGroup = 0;
|
||||
int separator = -1;
|
||||
int lastSeparatorOffset = segment.getOffset();
|
||||
int scientificAdjustment = 0;
|
||||
boolean hasPartialPrefix = false;
|
||||
boolean seenBothSeparators = false;
|
||||
while (segment.length() > 0) {
|
||||
@ -97,7 +118,7 @@ public class DecimalMatcher implements NumberParseMatcher {
|
||||
// If found, save it in the DecimalQuantity or scientific adjustment.
|
||||
if (digit >= 0) {
|
||||
if (isScientific) {
|
||||
result.scientificAdjustment = digit + result.scientificAdjustment * 10;
|
||||
scientificAdjustment = digit + scientificAdjustment * 10;
|
||||
} else {
|
||||
if (result.quantity == null) {
|
||||
result.quantity = new DecimalQuantity_DualStorageBCD();
|
||||
@ -117,13 +138,13 @@ public class DecimalMatcher implements NumberParseMatcher {
|
||||
if (requireGroupingMatch && currGroup == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (separator == cp && groupingUniSet.contains(cp)) {
|
||||
} else if (groupingEnabled && separator == cp && groupingUniSet.contains(cp)) {
|
||||
// Second or later grouping separator.
|
||||
if (requireGroupingMatch && currGroup != grouping2) {
|
||||
break;
|
||||
}
|
||||
} else if (separator != cp && decimalUniSet.contains(cp)) {
|
||||
// Decimal separator.
|
||||
} else if (groupingEnabled && separator != cp && decimalUniSet.contains(cp)) {
|
||||
// Decimal separator after a grouping separator.
|
||||
if (requireGroupingMatch && currGroup != grouping1) {
|
||||
break;
|
||||
}
|
||||
@ -141,10 +162,13 @@ public class DecimalMatcher implements NumberParseMatcher {
|
||||
break;
|
||||
}
|
||||
|
||||
if (seenBothSeparators || (separator != -1 && decimalUniSet.contains(separator))) {
|
||||
if (isScientific) {
|
||||
result.quantity.adjustMagnitude(scientificAdjustment);
|
||||
} else if (seenBothSeparators || (separator != -1 && decimalUniSet.contains(separator))) {
|
||||
result.quantity.adjustMagnitude(-currGroup);
|
||||
} else if (requireGroupingMatch && separator != -1 && groupingUniSet.contains(separator)
|
||||
&& currGroup != grouping1) {
|
||||
} else if (separator != -1
|
||||
&& ((requireGroupingMatch && groupingUniSet.contains(separator) && currGroup != grouping1)
|
||||
|| !groupingEnabled)) {
|
||||
result.quantity.adjustMagnitude(-currGroup);
|
||||
result.quantity.roundToMagnitude(0, RoundingUtils.mathContextUnlimited(RoundingMode.FLOOR));
|
||||
segment.setOffset(lastSeparatorOffset);
|
||||
|
@ -2,14 +2,19 @@
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.impl.number.parse;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import com.ibm.icu.impl.number.AffixPatternProvider;
|
||||
import com.ibm.icu.impl.number.AffixUtils;
|
||||
import com.ibm.icu.impl.number.CustomSymbolCurrency;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.MutablePatternModifier;
|
||||
import com.ibm.icu.impl.number.Parse.ParseMode;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.PropertiesAffixPatternProvider;
|
||||
import com.ibm.icu.number.NumberFormatter.SignDisplay;
|
||||
import com.ibm.icu.number.NumberFormatter.UnitWidth;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
@ -23,7 +28,9 @@ import com.ibm.icu.util.ULocale;
|
||||
*
|
||||
*/
|
||||
public class NumberParserImpl {
|
||||
public static NumberParserImpl createParserFromPattern(String pattern) {
|
||||
public static NumberParserImpl createParserFromPattern(String pattern, boolean strictGrouping) {
|
||||
// Temporary frontend for testing.
|
||||
|
||||
NumberParserImpl parser = new NumberParserImpl();
|
||||
ULocale locale = ULocale.ENGLISH;
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
@ -32,10 +39,7 @@ public class NumberParserImpl {
|
||||
AffixPatternProvider provider = PatternStringParser.parseToPatternInfo(pattern);
|
||||
mod.setPatternInfo(provider);
|
||||
mod.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
mod.setSymbols(symbols,
|
||||
Currency.getInstance("USD"),
|
||||
UnitWidth.FULL_NAME,
|
||||
null);
|
||||
mod.setSymbols(symbols, Currency.getInstance("USD"), UnitWidth.FULL_NAME, null);
|
||||
int flags = 0;
|
||||
if (provider.containsSymbolType(AffixUtils.TYPE_PERCENT)) {
|
||||
flags |= ParsedNumber.FLAG_PERCENT;
|
||||
@ -45,18 +49,77 @@ public class NumberParserImpl {
|
||||
}
|
||||
AffixMatcher.generateFromPatternModifier(mod, flags, parser);
|
||||
|
||||
parser.addMatcher(DecimalMatcher.getInstance(symbols));
|
||||
DecimalMatcher decimalMatcher = DecimalMatcher.getInstance(symbols);
|
||||
decimalMatcher.requireGroupingMatch = strictGrouping;
|
||||
parser.addMatcher(decimalMatcher);
|
||||
parser.addMatcher(WhitespaceMatcher.getInstance());
|
||||
parser.addMatcher(new MinusSignMatcher());
|
||||
parser.addMatcher(new ScientificMatcher(symbols));
|
||||
parser.addMatcher(new CurrencyMatcher(locale));
|
||||
|
||||
parser.setComparator(new Comparator<ParsedNumber>() {
|
||||
@Override
|
||||
public int compare(ParsedNumber o1, ParsedNumber o2) {
|
||||
return o1.charsConsumed - o2.charsConsumed;
|
||||
}
|
||||
});
|
||||
parser.freeze();
|
||||
return parser;
|
||||
}
|
||||
|
||||
public static Number parseStatic(String input,
|
||||
ParsePosition ppos,
|
||||
DecimalFormatProperties properties,
|
||||
DecimalFormatSymbols symbols) {
|
||||
NumberParserImpl parser = createParserFromProperties(properties, symbols);
|
||||
ParsedNumber result = new ParsedNumber();
|
||||
parser.parse(input, true, result);
|
||||
ppos.setIndex(result.charsConsumed);
|
||||
return result.getDouble();
|
||||
}
|
||||
|
||||
public static NumberParserImpl createParserFromProperties(
|
||||
DecimalFormatProperties properties,
|
||||
DecimalFormatSymbols symbols) {
|
||||
NumberParserImpl parser = new NumberParserImpl();
|
||||
ULocale locale = symbols.getULocale();
|
||||
Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
|
||||
|
||||
//////////////////////
|
||||
/// AFFIX MATCHERS ///
|
||||
//////////////////////
|
||||
|
||||
// Set up a pattern modifier with mostly defaults to generate AffixMatchers.
|
||||
MutablePatternModifier mod = new MutablePatternModifier(false);
|
||||
AffixPatternProvider provider = new PropertiesAffixPatternProvider(properties);
|
||||
mod.setPatternInfo(provider);
|
||||
mod.setPatternAttributes(SignDisplay.AUTO, false);
|
||||
mod.setSymbols(symbols, currency, UnitWidth.SHORT, null);
|
||||
|
||||
// Figure out which flags correspond to this pattern modifier. Note: negatives are taken care of in the
|
||||
// generateFromPatternModifier function.
|
||||
int flags = 0;
|
||||
if (provider.containsSymbolType(AffixUtils.TYPE_PERCENT)) {
|
||||
flags |= ParsedNumber.FLAG_PERCENT;
|
||||
}
|
||||
if (provider.containsSymbolType(AffixUtils.TYPE_PERMILLE)) {
|
||||
flags |= ParsedNumber.FLAG_PERMILLE;
|
||||
}
|
||||
|
||||
AffixMatcher.generateFromPatternModifier(mod, flags, parser);
|
||||
|
||||
///////////////////////////////
|
||||
/// OTHER STANDARD MATCHERS ///
|
||||
///////////////////////////////
|
||||
|
||||
DecimalMatcher decimalMatcher = DecimalMatcher.getInstance(symbols);
|
||||
decimalMatcher.groupingEnabled = properties.getGroupingSize() > 0;
|
||||
decimalMatcher.requireGroupingMatch = properties.getParseMode() == ParseMode.STRICT;
|
||||
parser.addMatcher(decimalMatcher);
|
||||
parser.addMatcher(WhitespaceMatcher.getInstance());
|
||||
parser.addMatcher(new MinusSignMatcher());
|
||||
parser.addMatcher(new ScientificMatcher(symbols));
|
||||
|
||||
////////////////////////
|
||||
/// CURRENCY MATCHER ///
|
||||
////////////////////////
|
||||
|
||||
parser.addMatcher(new CurrencyMatcher(locale));
|
||||
|
||||
parser.freeze();
|
||||
return parser;
|
||||
}
|
||||
@ -67,6 +130,7 @@ public class NumberParserImpl {
|
||||
|
||||
public NumberParserImpl() {
|
||||
matchers = new ArrayList<NumberParseMatcher>();
|
||||
comparator = ParsedNumber.COMPARATOR; // default value
|
||||
frozen = false;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.impl.number.parse;
|
||||
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
|
||||
|
||||
/**
|
||||
@ -12,17 +13,46 @@ import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
|
||||
public class ParsedNumber {
|
||||
|
||||
public DecimalQuantity_DualStorageBCD quantity = null;
|
||||
|
||||
/**
|
||||
* The number of chars accepted during parsing. This is NOT necessarily the same as the StringSegment offset; "weak"
|
||||
* chars, like whitespace, change the offset, but the charsConsumed is not touched until a "strong" char is
|
||||
* encountered.
|
||||
*/
|
||||
public int charsConsumed = 0;
|
||||
|
||||
/**
|
||||
* Boolean flags (see constants below).
|
||||
*/
|
||||
public int flags = 0;
|
||||
|
||||
/**
|
||||
* The prefix string that got consumed.
|
||||
*/
|
||||
public String prefix = null;
|
||||
|
||||
/**
|
||||
* The suffix string that got consumed.
|
||||
*/
|
||||
public String suffix = null;
|
||||
public int scientificAdjustment = 0;
|
||||
|
||||
/**
|
||||
* The currency that got consumed.
|
||||
*/
|
||||
public String currencyCode = null;
|
||||
|
||||
public static final int FLAG_NEGATIVE = 0x0001;
|
||||
public static final int FLAG_PERCENT = 0x0002;
|
||||
public static final int FLAG_PERMILLE = 0x0004;
|
||||
|
||||
/** A Comparator that favors ParsedNumbers with the most chars consumed. */
|
||||
public static final Comparator<ParsedNumber> COMPARATOR = new Comparator<ParsedNumber>() {
|
||||
@Override
|
||||
public int compare(ParsedNumber o1, ParsedNumber o2) {
|
||||
return o1.charsConsumed - o2.charsConsumed;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param other
|
||||
*/
|
||||
@ -32,7 +62,6 @@ public class ParsedNumber {
|
||||
flags = other.flags;
|
||||
prefix = other.prefix;
|
||||
suffix = other.suffix;
|
||||
scientificAdjustment = other.scientificAdjustment;
|
||||
currencyCode = other.currencyCode;
|
||||
}
|
||||
|
||||
@ -41,9 +70,7 @@ public class ParsedNumber {
|
||||
}
|
||||
|
||||
public double getDouble() {
|
||||
DecimalQuantity copy = quantity.createCopy();
|
||||
copy.adjustMagnitude(scientificAdjustment);
|
||||
double d = copy.toDouble();
|
||||
double d = quantity.toDouble();
|
||||
if (0 != (flags & FLAG_NEGATIVE)) {
|
||||
d = -d;
|
||||
}
|
||||
|
@ -11,10 +11,12 @@ import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
public class ScientificMatcher implements NumberParseMatcher {
|
||||
|
||||
private final String exponentSeparatorString;
|
||||
private final String minusSignString;
|
||||
private final DecimalMatcher exponentMatcher;
|
||||
|
||||
public ScientificMatcher(DecimalFormatSymbols symbols) {
|
||||
exponentSeparatorString = symbols.getExponentSeparator();
|
||||
minusSignString = symbols.getMinusSignString();
|
||||
exponentMatcher = DecimalMatcher.getExponentInstance(symbols);
|
||||
}
|
||||
|
||||
@ -26,19 +28,33 @@ public class ScientificMatcher implements NumberParseMatcher {
|
||||
}
|
||||
|
||||
// First match the scientific separator, and then match another number after it.
|
||||
int overlap = segment.getCommonPrefixLength(exponentSeparatorString);
|
||||
if (overlap == exponentSeparatorString.length()) {
|
||||
// Full exponent separator match; try to match digits.
|
||||
segment.adjustOffset(overlap);
|
||||
int overlap1 = segment.getCommonPrefixLength(exponentSeparatorString);
|
||||
if (overlap1 == exponentSeparatorString.length()) {
|
||||
// Full exponent separator match; allow a sign, and then try to match digits.
|
||||
segment.adjustOffset(overlap1);
|
||||
int overlap2 = segment.getCommonPrefixLength(minusSignString);
|
||||
boolean sign = false;
|
||||
if (overlap2 == minusSignString.length()) {
|
||||
sign = true;
|
||||
segment.adjustOffset(overlap2);
|
||||
} else if (overlap2 == segment.length()) {
|
||||
// Partial sign match
|
||||
return true;
|
||||
}
|
||||
|
||||
int digitsOffset = segment.getOffset();
|
||||
int oldMagnitude = result.quantity.getMagnitude();
|
||||
boolean digitsReturnValue = exponentMatcher.match(segment, result);
|
||||
if (result.quantity.getMagnitude() != oldMagnitude && sign) {
|
||||
result.quantity.adjustMagnitude(2*(oldMagnitude - result.quantity.getMagnitude()));
|
||||
}
|
||||
if (segment.getOffset() == digitsOffset) {
|
||||
// No digits were matched; un-match the exponent separator.
|
||||
segment.adjustOffset(-overlap);
|
||||
segment.adjustOffset(-overlap1);
|
||||
}
|
||||
return digitsReturnValue;
|
||||
|
||||
} else if (overlap == segment.length()) {
|
||||
} else if (overlap1 == segment.length()) {
|
||||
// Partial exponent separator match
|
||||
return true;
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
// © 2017 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.impl.number.parse;
|
||||
|
||||
/**
|
||||
* @author sffc
|
||||
*
|
||||
*/
|
||||
public class StrictMatcher implements NumberParseMatcher {
|
||||
|
||||
@Override
|
||||
public boolean match(StringSegment segment, ParsedNumber result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(ParsedNumber result) {
|
||||
if (result.prefix == null && result.suffix == null) {
|
||||
// Do something?
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -5,16 +5,15 @@ package com.ibm.icu.number;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.number.AffixPatternProvider;
|
||||
import com.ibm.icu.impl.number.AffixUtils;
|
||||
import com.ibm.icu.impl.number.CurrencyPluralInfoAffixProvider;
|
||||
import com.ibm.icu.impl.number.CustomSymbolCurrency;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
import com.ibm.icu.impl.number.MacroProps;
|
||||
import com.ibm.icu.impl.number.MultiplierImpl;
|
||||
import com.ibm.icu.impl.number.Padder;
|
||||
import com.ibm.icu.impl.number.PatternStringParser;
|
||||
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
|
||||
import com.ibm.icu.impl.number.PropertiesAffixPatternProvider;
|
||||
import com.ibm.icu.impl.number.RoundingUtils;
|
||||
import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
|
||||
import com.ibm.icu.number.NumberFormatter.SignDisplay;
|
||||
@ -22,7 +21,6 @@ import com.ibm.icu.number.Rounder.FractionRounderImpl;
|
||||
import com.ibm.icu.number.Rounder.IncrementRounderImpl;
|
||||
import com.ibm.icu.number.Rounder.SignificantRounderImpl;
|
||||
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
|
||||
import com.ibm.icu.text.CurrencyPluralInfo;
|
||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||
import com.ibm.icu.util.Currency;
|
||||
import com.ibm.icu.util.Currency.CurrencyUsage;
|
||||
@ -60,7 +58,7 @@ final class NumberPropertyMapper {
|
||||
* @param symbols
|
||||
* The symbols associated with the property bag.
|
||||
* @param exportedProperties
|
||||
* A property bag in which to store validated properties.
|
||||
* A property bag in which to store validated properties. Used by some DecimalFormat getters.
|
||||
* @return A new MacroProps containing all of the information in the Properties.
|
||||
*/
|
||||
public static MacroProps oldToNew(DecimalFormatProperties properties, DecimalFormatSymbols symbols,
|
||||
@ -334,178 +332,4 @@ final class NumberPropertyMapper {
|
||||
|
||||
return macros;
|
||||
}
|
||||
|
||||
private static class PropertiesAffixPatternProvider implements AffixPatternProvider {
|
||||
private final String posPrefix;
|
||||
private final String posSuffix;
|
||||
private final String negPrefix;
|
||||
private final String negSuffix;
|
||||
|
||||
public PropertiesAffixPatternProvider(DecimalFormatProperties properties) {
|
||||
// There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the
|
||||
// explicit setters (setPositivePrefix and friends). The way to resolve the settings is as follows:
|
||||
//
|
||||
// 1) If the explicit setting is present for the field, use it.
|
||||
// 2) Otherwise, follows UTS 35 rules based on the pattern string.
|
||||
//
|
||||
// Importantly, the explicit setters affect only the one field they override. If you set the positive
|
||||
// prefix, that should not affect the negative prefix. Since it is impossible for the user of this class
|
||||
// to know whether the origin for a string was the override or the pattern, we have to say that we always
|
||||
// have a negative subpattern and perform all resolution logic here.
|
||||
|
||||
// Convenience: Extract the properties into local variables.
|
||||
// Variables are named with three chars: [p/n][p/s][o/p]
|
||||
// [p/n] => p for positive, n for negative
|
||||
// [p/s] => p for prefix, s for suffix
|
||||
// [o/p] => o for escaped custom override string, p for pattern string
|
||||
String ppo = AffixUtils.escape(properties.getPositivePrefix());
|
||||
String pso = AffixUtils.escape(properties.getPositiveSuffix());
|
||||
String npo = AffixUtils.escape(properties.getNegativePrefix());
|
||||
String nso = AffixUtils.escape(properties.getNegativeSuffix());
|
||||
String ppp = properties.getPositivePrefixPattern();
|
||||
String psp = properties.getPositiveSuffixPattern();
|
||||
String npp = properties.getNegativePrefixPattern();
|
||||
String nsp = properties.getNegativeSuffixPattern();
|
||||
|
||||
if (ppo != null) {
|
||||
posPrefix = ppo;
|
||||
} else if (ppp != null) {
|
||||
posPrefix = ppp;
|
||||
} else {
|
||||
// UTS 35: Default positive prefix is empty string.
|
||||
posPrefix = "";
|
||||
}
|
||||
|
||||
if (pso != null) {
|
||||
posSuffix = pso;
|
||||
} else if (psp != null) {
|
||||
posSuffix = psp;
|
||||
} else {
|
||||
// UTS 35: Default positive suffix is empty string.
|
||||
posSuffix = "";
|
||||
}
|
||||
|
||||
if (npo != null) {
|
||||
negPrefix = npo;
|
||||
} else if (npp != null) {
|
||||
negPrefix = npp;
|
||||
} else {
|
||||
// UTS 35: Default negative prefix is "-" with positive prefix.
|
||||
// Important: We prepend the "-" to the pattern, not the override!
|
||||
negPrefix = ppp == null ? "-" : "-" + ppp;
|
||||
}
|
||||
|
||||
if (nso != null) {
|
||||
negSuffix = nso;
|
||||
} else if (nsp != null) {
|
||||
negSuffix = nsp;
|
||||
} else {
|
||||
// UTS 35: Default negative prefix is the positive prefix.
|
||||
negSuffix = psp == null ? "" : psp;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int flags, int i) {
|
||||
return getStringForFlags(flags).charAt(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length(int flags) {
|
||||
return getStringForFlags(flags).length();
|
||||
}
|
||||
|
||||
private String getStringForFlags(int flags) {
|
||||
boolean prefix = (flags & Flags.PREFIX) != 0;
|
||||
boolean negative = (flags & Flags.NEGATIVE_SUBPATTERN) != 0;
|
||||
if (prefix && negative) {
|
||||
return negPrefix;
|
||||
} else if (prefix) {
|
||||
return posPrefix;
|
||||
} else if (negative) {
|
||||
return negSuffix;
|
||||
} else {
|
||||
return posSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean positiveHasPlusSign() {
|
||||
return AffixUtils.containsType(posPrefix, AffixUtils.TYPE_PLUS_SIGN)
|
||||
|| AffixUtils.containsType(posSuffix, AffixUtils.TYPE_PLUS_SIGN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNegativeSubpattern() {
|
||||
// See comments in the constructor for more information on why this is always true.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean negativeHasMinusSign() {
|
||||
return AffixUtils.containsType(negPrefix, AffixUtils.TYPE_MINUS_SIGN)
|
||||
|| AffixUtils.containsType(negSuffix, AffixUtils.TYPE_MINUS_SIGN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrencySign() {
|
||||
return AffixUtils.hasCurrencySymbols(posPrefix) || AffixUtils.hasCurrencySymbols(posSuffix)
|
||||
|| AffixUtils.hasCurrencySymbols(negPrefix) || AffixUtils.hasCurrencySymbols(negSuffix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSymbolType(int type) {
|
||||
return AffixUtils.containsType(posPrefix, type) || AffixUtils.containsType(posSuffix, type)
|
||||
|| AffixUtils.containsType(negPrefix, type) || AffixUtils.containsType(negSuffix, type);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CurrencyPluralInfoAffixProvider implements AffixPatternProvider {
|
||||
private final AffixPatternProvider[] affixesByPlural;
|
||||
|
||||
public CurrencyPluralInfoAffixProvider(CurrencyPluralInfo cpi) {
|
||||
affixesByPlural = new ParsedPatternInfo[StandardPlural.COUNT];
|
||||
for (StandardPlural plural : StandardPlural.VALUES) {
|
||||
affixesByPlural[plural.ordinal()] = PatternStringParser
|
||||
.parseToPatternInfo(cpi.getCurrencyPluralPattern(plural.getKeyword()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int flags, int i) {
|
||||
int pluralOrdinal = (flags & Flags.PLURAL_MASK);
|
||||
return affixesByPlural[pluralOrdinal].charAt(flags, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length(int flags) {
|
||||
int pluralOrdinal = (flags & Flags.PLURAL_MASK);
|
||||
return affixesByPlural[pluralOrdinal].length(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean positiveHasPlusSign() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].positiveHasPlusSign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNegativeSubpattern() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].hasNegativeSubpattern();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean negativeHasMinusSign() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].negativeHasMinusSign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCurrencySign() {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].hasCurrencySign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSymbolType(int type) {
|
||||
return affixesByPlural[StandardPlural.OTHER.ordinal()].containsSymbolType(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -441,11 +441,11 @@ en_US 1 123,456 123456
|
||||
en_US 0 123,456 123
|
||||
en_US 1 123.456 123.456
|
||||
en_US 0 123.456 123.456
|
||||
fr_FR 1 123,456 123.456
|
||||
fr_FR 0 123,456 123.456
|
||||
it_IT 1 123,456 123.456
|
||||
it_IT 0 123,456 123.456
|
||||
// JDK returns 123 here; not sure why.
|
||||
fr_FR 1 123.456 123456 K
|
||||
fr_FR 0 123.456 123
|
||||
it_IT 1 123.456 123456 K
|
||||
it_IT 0 123.456 123
|
||||
|
||||
test no grouping in pattern with parsing
|
||||
set pattern 0
|
||||
@ -736,7 +736,7 @@ parse output breaks
|
||||
(5,347.25) -5347.25
|
||||
// J requires prefix and suffix for lenient parsing, but C doesn't
|
||||
5,347.25 5347.25 JK
|
||||
(5,347.25 -5347.25 J
|
||||
(5,347.25 -5347.25 JP
|
||||
// S is successful at parsing this as -5347.25 in lenient mode
|
||||
-5,347.25 -5347.25 CJK
|
||||
+3.52E4 35200
|
||||
@ -750,7 +750,8 @@ parse output breaks
|
||||
(34,,25 E-1) -342.5 CJK
|
||||
// Spaces are not allowed after exponent symbol
|
||||
// C parses up to the E but J bails
|
||||
(34 25E -1) -3425 JK
|
||||
// P does not make the number negative
|
||||
(34 25E -1) -3425 JKP
|
||||
+3.52EE4 3.52
|
||||
+1,234,567.8901 1234567.8901
|
||||
+1,23,4567.8901 1234567.8901
|
||||
@ -775,18 +776,19 @@ parse output breaks
|
||||
( 19 45 ) -1945 JK
|
||||
(,,19,45) -1945
|
||||
// C parses to the space, but J bails
|
||||
(,,19 45) -19 J
|
||||
// P makes the number positive
|
||||
(,,19 45) -19 JP
|
||||
// J bails b/c comma different separator than space. C doesn't treat leading spaces
|
||||
// as a separator.
|
||||
( 19,45) -1945 JK
|
||||
( 19,45) -1945 JKP
|
||||
// J bails. Doesn't allow trailing separators when there is prefix and suffix.
|
||||
(,,19,45,) -1945 J
|
||||
// J bails on next 4 because J doesn't allow letters inside prefix and suffix.
|
||||
// C will parse up to the letter.
|
||||
(,,19,45,d1) -1945 J
|
||||
(,,19,45d1) -1945 J
|
||||
( 19 45 d1) -1945 JK
|
||||
( 19 45d1) -1945 JK
|
||||
(,,19,45,d1) -1945 JP
|
||||
(,,19,45d1) -1945 JP
|
||||
( 19 45 d1) -1945 JKP
|
||||
( 19 45d1) -1945 JKP
|
||||
// J does allow trailing separator before a decimal point
|
||||
(19,45,.25) -1945.25
|
||||
// 2nd decimal points are ignored
|
||||
@ -829,7 +831,7 @@ parse output breaks
|
||||
(65347.25) -65347.25
|
||||
(65,347.25) -65347.25
|
||||
// JDK does allow separators in the wrong place and parses as -5347.25
|
||||
(53,47.25) fail K
|
||||
(53,47.25) fail KP
|
||||
// strict requires prefix or suffix, except in C
|
||||
65,347.25 fail
|
||||
+3.52E4 35200
|
||||
|
@ -7,6 +7,7 @@ import java.math.RoundingMode;
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.ibm.icu.dev.test.TestUtil;
|
||||
@ -16,6 +17,7 @@ import com.ibm.icu.impl.number.Parse;
|
||||
import com.ibm.icu.impl.number.Parse.ParseMode;
|
||||
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.number.LocalizedNumberFormatter;
|
||||
import com.ibm.icu.number.NumberFormatter;
|
||||
import com.ibm.icu.text.DecimalFormat;
|
||||
@ -573,6 +575,100 @@ public class NumberFormatDataDrivenTest {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parsing, but no other features.
|
||||
*/
|
||||
private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU60_Parsing =
|
||||
new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
|
||||
|
||||
@Override
|
||||
public Character Id() {
|
||||
return 'P';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(DataDrivenNumberFormatTestData tuple) {
|
||||
String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
|
||||
DecimalFormatProperties properties;
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number actual;
|
||||
try {
|
||||
properties = PatternStringParser.parseToProperties(pattern,
|
||||
tuple.currency != null ? PatternStringParser.IGNORE_ROUNDING_ALWAYS
|
||||
: PatternStringParser.IGNORE_ROUNDING_NEVER);
|
||||
propertiesFromTuple(tuple, properties);
|
||||
actual = NumberParserImpl.parseStatic(tuple.parse,
|
||||
ppos,
|
||||
properties,
|
||||
DecimalFormatSymbols.getInstance(tuple.locale));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return "parse exception: " + e.getMessage();
|
||||
}
|
||||
if (actual == null && ppos.getIndex() != 0) {
|
||||
throw new AssertionError("Error: value is null but parse position is not zero");
|
||||
}
|
||||
if (ppos.getIndex() == 0) {
|
||||
return "Parse failed; got " + actual + ", but expected " + tuple.output;
|
||||
}
|
||||
if (tuple.output.equals("NaN")) {
|
||||
if (!Double.isNaN(actual.doubleValue())) {
|
||||
return "Expected NaN, but got: " + actual;
|
||||
}
|
||||
return null;
|
||||
} else if (tuple.output.equals("Inf")) {
|
||||
if (!Double.isInfinite(actual.doubleValue()) || Double.compare(actual.doubleValue(), 0.0) < 0) {
|
||||
return "Expected Inf, but got: " + actual;
|
||||
}
|
||||
return null;
|
||||
} else if (tuple.output.equals("-Inf")) {
|
||||
if (!Double.isInfinite(actual.doubleValue()) || Double.compare(actual.doubleValue(), 0.0) > 0) {
|
||||
return "Expected -Inf, but got: " + actual;
|
||||
}
|
||||
return null;
|
||||
} else if (tuple.output.equals("fail")) {
|
||||
return null;
|
||||
} else if (new BigDecimal(tuple.output).compareTo(new BigDecimal(actual.toString())) != 0) {
|
||||
return "Expected: " + tuple.output + ", got: " + actual;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String parseCurrency(DataDrivenNumberFormatTestData tuple) {
|
||||
// String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
|
||||
// DecimalFormatProperties properties;
|
||||
// ParsePosition ppos = new ParsePosition(0);
|
||||
// CurrencyAmount actual;
|
||||
// try {
|
||||
// properties = PatternStringParser.parseToProperties(
|
||||
// pattern,
|
||||
// tuple.currency != null ? PatternStringParser.IGNORE_ROUNDING_ALWAYS
|
||||
// : PatternStringParser.IGNORE_ROUNDING_NEVER);
|
||||
// propertiesFromTuple(tuple, properties);
|
||||
// actual = NumberParserImpl.parseStatic(tuple.parse,
|
||||
// ppos,
|
||||
// properties,
|
||||
// DecimalFormatSymbols.getInstance(tuple.locale));
|
||||
// } catch (ParseException e) {
|
||||
// e.printStackTrace();
|
||||
// return "parse exception: " + e.getMessage();
|
||||
// }
|
||||
// if (ppos.getIndex() == 0 || actual.getCurrency().getCurrencyCode().equals("XXX")) {
|
||||
// return "Parse failed; got " + actual + ", but expected " + tuple.output;
|
||||
// }
|
||||
// BigDecimal expectedNumber = new BigDecimal(tuple.output);
|
||||
// if (expectedNumber.compareTo(new BigDecimal(actual.getNumber().toString())) != 0) {
|
||||
// return "Wrong number: Expected: " + expectedNumber + ", got: " + actual;
|
||||
// }
|
||||
// String expectedCurrency = tuple.outputCurrency;
|
||||
// if (!expectedCurrency.equals(actual.getCurrency().toString())) {
|
||||
// return "Wrong currency: Expected: " + expectedCurrency + ", got: " + actual;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
};
|
||||
|
||||
/**
|
||||
* All features except formatting.
|
||||
*/
|
||||
@ -738,6 +834,7 @@ public class NumberFormatDataDrivenTest {
|
||||
};
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void TestDataDrivenICU58() {
|
||||
// Android can't access DecimalFormat_ICU58 for testing (ticket #13283).
|
||||
if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android) return;
|
||||
@ -750,6 +847,7 @@ public class NumberFormatDataDrivenTest {
|
||||
// something may or may not work. However the test data assumes a specific
|
||||
// Java runtime version. We should probably disable this test case - #13372
|
||||
@Test
|
||||
@Ignore
|
||||
public void TestDataDrivenJDK() {
|
||||
// Android implements java.text.DecimalFormat with ICU4J (ticket #13322).
|
||||
// Oracle/OpenJDK 9's behavior is not exactly same with Oracle/OpenJDK 8.
|
||||
@ -764,12 +862,20 @@ public class NumberFormatDataDrivenTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void TestDataDrivenICULatest_Format() {
|
||||
DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
|
||||
"numberformattestspecification.txt", ICU60);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestDataDrivenICULatest_Parsing() {
|
||||
DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
|
||||
"numberformattestspecification.txt", ICU60_Parsing);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void TestDataDrivenICULatest_Other() {
|
||||
DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
|
||||
"numberformattestspecification.txt", ICU60_Other);
|
||||
|
@ -22,6 +22,7 @@ public class NumberParserTest {
|
||||
// a) Flags:
|
||||
// --- Bit 0x01 => Test greedy implementation
|
||||
// --- Bit 0x02 => Test slow implementation
|
||||
// --- Bit 0x04 => Test strict grouping separators
|
||||
// b) Input string
|
||||
// c) Pattern
|
||||
// d) Expected chars consumed
|
||||
@ -34,13 +35,13 @@ public class NumberParserTest {
|
||||
{ 3, "𝟱𝟭𝟰𝟮𝟯x", "0", 10, 51423. },
|
||||
{ 3, " 𝟱𝟭𝟰𝟮𝟯", "0", 11, 51423. },
|
||||
{ 3, "𝟱𝟭𝟰𝟮𝟯 ", "0", 10, 51423. },
|
||||
{ 3, "𝟱𝟭,𝟰𝟮𝟯", "0", 11, 51423. },
|
||||
{ 3, "𝟳𝟴,𝟵𝟱𝟭,𝟰𝟮𝟯", "0", 18, 78951423. },
|
||||
{ 3, "𝟳𝟴,𝟵𝟱𝟭.𝟰𝟮𝟯", "0", 18, 78951.423 },
|
||||
{ 3, "𝟳𝟴,𝟬𝟬𝟬", "0", 11, 78000. },
|
||||
{ 3, "𝟳𝟴,𝟬𝟬𝟬.𝟬𝟬𝟬", "0", 18, 78000. },
|
||||
{ 3, "𝟳𝟴,𝟬𝟬𝟬.𝟬𝟮𝟯", "0", 18, 78000.023 },
|
||||
{ 3, "𝟳𝟴.𝟬𝟬𝟬.𝟬𝟮𝟯", "0", 11, 78. },
|
||||
{ 7, "𝟱𝟭,𝟰𝟮𝟯", "0", 11, 51423. },
|
||||
{ 7, "𝟳𝟴,𝟵𝟱𝟭,𝟰𝟮𝟯", "0", 18, 78951423. },
|
||||
{ 7, "𝟳𝟴,𝟵𝟱𝟭.𝟰𝟮𝟯", "0", 18, 78951.423 },
|
||||
{ 7, "𝟳𝟴,𝟬𝟬𝟬", "0", 11, 78000. },
|
||||
{ 7, "𝟳𝟴,𝟬𝟬𝟬.𝟬𝟬𝟬", "0", 18, 78000. },
|
||||
{ 7, "𝟳𝟴,𝟬𝟬𝟬.𝟬𝟮𝟯", "0", 18, 78000.023 },
|
||||
{ 7, "𝟳𝟴.𝟬𝟬𝟬.𝟬𝟮𝟯", "0", 11, 78. },
|
||||
{ 3, "-𝟱𝟭𝟰𝟮𝟯", "0", 11, -51423. },
|
||||
{ 3, "-𝟱𝟭𝟰𝟮𝟯-", "0", 11, -51423. },
|
||||
{ 3, "a51423US dollars", "a0¤¤¤", 16, 51423. },
|
||||
@ -51,6 +52,7 @@ public class NumberParserTest {
|
||||
{ 1, "a40b", "a0'0b'", 3, 40. }, // greedy code path thinks "40" is the number
|
||||
{ 2, "a40b", "a0'0b'", 4, 4. }, // slow code path find the suffix "0b"
|
||||
{ 3, "𝟱.𝟭𝟰𝟮E𝟯", "0", 12, 5142. },
|
||||
{ 3, "𝟱.𝟭𝟰𝟮E-𝟯", "0", 13, 0.005142 },
|
||||
{ 3, "5,142.50 Canadian dollars", "0", 25, 5142.5 },
|
||||
{ 3, "0", "0", 1, 0.0 } };
|
||||
|
||||
@ -60,7 +62,7 @@ public class NumberParserTest {
|
||||
String pattern = (String) cas[2];
|
||||
int expectedCharsConsumed = (Integer) cas[3];
|
||||
double resultDouble = (Double) cas[4];
|
||||
NumberParserImpl parser = NumberParserImpl.createParserFromPattern(pattern);
|
||||
NumberParserImpl parser = NumberParserImpl.createParserFromPattern(pattern, false);
|
||||
String message = "Input <" + input + "> Parser " + parser;
|
||||
|
||||
if (0 != (flags & 0x01)) {
|
||||
@ -80,6 +82,16 @@ public class NumberParserTest {
|
||||
assertEquals(message, resultDouble, resultObject.getDouble(), 0.0);
|
||||
assertEquals(message, expectedCharsConsumed, resultObject.charsConsumed);
|
||||
}
|
||||
|
||||
if (0 != (flags & 0x04)) {
|
||||
// Test with strict separators
|
||||
parser = NumberParserImpl.createParserFromPattern(pattern, true);
|
||||
ParsedNumber resultObject = new ParsedNumber();
|
||||
parser.parse(input, true, resultObject);
|
||||
assertNotNull(message, resultObject.quantity);
|
||||
assertEquals(message, resultDouble, resultObject.getDouble(), 0.0);
|
||||
assertEquals(message, expectedCharsConsumed, resultObject.charsConsumed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user