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. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
@ -697,7 +697,11 @@ public class DecimalFormat extends NumberFormat {
|
|||||||
private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) {
|
private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) {
|
||||||
// Always applyPattern after the symbols are set
|
// Always applyPattern after the symbols are set
|
||||||
symbols = (DecimalFormatSymbols) inputSymbols.clone();
|
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);
|
applyPatternWithoutExpandAffix(pattern, false);
|
||||||
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||||
currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale());
|
currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale());
|
||||||
@ -2332,7 +2336,7 @@ public class DecimalFormat extends NumberFormat {
|
|||||||
* @param negSuffix negative suffix pattern
|
* @param negSuffix negative suffix pattern
|
||||||
* @param posPrefix positive prefix pattern
|
* @param posPrefix positive prefix pattern
|
||||||
* @param negSuffix negative suffix 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.
|
* @param type type of currency to parse against, LONG_NAME only or not.
|
||||||
*/
|
*/
|
||||||
private final boolean subparse(
|
private final boolean subparse(
|
||||||
@ -2779,7 +2783,7 @@ public class DecimalFormat extends NumberFormat {
|
|||||||
* @param isNegative
|
* @param isNegative
|
||||||
* @param isPrefix
|
* @param isPrefix
|
||||||
* @param affixPat affix pattern used for currency affix comparison
|
* @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 type compare against currency type, LONG_NAME only or not.
|
||||||
* @param currency return value for parsed currency, for generic currency parsing
|
* @param currency return value for parsed currency, for generic currency parsing
|
||||||
* mode, or null for normal parsing. In generic currency parsing mode, any currency
|
* 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 java.util.List;
|
||||||
|
|
||||||
import com.ibm.icu.impl.PatternProps;
|
import com.ibm.icu.impl.PatternProps;
|
||||||
|
import com.ibm.icu.impl.Utility;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class representing a single rule in a RuleBasedNumberFormat. A rule
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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
|
// data members
|
||||||
@ -63,6 +76,11 @@ final class NFRule {
|
|||||||
*/
|
*/
|
||||||
private short exponent = 0;
|
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
|
* 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
|
* 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
|
// new it up and initialize its basevalue and divisor
|
||||||
// (this also strips the rule descriptor, if any, off the
|
// (this also strips the rule descriptor, if any, off the
|
||||||
// description string)
|
// description string)
|
||||||
NFRule rule1 = new NFRule(ownersOwner);
|
NFRule rule1 = new NFRule(ownersOwner, description);
|
||||||
description = rule1.parseRuleDescriptor(description);
|
description = rule1.ruleText;
|
||||||
|
|
||||||
// check the description to see whether there's text enclosed
|
// check the description to see whether there's text enclosed
|
||||||
// in brackets
|
// in brackets
|
||||||
int brack1 = description.indexOf("[");
|
int brack1 = description.indexOf('[');
|
||||||
int brack2 = description.indexOf("]");
|
int brack2 = brack1 < 0 ? -1 : description.indexOf(']');
|
||||||
|
|
||||||
// if the description doesn't contain a matched pair of brackets,
|
// if the description doesn't contain a matched pair of brackets,
|
||||||
// or if it's of a type that doesn't recognize bracketed text,
|
// or if it's of a type that doesn't recognize bracketed text,
|
||||||
// then leave the description alone, initialize the rule's
|
// then leave the description alone, initialize the rule's
|
||||||
// rule text and substitutions, and return that rule
|
// rule text and substitutions, and return that rule
|
||||||
if (brack1 == -1 || brack2 == -1 || brack1 > brack2
|
if (brack2 < 0 || brack1 > brack2
|
||||||
|| rule1.getBaseValue() == PROPER_FRACTION_RULE
|
|| rule1.baseValue == PROPER_FRACTION_RULE
|
||||||
|| rule1.getBaseValue() == NEGATIVE_NUMBER_RULE)
|
|| rule1.baseValue == NEGATIVE_NUMBER_RULE
|
||||||
|
|| rule1.baseValue == INFINITY_RULE
|
||||||
|
|| rule1.baseValue == NAN_RULE)
|
||||||
{
|
{
|
||||||
rule1.extractSubstitutions(owner, description, predecessor);
|
rule1.extractSubstitutions(owner, description, predecessor);
|
||||||
}
|
}
|
||||||
@ -155,7 +175,7 @@ final class NFRule {
|
|||||||
// set, they both have the same base value; otherwise,
|
// set, they both have the same base value; otherwise,
|
||||||
// increment the original rule's base value ("rule1" actually
|
// increment the original rule's base value ("rule1" actually
|
||||||
// goes SECOND in the rule set's rule list)
|
// goes SECOND in the rule set's rule list)
|
||||||
rule2 = new NFRule(ownersOwner);
|
rule2 = new NFRule(ownersOwner, null);
|
||||||
if (rule1.baseValue >= 0) {
|
if (rule1.baseValue >= 0) {
|
||||||
rule2.baseValue = rule1.baseValue;
|
rule2.baseValue = rule1.baseValue;
|
||||||
if (!owner.isFractionSet()) {
|
if (!owner.isFractionSet()) {
|
||||||
@ -207,18 +227,29 @@ final class NFRule {
|
|||||||
// material in the brackets and rule1 INCLUDES the material
|
// material in the brackets and rule1 INCLUDES the material
|
||||||
// in the brackets)
|
// in the brackets)
|
||||||
if (rule2 != null) {
|
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
|
* Nominal constructor for NFRule. Most of the work of constructing
|
||||||
* an NFRule is actually performed by makeRules().
|
* an NFRule is actually performed by makeRules().
|
||||||
*/
|
*/
|
||||||
public NFRule(RuleBasedNumberFormat formatter) {
|
public NFRule(RuleBasedNumberFormat formatter, String ruleText) {
|
||||||
this.formatter = formatter;
|
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
|
// separated by a colon. The rule descriptor is optional. If
|
||||||
// it's omitted, just set the base value to 0.
|
// it's omitted, just set the base value to 0.
|
||||||
int p = description.indexOf(":");
|
int p = description.indexOf(":");
|
||||||
if (p == -1) {
|
if (p != -1) {
|
||||||
setBaseValue(0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// copy the descriptor out into its own string and strip it,
|
// copy the descriptor out into its own string and strip it,
|
||||||
// along with any trailing whitespace, out of the original
|
// along with any trailing whitespace, out of the original
|
||||||
// description
|
// description
|
||||||
@ -257,14 +285,13 @@ final class NFRule {
|
|||||||
// check first to see if the rule descriptor matches the token
|
// check first to see if the rule descriptor matches the token
|
||||||
// for one of the special rules. If it does, set the base
|
// for one of the special rules. If it does, set the base
|
||||||
// value to the correct identifier value
|
// value to the correct identifier value
|
||||||
if (descriptor.equals("0.x")) {
|
int descriptorLength = descriptor.length();
|
||||||
setBaseValue(PROPER_FRACTION_RULE);
|
char firstChar = descriptor.charAt(0);
|
||||||
}
|
char lastChar = descriptor.charAt(descriptorLength - 1);
|
||||||
else if (descriptor.charAt(0) >= '0' && descriptor.charAt(0) <= '9') {
|
if (firstChar >= '0' && firstChar <= '9' && lastChar != 'x') {
|
||||||
// if the rule descriptor begins with a digit, it's a descriptor
|
// if the rule descriptor begins with a digit, it's a descriptor
|
||||||
// for a normal rule
|
// for a normal rule
|
||||||
long tempValue = 0;
|
long tempValue = 0;
|
||||||
int descriptorLength = descriptor.length();
|
|
||||||
char c = 0;
|
char c = 0;
|
||||||
p = 0;
|
p = 0;
|
||||||
|
|
||||||
@ -339,13 +366,28 @@ final class NFRule {
|
|||||||
else if (descriptor.equals("-x")) {
|
else if (descriptor.equals("-x")) {
|
||||||
setBaseValue(NEGATIVE_NUMBER_RULE);
|
setBaseValue(NEGATIVE_NUMBER_RULE);
|
||||||
}
|
}
|
||||||
else if (descriptor.equals("x.x")) {
|
else if (descriptorLength == 3) {
|
||||||
setBaseValue(IMPROPER_FRACTION_RULE);
|
if (firstChar == '0' && lastChar == 'x') {
|
||||||
}
|
setBaseValue(PROPER_FRACTION_RULE);
|
||||||
else if (descriptor.equals("x.0")) {
|
decimalPoint = descriptor.charAt(1);
|
||||||
setBaseValue(MASTER_RULE);
|
}
|
||||||
|
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
|
// finally, if the rule body begins with an apostrophe, strip it off
|
||||||
// (this is generally used to put whitespace at the beginning of
|
// (this is generally used to put whitespace at the beginning of
|
||||||
@ -371,11 +413,10 @@ final class NFRule {
|
|||||||
String ruleText,
|
String ruleText,
|
||||||
NFRule predecessor) {
|
NFRule predecessor) {
|
||||||
this.ruleText = ruleText;
|
this.ruleText = ruleText;
|
||||||
this.rulePatternFormat = null;
|
|
||||||
sub1 = extractSubstitution(owner, predecessor);
|
sub1 = extractSubstitution(owner, predecessor);
|
||||||
if (sub1.isNullSubstitution()) {
|
if (sub1 == null) {
|
||||||
// Small optimization. There is no need to create a redundant NullSubstitution.
|
// Small optimization. There is no need to create a redundant NullSubstitution.
|
||||||
sub2 = sub1;
|
sub2 = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sub2 = extractSubstitution(owner, predecessor);
|
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,
|
* Searches the rule's rule text for the first substitution token,
|
||||||
* creates a substitution based on it, and removes the token from
|
* 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
|
// search the rule's rule text for the first two characters of
|
||||||
// a substitution token
|
// a substitution token
|
||||||
subStart = indexOfAny(ruleText, RULE_PREFIXES);
|
subStart = indexOfAnyRulePrefix(ruleText);
|
||||||
|
|
||||||
// if we didn't find one, create a null substitution positioned
|
// if we didn't find one, create a null substitution positioned
|
||||||
// at the end of the rule text
|
// at the end of the rule text
|
||||||
if (subStart == -1) {
|
if (subStart == -1) {
|
||||||
return NFSubstitution.makeSubstitution(ruleText.length(), this, predecessor,
|
return null;
|
||||||
owner, this.formatter, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// special-case the ">>>" token, since searching for the > at the
|
// special-case the ">>>" token, since searching for the > at the
|
||||||
// end will actually find the > in the middle
|
// end will actually find the > in the middle
|
||||||
if (ruleText.substring(subStart).startsWith(">>>")) {
|
if (ruleText.startsWith(">>>", subStart)) {
|
||||||
subEnd = subStart + 2;
|
subEnd = subStart + 2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -462,8 +496,7 @@ final class NFRule {
|
|||||||
// unmatched token character), create a null substitution positioned
|
// unmatched token character), create a null substitution positioned
|
||||||
// at the end of the rule
|
// at the end of the rule
|
||||||
if (subEnd == -1) {
|
if (subEnd == -1) {
|
||||||
return NFSubstitution.makeSubstitution(ruleText.length(), this, predecessor,
|
return null;
|
||||||
owner, this.formatter, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we get here, we have a real substitution token (or at least
|
// 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) {
|
final void setBaseValue(long newBaseValue) {
|
||||||
// set the base value
|
// set the base value
|
||||||
baseValue = newBaseValue;
|
baseValue = newBaseValue;
|
||||||
|
radix = 10;
|
||||||
|
|
||||||
// if this isn't a special rule, recalculate the radix and exponent
|
// 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
|
// (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
|
// recalculated again-- the only function that does this is
|
||||||
// NFRule.parseRuleDescriptor() )
|
// NFRule.parseRuleDescriptor() )
|
||||||
if (baseValue >= 1) {
|
if (baseValue >= 1) {
|
||||||
radix = 10;
|
|
||||||
exponent = expectedExponent();
|
exponent = expectedExponent();
|
||||||
|
|
||||||
// this function gets called on a fully-constructed rule whose
|
// this function gets called on a fully-constructed rule whose
|
||||||
@ -511,7 +544,6 @@ final class NFRule {
|
|||||||
else {
|
else {
|
||||||
// if this is a special rule, its radix and exponent are basically
|
// if this is a special rule, its radix and exponent are basically
|
||||||
// ignored. Set them to "safe" default values
|
// ignored. Set them to "safe" default values
|
||||||
radix = 10;
|
|
||||||
exponent = 0;
|
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.
|
* 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
|
* @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
|
* (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
|
* _any_ of the strings in "strings"). If none of the strings in
|
||||||
* "strings" is found in the rule's rule text, returns -1.
|
* "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;
|
int result = -1;
|
||||||
if (ruleText.length() > 0) {
|
if (ruleText.length() > 0) {
|
||||||
int pos;
|
int pos;
|
||||||
for (String string : strings) {
|
for (String string : RULE_PREFIXES) {
|
||||||
pos = ruleText.indexOf(string);
|
pos = ruleText.indexOf(string);
|
||||||
if (pos != -1 && (result == -1 || pos < result)) {
|
if (pos != -1 && (result == -1 || pos < result)) {
|
||||||
result = pos;
|
result = pos;
|
||||||
@ -580,8 +616,8 @@ final class NFRule {
|
|||||||
&& radix == that2.radix
|
&& radix == that2.radix
|
||||||
&& exponent == that2.exponent
|
&& exponent == that2.exponent
|
||||||
&& ruleText.equals(that2.ruleText)
|
&& ruleText.equals(that2.ruleText)
|
||||||
&& sub1.equals(that2.sub1)
|
&& Utility.objectEquals(sub1, that2.sub1)
|
||||||
&& sub2.equals(that2.sub2);
|
&& Utility.objectEquals(sub2, that2.sub2);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -605,13 +641,19 @@ final class NFRule {
|
|||||||
result.append("-x: ");
|
result.append("-x: ");
|
||||||
}
|
}
|
||||||
else if (baseValue == IMPROPER_FRACTION_RULE) {
|
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) {
|
else if (baseValue == PROPER_FRACTION_RULE) {
|
||||||
result.append("0.x: ");
|
result.append('0').append(decimalPoint == 0 ? '.' : decimalPoint).append("x: ");
|
||||||
}
|
}
|
||||||
else if (baseValue == MASTER_RULE) {
|
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 {
|
else {
|
||||||
// for a normal rule, write out its base value, and if the radix is
|
// 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
|
// (whitespace after the rule descriptor is ignored; the
|
||||||
// apostrophe is used to make the whitespace significant)
|
// apostrophe is used to make the whitespace significant)
|
||||||
if (ruleText.startsWith(" ") && (sub1 == null || sub1.getPos() != 0)) {
|
if (ruleText.startsWith(" ") && (sub1 == null || sub1.getPos() != 0)) {
|
||||||
result.append("\'");
|
result.append('\'');
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, write the rule's rule text, inserting appropriate
|
// now, write the rule's rule text, inserting appropriate
|
||||||
// substitution tokens in the appropriate places
|
// substitution tokens in the appropriate places
|
||||||
StringBuilder ruleTextCopy = new StringBuilder(ruleText);
|
StringBuilder ruleTextCopy = new StringBuilder(ruleText);
|
||||||
ruleTextCopy.insert(sub2.getPos(), sub2.toString());
|
if (sub2 != null) {
|
||||||
ruleTextCopy.insert(sub1.getPos(), sub1.toString());
|
ruleTextCopy.insert(sub2.getPos(), sub2.toString());
|
||||||
|
}
|
||||||
|
if (sub1 != null) {
|
||||||
|
ruleTextCopy.insert(sub1.getPos(), sub1.toString());
|
||||||
|
}
|
||||||
result.append(ruleTextCopy.toString());
|
result.append(ruleTextCopy.toString());
|
||||||
|
|
||||||
// and finally, top the whole thing off with a semicolon and
|
// and finally, top the whole thing off with a semicolon and
|
||||||
@ -654,6 +700,14 @@ final class NFRule {
|
|||||||
// simple accessors
|
// 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
|
* Returns the rule's base value
|
||||||
* @return The rule's base value
|
* @return The rule's base value
|
||||||
@ -708,10 +762,10 @@ final class NFRule {
|
|||||||
}
|
}
|
||||||
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
|
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
|
||||||
}
|
}
|
||||||
if (!sub2.isNullSubstitution()) {
|
if (sub2 != null) {
|
||||||
sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
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);
|
sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -750,10 +804,10 @@ final class NFRule {
|
|||||||
}
|
}
|
||||||
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
|
lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength);
|
||||||
}
|
}
|
||||||
if (!sub2.isNullSubstitution()) {
|
if (sub2 != null) {
|
||||||
sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount);
|
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);
|
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
|
// a modulus substitution, its base value isn't an even multiple
|
||||||
// of 100, and the value we're trying to format _is_ an even
|
// of 100, and the value we're trying to format _is_ an even
|
||||||
// multiple of 100. This is called the "rollback rule."
|
// 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;
|
&& (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
|
// matches the text at the beginning of the string being
|
||||||
// parsed. If it does, strip that off the front of workText;
|
// parsed. If it does, strip that off the front of workText;
|
||||||
// otherwise, dump out with a mismatch
|
// 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();
|
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
|
// commented out because ParsePosition doesn't have error index in 1.1.x
|
||||||
// parsePosition.setErrorIndex(pp.getErrorIndex());
|
// 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
|
// 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
|
// the substitution, giving us a new partial parse result
|
||||||
pp.setIndex(0);
|
pp.setIndex(0);
|
||||||
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
|
double partialResult = matchToDelimiter(workText, start, tempBaseValue,
|
||||||
ruleText.substring(sub1.getPos(), sub2.getPos()), rulePatternFormat,
|
ruleText.substring(sub1Pos, sub2Pos), rulePatternFormat,
|
||||||
pp, sub1, upperBound).doubleValue();
|
pp, sub1, upperBound).doubleValue();
|
||||||
|
|
||||||
// if we got a successful match (or were trying to match a
|
// if we got a successful match (or were trying to match a
|
||||||
// null substitution), pp is now pointing at the first unmatched
|
// null substitution), pp is now pointing at the first unmatched
|
||||||
// character. Take note of that, and try matchToDelimiter()
|
// character. Take note of that, and try matchToDelimiter()
|
||||||
// on the input text again
|
// on the input text again
|
||||||
if (pp.getIndex() != 0 || sub1.isNullSubstitution()) {
|
if (pp.getIndex() != 0 || sub1 == null) {
|
||||||
start = pp.getIndex();
|
start = pp.getIndex();
|
||||||
|
|
||||||
String workText2 = workText.substring(pp.getIndex());
|
String workText2 = workText.substring(pp.getIndex());
|
||||||
@ -888,13 +954,13 @@ final class NFRule {
|
|||||||
// substitution if there's a successful match, giving us
|
// substitution if there's a successful match, giving us
|
||||||
// a real result
|
// a real result
|
||||||
partialResult = matchToDelimiter(workText2, 0, partialResult,
|
partialResult = matchToDelimiter(workText2, 0, partialResult,
|
||||||
ruleText.substring(sub2.getPos()), rulePatternFormat, pp2, sub2,
|
ruleText.substring(sub2Pos), rulePatternFormat, pp2, sub2,
|
||||||
upperBound).doubleValue();
|
upperBound).doubleValue();
|
||||||
|
|
||||||
// if we got a successful match on this second
|
// if we got a successful match on this second
|
||||||
// matchToDelimiter() call, update the high-water mark
|
// matchToDelimiter() call, update the high-water mark
|
||||||
// and result (if necessary)
|
// and result (if necessary)
|
||||||
if (pp2.getIndex() != 0 || sub2.isNullSubstitution()) {
|
if (pp2.getIndex() != 0 || sub2 == null) {
|
||||||
if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) {
|
if (prefixLength + pp.getIndex() + pp2.getIndex() > highWaterMark) {
|
||||||
highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex();
|
highWaterMark = prefixLength + pp.getIndex() + pp2.getIndex();
|
||||||
result = partialResult;
|
result = partialResult;
|
||||||
@ -918,7 +984,8 @@ final class NFRule {
|
|||||||
// keep trying to match things until the outer matchToDelimiter()
|
// keep trying to match things until the outer matchToDelimiter()
|
||||||
// call fails to make a match (each time, it picks up where it
|
// call fails to make a match (each time, it picks up where it
|
||||||
// left off the previous time)
|
// 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);
|
< workText.length() && pp.getIndex() != start);
|
||||||
|
|
||||||
// update the caller's ParsePosition with our high-water mark
|
// 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
|
// we have to account for it here. By definition, if the matching
|
||||||
// rule in a fraction rule set has no substitutions, its numerator
|
// rule in a fraction rule set has no substitutions, its numerator
|
||||||
// is 1, and so the result is the reciprocal of its base value.
|
// 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;
|
result = 1 / result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1071,21 +1138,23 @@ final class NFRule {
|
|||||||
// if we make it here, this was an unsuccessful match, and we
|
// if we make it here, this was an unsuccessful match, and we
|
||||||
// leave pp unchanged and return 0
|
// leave pp unchanged and return 0
|
||||||
pp.setIndex(0);
|
pp.setIndex(0);
|
||||||
return Long.valueOf(0);
|
return ZERO;
|
||||||
|
|
||||||
// if "delimiter" is empty, or consists only of ignorable characters
|
// if "delimiter" is empty, or consists only of ignorable characters
|
||||||
// (i.e., is semantically empty), thwe we obviously can't search
|
// (i.e., is semantically empty), thwe we obviously can't search
|
||||||
// for "delimiter". Instead, just use "sub" to parse as much of
|
// for "delimiter". Instead, just use "sub" to parse as much of
|
||||||
// "text" as possible.
|
// "text" as possible.
|
||||||
} else {
|
}
|
||||||
|
else if (sub == null) {
|
||||||
|
return baseVal;
|
||||||
|
}
|
||||||
|
else {
|
||||||
ParsePosition tempPP = new ParsePosition(0);
|
ParsePosition tempPP = new ParsePosition(0);
|
||||||
Number result = Long.valueOf(0);
|
Number result = ZERO;
|
||||||
Number tempResult;
|
|
||||||
|
|
||||||
// try to match the whole string against the substitution
|
// try to match the whole string against the substitution
|
||||||
tempResult = sub.doParse(text, tempPP, baseVal, upperBound,
|
Number tempResult = sub.doParse(text, tempPP, baseVal, upperBound,
|
||||||
formatter.lenientParseEnabled());
|
formatter.lenientParseEnabled());
|
||||||
if (tempPP.getIndex() != 0 || sub.isNullSubstitution()) {
|
if (tempPP.getIndex() != 0) {
|
||||||
// if there's a successful match (or it's a null
|
// if there's a successful match (or it's a null
|
||||||
// substitution), update pp to point to the first
|
// substitution), update pp to point to the first
|
||||||
// character we didn't match, and pass the result from
|
// 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.text.ParsePosition;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.ibm.icu.impl.PatternProps;
|
import com.ibm.icu.impl.PatternProps;
|
||||||
@ -37,16 +38,33 @@ final class NFRuleSet {
|
|||||||
private NFRule[] rules;
|
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
|
* These are a pile of fraction rules in declared order. They may have alternate
|
||||||
* (0.x) rule, element 1 is the improper fraction (x.x) rule, and
|
* ways to represent fractions.
|
||||||
* element 2 is the master (x.0) rule.
|
|
||||||
*/
|
*/
|
||||||
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
|
* True if the rule set is a fraction rule set. A fraction rule set
|
||||||
@ -72,15 +90,17 @@ final class NFRuleSet {
|
|||||||
// construction
|
// construction
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Constructs a rule set.
|
* Constructs a rule set.
|
||||||
|
* @param owner The formatter that owns this rule set
|
||||||
* @param descriptions An array of Strings representing rule set
|
* @param descriptions An array of Strings representing rule set
|
||||||
* descriptions. On exit, this rule set's entry in the array will
|
* descriptions. On exit, this rule set's entry in the array will
|
||||||
* have been stripped of its rule set name and any trailing whitespace.
|
* have been stripped of its rule set name and any trailing whitespace.
|
||||||
* @param index The index into "descriptions" of the description
|
* @param index The index into "descriptions" of the description
|
||||||
* for the rule to be constructed
|
* 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];
|
String description = descriptions[index];
|
||||||
|
|
||||||
if (description.length() == 0) {
|
if (description.length() == 0) {
|
||||||
@ -104,6 +124,7 @@ final class NFRuleSet {
|
|||||||
}
|
}
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
while (pos < description.length() && PatternProps.isWhiteSpace(description.charAt(++pos))) {
|
while (pos < description.length() && PatternProps.isWhiteSpace(description.charAt(++pos))) {
|
||||||
}
|
}
|
||||||
description = description.substring(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
|
* can refer to any other rule set, we have to have created all of
|
||||||
* them before we can create anything else.
|
* them before we can create anything else.
|
||||||
* @param description The textual description of this rule set
|
* @param description The textual description of this rule set
|
||||||
* @param owner The formatter that owns this rule set
|
|
||||||
*/
|
*/
|
||||||
public void parseRules(String description,
|
public void parseRules(String description) {
|
||||||
RuleBasedNumberFormat owner) {
|
|
||||||
// (the number of elements in the description list isn't necessarily
|
// (the number of elements in the description list isn't necessarily
|
||||||
// the number of rules-- some descriptions may expend into two rules)
|
// the number of rules-- some descriptions may expend into two rules)
|
||||||
List<NFRule> tempRules = new ArrayList<NFRule>();
|
List<NFRule> tempRules = new ArrayList<NFRule>();
|
||||||
@ -162,7 +181,9 @@ final class NFRuleSet {
|
|||||||
// to our rule vector
|
// to our rule vector
|
||||||
NFRule.makeRules(description.substring(oldP, p),
|
NFRule.makeRules(description.substring(oldP, p),
|
||||||
this, predecessor, owner, tempRules);
|
this, predecessor, owner, tempRules);
|
||||||
predecessor = tempRules.get(tempRules.size() - 1);
|
if (!tempRules.isEmpty()) {
|
||||||
|
predecessor = tempRules.get(tempRules.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
oldP = p + 1;
|
oldP = p + 1;
|
||||||
}
|
}
|
||||||
@ -174,69 +195,28 @@ final class NFRuleSet {
|
|||||||
// rules from the list and put them into their own member variables
|
// rules from the list and put them into their own member variables
|
||||||
long defaultBaseValue = 0;
|
long defaultBaseValue = 0;
|
||||||
|
|
||||||
// (this isn't a for loop because we might be deleting items from
|
for (NFRule rule : tempRules) {
|
||||||
// the vector-- we want to make sure we only increment i when
|
long baseValue = rule.getBaseValue();
|
||||||
// we _didn't_ delete anything from the vector)
|
if (baseValue == 0) {
|
||||||
int i = 0;
|
// if the rule's base value is 0, fill in a default
|
||||||
while (i < tempRules.size()) {
|
// base value (this will be 1 plus the preceding
|
||||||
NFRule rule = tempRules.get(i);
|
// rule's base value for regular rule sets, and the
|
||||||
|
// same as the preceding rule's base value in fraction
|
||||||
switch ((int)rule.getBaseValue()) {
|
// rule sets)
|
||||||
case 0:
|
rule.setBaseValue(defaultBaseValue);
|
||||||
// if the rule's base value is 0, fill in a default
|
}
|
||||||
// base value (this will be 1 plus the preceding
|
else {
|
||||||
// rule's base value for regular rule sets, and the
|
// if it's a regular rule that already knows its base value,
|
||||||
// same as the preceding rule's base value in fraction
|
// check to make sure the rules are in order, and update
|
||||||
// rule sets)
|
// the default base value for the next rule
|
||||||
rule.setBaseValue(defaultBaseValue);
|
if (baseValue < defaultBaseValue) {
|
||||||
if (!isFractionRuleSet) {
|
throw new IllegalArgumentException("Rules are not in order, base: " +
|
||||||
++defaultBaseValue;
|
baseValue + " < " + defaultBaseValue);
|
||||||
}
|
}
|
||||||
++i;
|
defaultBaseValue = baseValue;
|
||||||
break;
|
}
|
||||||
|
if (!isFractionRuleSet) {
|
||||||
case NFRule.NEGATIVE_NUMBER_RULE:
|
++defaultBaseValue;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +226,60 @@ final class NFRuleSet {
|
|||||||
tempRules.toArray(rules);
|
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
|
* Flags this rule set as a fraction rule set. This function is
|
||||||
* called during the construction process once we know this rule
|
* called during the construction process once we know this rule
|
||||||
@ -276,16 +310,19 @@ final class NFRuleSet {
|
|||||||
NFRuleSet that2 = (NFRuleSet)that;
|
NFRuleSet that2 = (NFRuleSet)that;
|
||||||
|
|
||||||
if (!name.equals(that2.name)
|
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
|
|| rules.length != that2.rules.length
|
||||||
|| isFractionRuleSet != that2.isFractionRuleSet)
|
|| isFractionRuleSet != that2.isFractionRuleSet)
|
||||||
{
|
{
|
||||||
return false;
|
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...
|
// ...then compare the rule lists...
|
||||||
for (int i = 0; i < rules.length; i++) {
|
for (int i = 0; i < rules.length; i++) {
|
||||||
if (!rules[i].equals(that2.rules[i])) {
|
if (!rules[i].equals(that2.rules[i])) {
|
||||||
@ -317,22 +354,27 @@ final class NFRuleSet {
|
|||||||
result.append(name).append(":\n");
|
result.append(name).append(":\n");
|
||||||
|
|
||||||
// followed by the regular rules...
|
// followed by the regular rules...
|
||||||
for (int i = 0; i < rules.length; i++) {
|
for (NFRule rule : rules) {
|
||||||
result.append(" ").append(rules[i].toString()).append("\n");
|
result.append(rule.toString()).append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// followed by the special rules (if they exist)
|
// followed by the special rules (if they exist)
|
||||||
if (negativeNumberRule != null) {
|
for (NFRule rule : nonNumericalRules) {
|
||||||
result.append(" ").append(negativeNumberRule.toString()).append("\n");
|
if (rule != null) {
|
||||||
}
|
if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
|
||||||
if (fractionRules[0] != null) {
|
|| rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
|
||||||
result.append(" ").append(fractionRules[0].toString()).append("\n");
|
|| rule.getBaseValue() == NFRule.MASTER_RULE)
|
||||||
}
|
{
|
||||||
if (fractionRules[1] != null) {
|
for (NFRule fractionRule : fractionRules) {
|
||||||
result.append(" ").append(fractionRules[1].toString()).append("\n");
|
if (fractionRule.getBaseValue() == rule.getBaseValue()) {
|
||||||
}
|
result.append(fractionRule.toString()).append("\n");
|
||||||
if (fractionRules[2] != null) {
|
}
|
||||||
result.append(" ").append(fractionRules[2].toString()).append("\n");
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result.append(rule.toString()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toString();
|
return result.toString();
|
||||||
@ -387,11 +429,10 @@ final class NFRuleSet {
|
|||||||
* this operation is to be inserted
|
* this operation is to be inserted
|
||||||
*/
|
*/
|
||||||
public void format(long number, StringBuffer toInsertInto, int pos, int recursionCount) {
|
public void format(long number, StringBuffer toInsertInto, int pos, int recursionCount) {
|
||||||
NFRule applicableRule = findNormalRule(number);
|
|
||||||
|
|
||||||
if (recursionCount >= RECURSION_LIMIT) {
|
if (recursionCount >= RECURSION_LIMIT) {
|
||||||
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
||||||
}
|
}
|
||||||
|
NFRule applicableRule = findNormalRule(number);
|
||||||
applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
|
applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,11 +445,10 @@ final class NFRuleSet {
|
|||||||
* this operation is to be inserted
|
* this operation is to be inserted
|
||||||
*/
|
*/
|
||||||
public void format(double number, StringBuffer toInsertInto, int pos, int recursionCount) {
|
public void format(double number, StringBuffer toInsertInto, int pos, int recursionCount) {
|
||||||
NFRule applicableRule = findRule(number);
|
|
||||||
|
|
||||||
if (recursionCount >= RECURSION_LIMIT) {
|
if (recursionCount >= RECURSION_LIMIT) {
|
||||||
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name);
|
||||||
}
|
}
|
||||||
|
NFRule applicableRule = findRule(number);
|
||||||
applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
|
applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,42 +457,57 @@ final class NFRuleSet {
|
|||||||
* @param number The number being formatted.
|
* @param number The number being formatted.
|
||||||
* @return The rule that should be used to format it
|
* @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 this is a fraction rule set, use findFractionRuleSetRule()
|
||||||
if (isFractionRuleSet) {
|
if (isFractionRuleSet) {
|
||||||
return findFractionRuleSetRule(number);
|
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 the number is negative, return the negative number rule
|
||||||
// (if there isn't a negative-number rule, we pretend it's a
|
// (if there isn't a negative-number rule, we pretend it's a
|
||||||
// positive number)
|
// positive number)
|
||||||
if (number < 0) {
|
if (number < 0) {
|
||||||
if (negativeNumberRule != null) {
|
if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) {
|
||||||
return negativeNumberRule;
|
return nonNumericalRules[NEGATIVE_RULE_INDEX];
|
||||||
} else {
|
} else {
|
||||||
number = -number;
|
number = -number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the number isn't an integer, we use one f the fraction rules...
|
if (Double.isInfinite(number)) {
|
||||||
if (number != Math.floor(number)) {
|
NFRule rule = nonNumericalRules[INFINITY_RULE_INDEX];
|
||||||
// if the number is between 0 and 1, return the proper
|
if (rule == null) {
|
||||||
// fraction rule
|
rule = owner.getDefaultInfinityRule();
|
||||||
if (number < 1 && fractionRules[1] != null) {
|
|
||||||
return fractionRules[1];
|
|
||||||
}
|
}
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
// otherwise, return the improper fraction rule
|
// if the number isn't an integer, we use one f the fraction rules...
|
||||||
else if (fractionRules[0] != null) {
|
if (nonNumericalRules != null && number != Math.floor(number)) {
|
||||||
return fractionRules[0];
|
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 there's a master rule, use it to format the number
|
||||||
if (fractionRules[2] != null) {
|
if (nonNumericalRules != null && nonNumericalRules[MASTER_RULE_INDEX] != null) {
|
||||||
return fractionRules[2];
|
return nonNumericalRules[MASTER_RULE_INDEX];
|
||||||
|
}
|
||||||
} else {
|
else {
|
||||||
// and if we haven't yet returned a rule, use findNormalRule()
|
// and if we haven't yet returned a rule, use findNormalRule()
|
||||||
// to find the applicable rule
|
// to find the applicable rule
|
||||||
return findNormalRule(Math.round(number));
|
return findNormalRule(Math.round(number));
|
||||||
@ -486,8 +541,8 @@ final class NFRuleSet {
|
|||||||
// if the number is negative, return the negative-number rule
|
// if the number is negative, return the negative-number rule
|
||||||
// (if there isn't one, pretend the number is positive)
|
// (if there isn't one, pretend the number is positive)
|
||||||
if (number < 0) {
|
if (number < 0) {
|
||||||
if (negativeNumberRule != null) {
|
if (nonNumericalRules[NEGATIVE_RULE_INDEX] != null) {
|
||||||
return negativeNumberRule;
|
return nonNumericalRules[NEGATIVE_RULE_INDEX];
|
||||||
} else {
|
} else {
|
||||||
number = -number;
|
number = -number;
|
||||||
}
|
}
|
||||||
@ -541,7 +596,7 @@ final class NFRuleSet {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// else use the master rule
|
// 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.
|
// that determines the value we return.
|
||||||
|
|
||||||
ParsePosition highWaterMark = new ParsePosition(0);
|
ParsePosition highWaterMark = new ParsePosition(0);
|
||||||
Number result = Long.valueOf(0);
|
Number result = NFRule.ZERO;
|
||||||
Number tempResult = null;
|
Number tempResult;
|
||||||
|
|
||||||
// dump out if there's no text to parse
|
// dump out if there's no text to parse
|
||||||
if (text.length() == 0) {
|
if (text.length() == 0) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start by trying the negative number rule (if there is one)
|
// Try each of the negative rules, fraction rules, infinity rules and NaN rules
|
||||||
if (negativeNumberRule != null) {
|
for (NFRule fractionRule : nonNumericalRules) {
|
||||||
tempResult = negativeNumberRule.doParse(text, parsePosition, false, upperBound);
|
if (fractionRule != null) {
|
||||||
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
tempResult = fractionRule.doParse(text, parsePosition, false, upperBound);
|
||||||
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);
|
|
||||||
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
if (parsePosition.getIndex() > highWaterMark.getIndex()) {
|
||||||
result = tempResult;
|
result = tempResult;
|
||||||
highWaterMark.setIndex(parsePosition.getIndex());
|
highWaterMark.setIndex(parsePosition.getIndex());
|
||||||
@ -777,5 +818,23 @@ final class NFRuleSet {
|
|||||||
for (NFRule rule : rules) {
|
for (NFRule rule : rules) {
|
||||||
rule.setDecimalFormatSymbols(newSymbols);
|
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;
|
final DecimalFormat numberFormat;
|
||||||
|
|
||||||
/**
|
|
||||||
* Link to the RBNF so that we can access its decimalFormat if need be.
|
|
||||||
*/
|
|
||||||
final RuleBasedNumberFormat rbnf;
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
// construction
|
// construction
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -75,7 +70,7 @@ abstract class NFSubstitution {
|
|||||||
String description) {
|
String description) {
|
||||||
// if the description is empty, return a NullSubstitution
|
// if the description is empty, return a NullSubstitution
|
||||||
if (description.length() == 0) {
|
if (description.length() == 0) {
|
||||||
return new NullSubstitution(pos, ruleSet, formatter, description);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (description.charAt(0)) {
|
switch (description.charAt(0)) {
|
||||||
@ -94,25 +89,25 @@ abstract class NFSubstitution {
|
|||||||
|| rule.getBaseValue() == NFRule.MASTER_RULE)
|
|| rule.getBaseValue() == NFRule.MASTER_RULE)
|
||||||
{
|
{
|
||||||
// if the rule is a fraction rule, return an IntegralPartSubstitution
|
// 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()) {
|
else if (ruleSet.isFractionSet()) {
|
||||||
// if the rule set containing the rule is a fraction
|
// if the rule set containing the rule is a fraction
|
||||||
// rule set, return a NumeratorSubstitution
|
// rule set, return a NumeratorSubstitution
|
||||||
return new NumeratorSubstitution(pos, rule.getBaseValue(),
|
return new NumeratorSubstitution(pos, rule.getBaseValue(),
|
||||||
formatter.getDefaultRuleSet(), formatter, description);
|
formatter.getDefaultRuleSet(), description);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// otherwise, return a MultiplierSubstitution
|
// otherwise, return a MultiplierSubstitution
|
||||||
return new MultiplierSubstitution(pos, rule.getDivisor(), ruleSet,
|
return new MultiplierSubstitution(pos, rule.getDivisor(), ruleSet,
|
||||||
formatter, description);
|
description);
|
||||||
}
|
}
|
||||||
|
|
||||||
case '>':
|
case '>':
|
||||||
if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
|
if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
|
||||||
// if the rule is a negative-number rule, return
|
// if the rule is a negative-number rule, return
|
||||||
// an AbsoluteValueSubstitution
|
// an AbsoluteValueSubstitution
|
||||||
return new AbsoluteValueSubstitution(pos, ruleSet, formatter, description);
|
return new AbsoluteValueSubstitution(pos, ruleSet, description);
|
||||||
}
|
}
|
||||||
else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
|
else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
|
||||||
|| rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
|
|| rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
|
||||||
@ -120,7 +115,7 @@ abstract class NFSubstitution {
|
|||||||
{
|
{
|
||||||
// if the rule is a fraction rule, return a
|
// if the rule is a fraction rule, return a
|
||||||
// FractionalPartSubstitution
|
// FractionalPartSubstitution
|
||||||
return new FractionalPartSubstitution(pos, ruleSet, formatter, description);
|
return new FractionalPartSubstitution(pos, ruleSet, description);
|
||||||
}
|
}
|
||||||
else if (ruleSet.isFractionSet()) {
|
else if (ruleSet.isFractionSet()) {
|
||||||
// if the rule set owning the rule is a fraction rule set,
|
// if the rule set owning the rule is a fraction rule set,
|
||||||
@ -135,10 +130,10 @@ abstract class NFSubstitution {
|
|||||||
else {
|
else {
|
||||||
// otherwise, return a ModulusSubstitution
|
// otherwise, return a ModulusSubstitution
|
||||||
return new ModulusSubstitution(pos, rule.getDivisor(), rulePredecessor,
|
return new ModulusSubstitution(pos, rule.getDivisor(), rulePredecessor,
|
||||||
ruleSet, formatter, description);
|
ruleSet, description);
|
||||||
}
|
}
|
||||||
case '=':
|
case '=':
|
||||||
return new SameValueSubstitution(pos, ruleSet, formatter, description);
|
return new SameValueSubstitution(pos, ruleSet, description);
|
||||||
default:
|
default:
|
||||||
// and if it's anything else, throw an exception
|
// and if it's anything else, throw an exception
|
||||||
///CLOVER:OFF
|
///CLOVER:OFF
|
||||||
@ -156,33 +151,31 @@ abstract class NFSubstitution {
|
|||||||
* @param pos The substitution's position in the owning rule's rule
|
* @param pos The substitution's position in the owning rule's rule
|
||||||
* text
|
* text
|
||||||
* @param ruleSet The rule set that owns this substitution
|
* @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
|
* @param description The substitution descriptor (i.e., the text
|
||||||
* inside the token characters)
|
* inside the token characters)
|
||||||
*/
|
*/
|
||||||
NFSubstitution(int pos,
|
NFSubstitution(int pos,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
// initialize the substitution's position in its parent rule
|
// initialize the substitution's position in its parent rule
|
||||||
this.pos = pos;
|
this.pos = pos;
|
||||||
this.rbnf = formatter;
|
int descriptionLen = description.length();
|
||||||
|
|
||||||
// the description should begin and end with the same character.
|
// the description should begin and end with the same character.
|
||||||
// If it doesn't that's a syntax error. Otherwise,
|
// If it doesn't that's a syntax error. Otherwise,
|
||||||
// makeSubstitution() was the only thing that needed to know
|
// makeSubstitution() was the only thing that needed to know
|
||||||
// about these characters, so strip them off
|
// about these characters, so strip them off
|
||||||
if (description.length() >= 2 && description.charAt(0) == description.charAt(description.length() - 1)) {
|
if (descriptionLen >= 2 && description.charAt(0) == description.charAt(descriptionLen - 1)) {
|
||||||
description = description.substring(1, description.length() - 1);
|
description = description.substring(1, descriptionLen - 1);
|
||||||
}
|
}
|
||||||
else if (description.length() != 0) {
|
else if (descriptionLen != 0) {
|
||||||
throw new IllegalArgumentException("Illegal substitution syntax");
|
throw new IllegalArgumentException("Illegal substitution syntax");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the description was just two paired token characters
|
// if the description was just two paired token characters
|
||||||
// (i.e., "<<" or ">>"), it uses the rule set it belongs to to
|
// (i.e., "<<" or ">>"), it uses the rule set it belongs to to
|
||||||
// format its result
|
// format its result
|
||||||
if (description.length() == 0) {
|
if (description.isEmpty()) {
|
||||||
this.ruleSet = ruleSet;
|
this.ruleSet = ruleSet;
|
||||||
this.numberFormat = null;
|
this.numberFormat = null;
|
||||||
}
|
}
|
||||||
@ -190,7 +183,7 @@ abstract class NFSubstitution {
|
|||||||
// if the description contains a rule set name, that's the rule
|
// if the description contains a rule set name, that's the rule
|
||||||
// set we use to format the result: get a reference to the
|
// set we use to format the result: get a reference to the
|
||||||
// names rule set
|
// names rule set
|
||||||
this.ruleSet = formatter.findRuleSet(description);
|
this.ruleSet = ruleSet.owner.findRuleSet(description);
|
||||||
this.numberFormat = null;
|
this.numberFormat = null;
|
||||||
}
|
}
|
||||||
else if (description.charAt(0) == '#' || description.charAt(0) == '0') {
|
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
|
// that pattern (then set it to use the DecimalFormatSymbols
|
||||||
// belonging to our formatter)
|
// belonging to our formatter)
|
||||||
this.ruleSet = null;
|
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) == '>') {
|
else if (description.charAt(0) == '>') {
|
||||||
// if the description is ">>>", this substitution bypasses the
|
// if the description is ">>>", this substitution bypasses the
|
||||||
@ -329,6 +323,13 @@ abstract class NFSubstitution {
|
|||||||
// is dependent on the type of substitution this is
|
// is dependent on the type of substitution this is
|
||||||
double numberToFormat = transformNumber(number);
|
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
|
// if the result is an integer, from here on out we work in integer
|
||||||
// space (saving time and memory and preserving accuracy)
|
// space (saving time and memory and preserving accuracy)
|
||||||
if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {
|
if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) {
|
||||||
@ -422,7 +423,7 @@ abstract class NFSubstitution {
|
|||||||
if (ruleSet != null) {
|
if (ruleSet != null) {
|
||||||
tempResult = ruleSet.parse(text, parsePosition, upperBound);
|
tempResult = ruleSet.parse(text, parsePosition, upperBound);
|
||||||
if (lenientParse && !ruleSet.isFractionSet() && parsePosition.getIndex() == 0) {
|
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
|
// ...or use our DecimalFormat to parse the text
|
||||||
@ -514,16 +515,6 @@ abstract class NFSubstitution {
|
|||||||
*/
|
*/
|
||||||
abstract char tokenChar();
|
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
|
* Returns true if this is a modulus substitution. (We didn't do this
|
||||||
* with instanceof partially because it causes source files to
|
* with instanceof partially because it causes source files to
|
||||||
@ -563,9 +554,8 @@ class SameValueSubstitution extends NFSubstitution {
|
|||||||
*/
|
*/
|
||||||
SameValueSubstitution(int pos,
|
SameValueSubstitution(int pos,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
super(pos, ruleSet, formatter, description);
|
super(pos, ruleSet, description);
|
||||||
if (description.equals("==")) {
|
if (description.equals("==")) {
|
||||||
throw new IllegalArgumentException("== is not a legal token");
|
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 pos The substitution's position in its rule's rule text
|
||||||
* @param divisor The owning rule's divisor
|
* @param divisor The owning rule's divisor
|
||||||
* @param ruleSet The ruleSet this substitution uses to format its result
|
* @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
|
* @param description The description describing this substitution
|
||||||
*/
|
*/
|
||||||
MultiplierSubstitution(int pos,
|
MultiplierSubstitution(int pos,
|
||||||
double divisor,
|
double divisor,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
super(pos, ruleSet, formatter, description);
|
super(pos, ruleSet, description);
|
||||||
|
|
||||||
// the owning rule's divisor affects the behavior of this
|
// the owning rule's divisor affects the behavior of this
|
||||||
// substitution. Rather than keeping a back-pointer to the
|
// 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 divisor The divisor of the rule that owns this substitution
|
||||||
* @param rulePredecessor The rule that precedes this substitution's
|
* @param rulePredecessor The rule that precedes this substitution's
|
||||||
* rule in its rule set's rule list
|
* rule in its rule set's rule list
|
||||||
* @param formatter The RuleBasedNumberFormat owning this substitution
|
|
||||||
* @param description The description for this substitution
|
* @param description The description for this substitution
|
||||||
*/
|
*/
|
||||||
ModulusSubstitution(int pos,
|
ModulusSubstitution(int pos,
|
||||||
double divisor,
|
double divisor,
|
||||||
NFRule rulePredecessor,
|
NFRule rulePredecessor,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
String description)
|
||||||
String description) {
|
{
|
||||||
super(pos, ruleSet, formatter, description);
|
super(pos, ruleSet, description);
|
||||||
|
|
||||||
// the owning rule's divisor controls the behavior of this
|
// the owning rule's divisor controls the behavior of this
|
||||||
// substitution: rather than keeping a backpointer to the rule,
|
// 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 newRuleValue The result of parsing the substitution
|
||||||
* @param oldRuleValue The base value of the rule containing the
|
* @param oldRuleValue The base value of the rule containing the
|
||||||
* substitution
|
* substitution
|
||||||
* @return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue
|
|
||||||
*/
|
*/
|
||||||
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
|
public double composeRuleValue(double newRuleValue, double oldRuleValue) {
|
||||||
return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
|
return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
|
||||||
@ -1060,9 +1046,8 @@ class IntegralPartSubstitution extends NFSubstitution {
|
|||||||
*/
|
*/
|
||||||
IntegralPartSubstitution(int pos,
|
IntegralPartSubstitution(int pos,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
super(pos, ruleSet, formatter, description);
|
super(pos, ruleSet, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -1165,9 +1150,8 @@ class FractionalPartSubstitution extends NFSubstitution {
|
|||||||
*/
|
*/
|
||||||
FractionalPartSubstitution(int pos,
|
FractionalPartSubstitution(int pos,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
super(pos, ruleSet, formatter, description);
|
super(pos, ruleSet, description);
|
||||||
if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) {
|
if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) {
|
||||||
byDigits = true;
|
byDigits = true;
|
||||||
useSpaces = !description.equals(">>>");
|
useSpaces = !description.equals(">>>");
|
||||||
@ -1284,7 +1268,7 @@ class FractionalPartSubstitution extends NFSubstitution {
|
|||||||
// nonmatching text
|
// nonmatching text
|
||||||
String workText = text;
|
String workText = text;
|
||||||
ParsePosition workPos = new ParsePosition(1);
|
ParsePosition workPos = new ParsePosition(1);
|
||||||
double result = 0;
|
double result;
|
||||||
int digit;
|
int digit;
|
||||||
|
|
||||||
DigitList dl = new DigitList();
|
DigitList dl = new DigitList();
|
||||||
@ -1292,7 +1276,7 @@ class FractionalPartSubstitution extends NFSubstitution {
|
|||||||
workPos.setIndex(0);
|
workPos.setIndex(0);
|
||||||
digit = ruleSet.parse(workText, workPos, 10).intValue();
|
digit = ruleSet.parse(workText, workPos, 10).intValue();
|
||||||
if (lenientParse && workPos.getIndex() == 0) {
|
if (lenientParse && workPos.getIndex() == 0) {
|
||||||
Number n = rbnf.getDecimalFormat().parse(workText, workPos);
|
Number n = ruleSet.owner.getDecimalFormat().parse(workText, workPos);
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
digit = n.intValue();
|
digit = n.intValue();
|
||||||
}
|
}
|
||||||
@ -1366,9 +1350,8 @@ class AbsoluteValueSubstitution extends NFSubstitution {
|
|||||||
*/
|
*/
|
||||||
AbsoluteValueSubstitution(int pos,
|
AbsoluteValueSubstitution(int pos,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
super(pos, ruleSet, formatter, description);
|
super(pos, ruleSet, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -1469,9 +1452,8 @@ class NumeratorSubstitution extends NFSubstitution {
|
|||||||
NumeratorSubstitution(int pos,
|
NumeratorSubstitution(int pos,
|
||||||
double denominator,
|
double denominator,
|
||||||
NFRuleSet ruleSet,
|
NFRuleSet ruleSet,
|
||||||
RuleBasedNumberFormat formatter,
|
|
||||||
String description) {
|
String description) {
|
||||||
super(pos, ruleSet, formatter, fixdesc(description));
|
super(pos, ruleSet, fixdesc(description));
|
||||||
|
|
||||||
// this substitution's behavior depends on the rule's base value
|
// this substitution's behavior depends on the rule's base value
|
||||||
// Rather than keeping a backpointer to the rule, we copy its
|
// Rather than keeping a backpointer to the rule, we copy its
|
||||||
@ -1672,131 +1654,3 @@ class NumeratorSubstitution extends NFSubstitution {
|
|||||||
return '<';
|
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>
|
* <tr>
|
||||||
* <td width="5%" valign="top"></td>
|
* <td width="5%" valign="top"></td>
|
||||||
* <td width="8%" valign="top">x.x:</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>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td width="5%" valign="top"></td>
|
* <td width="5%" valign="top"></td>
|
||||||
* <td width="8%" valign="top">0.x:</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>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td width="5%" valign="top"></td>
|
* <td width="5%" valign="top"></td>
|
||||||
* <td width="8%" valign="top">x.0:</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>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td width="5%" valign="top"></td>
|
* <td width="5%" valign="top"></td>
|
||||||
@ -586,6 +617,18 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
*/
|
*/
|
||||||
private transient DecimalFormat decimalFormat = null;
|
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.
|
* Flag specifying whether lenient parse mode is on or off. Off by default.
|
||||||
* @serial
|
* @serial
|
||||||
@ -797,19 +840,18 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
catch (MissingResourceException e1) {
|
catch (MissingResourceException e1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// We use findTopLevel() instead of get() because
|
||||||
UResourceBundle locb = bundle.get(locnames[format-1]);
|
// it's faster when we know that it's usually going to fail.
|
||||||
localizations = new String[locb.getSize()][];
|
UResourceBundle locNamesBundle = bundle.findTopLevel(locnames[format - 1]);
|
||||||
|
if (locNamesBundle != null) {
|
||||||
|
localizations = new String[locNamesBundle.getSize()][];
|
||||||
for (int i = 0; i < localizations.length; ++i) {
|
for (int i = 0; i < localizations.length; ++i) {
|
||||||
localizations[i] = locb.get(i).getStringArray();
|
localizations[i] = locNamesBundle.get(i).getStringArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (MissingResourceException e) {
|
// else there are no localized names. It's not that important.
|
||||||
// might have description and no localizations, or no description...
|
|
||||||
}
|
|
||||||
|
|
||||||
init(description.toString(), localizations);
|
init(description.toString(), localizations);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] rulenames = {
|
private static final String[] rulenames = {
|
||||||
@ -1228,7 +1270,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
|
|
||||||
// keep track of the largest number of characters consumed in
|
// keep track of the largest number of characters consumed in
|
||||||
// the various trials, and the result that corresponds to it
|
// 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());
|
ParsePosition highWaterMark = new ParsePosition(workingPos.getIndex());
|
||||||
|
|
||||||
// iterate over the public rule sets (beginning with the default one)
|
// iterate over the public rule sets (beginning with the default one)
|
||||||
@ -1409,6 +1451,14 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
if (decimalFormat != null) {
|
if (decimalFormat != null) {
|
||||||
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
|
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
|
// Apply the new decimalFormatSymbols by reparsing the rulesets
|
||||||
for (NFRuleSet ruleSet : ruleSets) {
|
for (NFRuleSet ruleSet : ruleSets) {
|
||||||
@ -1490,11 +1540,9 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
|
|
||||||
DecimalFormat getDecimalFormat() {
|
DecimalFormat getDecimalFormat() {
|
||||||
if (decimalFormat == null) {
|
if (decimalFormat == null) {
|
||||||
decimalFormat = (DecimalFormat)NumberFormat.getInstance(locale);
|
// Don't use NumberFormat.getInstance, which can cause a recursive call
|
||||||
|
String pattern = getPattern(locale, NUMBERSTYLE);
|
||||||
if (decimalFormatSymbols != null) {
|
decimalFormat = new DecimalFormat(pattern, getDecimalFormatSymbols());
|
||||||
decimalFormat.setDecimalFormatSymbols(decimalFormatSymbols);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return decimalFormat;
|
return decimalFormat;
|
||||||
}
|
}
|
||||||
@ -1503,6 +1551,28 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
return new PluralFormat(locale, pluralType, pattern, getDecimalFormat());
|
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
|
// construction implementation
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -1613,7 +1683,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
p = descBuf.length() - 1;
|
p = descBuf.length() - 1;
|
||||||
}
|
}
|
||||||
ruleSetDescriptions[curRuleSet] = descBuf.substring(start, p + 1);
|
ruleSetDescriptions[curRuleSet] = descBuf.substring(start, p + 1);
|
||||||
NFRuleSet ruleSet = new NFRuleSet(ruleSetDescriptions, curRuleSet);
|
NFRuleSet ruleSet = new NFRuleSet(this, ruleSetDescriptions, curRuleSet);
|
||||||
ruleSets[curRuleSet] = ruleSet;
|
ruleSets[curRuleSet] = ruleSet;
|
||||||
String currentName = ruleSet.getName();
|
String currentName = ruleSet.getName();
|
||||||
ruleSetsMap.put(currentName, ruleSet);
|
ruleSetsMap.put(currentName, ruleSet);
|
||||||
@ -1659,7 +1729,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
// finally, we can go back through the temporary descriptions
|
// finally, we can go back through the temporary descriptions
|
||||||
// list and finish setting up the substructure
|
// list and finish setting up the substructure
|
||||||
for (int i = 0; i < ruleSets.length; i++) {
|
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
|
// 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