From a423e7cb099fb3324c7bdfb399482ba47e403f76 Mon Sep 17 00:00:00 2001 From: George Rhoten Date: Thu, 23 Apr 2015 07:44:14 +0000 Subject: [PATCH] ICU-11564 Improve the thread safety of RBNF. The recursion count is now a method argument instead of a data member. Some data fields are now marked final to ensure thread safety, which caused reordering of some data initialization/parsing. Setting the DecimalFormatSymbols no longer reparses the rules, but it just sets a new version of DecimalFormatSymbols instead. X-SVN-Rev: 37383 --- .../core/src/com/ibm/icu/text/NFRule.java | 23 +++- .../core/src/com/ibm/icu/text/NFRuleSet.java | 49 ++++--- .../src/com/ibm/icu/text/NFSubstitution.java | 94 +++++++------- .../ibm/icu/text/RuleBasedNumberFormat.java | 120 ++++++++---------- 4 files changed, 135 insertions(+), 151 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java index 04fe78b3e6a..d29145c9645 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NFRule.java @@ -106,7 +106,7 @@ final class NFRule { * rule list * @param ownersOwner The RuleBasedNumberFormat that owns the * rule set that owns the new rule(s) - * @param returnList A list of NFRules + * @param returnList One or more instances of NFRule are added and returned here */ public static void makeRules(String description, NFRuleSet owner, @@ -684,7 +684,7 @@ final class NFRule { * @param pos The position in toInsertInto where the resultant text * should be inserted */ - public void doFormat(long number, StringBuffer toInsertInto, int pos) { + public void doFormat(long number, StringBuffer toInsertInto, int pos, int recursionCount) { // first, insert the rule's rule text into toInsertInto at the // specified position, then insert the results of the substitutions // into the right places in toInsertInto (notice we do the @@ -709,10 +709,10 @@ final class NFRule { lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength); } if (!sub2.isNullSubstitution()) { - sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0)); + sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount); } if (!sub1.isNullSubstitution()) { - sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0)); + sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount); } } @@ -725,7 +725,7 @@ final class NFRule { * @param pos The position in toInsertInto where the resultant text * should be inserted */ - public void doFormat(double number, StringBuffer toInsertInto, int pos) { + public void doFormat(double number, StringBuffer toInsertInto, int pos, int recursionCount) { // first, insert the rule's rule text into toInsertInto at the // specified position, then insert the results of the substitutions // into the right places in toInsertInto @@ -751,10 +751,10 @@ final class NFRule { lengthOffset = ruleText.length() - (toInsertInto.length() - initialLength); } if (!sub2.isNullSubstitution()) { - sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0)); + sub2.doSubstitution(number, toInsertInto, pos - (sub2.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount); } if (!sub1.isNullSubstitution()) { - sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0)); + sub1.doSubstitution(number, toInsertInto, pos - (sub1.getPos() > pluralRuleStart ? lengthOffset : 0), recursionCount); } } @@ -1203,4 +1203,13 @@ final class NFRule { RbnfLenientScanner scanner = formatter.getLenientScanner(); return scanner != null && scanner.allIgnorable(str); } + + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { + if (sub1 != null) { + sub1.setDecimalFormatSymbols(newSymbols); + } + if (sub2 != null) { + sub2.setDecimalFormatSymbols(newSymbols); + } + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NFRuleSet.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NFRuleSet.java index 7d1e739cd85..8fd141190cb 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NFRuleSet.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NFRuleSet.java @@ -29,7 +29,7 @@ final class NFRuleSet { /** * The rule set's name */ - private String name; + private final String name; /** * The rule set's regular rules @@ -61,17 +61,12 @@ final class NFRuleSet { /** * True if the rule set is parseable. */ - private boolean isParseable = true; + private final boolean isParseable; /** - * Used to limit recursion for bad rule sets. + * Limit of recursion. It's about a 64 bit number formatted in base 2. */ - private int recursionCount = 0; - - /** - * Limit of recursion. - */ - private static final int RECURSION_LIMIT = 50; + private static final int RECURSION_LIMIT = 64; //----------------------------------------------------------------------- // construction @@ -102,7 +97,13 @@ final class NFRuleSet { throw new IllegalArgumentException("Rule set name doesn't end in colon"); } else { - name = description.substring(0, pos); + String name = description.substring(0, pos); + this.isParseable = !name.endsWith("@noparse"); + if (!this.isParseable) { + name = name.substring(0,name.length()-8); // Remove the @noparse from the name + } + this.name = name; + while (pos < description.length() && PatternProps.isWhiteSpace(description.charAt(++pos))) { } description = description.substring(pos); @@ -113,17 +114,13 @@ final class NFRuleSet { // if the description doesn't begin with a rule set name, its // name is "%default" name = "%default"; + isParseable = true; } if (description.length() == 0) { throw new IllegalArgumentException("Empty rule set description"); } - if ( name.endsWith("@noparse")) { - name = name.substring(0,name.length()-8); // Remove the @noparse from the name - isParseable = false; - } - // all of the other members of NFRuleSet are initialized // by parseRules() } @@ -389,15 +386,13 @@ final class NFRuleSet { * @param pos The position in toInsertInto where the result of * this operation is to be inserted */ - public void format(long number, StringBuffer toInsertInto, int pos) { + public void format(long number, StringBuffer toInsertInto, int pos, int recursionCount) { NFRule applicableRule = findNormalRule(number); - if (++recursionCount >= RECURSION_LIMIT) { - recursionCount = 0; + if (recursionCount >= RECURSION_LIMIT) { throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name); } - applicableRule.doFormat(number, toInsertInto, pos); - --recursionCount; + applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount); } /** @@ -408,15 +403,13 @@ final class NFRuleSet { * @param pos The position in toInsertInto where the result of * this operation is to be inserted */ - public void format(double number, StringBuffer toInsertInto, int pos) { + public void format(double number, StringBuffer toInsertInto, int pos, int recursionCount) { NFRule applicableRule = findRule(number); - if (++recursionCount >= RECURSION_LIMIT) { - recursionCount = 0; + if (recursionCount >= RECURSION_LIMIT) { throw new IllegalStateException("Recursion limit exceeded when applying ruleSet " + name); } - applicableRule.doFormat(number, toInsertInto, pos); - --recursionCount; + applicableRule.doFormat(number, toInsertInto, pos, ++recursionCount); } /** @@ -779,4 +772,10 @@ final class NFRuleSet { return result; } + + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { + for (NFRule rule : rules) { + rule.setDecimalFormatSymbols(newSymbols); + } + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NFSubstitution.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NFSubstitution.java index 49a0d2b60b0..3fc06bae60b 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NFSubstitution.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NFSubstitution.java @@ -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. * ******************************************************************************* */ @@ -292,14 +292,14 @@ abstract class NFSubstitution { * rule text begins (this value is added to this substitution's * position to determine exactly where to insert the new text) */ - public void doSubstitution(long number, StringBuffer toInsertInto, int position) { + public void doSubstitution(long number, StringBuffer toInsertInto, int position, int recursionCount) { if (ruleSet != null) { // perform a transformation on the number that is dependent // on the type of substitution this is, then just call its // rule set's format() method to format the result long numberToFormat = transformNumber(number); - ruleSet.format(numberToFormat, toInsertInto, position + pos); + ruleSet.format(numberToFormat, toInsertInto, position + pos, recursionCount); } else { // or perform the transformation on the number (preserving // the result's fractional part if the formatter it set @@ -324,7 +324,7 @@ abstract class NFSubstitution { * rule text begins (this value is added to this substitution's * position to determine exactly where to insert the new text) */ - public void doSubstitution(double number, StringBuffer toInsertInto, int position) { + public void doSubstitution(double number, StringBuffer toInsertInto, int position, int recursionCount) { // perform a transformation on the number being formatted that // is dependent on the type of substitution this is double numberToFormat = transformNumber(number); @@ -332,14 +332,14 @@ abstract class NFSubstitution { // if the result is an integer, from here on out we work in integer // space (saving time and memory and preserving accuracy) if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) { - ruleSet.format((long)numberToFormat, toInsertInto, position + pos); + ruleSet.format((long)numberToFormat, toInsertInto, position + pos, recursionCount); // if the result isn't an integer, then call either our rule set's // format() method or our DecimalFormat's format() method to // format the result } else { if (ruleSet != null) { - ruleSet.format(numberToFormat, toInsertInto, position + pos); + ruleSet.format(numberToFormat, toInsertInto, position + pos, recursionCount); } else { toInsertInto.insert(position + this.pos, numberFormat.format(numberToFormat)); } @@ -533,6 +533,13 @@ abstract class NFSubstitution { public boolean isModulusSubstitution() { return false; } + + + public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { + if (numberFormat != null) { + numberFormat.setDecimalFormatSymbols(newSymbols); + } + } } //=================================================================== @@ -697,13 +704,7 @@ class MultiplierSubstitution extends NFSubstitution { * @return true if the two substitutions are functionally equal */ public boolean equals(Object that) { - if (super.equals(that)) { - MultiplierSubstitution that2 = (MultiplierSubstitution)that; - - return divisor == that2.divisor; - } else { - return false; - } + return super.equals(that) && divisor == ((MultiplierSubstitution) that).divisor; } //----------------------------------------------------------------------- @@ -798,7 +799,7 @@ class ModulusSubstitution extends NFSubstitution { * If this is a >>> substitution, the rule to use to format * the substitution value. Otherwise, null. */ - NFRule ruleToUse; + private final NFRule ruleToUse; //----------------------------------------------------------------------- // construction @@ -893,18 +894,18 @@ class ModulusSubstitution extends NFSubstitution { * into * @param position The position of the rule text in toInsertInto */ - public void doSubstitution(long number, StringBuffer toInsertInto, int position) { + public void doSubstitution(long number, StringBuffer toInsertInto, int position, int recursionCount) { // if this isn't a >>> substitution, just use the inherited version // of this function (which uses either a rule set or a DecimalFormat // to format its substitution value) if (ruleToUse == null) { - super.doSubstitution(number, toInsertInto, position); + super.doSubstitution(number, toInsertInto, position, recursionCount); - // a >>> substitution goes straight to a particular rule to - // format the substitution value } else { + // a >>> substitution goes straight to a particular rule to + // format the substitution value long numberToFormat = transformNumber(number); - ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos); + ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos, recursionCount); } } @@ -916,19 +917,19 @@ class ModulusSubstitution extends NFSubstitution { * into * @param position The position of the rule text in toInsertInto */ - public void doSubstitution(double number, StringBuffer toInsertInto, int position) { + public void doSubstitution(double number, StringBuffer toInsertInto, int position, int recursionCount) { // if this isn't a >>> substitution, just use the inherited version // of this function (which uses either a rule set or a DecimalFormat // to format its substitution value) if (ruleToUse == null) { - super.doSubstitution(number, toInsertInto, position); + super.doSubstitution(number, toInsertInto, position, recursionCount); - // a >>> substitution goes straight to a particular rule to - // format the substitution value } else { + // a >>> substitution goes straight to a particular rule to + // format the substitution value double numberToFormat = transformNumber(number); - ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos); + ruleToUse.doFormat(numberToFormat, toInsertInto, position + pos, recursionCount); } } @@ -972,10 +973,10 @@ class ModulusSubstitution extends NFSubstitution { if (ruleToUse == null) { return super.doParse(text, parsePosition, baseValue, upperBound, lenientParse); - // but if it IS a >>> substitution, we have to do it here: we - // use the specific rule's doParse() method, and then we have to - // do some of the other work of NFRuleSet.parse() } else { + // but if it IS a >>> substitution, we have to do it here: we + // use the specific rule's doParse() method, and then we have to + // do some of the other work of NFRuleSet.parse() Number tempResult = ruleToUse.doParse(text, parsePosition, false, upperBound); if (parsePosition.getIndex() != 0) { @@ -1145,13 +1146,13 @@ class FractionalPartSubstitution extends NFSubstitution { * true if this substitution should have the default "by digits" * behavior, false otherwise */ - private boolean byDigits = false; + private final boolean byDigits; /** * true if we automatically insert spaces to separate names of digits * set to false by '>>>' in fraction rules, used by Thai. */ - private boolean useSpaces = true; + private final boolean useSpaces; //----------------------------------------------------------------------- // construction @@ -1169,10 +1170,10 @@ class FractionalPartSubstitution extends NFSubstitution { super(pos, ruleSet, formatter, description); if (description.equals(">>") || description.equals(">>>") || ruleSet == this.ruleSet) { byDigits = true; - if (description.equals(">>>")) { - useSpaces = false; - } + useSpaces = !description.equals(">>>"); } else { + byDigits = false; + useSpaces = true; this.ruleSet.makeIntoFractionRuleSet(); } } @@ -1191,11 +1192,11 @@ class FractionalPartSubstitution extends NFSubstitution { * @param position The position of the owning rule's rule text in * toInsertInto */ - public void doSubstitution(double number, StringBuffer toInsertInto, int position) { + public void doSubstitution(double number, StringBuffer toInsertInto, int position, int recursionCount) { if (!byDigits) { // if we're not in "byDigits" mode, just use the inherited // doSubstitution() routine - super.doSubstitution(number, toInsertInto, position); + super.doSubstitution(number, toInsertInto, position, recursionCount); } else { // if we're in "byDigits" mode, transform the value into an integer @@ -1216,7 +1217,7 @@ class FractionalPartSubstitution extends NFSubstitution { } else { pad = true; } - ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, position + pos); + ruleSet.format(dl.digits[--dl.count] - '0', toInsertInto, position + pos, recursionCount); } while (dl.decimalAt < 0) { if (pad && useSpaces) { @@ -1224,7 +1225,7 @@ class FractionalPartSubstitution extends NFSubstitution { } else { pad = true; } - ruleSet.format(0, toInsertInto, position + pos); + ruleSet.format(0, toInsertInto, position + pos, recursionCount); ++dl.decimalAt; } } @@ -1449,12 +1450,12 @@ class NumeratorSubstitution extends NFSubstitution { * The denominator of the fraction we're finding the numerator for. * (The base value of the rule that owns this substitution.) */ - double denominator; + private final double denominator; /** * True if we format leading zeros (this is a hack for Hebrew spellout) */ - boolean withZeros; + private final boolean withZeros; //----------------------------------------------------------------------- // construction @@ -1498,7 +1499,7 @@ class NumeratorSubstitution extends NFSubstitution { public boolean equals(Object that) { if (super.equals(that)) { NumeratorSubstitution that2 = (NumeratorSubstitution)that; - return denominator == that2.denominator; + return denominator == that2.denominator && withZeros == that2.withZeros; } else { return false; } @@ -1518,7 +1519,7 @@ class NumeratorSubstitution extends NFSubstitution { * rule text begins (this value is added to this substitution's * position to determine exactly where to insert the new text) */ - public void doSubstitution(double number, StringBuffer toInsertInto, int position) { + public void doSubstitution(double number, StringBuffer toInsertInto, int position, int recursionCount) { // perform a transformation on the number being formatted that // is dependent on the type of substitution this is //String s = toInsertInto.toString(); @@ -1530,7 +1531,7 @@ class NumeratorSubstitution extends NFSubstitution { int len = toInsertInto.length(); while ((nf *= 10) < denominator) { toInsertInto.insert(position + pos, ' '); - ruleSet.format(0, toInsertInto, position + pos); + ruleSet.format(0, toInsertInto, position + pos, recursionCount); } position += toInsertInto.length() - len; } @@ -1538,14 +1539,14 @@ class NumeratorSubstitution extends NFSubstitution { // if the result is an integer, from here on out we work in integer // space (saving time and memory and preserving accuracy) if (numberToFormat == Math.floor(numberToFormat) && ruleSet != null) { - ruleSet.format((long)numberToFormat, toInsertInto, position + pos); + ruleSet.format((long)numberToFormat, toInsertInto, position + pos, recursionCount); // if the result isn't an integer, then call either our rule set's // format() method or our DecimalFormat's format() method to // format the result } else { if (ruleSet != null) { - ruleSet.format(numberToFormat, toInsertInto, position + pos); + ruleSet.format(numberToFormat, toInsertInto, position + pos, recursionCount); } else { toInsertInto.insert(position + pos, numberFormat.format(numberToFormat)); } @@ -1701,13 +1702,6 @@ class NullSubstitution extends NFSubstitution { // boilerplate //----------------------------------------------------------------------- - /** - * Only checks for class equality - */ - public boolean equals(Object that) { - return super.equals(that); - } - /** * NullSubstitutions don't show up in the textual representation * of a RuleBasedNumberFormat diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java index 3fc93a668dc..29de0ae501b 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/RuleBasedNumberFormat.java @@ -543,12 +543,12 @@ public class RuleBasedNumberFormat extends NumberFormat { * The formatter's rule sets. */ private transient NFRuleSet[] ruleSets = null; - + /** - * The formatter's rule sets' descriptions. + * The formatter's rule names mapped to rule sets. */ - private transient String[] ruleSetDescriptions = null; - + private transient Map ruleSetsMap = null; + /** * A pointer to the formatter's default rule set. This is always included * in ruleSets. @@ -910,8 +910,8 @@ public class RuleBasedNumberFormat extends NumberFormat { // accumulate the descriptions of all the rule sets in a // StringBuffer, then cast it to a String and return it StringBuilder result = new StringBuilder(); - for (int i = 0; i < ruleSets.length; i++) { - result.append(ruleSets[i].toString()); + for (NFRuleSet ruleSet : ruleSets) { + result.append(ruleSet.toString()); } return result.toString(); } @@ -951,6 +951,7 @@ public class RuleBasedNumberFormat extends NumberFormat { // get swept up by the garbage collector RuleBasedNumberFormat temp = new RuleBasedNumberFormat(description, loc); ruleSets = temp.ruleSets; + ruleSetsMap = temp.ruleSetsMap; defaultRuleSet = temp.defaultRuleSet; publicRuleSetNames = temp.publicRuleSetNames; decimalFormatSymbols = temp.decimalFormatSymbols; @@ -995,8 +996,7 @@ public class RuleBasedNumberFormat extends NumberFormat { private String[] getNameListForLocale(ULocale loc) { if (loc != null && ruleSetDisplayNames != null) { String[] localeNames = { loc.getBaseName(), ULocale.getDefault(Category.DISPLAY).getBaseName() }; - for (int i = 0; i < localeNames.length; ++i) { - String lname = localeNames[i]; + for (String lname : localeNames) { while (lname.length() > 0) { String[] names = ruleSetDisplayNames.get(lname); if (names != null) { @@ -1411,8 +1411,8 @@ public class RuleBasedNumberFormat extends NumberFormat { } // Apply the new decimalFormatSymbols by reparsing the rulesets - for (int i = 0; i < ruleSets.length; i++) { - ruleSets[i].parseRules(ruleSetDescriptions[i], this); + for (NFRuleSet ruleSet : ruleSets) { + ruleSet.setDecimalFormatSymbols(decimalFormatSymbols); } } } @@ -1579,15 +1579,21 @@ public class RuleBasedNumberFormat extends NumberFormat { // pre-flight parsing the description and count the number of // rule sets (";%" marks the end of one rule set and the beginning // of the next) - int numRuleSets = 0; - for (int p = descBuf.indexOf(";%"); p != -1; p = descBuf.indexOf(";%", p)) { + int numRuleSets = 1; + int p = 0; + while ((p = descBuf.indexOf(";%", p)) != -1) { ++numRuleSets; - ++p; + p += 2; // Skip the length of ";%" } - ++numRuleSets; // our rule list is an array of the appropriate size ruleSets = new NFRuleSet[numRuleSets]; + ruleSetsMap = new HashMap(numRuleSets * 2 + 1); + defaultRuleSet = null; + + // Used to count the number of public rule sets + // Public rule sets have names that begin with % instead of %%. + int publicRuleSetCount = 0; // divide up the descriptions into individual rule-set descriptions // and store them in a temporary array. At each step, we also @@ -1596,18 +1602,34 @@ public class RuleBasedNumberFormat extends NumberFormat { // the rest of the descriptions and finish initializing everything // because we have to know the names and locations of all the rule // sets before we can actually set everything up - ruleSetDescriptions = new String[numRuleSets]; + String[] ruleSetDescriptions = new String[numRuleSets]; int curRuleSet = 0; int start = 0; - for (int p = descBuf.indexOf(";%"); p != -1; p = descBuf.indexOf(";%", start)) { + + while (curRuleSet < ruleSets.length) { + p = descBuf.indexOf(";%", start); + if (p < 0) { + p = descBuf.length() - 1; + } ruleSetDescriptions[curRuleSet] = descBuf.substring(start, p + 1); - ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet); + NFRuleSet ruleSet = new NFRuleSet(ruleSetDescriptions, curRuleSet); + ruleSets[curRuleSet] = ruleSet; + String currentName = ruleSet.getName(); + ruleSetsMap.put(currentName, ruleSet); + if (!currentName.startsWith("%%")) { + ++publicRuleSetCount; + if (defaultRuleSet == null + && currentName.equals("%spellout-numbering") + || currentName.equals("%digits-ordinal") + || currentName.equals("%duration")) + { + defaultRuleSet = ruleSet; + } + } ++curRuleSet; start = p + 1; } - ruleSetDescriptions[curRuleSet] = descBuf.substring(start); - ruleSets[curRuleSet] = new NFRuleSet(ruleSetDescriptions, curRuleSet); // now we can take note of the formatter's default rule set, which // is the last public rule set in the description (it's the last @@ -1622,20 +1644,7 @@ public class RuleBasedNumberFormat extends NumberFormat { // Set the default ruleset to the last public ruleset, unless one of the predefined // ruleset names %spellout-numbering, %digits-ordinal, or %duration is found - boolean defaultNameFound = false; - int n = ruleSets.length; - defaultRuleSet = ruleSets[ruleSets.length - 1]; - - while (--n >= 0) { - String currentName = ruleSets[n].getName(); - if (currentName.equals("%spellout-numbering") || currentName.equals("%digits-ordinal") || currentName.equals("%duration")) { - defaultRuleSet = ruleSets[n]; - defaultNameFound = true; - break; - } - } - - if ( !defaultNameFound ) { + if (defaultRuleSet == null) { for (int i = ruleSets.length - 1; i >= 0; --i) { if (!ruleSets[i].getName().startsWith("%%")) { defaultRuleSet = ruleSets[i]; @@ -1643,6 +1652,9 @@ public class RuleBasedNumberFormat extends NumberFormat { } } } + if (defaultRuleSet == null) { + defaultRuleSet = ruleSets[ruleSets.length - 1]; + } // finally, we can go back through the temporary descriptions // list and finish setting up the substructure @@ -1653,15 +1665,6 @@ public class RuleBasedNumberFormat extends NumberFormat { // Now that the rules are initialized, the 'real' default rule // set can be adjusted by the localization data. - // count the number of public rule sets - // (public rule sets have names that begin with % instead of %%) - int publicRuleSetCount = 0; - for (int i = 0; i < ruleSets.length; i++) { - if (!ruleSets[i].getName().startsWith("%%")) { - ++publicRuleSetCount; - } - } - // prepare an array of the proper size and copy the names into it String[] publicRuleSetTemp = new String[publicRuleSetCount]; publicRuleSetCount = 0; @@ -1787,26 +1790,6 @@ public class RuleBasedNumberFormat extends NumberFormat { return result; } -// /** -// * This function is called ONLY DURING CONSTRUCTION to fill in the -// * defaultRuleSet variable once we've set up all the rule sets. -// * The default rule set is the last public rule set in the description. -// * (It's the last rather than the first so that a caller can append -// * text to the end of an existing formatter description to change its -// * behavior.) -// */ -// private void initDefaultRuleSet() { -// // seek backward from the end of the list until we reach a rule set -// // whose name DOESN'T begin with %%. That's the default rule set -// for (int i = ruleSets.length - 1; i >= 0; --i) { -// if (!ruleSets[i].getName().startsWith("%%")) { -// defaultRuleSet = ruleSets[i]; -// return; -// } -// } -// defaultRuleSet = ruleSets[ruleSets.length - 1]; -// } - //----------------------------------------------------------------------- // formatting implementation //----------------------------------------------------------------------- @@ -1826,7 +1809,7 @@ public class RuleBasedNumberFormat extends NumberFormat { // position of 0 and the number being formatted) to the rule set // for formatting StringBuffer result = new StringBuffer(); - ruleSet.format(number, result, 0); + ruleSet.format(number, result, 0, 0); postProcess(result, ruleSet); return result.toString(); } @@ -1851,7 +1834,7 @@ public class RuleBasedNumberFormat extends NumberFormat { // position of 0 and the number being formatted) to the rule set // for formatting StringBuffer result = new StringBuffer(); - ruleSet.format(number, result, 0); + ruleSet.format(number, result, 0, 0); postProcess(result, ruleSet); return result.toString(); } @@ -1913,11 +1896,10 @@ public class RuleBasedNumberFormat extends NumberFormat { * @return The rule set with that name */ NFRuleSet findRuleSet(String name) throws IllegalArgumentException { - for (int i = 0; i < ruleSets.length; i++) { - if (ruleSets[i].getName().equals(name)) { - return ruleSets[i]; - } + NFRuleSet result = ruleSetsMap.get(name); + if (result == null) { + throw new IllegalArgumentException("No rule set named " + name); } - throw new IllegalArgumentException("No rule set named " + name); + return result; } }