ICU-11760 Add rule syntax for NaN, infinity and alternate decimal points.
Some performance enhancements were added for good measure too. Creating new RuleBasedNumberFormat objects can take a long time due to all the rule parsing. Also I fixed a potential infinite recursion problem when RuleBasedNumberFormat used NumberFormat.createInstance, which could occasionally depend on creating RuleBasedNumberFormat for itself, which was bad. X-SVN-Rev: 37778
This commit is contained in:
parent
03eab980da
commit
8653b95982
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1996-2014, International Business Machines Corporation and *
|
||||
* Copyright (C) 1996-2015, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -697,7 +697,11 @@ public class DecimalFormat extends NumberFormat {
|
||||
private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) {
|
||||
// Always applyPattern after the symbols are set
|
||||
symbols = (DecimalFormatSymbols) inputSymbols.clone();
|
||||
setCurrencyForSymbols();
|
||||
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());
|
||||
@ -2332,7 +2336,7 @@ public class DecimalFormat extends NumberFormat {
|
||||
* @param negSuffix negative suffix pattern
|
||||
* @param posPrefix positive prefix pattern
|
||||
* @param negSuffix negative suffix pattern
|
||||
* @param complexCurrencyParsing whether it is complex currency parsing or not.
|
||||
* @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(
|
||||
@ -2779,7 +2783,7 @@ public class DecimalFormat extends NumberFormat {
|
||||
* @param isNegative
|
||||
* @param isPrefix
|
||||
* @param affixPat affix pattern used for currency affix comparison
|
||||
* @param copmplexCurrencyParsing whether it is currency parsing or not
|
||||
* @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
|
||||
|
@ -11,6 +11,7 @@ import java.text.ParsePosition;
|
||||
import java.util.List;
|
||||
|
||||
import com.ibm.icu.impl.PatternProps;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
|
||||
/**
|
||||
* A class representing a single rule in a RuleBasedNumberFormat. A rule
|
||||
@ -25,22 +26,34 @@ final class NFRule {
|
||||
/**
|
||||
* Special base value used to identify a negative-number rule
|
||||
*/
|
||||
public static final int NEGATIVE_NUMBER_RULE = -1;
|
||||
static final int NEGATIVE_NUMBER_RULE = -1;
|
||||
|
||||
/**
|
||||
* Special base value used to identify an improper fraction (x.x) rule
|
||||
*/
|
||||
public static final int IMPROPER_FRACTION_RULE = -2;
|
||||
static final int IMPROPER_FRACTION_RULE = -2;
|
||||
|
||||
/**
|
||||
* Special base value used to identify a proper fraction (0.x) rule
|
||||
*/
|
||||
public static final int PROPER_FRACTION_RULE = -3;
|
||||
static final int PROPER_FRACTION_RULE = -3;
|
||||
|
||||
/**
|
||||
* Special base value used to identify a master rule
|
||||
*/
|
||||
public static final int MASTER_RULE = -4;
|
||||
static final int MASTER_RULE = -4;
|
||||
|
||||
/**
|
||||
* Special base value used to identify an infinity rule
|
||||
*/
|
||||
static final int INFINITY_RULE = -5;
|
||||
|
||||
/**
|
||||
* Special base value used to identify a not a number rule
|
||||
*/
|
||||
static final int NAN_RULE = -6;
|
||||
|
||||
static final Long ZERO = (long) 0;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// data members
|
||||
@ -63,6 +76,11 @@ final class NFRule {
|
||||
*/
|
||||
private short exponent = 0;
|
||||
|
||||
/**
|
||||
* If this is a fraction rule, this is the decimal point from DecimalFormatSymbols to match.
|
||||
*/
|
||||
private char decimalPoint = 0;
|
||||
|
||||
/**
|
||||
* The rule's rule text. When formatting a number, the rule's text
|
||||
* is inserted into the result string, and then the text from any
|
||||
@ -117,21 +135,23 @@ final class NFRule {
|
||||
// new it up and initialize its basevalue and divisor
|
||||
// (this also strips the rule descriptor, if any, off the
|
||||
// description string)
|
||||
NFRule rule1 = new NFRule(ownersOwner);
|
||||
description = rule1.parseRuleDescriptor(description);
|
||||
NFRule rule1 = new NFRule(ownersOwner, description);
|
||||
description = rule1.ruleText;
|
||||
|
||||
// check the description to see whether there's text enclosed
|
||||
// in brackets
|
||||
int brack1 = description.indexOf("[");
|
||||
int brack2 = description.indexOf("]");
|
||||
int brack1 = description.indexOf('[');
|
||||
int brack2 = brack1 < 0 ? -1 : description.indexOf(']');
|
||||
|
||||
// if the description doesn't contain a matched pair of brackets,
|
||||
// or if it's of a type that doesn't recognize bracketed text,
|
||||
// then leave the description alone, initialize the rule's
|
||||
// rule text and substitutions, and return that rule
|
||||
if (brack1 == -1 || brack2 == -1 || brack1 > brack2
|
||||
|| rule1.getBaseValue() == PROPER_FRACTION_RULE
|
||||
|| rule1.getBaseValue() == NEGATIVE_NUMBER_RULE)
|
||||
if (brack2 < 0 || brack1 > brack2
|
||||
|| rule1.baseValue == PROPER_FRACTION_RULE
|
||||
|| rule1.baseValue == NEGATIVE_NUMBER_RULE
|
||||
|| rule1.baseValue == INFINITY_RULE
|
||||
|| rule1.baseValue == NAN_RULE)
|
||||
{
|
||||
rule1.extractSubstitutions(owner, description, predecessor);
|
||||
}
|
||||
@ -155,7 +175,7 @@ final class NFRule {
|
||||
// set, they both have the same base value; otherwise,
|
||||
// increment the original rule's base value ("rule1" actually
|
||||
// goes SECOND in the rule set's rule list)
|
||||
rule2 = new NFRule(ownersOwner);
|
||||
rule2 = new NFRule(ownersOwner, null);
|
||||
if (rule1.baseValue >= 0) {
|
||||
rule2.baseValue = rule1.baseValue;
|
||||
if (!owner.isFractionSet()) {
|
||||
@ -207,18 +227,29 @@ final class NFRule {
|
||||
// material in the brackets and rule1 INCLUDES the material
|
||||
// in the brackets)
|
||||
if (rule2 != null) {
|
||||
returnList.add(rule2);
|
||||
if (rule2.baseValue >= 0) {
|
||||
returnList.add(rule2);
|
||||
}
|
||||
else {
|
||||
owner.setNonNumericalRule(rule2);
|
||||
}
|
||||
}
|
||||
}
|
||||
returnList.add(rule1);
|
||||
if (rule1.baseValue >= 0) {
|
||||
returnList.add(rule1);
|
||||
}
|
||||
else {
|
||||
owner.setNonNumericalRule(rule1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nominal constructor for NFRule. Most of the work of constructing
|
||||
* an NFRule is actually performed by makeRules().
|
||||
*/
|
||||
public NFRule(RuleBasedNumberFormat formatter) {
|
||||
public NFRule(RuleBasedNumberFormat formatter, String ruleText) {
|
||||
this.formatter = formatter;
|
||||
this.ruleText = ruleText == null ? null : parseRuleDescriptor(ruleText);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,10 +271,7 @@ final class NFRule {
|
||||
// separated by a colon. The rule descriptor is optional. If
|
||||
// it's omitted, just set the base value to 0.
|
||||
int p = description.indexOf(":");
|
||||
if (p == -1) {
|
||||
setBaseValue(0);
|
||||
}
|
||||
else {
|
||||
if (p != -1) {
|
||||
// copy the descriptor out into its own string and strip it,
|
||||
// along with any trailing whitespace, out of the original
|
||||
// description
|
||||
@ -257,14 +285,13 @@ final class NFRule {
|
||||
// check first to see if the rule descriptor matches the token
|
||||
// for one of the special rules. If it does, set the base
|
||||
// value to the correct identifier value
|
||||
if (descriptor.equals("0.x")) {
|
||||
setBaseValue(PROPER_FRACTION_RULE);
|
||||
}
|
||||
else if (descriptor.charAt(0) >= '0' && descriptor.charAt(0) <= '9') {
|
||||
int descriptorLength = descriptor.length();
|
||||
char firstChar = descriptor.charAt(0);
|
||||
char lastChar = descriptor.charAt(descriptorLength - 1);
|
||||
if (firstChar >= '0' && firstChar <= '9' && lastChar != 'x') {
|
||||
// if the rule descriptor begins with a digit, it's a descriptor
|
||||
// for a normal rule
|
||||
long tempValue = 0;
|
||||
int descriptorLength = descriptor.length();
|
||||
char c = 0;
|
||||
p = 0;
|
||||
|
||||
@ -339,13 +366,28 @@ final class NFRule {
|
||||
else if (descriptor.equals("-x")) {
|
||||
setBaseValue(NEGATIVE_NUMBER_RULE);
|
||||
}
|
||||
else if (descriptor.equals("x.x")) {
|
||||
setBaseValue(IMPROPER_FRACTION_RULE);
|
||||
}
|
||||
else if (descriptor.equals("x.0")) {
|
||||
setBaseValue(MASTER_RULE);
|
||||
else if (descriptorLength == 3) {
|
||||
if (firstChar == '0' && lastChar == 'x') {
|
||||
setBaseValue(PROPER_FRACTION_RULE);
|
||||
decimalPoint = descriptor.charAt(1);
|
||||
}
|
||||
else if (firstChar == 'x' && lastChar == 'x') {
|
||||
setBaseValue(IMPROPER_FRACTION_RULE);
|
||||
decimalPoint = descriptor.charAt(1);
|
||||
}
|
||||
else if (firstChar == 'x' && lastChar == '0') {
|
||||
setBaseValue(MASTER_RULE);
|
||||
decimalPoint = descriptor.charAt(1);
|
||||
}
|
||||
else if (descriptor.equals("NaN")) {
|
||||
setBaseValue(NAN_RULE);
|
||||
}
|
||||
else if (descriptor.equals("Inf")) {
|
||||
setBaseValue(INFINITY_RULE);
|
||||
}
|
||||
}
|
||||
}
|
||||
// else use the default base value for now.
|
||||
|
||||
// finally, if the rule body begins with an apostrophe, strip it off
|
||||
// (this is generally used to put whitespace at the beginning of
|
||||
@ -371,11 +413,10 @@ final class NFRule {
|
||||
String ruleText,
|
||||
NFRule predecessor) {
|
||||
this.ruleText = ruleText;
|
||||
this.rulePatternFormat = null;
|
||||
sub1 = extractSubstitution(owner, predecessor);
|
||||
if (sub1.isNullSubstitution()) {
|
||||
if (sub1 == null) {
|
||||
// Small optimization. There is no need to create a redundant NullSubstitution.
|
||||
sub2 = sub1;
|
||||
sub2 = null;
|
||||
}
|
||||
else {
|
||||
sub2 = extractSubstitution(owner, predecessor);
|
||||
@ -404,12 +445,6 @@ final class NFRule {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] RULE_PREFIXES = new String[] {
|
||||
"<<", "<%", "<#", "<0",
|
||||
">>", ">%", ">#", ">0",
|
||||
"=%", "=#", "=0"
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the rule's rule text for the first substitution token,
|
||||
* creates a substitution based on it, and removes the token from
|
||||
@ -429,18 +464,17 @@ final class NFRule {
|
||||
|
||||
// search the rule's rule text for the first two characters of
|
||||
// a substitution token
|
||||
subStart = indexOfAny(ruleText, RULE_PREFIXES);
|
||||
subStart = indexOfAnyRulePrefix(ruleText);
|
||||
|
||||
// if we didn't find one, create a null substitution positioned
|
||||
// at the end of the rule text
|
||||
if (subStart == -1) {
|
||||
return NFSubstitution.makeSubstitution(ruleText.length(), this, predecessor,
|
||||
owner, this.formatter, "");
|
||||
return null;
|
||||
}
|
||||
|
||||
// special-case the ">>>" token, since searching for the > at the
|
||||
// end will actually find the > in the middle
|
||||
if (ruleText.substring(subStart).startsWith(">>>")) {
|
||||
if (ruleText.startsWith(">>>", subStart)) {
|
||||
subEnd = subStart + 2;
|
||||
}
|
||||
else {
|
||||
@ -462,8 +496,7 @@ final class NFRule {
|
||||
// unmatched token character), create a null substitution positioned
|
||||
// at the end of the rule
|
||||
if (subEnd == -1) {
|
||||
return NFSubstitution.makeSubstitution(ruleText.length(), this, predecessor,
|
||||
owner, this.formatter, "");
|
||||
return null;
|
||||
}
|
||||
|
||||
// if we get here, we have a real substitution token (or at least
|
||||
@ -487,6 +520,7 @@ final class NFRule {
|
||||
final void setBaseValue(long newBaseValue) {
|
||||
// set the base value
|
||||
baseValue = newBaseValue;
|
||||
radix = 10;
|
||||
|
||||
// if this isn't a special rule, recalculate the radix and exponent
|
||||
// (the radix always defaults to 10; if it's supposed to be something
|
||||
@ -494,7 +528,6 @@ final class NFRule {
|
||||
// recalculated again-- the only function that does this is
|
||||
// NFRule.parseRuleDescriptor() )
|
||||
if (baseValue >= 1) {
|
||||
radix = 10;
|
||||
exponent = expectedExponent();
|
||||
|
||||
// this function gets called on a fully-constructed rule whose
|
||||
@ -511,7 +544,6 @@ final class NFRule {
|
||||
else {
|
||||
// if this is a special rule, its radix and exponent are basically
|
||||
// ignored. Set them to "safe" default values
|
||||
radix = 10;
|
||||
exponent = 0;
|
||||
}
|
||||
}
|
||||
@ -540,20 +572,24 @@ final class NFRule {
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] RULE_PREFIXES = new String[] {
|
||||
"<<", "<%", "<#", "<0",
|
||||
">>", ">%", ">#", ">0",
|
||||
"=%", "=#", "=0"
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the rule's rule text for any of the specified strings.
|
||||
* @param strings An array of strings to search the rule's rule
|
||||
* text for
|
||||
* @return The index of the first match in the rule's rule text
|
||||
* (i.e., the first substring in the rule's rule text that matches
|
||||
* _any_ of the strings in "strings"). If none of the strings in
|
||||
* "strings" is found in the rule's rule text, returns -1.
|
||||
*/
|
||||
private static int indexOfAny(String ruleText, String[] strings) {
|
||||
private static int indexOfAnyRulePrefix(String ruleText) {
|
||||
int result = -1;
|
||||
if (ruleText.length() > 0) {
|
||||
int pos;
|
||||
for (String string : strings) {
|
||||
for (String string : RULE_PREFIXES) {
|
||||
pos = ruleText.indexOf(string);
|
||||
if (pos != -1 && (result == -1 || pos < result)) {
|
||||
result = pos;
|
||||
@ -580,8 +616,8 @@ final class NFRule {
|
||||
&& radix == that2.radix
|
||||
&& exponent == that2.exponent
|
||||
&& ruleText.equals(that2.ruleText)
|
||||
&& sub1.equals(that2.sub1)
|
||||
&& sub2.equals(that2.sub2);
|
||||
&& Utility.objectEquals(sub1, that2.sub1)
|
||||
&& Utility.objectEquals(sub2, that2.sub2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -605,13 +641,19 @@ final class NFRule {
|
||||
result.append("-x: ");
|
||||
}
|
||||
else if (baseValue == IMPROPER_FRACTION_RULE) {
|
||||
result.append("x.x: ");
|
||||
result.append('x').append(decimalPoint == 0 ? '.' : decimalPoint).append("x: ");
|
||||
}
|
||||
else if (baseValue == PROPER_FRACTION_RULE) {
|
||||
result.append("0.x: ");
|
||||
result.append('0').append(decimalPoint == 0 ? '.' : decimalPoint).append("x: ");
|
||||
}
|
||||
else if (baseValue == MASTER_RULE) {
|
||||
result.append("x.0: ");
|
||||
result.append('x').append(decimalPoint == 0 ? '.' : decimalPoint).append("0: ");
|
||||
}
|
||||
else if (baseValue == INFINITY_RULE) {
|
||||
result.append("Inf: ");
|
||||
}
|
||||
else if (baseValue == NAN_RULE) {
|
||||
result.append("NaN: ");
|
||||
}
|
||||
else {
|
||||
// for a normal rule, write out its base value, and if the radix is
|
||||
@ -634,14 +676,18 @@ final class NFRule {
|
||||
// (whitespace after the rule descriptor is ignored; the
|
||||
// apostrophe is used to make the whitespace significant)
|
||||
if (ruleText.startsWith(" ") && (sub1 == null || sub1.getPos() != 0)) {
|
||||
result.append("\'");
|
||||
result.append('\'');
|
||||
}
|
||||
|
||||
// now, write the rule's rule text, inserting appropriate
|
||||
// substitution tokens in the appropriate places
|
||||
StringBuilder ruleTextCopy = new StringBuilder(ruleText);
|
||||
ruleTextCopy.insert(sub2.getPos(), sub2.toString());
|
||||
ruleTextCopy.insert(sub1.getPos(), sub1.toString());
|
||||
if (sub2 != null) {
|
||||
ruleTextCopy.insert(sub2.getPos(), sub2.toString());
|
||||
}
|
||||
if (sub1 != null) {
|
||||
ruleTextCopy.insert(sub1.getPos(), sub1.toString());
|
||||
}
|
||||
result.append(ruleTextCopy.toString());
|
||||
|
||||
// and finally, top the whole thing off with a semicolon and
|
||||
@ -654,6 +700,14 @@ final class NFRule {
|
||||
// simple accessors
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the rule's base value
|
||||
* @return The rule's base value
|
||||
*/
|
||||
public final char getDecimalPoint() {
|
||||
return decimalPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rule's base value
|
||||
* @return The rule's base value
|
||||
@ -708,10 +762,10 @@ final class NFRule {
|
||||
}
|
||||
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
|
||||
}
|
||||
if (!sub2.isNullSubstitution()) {
|
||||
if (sub2 != null) {
|
||||
sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
||||
}
|
||||
if (!sub1.isNullSubstitution()) {
|
||||
if (sub1 != null) {
|
||||
sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
||||
}
|
||||
}
|
||||
@ -750,10 +804,10 @@ final class NFRule {
|
||||
}
|
||||
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
|
||||
}
|
||||
if (!sub2.isNullSubstitution()) {
|
||||
if (sub2 != null) {
|
||||
sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
||||
}
|
||||
if (!sub1.isNullSubstitution()) {
|
||||
if (sub1 != null) {
|
||||
sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
||||
}
|
||||
}
|
||||
@ -783,7 +837,7 @@ final class NFRule {
|
||||
// a modulus substitution, its base value isn't an even multiple
|
||||
// of 100, and the value we're trying to format _is_ an even
|
||||
// multiple of 100. This is called the "rollback rule."
|
||||
return ((sub1.isModulusSubstitution()) || (sub2.isModulusSubstitution()))
|
||||
return ((sub1 != null && sub1.isModulusSubstitution()) || (sub2 != null && sub2.isModulusSubstitution()))
|
||||
&& (number % Math.pow(radix, exponent)) == 0 && (baseValue % Math.pow(radix, exponent)) != 0;
|
||||
}
|
||||
|
||||
@ -820,13 +874,25 @@ final class NFRule {
|
||||
// matches the text at the beginning of the string being
|
||||
// parsed. If it does, strip that off the front of workText;
|
||||
// otherwise, dump out with a mismatch
|
||||
String workText = stripPrefix(text, ruleText.substring(0, sub1.getPos()), pp);
|
||||
int sub1Pos = sub1 != null ? sub1.getPos() : ruleText.length();
|
||||
int sub2Pos = sub2 != null ? sub2.getPos() : ruleText.length();
|
||||
String workText = stripPrefix(text, ruleText.substring(0, sub1Pos), pp);
|
||||
int prefixLength = text.length() - workText.length();
|
||||
|
||||
if (pp.getIndex() == 0 && sub1.getPos() != 0) {
|
||||
if (pp.getIndex() == 0 && sub1Pos != 0) {
|
||||
// commented out because ParsePosition doesn't have error index in 1.1.x
|
||||
// parsePosition.setErrorIndex(pp.getErrorIndex());
|
||||
return Long.valueOf(0);
|
||||
return ZERO;
|
||||
}
|
||||
if (baseValue == INFINITY_RULE) {
|
||||
// If you match this, don't try to perform any calculations on it.
|
||||
parsePosition.setIndex(pp.getIndex());
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
if (baseValue == NAN_RULE) {
|
||||
// If you match this, don't try to perform any calculations on it.
|
||||
parsePosition.setIndex(pp.getIndex());
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
// this is the fun part. The basic guts of the rule-matching
|
||||
@ -870,14 +936,14 @@ final class NFRule {
|
||||
// the substitution, giving us a new partial parse result
|
||||
pp.setIndex(0);
|
||||
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
|
||||
ruleText.substring(sub1.getPos(), sub2.getPos()), rulePatternFormat,
|
||||
ruleText.substring(sub1Pos, sub2Pos), rulePatternFormat,
|
||||
pp, sub1, upperBound).doubleValue();
|
||||
|
||||
// if we got a successful match (or were trying to match a
|
||||
// null substitution), pp is now pointing at the first unmatched
|
||||
// character. Take note of that, and try matchToDelimiter()
|
||||
// on the input text again
|
||||
if (pp.getIndex() != 0 || sub1.isNullSubstitution()) {
|
||||
if (pp.getIndex() != 0 || sub1 == null) {
|
||||
start = pp.getIndex();
|
||||
|
||||
String workText2 = workText.substring(pp.getIndex());
|
||||
@ -888,13 +954,13 @@ final class NFRule {
|
||||
// substitution if there's a successful match, giving us
|
||||
// a real result
|
||||
partialResult = matchToDelimiter(workText2, 0, partialResult,
|
||||
ruleText.substring(sub2.getPos()), rulePatternFormat, pp2, sub2,
|
||||
ruleText.substring(sub2Pos), rulePatternFormat, pp2, sub2,
|
||||
upperBound).doubleValue();
|
||||
|
||||
// if we got a successful match on this second
|
||||
// matchToDelimiter() call, update the high-water mark
|
||||
// and result (if necessary)
|
||||
if (pp2.getIndex() != 0 || sub2.isNullSubstitution()) {
|
||||
if (pp2.getIndex() != 0 || sub2 == null) {
|
||||
if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) {
|
||||
highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex();
|
||||
result = partialResult;
|
||||
@ -918,7 +984,8 @@ final class NFRule {
|
||||
// keep trying to match things until the outer matchToDelimiter()
|
||||
// call fails to make a match (each time, it picks up where it
|
||||
// left off the previous time)
|
||||
} while (sub1.getPos() != sub2.getPos() && pp.getIndex() > 0 && pp.getIndex()
|
||||
}
|
||||
while (sub1Pos != sub2Pos && pp.getIndex() > 0 && pp.getIndex()
|
||||
< workText.length() && pp.getIndex() != start);
|
||||
|
||||
// update the caller's ParsePosition with our high-water mark
|
||||
@ -937,7 +1004,7 @@ final class NFRule {
|
||||
// we have to account for it here. By definition, if the matching
|
||||
// rule in a fraction rule set has no substitutions, its numerator
|
||||
// is 1, and so the result is the reciprocal of its base value.
|
||||
if (isFractionRule && highWaterMark > 0 && sub1.isNullSubstitution()) {
|
||||
if (isFractionRule && highWaterMark > 0 && sub1 == null) {
|
||||
result = 1 / result;
|
||||
}
|
||||
|
||||
@ -1071,21 +1138,23 @@ final class NFRule {
|
||||
// if we make it here, this was an unsuccessful match, and we
|
||||
// leave pp unchanged and return 0
|
||||
pp.setIndex(0);
|
||||
return Long.valueOf(0);
|
||||
return ZERO;
|
||||
|
||||
// if "delimiter" is empty, or consists only of ignorable characters
|
||||
// (i.e., is semantically empty), thwe we obviously can't search
|
||||
// for "delimiter". Instead, just use "sub" to parse as much of
|
||||
// "text" as possible.
|
||||
} else {
|
||||
}
|
||||
else if (sub == null) {
|
||||
return baseVal;
|
||||
}
|
||||
else {
|
||||
ParsePosition tempPP = new ParsePosition(0);
|
||||
Number result = Long.valueOf(0);
|
||||
Number tempResult;
|
||||
|
||||
Number result = ZERO;
|
||||
// try to match the whole string against the substitution
|
||||
tempResult = sub.doParse(text, tempPP, baseVal, upperBound,
|
||||
formatter.lenientParseEnabled());
|
||||
if (tempPP.getIndex() != 0 || sub.isNullSubstitution()) {
|
||||
Number tempResult = sub.doParse(text, tempPP, baseVal, upperBound,
|
||||
formatter.lenientParseEnabled());
|
||||
if (tempPP.getIndex() != 0) {
|
||||
// if there's a successful match (or it's a null
|
||||
// substitution), update pp to point to the first
|
||||
// character we didn't match, and pass the result from
|
||||
|
@ -8,6 +8,7 @@ package com.ibm.icu.text;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.ibm.icu.impl.PatternProps;
|
||||
@ -37,16 +38,33 @@ final class NFRuleSet {
|
||||
private NFRule[] rules;
|
||||
|
||||
/**
|
||||
* The rule set's negative-number rule
|
||||
* The rule set's non-numerical rules like negative, fractions, infinity and NaN
|
||||
*/
|
||||
private NFRule negativeNumberRule = null;
|
||||
final NFRule[] nonNumericalRules = new NFRule[6];
|
||||
|
||||
/**
|
||||
* The rule set's fraction rules: element 0 is the proper fraction
|
||||
* (0.x) rule, element 1 is the improper fraction (x.x) rule, and
|
||||
* element 2 is the master (x.0) rule.
|
||||
* These are a pile of fraction rules in declared order. They may have alternate
|
||||
* ways to represent fractions.
|
||||
*/
|
||||
private NFRule[] fractionRules = new NFRule[3];
|
||||
LinkedList<NFRule> fractionRules;
|
||||
|
||||
/** -x */
|
||||
static final int NEGATIVE_RULE_INDEX = 0;
|
||||
/** x.x */
|
||||
static final int IMPROPER_FRACTION_RULE_INDEX = 1;
|
||||
/** 0.x */
|
||||
static final int PROPER_FRACTION_RULE_INDEX = 2;
|
||||
/** x.0 */
|
||||
static final int MASTER_RULE_INDEX = 3;
|
||||
/** Inf */
|
||||
static final int INFINITY_RULE_INDEX = 4;
|
||||
/** NaN */
|
||||
static final int NAN_RULE_INDEX = 5;
|
||||
|
||||
/**
|
||||
* The RuleBasedNumberFormat that owns this rule
|
||||
*/
|
||||
final RuleBasedNumberFormat owner;
|
||||
|
||||
/**
|
||||
* True if the rule set is a fraction rule set. A fraction rule set
|
||||
@ -72,15 +90,17 @@ final class NFRuleSet {
|
||||
// construction
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
/**
|
||||
* Constructs a rule set.
|
||||
* @param owner The formatter that owns this rule set
|
||||
* @param descriptions An array of Strings representing rule set
|
||||
* descriptions. On exit, this rule set's entry in the array will
|
||||
* have been stripped of its rule set name and any trailing whitespace.
|
||||
* @param index The index into "descriptions" of the description
|
||||
* for the rule to be constructed
|
||||
*/
|
||||
public NFRuleSet(String[] descriptions, int index) throws IllegalArgumentException {
|
||||
public NFRuleSet(RuleBasedNumberFormat owner, String[] descriptions, int index) throws IllegalArgumentException {
|
||||
this.owner = owner;
|
||||
String description = descriptions[index];
|
||||
|
||||
if (description.length() == 0) {
|
||||
@ -104,6 +124,7 @@ final class NFRuleSet {
|
||||
}
|
||||
this.name = name;
|
||||
|
||||
//noinspection StatementWithEmptyBody
|
||||
while (pos < description.length() && PatternProps.isWhiteSpace(description.charAt(++pos))) {
|
||||
}
|
||||
description = description.substring(pos);
|
||||
@ -133,10 +154,8 @@ final class NFRuleSet {
|
||||
* can refer to any other rule set, we have to have created all of
|
||||
* them before we can create anything else.
|
||||
* @param description The textual description of this rule set
|
||||
* @param owner The formatter that owns this rule set
|
||||
*/
|
||||
public void parseRules(String description,
|
||||
RuleBasedNumberFormat owner) {
|
||||
public void parseRules(String description) {
|
||||
// (the number of elements in the description list isn't necessarily
|
||||
// the number of rules-- some descriptions may expend into two rules)
|
||||
List<NFRule> tempRules = new ArrayList<NFRule>();
|
||||
@ -162,7 +181,9 @@ final class NFRuleSet {
|
||||
// to our rule vector
|
||||
NFRule.makeRules(description.substring(oldP, p),
|
||||
this, predecessor, owner, tempRules);
|
||||
predecessor = tempRules.get(tempRules.size() - 1);
|
||||
if (!tempRules.isEmpty()) {
|
||||
predecessor = tempRules.get(tempRules.size() - 1);
|
||||
}
|
||||
|
||||
oldP = p + 1;
|
||||
}
|
||||
@ -174,69 +195,28 @@ final class NFRuleSet {
|
||||
// rules from the list and put them into their own member variables
|
||||
long defaultBaseValue = 0;
|
||||
|
||||
// (this isn't a for loop because we might be deleting items from
|
||||
// the vector-- we want to make sure we only increment i when
|
||||
// we _didn't_ delete anything from the vector)
|
||||
int i = 0;
|
||||
while (i < tempRules.size()) {
|
||||
NFRule rule = tempRules.get(i);
|
||||
|
||||
switch ((int)rule.getBaseValue()) {
|
||||
case 0:
|
||||
// if the rule's base value is 0, fill in a default
|
||||
// base value (this will be 1 plus the preceding
|
||||
// rule's base value for regular rule sets, and the
|
||||
// same as the preceding rule's base value in fraction
|
||||
// rule sets)
|
||||
rule.setBaseValue(defaultBaseValue);
|
||||
if (!isFractionRuleSet) {
|
||||
++defaultBaseValue;
|
||||
}
|
||||
++i;
|
||||
break;
|
||||
|
||||
case NFRule.NEGATIVE_NUMBER_RULE:
|
||||
// if it's the negative-number rule, copy it into its own
|
||||
// data member and delete it from the list
|
||||
negativeNumberRule = rule;
|
||||
tempRules.remove(i);
|
||||
break;
|
||||
|
||||
case NFRule.IMPROPER_FRACTION_RULE:
|
||||
// if it's the improper fraction rule, copy it into the
|
||||
// correct element of fractionRules
|
||||
fractionRules[0] = rule;
|
||||
tempRules.remove(i);
|
||||
break;
|
||||
|
||||
case NFRule.PROPER_FRACTION_RULE:
|
||||
// if it's the proper fraction rule, copy it into the
|
||||
// correct element of fractionRules
|
||||
fractionRules[1] = rule;
|
||||
tempRules.remove(i);
|
||||
break;
|
||||
|
||||
case NFRule.MASTER_RULE:
|
||||
// if it's the master rule, copy it into the
|
||||
// correct element of fractionRules
|
||||
fractionRules[2] = rule;
|
||||
tempRules.remove(i);
|
||||
break;
|
||||
|
||||
default:
|
||||
// if it's a regular rule that already knows its base value,
|
||||
// check to make sure the rules are in order, and update
|
||||
// the default base value for the next rule
|
||||
if (rule.getBaseValue() < defaultBaseValue) {
|
||||
throw new IllegalArgumentException("Rules are not in order, base: " +
|
||||
rule.getBaseValue() + " < " + defaultBaseValue);
|
||||
}
|
||||
defaultBaseValue = rule.getBaseValue();
|
||||
if (!isFractionRuleSet) {
|
||||
++defaultBaseValue;
|
||||
}
|
||||
++i;
|
||||
break;
|
||||
for (NFRule rule : tempRules) {
|
||||
long baseValue = rule.getBaseValue();
|
||||
if (baseValue == 0) {
|
||||
// if the rule's base value is 0, fill in a default
|
||||
// base value (this will be 1 plus the preceding
|
||||
// rule's base value for regular rule sets, and the
|
||||
// same as the preceding rule's base value in fraction
|
||||
// rule sets)
|
||||
rule.setBaseValue(defaultBaseValue);
|
||||
}
|
||||
else {
|
||||
// if it's a regular rule that already knows its base value,
|
||||
// check to make sure the rules are in order, and update
|
||||
// the default base value for the next rule
|
||||
if (baseValue < defaultBaseValue) {
|
||||
throw new IllegalArgumentException("Rules are not in order, base: " +
|
||||
baseValue + " < " + defaultBaseValue);
|
||||
}
|
||||
defaultBaseValue = baseValue;
|
||||
}
|
||||
if (!isFractionRuleSet) {
|
||||
++defaultBaseValue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,6 +226,60 @@ final class NFRuleSet {
|
||||
tempRules.toArray(rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one of the non-numerical rules.
|
||||
* @param rule The rule to set.
|
||||
*/
|
||||
void setNonNumericalRule(NFRule rule) {
|
||||
long baseValue = rule.getBaseValue();
|
||||
if (baseValue == NFRule.NEGATIVE_NUMBER_RULE) {
|
||||
nonNumericalRules[NFRuleSet.NEGATIVE_RULE_INDEX] = rule;
|
||||
}
|
||||
else if (baseValue == NFRule.IMPROPER_FRACTION_RULE) {
|
||||
setBestFractionRule(NFRuleSet.IMPROPER_FRACTION_RULE_INDEX, rule, true);
|
||||
}
|
||||
else if (baseValue == NFRule.PROPER_FRACTION_RULE) {
|
||||
setBestFractionRule(NFRuleSet.PROPER_FRACTION_RULE_INDEX, rule, true);
|
||||
}
|
||||
else if (baseValue == NFRule.MASTER_RULE) {
|
||||
setBestFractionRule(NFRuleSet.MASTER_RULE_INDEX, rule, true);
|
||||
}
|
||||
else if (baseValue == NFRule.INFINITY_RULE) {
|
||||
nonNumericalRules[NFRuleSet.INFINITY_RULE_INDEX] = rule;
|
||||
}
|
||||
else if (baseValue == NFRule.NAN_RULE) {
|
||||
nonNumericalRules[NFRuleSet.NAN_RULE_INDEX] = rule;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the best fraction rule to use. Rules matching the decimal point from
|
||||
* DecimalFormatSymbols become the main set of rules to use.
|
||||
* @param originalIndex The index into nonNumericalRules
|
||||
* @param newRule The new rule to consider
|
||||
* @param rememberRule Should the new rule be added to fractionRules.
|
||||
*/
|
||||
private void setBestFractionRule(int originalIndex, NFRule newRule, boolean rememberRule) {
|
||||
if (rememberRule) {
|
||||
if (fractionRules == null) {
|
||||
fractionRules = new LinkedList<NFRule>();
|
||||
}
|
||||
fractionRules.add(newRule);
|
||||
}
|
||||
NFRule bestResult = nonNumericalRules[originalIndex];
|
||||
if (bestResult == null) {
|
||||
nonNumericalRules[originalIndex] = newRule;
|
||||
}
|
||||
else {
|
||||
// We have more than one. Which one is better?
|
||||
DecimalFormatSymbols decimalFormatSymbols = owner.getDecimalFormatSymbols();
|
||||
if (decimalFormatSymbols.getDecimalSeparator() == newRule.getDecimalPoint()) {
|
||||
nonNumericalRules[originalIndex] = newRule;
|
||||
}
|
||||
// else leave it alone
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags this rule set as a fraction rule set. This function is
|
||||
* called during the construction process once we know this rule
|
||||
@ -276,16 +310,19 @@ final class NFRuleSet {
|
||||
NFRuleSet that2 = (NFRuleSet)that;
|
||||
|
||||
if (!name.equals(that2.name)
|
||||
|| !Utility.objectEquals(negativeNumberRule, that2.negativeNumberRule)
|
||||
|| !Utility.objectEquals(fractionRules[0], that2.fractionRules[0])
|
||||
|| !Utility.objectEquals(fractionRules[1], that2.fractionRules[1])
|
||||
|| !Utility.objectEquals(fractionRules[2], that2.fractionRules[2])
|
||||
|| rules.length != that2.rules.length
|
||||
|| isFractionRuleSet != that2.isFractionRuleSet)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// ...then compare the non-numerical rule lists...
|
||||
for (int i = 0; i < nonNumericalRules.length; i++) {
|
||||
if (!Utility.objectEquals(nonNumericalRules[i], that2.nonNumericalRules[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ...then compare the rule lists...
|
||||
for (int i = 0; i < rules.length; i++) {
|
||||
if (!rules[i].equals(that2.rules[i])) {
|
||||
@ -317,22 +354,27 @@ final class NFRuleSet {
|
||||
result.append(name).append(":\n");
|
||||
|
||||
// followed by the regular rules...
|
||||
for (int i = 0; i < rules.length; i++) {
|
||||
result.append(" ").append(rules[i].toString()).append("\n");
|
||||
for (NFRule rule : rules) {
|
||||
result.append(rule.toString()).append("\n");
|
||||
}
|
||||
|
||||
// followed by the special rules (if they exist)
|
||||
if (negativeNumberRule != null) {
|
||||
result.append(" ").append(negativeNumberRule.toString()).append("\n");
|
||||
}
|
||||
if (fractionRules[0] != null) {
|
||||
result.append(" ").append(fractionRules[0].toString()).append("\n");
|
||||
}
|
||||
if (fractionRules[1] != null) {
|
||||
result.append(" ").append(fractionRules[1].toString()).append("\n");
|
||||
}
|
||||
if (fractionRules[2] != null) {
|
||||
result.append(" ").append(fractionRules[2].toString()).append("\n");
|
||||
for (NFRule rule : nonNumericalRules) {
|
||||
if (rule != null) {
|
||||
if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
|
||||
|| rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
|
||||
|| rule.getBaseValue() == NFRule.MASTER_RULE)
|
||||
{
|
||||
for (NFRule fractionRule : fractionRules) {
|
||||
if (fractionRule.getBaseValue() == rule.getBaseValue()) {
|
||||
result.append(fractionRule.toString()).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.append(rule.toString()).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
@ -387,11 +429,10 @@ final class NFRuleSet {
|
||||
* this operation is to be inserted
|
||||
*/
|
||||
public void format(long number, StringBuffer toInsertInto, int pos, int recursionCount) {
|
||||
NFRule applicableRule = findNormalRule(number);
|
||||
|
||||
if (recursionCount >= RECURSION_LIMIT) {
|
||||
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
||||
}
|
||||
NFRule applicableRule = findNormalRule(number);
|
||||
applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
|
||||
}
|
||||
|
||||
@ -404,11 +445,10 @@ final class NFRuleSet {
|
||||
* this operation is to be inserted
|
||||
*/
|
||||
public void format(double number, StringBuffer toInsertInto, int pos, int recursionCount) {
|
||||
NFRule applicableRule = findRule(number);
|
||||
|
||||
if (recursionCount >= RECURSION_LIMIT) {
|
||||
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
||||
}
|
||||
NFRule applicableRule = findRule(number);
|
||||
applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
|
||||
}
|
||||
|
||||
@ -417,42 +457,57 @@ final class NFRuleSet {
|
||||
* @param number The number being formatted.
|
||||
* @return The rule that should be used to format it
|
||||
*/
|
||||
private NFRule findRule(double number) {
|
||||
NFRule findRule(double number) {
|
||||
// if this is a fraction rule set, use findFractionRuleSetRule()
|
||||
if (isFractionRuleSet) {
|
||||
return findFractionRuleSetRule(number);
|
||||
}
|
||||
|
||||
if (Double.isNaN(number)) {
|
||||
NFRule rule = nonNumericalRules[NAN_RULE_INDEX];
|
||||
if (rule == null) {
|
||||
rule = owner.getDefaultNaNRule();
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
// if the number is negative, return the negative number rule
|
||||
// (if there isn't a negative-number rule, we pretend it's a
|
||||
// positive number)
|
||||
if (number < 0) {
|
||||
if (negativeNumberRule != null) {
|
||||
return negativeNumberRule;
|
||||
if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) {
|
||||
return nonNumericalRules[NEGATIVE_RULE_INDEX];
|
||||
} else {
|
||||
number = -number;
|
||||
}
|
||||
}
|
||||
|
||||
// if the number isn't an integer, we use one f the fraction rules...
|
||||
if (number != Math.floor(number)) {
|
||||
// if the number is between 0 and 1, return the proper
|
||||
// fraction rule
|
||||
if (number < 1 && fractionRules[1] != null) {
|
||||
return fractionRules[1];
|
||||
if (Double.isInfinite(number)) {
|
||||
NFRule rule = nonNumericalRules[INFINITY_RULE_INDEX];
|
||||
if (rule == null) {
|
||||
rule = owner.getDefaultInfinityRule();
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
|
||||
// otherwise, return the improper fraction rule
|
||||
else if (fractionRules[0] != null) {
|
||||
return fractionRules[0];
|
||||
// if the number isn't an integer, we use one f the fraction rules...
|
||||
if (nonNumericalRules != null && number != Math.floor(number)) {
|
||||
if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX] != null) {
|
||||
// if the number is between 0 and 1, return the proper
|
||||
// fraction rule
|
||||
return nonNumericalRules[PROPER_FRACTION_RULE_INDEX];
|
||||
}
|
||||
else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX] != null) {
|
||||
// otherwise, return the improper fraction rule
|
||||
return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX];
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a master rule, use it to format the number
|
||||
if (fractionRules[2] != null) {
|
||||
return fractionRules[2];
|
||||
|
||||
} else {
|
||||
if (nonNumericalRules != null && nonNumericalRules[MASTER_RULE_INDEX] != null) {
|
||||
return nonNumericalRules[MASTER_RULE_INDEX];
|
||||
}
|
||||
else {
|
||||
// and if we haven't yet returned a rule, use findNormalRule()
|
||||
// to find the applicable rule
|
||||
return findNormalRule(Math.round(number));
|
||||
@ -486,8 +541,8 @@ final class NFRuleSet {
|
||||
// if the number is negative, return the negative-number rule
|
||||
// (if there isn't one, pretend the number is positive)
|
||||
if (number < 0) {
|
||||
if (negativeNumberRule != null) {
|
||||
return negativeNumberRule;
|
||||
if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) {
|
||||
return nonNumericalRules[NEGATIVE_RULE_INDEX];
|
||||
} else {
|
||||
number = -number;
|
||||
}
|
||||
@ -541,7 +596,7 @@ final class NFRuleSet {
|
||||
return result;
|
||||
}
|
||||
// else use the master rule
|
||||
return fractionRules[2];
|
||||
return nonNumericalRules[MASTER_RULE_INDEX];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -697,32 +752,18 @@ final class NFRuleSet {
|
||||
// that determines the value we return.
|
||||
|
||||
ParsePosition highWaterMark = new ParsePosition(0);
|
||||
Number result = Long.valueOf(0);
|
||||
Number tempResult = null;
|
||||
Number result = NFRule.ZERO;
|
||||
Number tempResult;
|
||||
|
||||
// dump out if there's no text to parse
|
||||
if (text.length() == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// start by trying the negative number rule (if there is one)
|
||||
if (negativeNumberRule != null) {
|
||||
tempResult = negativeNumberRule.doParse(text, parsePosition, false, upperBound);
|
||||
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
||||
result = tempResult;
|
||||
highWaterMark.setIndex(parsePosition.getIndex());
|
||||
}
|
||||
// commented out because the error-index API on ParsePosition isn't there in 1.1.x
|
||||
// if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
|
||||
// highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
|
||||
// }
|
||||
parsePosition.setIndex(0);
|
||||
}
|
||||
|
||||
// then try each of the fraction rules
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (fractionRules[i] != null) {
|
||||
tempResult = fractionRules[i].doParse(text, parsePosition, false, upperBound);
|
||||
// Try each of the negative rules, fraction rules, infinity rules and NaN rules
|
||||
for (NFRule fractionRule : nonNumericalRules) {
|
||||
if (fractionRule != null) {
|
||||
tempResult = fractionRule.doParse(text, parsePosition, false, upperBound);
|
||||
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
||||
result = tempResult;
|
||||
highWaterMark.setIndex(parsePosition.getIndex());
|
||||
@ -777,5 +818,23 @@ final class NFRuleSet {
|
||||
for (NFRule rule : rules) {
|
||||
rule.setDecimalFormatSymbols(newSymbols);
|
||||
}
|
||||
// Switch the fraction rules to mirror the DecimalFormatSymbols.
|
||||
if (fractionRules != null) {
|
||||
for (int nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= MASTER_RULE_INDEX; nonNumericalIdx++) {
|
||||
if (nonNumericalRules[nonNumericalIdx] != null) {
|
||||
for (NFRule rule : fractionRules) {
|
||||
if (nonNumericalRules[nonNumericalIdx].getBaseValue() == rule.getBaseValue()) {
|
||||
setBestFractionRule(nonNumericalIdx, rule, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (NFRule rule : nonNumericalRules) {
|
||||
if (rule != null) {
|
||||
rule.setDecimalFormatSymbols(newSymbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,11 +40,6 @@ abstract class NFSubstitution {
|
||||
*/
|
||||
final DecimalFormat numberFormat;
|
||||
|
||||
/**
|
||||
* Link to the RBNF so that we can access its decimalFormat if need be.
|
||||
*/
|
||||
final RuleBasedNumberFormat rbnf;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// construction
|
||||
//-----------------------------------------------------------------------
|
||||
@ -75,7 +70,7 @@ abstract class NFSubstitution {
|
||||
String description) {
|
||||
// if the description is empty, return a NullSubstitution
|
||||
if (description.length() == 0) {
|
||||
return new NullSubstitution(pos, ruleSet, formatter, description);
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (description.charAt(0)) {
|
||||
@ -94,25 +89,25 @@ abstract class NFSubstitution {
|
||||
|| rule.getBaseValue() == NFRule.MASTER_RULE)
|
||||
{
|
||||
// if the rule is a fraction rule, return an IntegralPartSubstitution
|
||||
return new IntegralPartSubstitution(pos, ruleSet, formatter, description);
|
||||
return new IntegralPartSubstitution(pos, ruleSet, description);
|
||||
}
|
||||
else if (ruleSet.isFractionSet()) {
|
||||
// if the rule set containing the rule is a fraction
|
||||
// rule set, return a NumeratorSubstitution
|
||||
return new NumeratorSubstitution(pos, rule.getBaseValue(),
|
||||
formatter.getDefaultRuleSet(), formatter, description);
|
||||
formatter.getDefaultRuleSet(), description);
|
||||
}
|
||||
else {
|
||||
// otherwise, return a MultiplierSubstitution
|
||||
return new MultiplierSubstitution(pos, rule.getDivisor(), ruleSet,
|
||||
formatter, description);
|
||||
description);
|
||||
}
|
||||
|
||||
case '>':
|
||||
if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
|
||||
// if the rule is a negative-number rule, return
|
||||
// an AbsoluteValueSubstitution
|
||||
return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description);
|
||||
return new AbsoluteValueSubstitution(pos, ruleSet, description);
|
||||
}
|
||||
else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
|
||||
|| rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
|
||||
@ -120,7 +115,7 @@ abstract class NFSubstitution {
|
||||
{
|
||||
// if the rule is a fraction rule, return a
|
||||
// FractionalPartSubstitution
|
||||
return new FractionalPartSubstitution(pos, ruleSet, formatter, description);
|
||||
return new FractionalPartSubstitution(pos, ruleSet, description);
|
||||
}
|
||||
else if (ruleSet.isFractionSet()) {
|
||||
// if the rule set owning the rule is a fraction rule set,
|
||||
@ -135,10 +130,10 @@ abstract class NFSubstitution {
|
||||
else {
|
||||
// otherwise, return a ModulusSubstitution
|
||||
return new ModulusSubstitution(pos, rule.getDivisor(), rulePredecessor,
|
||||
ruleSet, formatter, description);
|
||||
ruleSet, description);
|
||||
}
|
||||
case '=':
|
||||
return new SameValueSubstitution(pos, ruleSet, formatter, description);
|
||||
return new SameValueSubstitution(pos, ruleSet, description);
|
||||
default:
|
||||
// and if it's anything else, throw an exception
|
||||
///CLOVER:OFF
|
||||
@ -156,33 +151,31 @@ abstract class NFSubstitution {
|
||||
* @param pos The substitution's position in the owning rule's rule
|
||||
* text
|
||||
* @param ruleSet The rule set that owns this substitution
|
||||
* @param formatter The RuleBasedNumberFormat that owns this substitution
|
||||
* @param description The substitution descriptor (i.e., the text
|
||||
* inside the token characters)
|
||||
*/
|
||||
NFSubstitution(int pos,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
// initialize the substitution's position in its parent rule
|
||||
this.pos = pos;
|
||||
this.rbnf = formatter;
|
||||
int descriptionLen = description.length();
|
||||
|
||||
// the description should begin and end with the same character.
|
||||
// If it doesn't that's a syntax error. Otherwise,
|
||||
// makeSubstitution() was the only thing that needed to know
|
||||
// about these characters, so strip them off
|
||||
if (description.length() >= 2 && description.charAt(0) == description.charAt(description.length() - 1)) {
|
||||
description = description.substring(1, description.length() - 1);
|
||||
if (descriptionLen >= 2 && description.charAt(0) == description.charAt(descriptionLen - 1)) {
|
||||
description = description.substring(1, descriptionLen - 1);
|
||||
}
|
||||
else if (description.length() != 0) {
|
||||
else if (descriptionLen != 0) {
|
||||
throw new IllegalArgumentException("Illegal substitution syntax");
|
||||
}
|
||||
|
||||
// if the description was just two paired token characters
|
||||
// (i.e., "<<" or ">>"), it uses the rule set it belongs to to
|
||||
// format its result
|
||||
if (description.length() == 0) {
|
||||
if (description.isEmpty()) {
|
||||
this.ruleSet = ruleSet;
|
||||
this.numberFormat = null;
|
||||
}
|
||||
@ -190,7 +183,7 @@ abstract class NFSubstitution {
|
||||
// if the description contains a rule set name, that's the rule
|
||||
// set we use to format the result: get a reference to the
|
||||
// names rule set
|
||||
this.ruleSet = formatter.findRuleSet(description);
|
||||
this.ruleSet = ruleSet.owner.findRuleSet(description);
|
||||
this.numberFormat = null;
|
||||
}
|
||||
else if (description.charAt(0) == '#' || description.charAt(0) == '0') {
|
||||
@ -199,7 +192,8 @@ abstract class NFSubstitution {
|
||||
// that pattern (then set it to use the DecimalFormatSymbols
|
||||
// belonging to our formatter)
|
||||
this.ruleSet = null;
|
||||
this.numberFormat = new DecimalFormat(description, formatter.getDecimalFormatSymbols());
|
||||
this.numberFormat = (DecimalFormat) ruleSet.owner.getDecimalFormat().clone();
|
||||
this.numberFormat.applyPattern(description);
|
||||
}
|
||||
else if (description.charAt(0) == '>') {
|
||||
// if the description is ">>>", this substitution bypasses the
|
||||
@ -329,6 +323,13 @@ abstract class NFSubstitution {
|
||||
// is dependent on the type of substitution this is
|
||||
double numberToFormat = transformNumber(number);
|
||||
|
||||
if (Double.isInfinite(numberToFormat)) {
|
||||
// This is probably a minus rule. Combine it with an infinite rule.
|
||||
NFRule infiniteRule = ruleSet.findRule(Double.POSITIVE_INFINITY);
|
||||
infiniteRule.doFormat(numberToFormat, toInsertInto, position + pos, recursionCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// if the result is an integer, from here on out we work in integer
|
||||
// space (saving time and memory and preserving accuracy)
|
||||
if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {
|
||||
@ -422,7 +423,7 @@ abstract class NFSubstitution {
|
||||
if (ruleSet != null) {
|
||||
tempResult = ruleSet.parse(text, parsePosition, upperBound);
|
||||
if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) {
|
||||
tempResult = rbnf.getDecimalFormat().parse(text, parsePosition);
|
||||
tempResult = ruleSet.owner.getDecimalFormat().parse(text, parsePosition);
|
||||
}
|
||||
|
||||
// ...or use our DecimalFormat to parse the text
|
||||
@ -514,16 +515,6 @@ abstract class NFSubstitution {
|
||||
*/
|
||||
abstract char tokenChar();
|
||||
|
||||
/**
|
||||
* Returns true if this is a null substitution. (We didn't do this
|
||||
* with instanceof partially because it causes source files to
|
||||
* proliferate and partially because we have to port this to C++.)
|
||||
* @return true if this object is an instance of NullSubstitution
|
||||
*/
|
||||
public boolean isNullSubstitution() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a modulus substitution. (We didn't do this
|
||||
* with instanceof partially because it causes source files to
|
||||
@ -563,9 +554,8 @@ class SameValueSubstitution extends NFSubstitution {
|
||||
*/
|
||||
SameValueSubstitution(int pos,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
super(pos, ruleSet, description);
|
||||
if (description.equals("==")) {
|
||||
throw new IllegalArgumentException("== is not a legal token");
|
||||
}
|
||||
@ -660,15 +650,13 @@ class MultiplierSubstitution extends NFSubstitution {
|
||||
* @param pos The substitution's position in its rule's rule text
|
||||
* @param divisor The owning rule's divisor
|
||||
* @param ruleSet The ruleSet this substitution uses to format its result
|
||||
* @param formatter The formatter that owns this substitution
|
||||
* @param description The description describing this substitution
|
||||
*/
|
||||
MultiplierSubstitution(int pos,
|
||||
double divisor,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
super(pos, ruleSet, description);
|
||||
|
||||
// the owning rule's divisor affects the behavior of this
|
||||
// substitution. Rather than keeping a back-pointer to the
|
||||
@ -815,16 +803,15 @@ class ModulusSubstitution extends NFSubstitution {
|
||||
* @param divisor The divisor of the rule that owns this substitution
|
||||
* @param rulePredecessor The rule that precedes this substitution's
|
||||
* rule in its rule set's rule list
|
||||
* @param formatter The RuleBasedNumberFormat owning this substitution
|
||||
* @param description The description for this substitution
|
||||
*/
|
||||
ModulusSubstitution(int pos,
|
||||
double divisor,
|
||||
NFRule rulePredecessor,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
String description)
|
||||
{
|
||||
super(pos, ruleSet, description);
|
||||
|
||||
// the owning rule's divisor controls the behavior of this
|
||||
// substitution: rather than keeping a backpointer to the rule,
|
||||
@ -1005,7 +992,6 @@ class ModulusSubstitution extends NFSubstitution {
|
||||
* @param newRuleValue The result of parsing the substitution
|
||||
* @param oldRuleValue The base value of the rule containing the
|
||||
* substitution
|
||||
* @return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue
|
||||
*/
|
||||
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
|
||||
return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
|
||||
@ -1060,9 +1046,8 @@ class IntegralPartSubstitution extends NFSubstitution {
|
||||
*/
|
||||
IntegralPartSubstitution(int pos,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
super(pos, ruleSet, description);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@ -1165,9 +1150,8 @@ class FractionalPartSubstitution extends NFSubstitution {
|
||||
*/
|
||||
FractionalPartSubstitution(int pos,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
super(pos, ruleSet, description);
|
||||
if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) {
|
||||
byDigits = true;
|
||||
useSpaces = !description.equals(">>>");
|
||||
@ -1284,7 +1268,7 @@ class FractionalPartSubstitution extends NFSubstitution {
|
||||
// nonmatching text
|
||||
String workText = text;
|
||||
ParsePosition workPos = new ParsePosition(1);
|
||||
double result = 0;
|
||||
double result;
|
||||
int digit;
|
||||
|
||||
DigitList dl = new DigitList();
|
||||
@ -1292,7 +1276,7 @@ class FractionalPartSubstitution extends NFSubstitution {
|
||||
workPos.setIndex(0);
|
||||
digit = ruleSet.parse(workText, workPos, 10).intValue();
|
||||
if (lenientParse && workPos.getIndex() == 0) {
|
||||
Number n = rbnf.getDecimalFormat().parse(workText, workPos);
|
||||
Number n = ruleSet.owner.getDecimalFormat().parse(workText, workPos);
|
||||
if (n != null) {
|
||||
digit = n.intValue();
|
||||
}
|
||||
@ -1366,9 +1350,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
|
||||
*/
|
||||
AbsoluteValueSubstitution(int pos,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
super(pos, ruleSet, description);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
@ -1469,9 +1452,8 @@ class NumeratorSubstitution extends NFSubstitution {
|
||||
NumeratorSubstitution(int pos,
|
||||
double denominator,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, fixdesc(description));
|
||||
super(pos, ruleSet, fixdesc(description));
|
||||
|
||||
// this substitution's behavior depends on the rule's base value
|
||||
// Rather than keeping a backpointer to the rule, we copy its
|
||||
@ -1672,131 +1654,3 @@ class NumeratorSubstitution extends NFSubstitution {
|
||||
return '<';
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// NullSubstitution
|
||||
//===================================================================
|
||||
|
||||
/**
|
||||
* A substitution which does nothing. This class exists just to simplify
|
||||
* the logic in some other routines so that they don't have to worry
|
||||
* about how many substitutions a rule has.
|
||||
*/
|
||||
class NullSubstitution extends NFSubstitution {
|
||||
//-----------------------------------------------------------------------
|
||||
// construction
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a NullSubstitution. This just delegates to the superclass
|
||||
* constructor, but the only value we really care about is the position.
|
||||
*/
|
||||
NullSubstitution(int pos,
|
||||
NFRuleSet ruleSet,
|
||||
RuleBasedNumberFormat formatter,
|
||||
String description) {
|
||||
super(pos, ruleSet, formatter, description);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// boilerplate
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* NullSubstitutions don't show up in the textual representation
|
||||
* of a RuleBasedNumberFormat
|
||||
*/
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// formatting
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*/
|
||||
public void doSubstitution(long number, StringBuffer toInsertInto, int position) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing.
|
||||
*/
|
||||
public void doSubstitution(double number, StringBuffer toInsertInto, int position) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Never called.
|
||||
*/
|
||||
///CLOVER:OFF
|
||||
public long transformNumber(long number) {
|
||||
return 0;
|
||||
}
|
||||
///CLOVER:ON
|
||||
|
||||
/**
|
||||
* Never called.
|
||||
*/
|
||||
///CLOVER:OFF
|
||||
public double transformNumber(double number) {
|
||||
return 0;
|
||||
}
|
||||
///CLOVER:ON
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// parsing
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the partial parse result unchanged
|
||||
*/
|
||||
public Number doParse(String text, ParsePosition parsePosition, double baseValue,
|
||||
double upperBound, boolean lenientParse) {
|
||||
if (baseValue == (long)baseValue) {
|
||||
return Long.valueOf((long)baseValue);
|
||||
} else {
|
||||
return new Double(baseValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Never called.
|
||||
*/
|
||||
///CLOVER:OFF
|
||||
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
|
||||
return 0;
|
||||
}
|
||||
///CLOVER:ON
|
||||
|
||||
/**
|
||||
* Never called.
|
||||
*/
|
||||
///CLOVER:OFF
|
||||
public double calcUpperBound(double oldUpperBound) {
|
||||
return 0;
|
||||
}
|
||||
///CLOVER:ON
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// simple accessors
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns true (this _is_ a NullSubstitution).
|
||||
* @return true
|
||||
*/
|
||||
public boolean isNullSubstitution() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Never called.
|
||||
*/
|
||||
///CLOVER:OFF
|
||||
char tokenChar() {
|
||||
return ' ';
|
||||
}
|
||||
///CLOVER:ON
|
||||
}
|
||||
|
||||
|
@ -247,17 +247,48 @@ import com.ibm.icu.util.UResourceBundleIterator;
|
||||
* <tr>
|
||||
* <td width="5%" valign="top"></td>
|
||||
* <td width="8%" valign="top">x.x:</td>
|
||||
* <td valign="top">The rule is an <em>improper fraction rule.</em></td>
|
||||
* <td valign="top">The rule is an <em>improper fraction rule</em>. If the full stop in
|
||||
* the middle of the rule name is replaced with the decimal point
|
||||
* that is used in the language or DecimalFormatSymbols, then that rule will
|
||||
* have precedence when formatting and parsing this rule. For example, some
|
||||
* languages use the comma, and can thus be written as x,x instead. For example,
|
||||
* you can use "x.x: << point >>;x,x: << comma >>;" to
|
||||
* handle the decimal point that matches the language's natural spelling of
|
||||
* the punctuation of either the full stop or comma.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td width="5%" valign="top"></td>
|
||||
* <td width="8%" valign="top">0.x:</td>
|
||||
* <td valign="top">The rule is a <em>proper fraction rule.</em></td>
|
||||
* <td valign="top">The rule is a <em>proper fraction rule</em>. If the full stop in
|
||||
* the middle of the rule name is replaced with the decimal point
|
||||
* that is used in the language or DecimalFormatSymbols, then that rule will
|
||||
* have precedence when formatting and parsing this rule. For example, some
|
||||
* languages use the comma, and can thus be written as 0,x instead. For example,
|
||||
* you can use "0.x: point >>;0,x: comma >>;" to
|
||||
* handle the decimal point that matches the language's natural spelling of
|
||||
* the punctuation of either the full stop or comma</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td width="5%" valign="top"></td>
|
||||
* <td width="8%" valign="top">x.0:</td>
|
||||
* <td valign="top">The rule is a <em>master rule.</em></td>
|
||||
* <td valign="top">The rule is a <em>master rule</em>. If the full stop in
|
||||
* the middle of the rule name is replaced with the decimal point
|
||||
* that is used in the language or DecimalFormatSymbols, then that rule will
|
||||
* have precedence when formatting and parsing this rule. For example, some
|
||||
* languages use the comma, and can thus be written as x,0 instead. For example,
|
||||
* you can use "x.0: << point;x,0: << comma;" to
|
||||
* handle the decimal point that matches the language's natural spelling of
|
||||
* the punctuation of either the full stop or comma</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td width="5%" valign="top"></td>
|
||||
* <td width="8%" valign="top">Inf:</td>
|
||||
* <td valign="top">The rule for infinity.</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td width="5%" valign="top"></td>
|
||||
* <td width="8%" valign="top">NaN:</td>
|
||||
* <td valign="top">The rule for an IEEE 754 NaN (not a number).</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td width="5%" valign="top"></td>
|
||||
@ -586,6 +617,18 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
*/
|
||||
private transient DecimalFormat decimalFormat = null;
|
||||
|
||||
/**
|
||||
* The rule used when dealing with infinity. This is lazy-evaluated, and derived from decimalFormat.
|
||||
* It is here so it can be shared by different NFRuleSets.
|
||||
*/
|
||||
private transient NFRule defaultInfinityRule = null;
|
||||
|
||||
/**
|
||||
* The rule used when dealing with IEEE 754 NaN. This is lazy-evaluated, and derived from decimalFormat.
|
||||
* It is here so it can be shared by different NFRuleSets.
|
||||
*/
|
||||
private transient NFRule defaultNaNRule = null;
|
||||
|
||||
/**
|
||||
* Flag specifying whether lenient parse mode is on or off. Off by default.
|
||||
* @serial
|
||||
@ -797,19 +840,18 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
catch (MissingResourceException e1) {
|
||||
}
|
||||
|
||||
try {
|
||||
UResourceBundle locb = bundle.get(locnames[format-1]);
|
||||
localizations = new String[locb.getSize()][];
|
||||
// We use findTopLevel() instead of get() because
|
||||
// it's faster when we know that it's usually going to fail.
|
||||
UResourceBundle locNamesBundle = bundle.findTopLevel(locnames[format - 1]);
|
||||
if (locNamesBundle != null) {
|
||||
localizations = new String[locNamesBundle.getSize()][];
|
||||
for (int i = 0; i < localizations.length; ++i) {
|
||||
localizations[i] = locb.get(i).getStringArray();
|
||||
localizations[i] = locNamesBundle.get(i).getStringArray();
|
||||
}
|
||||
}
|
||||
catch (MissingResourceException e) {
|
||||
// might have description and no localizations, or no description...
|
||||
}
|
||||
// else there are no localized names. It's not that important.
|
||||
|
||||
init(description.toString(), localizations);
|
||||
|
||||
}
|
||||
|
||||
private static final String[] rulenames = {
|
||||
@ -1228,7 +1270,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
|
||||
// keep track of the largest number of characters consumed in
|
||||
// the various trials, and the result that corresponds to it
|
||||
Number result = Long.valueOf(0);
|
||||
Number result = NFRule.ZERO;
|
||||
ParsePosition highWaterMark = new ParsePosition(workingPos.getIndex());
|
||||
|
||||
// iterate over the public rule sets (beginning with the default one)
|
||||
@ -1409,7 +1451,15 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
if (decimalFormat != null) {
|
||||
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
|
||||
}
|
||||
|
||||
if (defaultInfinityRule != null) {
|
||||
defaultInfinityRule = null;
|
||||
getDefaultInfinityRule(); // Reset with the new DecimalFormatSymbols
|
||||
}
|
||||
if (defaultNaNRule != null) {
|
||||
defaultNaNRule = null;
|
||||
getDefaultNaNRule(); // Reset with the new DecimalFormatSymbols
|
||||
}
|
||||
|
||||
// Apply the new decimalFormatSymbols by reparsing the rulesets
|
||||
for (NFRuleSet ruleSet : ruleSets) {
|
||||
ruleSet.setDecimalFormatSymbols(decimalFormatSymbols);
|
||||
@ -1490,11 +1540,9 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
|
||||
DecimalFormat getDecimalFormat() {
|
||||
if (decimalFormat == null) {
|
||||
decimalFormat = (DecimalFormat)NumberFormat.getInstance(locale);
|
||||
|
||||
if (decimalFormatSymbols != null) {
|
||||
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
|
||||
}
|
||||
// Don't use NumberFormat.getInstance, which can cause a recursive call
|
||||
String pattern = getPattern(locale, NUMBERSTYLE);
|
||||
decimalFormat = new DecimalFormat(pattern, getDecimalFormatSymbols());
|
||||
}
|
||||
return decimalFormat;
|
||||
}
|
||||
@ -1503,6 +1551,28 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
return new PluralFormat(locale, pluralType, pattern, getDecimalFormat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default rule for infinity. This object is lazily created: this function
|
||||
* creates it the first time it's called.
|
||||
*/
|
||||
NFRule getDefaultInfinityRule() {
|
||||
if (defaultInfinityRule == null) {
|
||||
defaultInfinityRule = new NFRule(this, "Inf: " + getDecimalFormatSymbols().getInfinity());
|
||||
}
|
||||
return defaultInfinityRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default rule for NaN. This object is lazily created: this function
|
||||
* creates it the first time it's called.
|
||||
*/
|
||||
NFRule getDefaultNaNRule() {
|
||||
if (defaultNaNRule == null) {
|
||||
defaultNaNRule = new NFRule(this, "NaN: " + getDecimalFormatSymbols().getNaN());
|
||||
}
|
||||
return defaultNaNRule;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// construction implementation
|
||||
//-----------------------------------------------------------------------
|
||||
@ -1613,7 +1683,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
p = descBuf.length() - 1;
|
||||
}
|
||||
ruleSetDescriptions[curRuleSet] = descBuf.substring(start, p + 1);
|
||||
NFRuleSet ruleSet = new NFRuleSet(ruleSetDescriptions, curRuleSet);
|
||||
NFRuleSet ruleSet = new NFRuleSet(this, ruleSetDescriptions, curRuleSet);
|
||||
ruleSets[curRuleSet] = ruleSet;
|
||||
String currentName = ruleSet.getName();
|
||||
ruleSetsMap.put(currentName, ruleSet);
|
||||
@ -1659,7 +1729,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
// finally, we can go back through the temporary descriptions
|
||||
// list and finish setting up the substructure
|
||||
for (int i = 0; i < ruleSets.length; i++) {
|
||||
ruleSets[i].parseRules(ruleSetDescriptions[i], this);
|
||||
ruleSets[i].parseRules(ruleSetDescriptions[i]);
|
||||
}
|
||||
|
||||
// Now that the rules are initialized, the 'real' default rule
|
||||
|
@ -1487,4 +1487,72 @@ public class RbnfTest extends TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
public void TestInfinityNaN() {
|
||||
String enRules = "%default:"
|
||||
+ "-x: minus >>;"
|
||||
+ "Inf: infinite;"
|
||||
+ "NaN: not a number;"
|
||||
+ "0: =#,##0=;";
|
||||
RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
|
||||
String[][] enTestData = {
|
||||
{"1", "1"},
|
||||
{"\u221E", "infinite"},
|
||||
{"-\u221E", "minus infinite"},
|
||||
{"NaN", "not a number"},
|
||||
|
||||
};
|
||||
|
||||
doTest(enFormatter, enTestData, true);
|
||||
|
||||
// Test the default behavior when the rules are undefined.
|
||||
enRules = "%default:"
|
||||
+ "-x: ->>;"
|
||||
+ "0: =#,##0=;";
|
||||
enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
|
||||
String[][] enDefaultTestData = {
|
||||
{"1", "1"},
|
||||
{"\u221E", "∞"},
|
||||
{"-\u221E", "-∞"},
|
||||
{"NaN", "NaN"},
|
||||
|
||||
};
|
||||
|
||||
doTest(enFormatter, enDefaultTestData, true);
|
||||
}
|
||||
|
||||
public void TestVariableDecimalPoint() {
|
||||
String enRules = "%spellout-numbering:"
|
||||
+ "-x: minus >>;"
|
||||
+ "x.x: << point >>;"
|
||||
+ "x,x: << comma >>;"
|
||||
+ "0.x: xpoint >>;"
|
||||
+ "0,x: xcomma >>;"
|
||||
+ "0: zero;"
|
||||
+ "1: one;"
|
||||
+ "2: two;"
|
||||
+ "3: three;"
|
||||
+ "4: four;"
|
||||
+ "5: five;"
|
||||
+ "6: six;"
|
||||
+ "7: seven;"
|
||||
+ "8: eight;"
|
||||
+ "9: nine;";
|
||||
RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
|
||||
String[][] enTestPointData = {
|
||||
{"1.1", "one point one"},
|
||||
{"1.23", "one point two three"},
|
||||
{"0.4", "xpoint four"},
|
||||
};
|
||||
doTest(enFormatter, enTestPointData, true);
|
||||
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(ULocale.ENGLISH);
|
||||
decimalFormatSymbols.setDecimalSeparator(',');
|
||||
enFormatter.setDecimalFormatSymbols(decimalFormatSymbols);
|
||||
String[][] enTestCommaData = {
|
||||
{"1.1", "one comma one"},
|
||||
{"1.23", "one comma two three"},
|
||||
{"0.4", "xcomma four"},
|
||||
};
|
||||
doTest(enFormatter, enTestCommaData, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user