ICU-10633 Implement context-sensitive number formatting (RBNF for J), handle serialization/equals for NumberFormat capitalizationSetting
X-SVN-Rev: 35301
This commit is contained in:
parent
22316fe74f
commit
62c9048bc0
@ -1047,7 +1047,8 @@ public abstract class NumberFormat extends UFormat {
|
|||||||
&& minimumFractionDigits == other.minimumFractionDigits
|
&& minimumFractionDigits == other.minimumFractionDigits
|
||||||
&& groupingUsed == other.groupingUsed
|
&& groupingUsed == other.groupingUsed
|
||||||
&& parseIntegerOnly == other.parseIntegerOnly
|
&& parseIntegerOnly == other.parseIntegerOnly
|
||||||
&& parseStrict == other.parseStrict;
|
&& parseStrict == other.parseStrict
|
||||||
|
&& capitalizationSetting == other.capitalizationSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1528,6 +1529,10 @@ public abstract class NumberFormat extends UFormat {
|
|||||||
maximumFractionDigits = maxFractionDigits;
|
maximumFractionDigits = maxFractionDigits;
|
||||||
minimumFractionDigits = minFractionDigits;
|
minimumFractionDigits = minFractionDigits;
|
||||||
}
|
}
|
||||||
|
if (serialVersionOnStream < 2) {
|
||||||
|
// Didn't have capitalizationSetting, set it to default
|
||||||
|
capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
|
||||||
|
}
|
||||||
///CLOVER:ON
|
///CLOVER:ON
|
||||||
/*Bug 4185761
|
/*Bug 4185761
|
||||||
Validate the min and max fields [Richard/GCL]
|
Validate the min and max fields [Richard/GCL]
|
||||||
@ -1714,7 +1719,7 @@ public abstract class NumberFormat extends UFormat {
|
|||||||
*/
|
*/
|
||||||
private Currency currency;
|
private Currency currency;
|
||||||
|
|
||||||
static final int currentSerialVersion = 1;
|
static final int currentSerialVersion = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the version of <code>NumberFormat</code> present on the stream.
|
* Describes the version of <code>NumberFormat</code> present on the stream.
|
||||||
@ -1729,6 +1734,8 @@ public abstract class NumberFormat extends UFormat {
|
|||||||
* <code>byte</code> fields such as <code>maxIntegerDigits</code> are ignored,
|
* <code>byte</code> fields such as <code>maxIntegerDigits</code> are ignored,
|
||||||
* and the <code>int</code> fields such as <code>maximumIntegerDigits</code>
|
* and the <code>int</code> fields such as <code>maximumIntegerDigits</code>
|
||||||
* are used instead.
|
* are used instead.
|
||||||
|
*
|
||||||
|
* <li><b>2</b>: adds capitalizationSetting.
|
||||||
* </ul>
|
* </ul>
|
||||||
* When streaming out a <code>NumberFormat</code>, the most recent format
|
* When streaming out a <code>NumberFormat</code>, the most recent format
|
||||||
* (corresponding to the highest allowable <code>serialVersionOnStream</code>)
|
* (corresponding to the highest allowable <code>serialVersionOnStream</code>)
|
||||||
@ -1755,9 +1762,10 @@ public abstract class NumberFormat extends UFormat {
|
|||||||
private boolean parseStrict;
|
private boolean parseStrict;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Capitalization setting, new in ICU 53
|
* Capitalization context setting, new in ICU 53
|
||||||
|
* @serial
|
||||||
*/
|
*/
|
||||||
private transient DisplayContext capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
|
private DisplayContext capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The instances of this inner class are used as attribute keys and values
|
* The instances of this inner class are used as attribute keys and values
|
||||||
|
@ -20,6 +20,9 @@ import java.util.Set;
|
|||||||
import com.ibm.icu.impl.ICUDebug;
|
import com.ibm.icu.impl.ICUDebug;
|
||||||
import com.ibm.icu.impl.ICUResourceBundle;
|
import com.ibm.icu.impl.ICUResourceBundle;
|
||||||
import com.ibm.icu.impl.PatternProps;
|
import com.ibm.icu.impl.PatternProps;
|
||||||
|
import com.ibm.icu.lang.UCharacter;
|
||||||
|
import com.ibm.icu.text.BreakIterator;
|
||||||
|
import com.ibm.icu.text.DisplayContext;
|
||||||
import com.ibm.icu.util.ULocale;
|
import com.ibm.icu.util.ULocale;
|
||||||
import com.ibm.icu.util.ULocale.Category;
|
import com.ibm.icu.util.ULocale.Category;
|
||||||
import com.ibm.icu.util.UResourceBundle;
|
import com.ibm.icu.util.UResourceBundle;
|
||||||
@ -600,6 +603,15 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
*/
|
*/
|
||||||
private String[] publicRuleSetNames;
|
private String[] publicRuleSetNames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data for handling context-based capitalization
|
||||||
|
*/
|
||||||
|
private boolean capitalizationInfoIsSet = false;
|
||||||
|
private boolean capitalizationForListOrMenu = false;
|
||||||
|
private boolean capitalizationForStandAlone = false;
|
||||||
|
private BreakIterator capitalizationBrkIter = null;
|
||||||
|
|
||||||
|
|
||||||
private static final boolean DEBUG = ICUDebug.enabled("rbnf");
|
private static final boolean DEBUG = ICUDebug.enabled("rbnf");
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -833,6 +845,9 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
public boolean equals(Object that) {
|
public boolean equals(Object that) {
|
||||||
// if the other object isn't a RuleBasedNumberFormat, that's
|
// if the other object isn't a RuleBasedNumberFormat, that's
|
||||||
// all we need to know
|
// all we need to know
|
||||||
|
// Test for capitalization info equality is adequately handled
|
||||||
|
// by the NumberFormat test for capitalizationSetting equality;
|
||||||
|
// the info here is just derived from that.
|
||||||
if (!(that instanceof RuleBasedNumberFormat)) {
|
if (!(that instanceof RuleBasedNumberFormat)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -1061,7 +1076,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
if (ruleSet.startsWith("%%")) {
|
if (ruleSet.startsWith("%%")) {
|
||||||
throw new IllegalArgumentException("Can't use internal rule set");
|
throw new IllegalArgumentException("Can't use internal rule set");
|
||||||
}
|
}
|
||||||
return format(number, findRuleSet(ruleSet));
|
return adjustForContext(format(number, findRuleSet(ruleSet)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1080,7 +1095,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
if (ruleSet.startsWith("%%")) {
|
if (ruleSet.startsWith("%%")) {
|
||||||
throw new IllegalArgumentException("Can't use internal rule set");
|
throw new IllegalArgumentException("Can't use internal rule set");
|
||||||
}
|
}
|
||||||
return format(number, findRuleSet(ruleSet));
|
return adjustForContext(format(number, findRuleSet(ruleSet)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1098,7 +1113,13 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
// this is one of the inherited format() methods. Since it doesn't
|
// this is one of the inherited format() methods. Since it doesn't
|
||||||
// have a way to select the rule set to use, it just uses the
|
// have a way to select the rule set to use, it just uses the
|
||||||
// default one
|
// default one
|
||||||
toAppendTo.append(format(number, defaultRuleSet));
|
// Note, the BigInteger/BigDecimal methods below currently go through this.
|
||||||
|
if (toAppendTo.length() == 0) {
|
||||||
|
toAppendTo.append(adjustForContext(format(number, defaultRuleSet)));
|
||||||
|
} else {
|
||||||
|
// appending to other text, don't capitalize
|
||||||
|
toAppendTo.append(format(number, defaultRuleSet));
|
||||||
|
}
|
||||||
return toAppendTo;
|
return toAppendTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1121,7 +1142,12 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
// this is one of the inherited format() methods. Since it doesn't
|
// this is one of the inherited format() methods. Since it doesn't
|
||||||
// have a way to select the rule set to use, it just uses the
|
// have a way to select the rule set to use, it just uses the
|
||||||
// default one
|
// default one
|
||||||
toAppendTo.append(format(number, defaultRuleSet));
|
if (toAppendTo.length() == 0) {
|
||||||
|
toAppendTo.append(adjustForContext(format(number, defaultRuleSet)));
|
||||||
|
} else {
|
||||||
|
// appending to other text, don't capitalize
|
||||||
|
toAppendTo.append(format(number, defaultRuleSet));
|
||||||
|
}
|
||||||
return toAppendTo;
|
return toAppendTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1381,6 +1407,31 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@icu} Set a particular DisplayContext value in the formatter,
|
||||||
|
* such as CAPITALIZATION_FOR_STANDALONE. Note: For getContext, see
|
||||||
|
* NumberFormat.
|
||||||
|
*
|
||||||
|
* @param context The DisplayContext value to set.
|
||||||
|
* @draft ICU 53
|
||||||
|
* @provisional This API might change or be removed in a future release.
|
||||||
|
*/
|
||||||
|
// Here we override the NumberFormat implementation in order to
|
||||||
|
// lazily initialize relevant items
|
||||||
|
public void setContext(DisplayContext context) {
|
||||||
|
super.setContext(context);
|
||||||
|
if (!capitalizationInfoIsSet &&
|
||||||
|
(context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU || context==DisplayContext.CAPITALIZATION_FOR_STANDALONE)) {
|
||||||
|
initCapitalizationContextInfo(locale);
|
||||||
|
capitalizationInfoIsSet = true;
|
||||||
|
}
|
||||||
|
if (capitalizationBrkIter == null && (context==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
|
||||||
|
(context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForListOrMenu) ||
|
||||||
|
(context==DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone) )) {
|
||||||
|
capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
// package-internal API
|
// package-internal API
|
||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
@ -1653,6 +1704,23 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set capitalizationForListOrMenu, capitalizationForStandAlone
|
||||||
|
*/
|
||||||
|
private void initCapitalizationContextInfo(ULocale theLocale) {
|
||||||
|
ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, theLocale);
|
||||||
|
try {
|
||||||
|
ICUResourceBundle rdb = rb.getWithFallback("contextTransforms/number-spellout");
|
||||||
|
int[] intVector = rdb.getIntVector();
|
||||||
|
if (intVector.length >= 2) {
|
||||||
|
capitalizationForListOrMenu = (intVector[0] != 0);
|
||||||
|
capitalizationForStandAlone = (intVector[1] != 0);
|
||||||
|
}
|
||||||
|
} catch (MissingResourceException e) {
|
||||||
|
// use default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is used by init() to strip whitespace between rules (i.e.,
|
* This function is used by init() to strip whitespace between rules (i.e.,
|
||||||
* after semicolons).
|
* after semicolons).
|
||||||
@ -1805,6 +1873,23 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust capitalization of formatted result for display context
|
||||||
|
*/
|
||||||
|
private String adjustForContext(String result) {
|
||||||
|
if (result != null && result.length() > 0 && UCharacter.isLowerCase(result.codePointAt(0)) &&
|
||||||
|
capitalizationBrkIter != null) {
|
||||||
|
DisplayContext capitalization = getContext(DisplayContext.Type.CAPITALIZATION);
|
||||||
|
if ( capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
|
||||||
|
(capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationForListOrMenu) ||
|
||||||
|
(capitalization == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationForStandAlone) ) {
|
||||||
|
return UCharacter.toTitleCase(locale, result, capitalizationBrkIter,
|
||||||
|
UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the named rule set. Throws an IllegalArgumentException
|
* Returns the named rule set. Throws an IllegalArgumentException
|
||||||
* if this formatter doesn't have a rule set with that name.
|
* if this formatter doesn't have a rule set with that name.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
* Copyright (C) 1996-2013, International Business Machines Corporation and *
|
* Copyright (C) 1996-2014, International Business Machines Corporation and *
|
||||||
* others. All Rights Reserved. *
|
* others. All Rights Reserved. *
|
||||||
*******************************************************************************
|
*******************************************************************************
|
||||||
*/
|
*/
|
||||||
@ -15,6 +15,7 @@ import java.util.Random;
|
|||||||
|
|
||||||
import com.ibm.icu.dev.test.TestFmwk;
|
import com.ibm.icu.dev.test.TestFmwk;
|
||||||
import com.ibm.icu.text.DecimalFormatSymbols;
|
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||||
|
import com.ibm.icu.text.DisplayContext;
|
||||||
import com.ibm.icu.text.RuleBasedNumberFormat;
|
import com.ibm.icu.text.RuleBasedNumberFormat;
|
||||||
import com.ibm.icu.util.ULocale;
|
import com.ibm.icu.util.ULocale;
|
||||||
|
|
||||||
@ -1302,4 +1303,51 @@ public class RbnfTest extends TestFmwk {
|
|||||||
errln("Format Error - Got: " + result + " Expected: " + expected[1]);
|
errln("Format Error - Got: " + result + " Expected: " + expected[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TestContext() {
|
||||||
|
class TextContextItem {
|
||||||
|
public String locale;
|
||||||
|
public int format;
|
||||||
|
public DisplayContext context;
|
||||||
|
public double value;
|
||||||
|
public String expectedResult;
|
||||||
|
// Simple constructor
|
||||||
|
public TextContextItem(String loc, int fmt, DisplayContext ctxt, double val, String expRes) {
|
||||||
|
locale = loc;
|
||||||
|
format = fmt;
|
||||||
|
context = ctxt;
|
||||||
|
value = val;
|
||||||
|
expectedResult = expRes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final TextContextItem[] items = {
|
||||||
|
new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, 123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
|
||||||
|
new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 123.45, "Ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
|
||||||
|
new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, 123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
|
||||||
|
new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_STANDALONE, 123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
|
||||||
|
new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE, 123.45, "one hundred twenty-three point four five" ),
|
||||||
|
new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 123.45, "One hundred twenty-three point four five" ),
|
||||||
|
new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU, 123.45, "One hundred twenty-three point four five" ),
|
||||||
|
new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_STANDALONE, 123.45, "One hundred twenty-three point four five" ),
|
||||||
|
};
|
||||||
|
for (TextContextItem item: items) {
|
||||||
|
ULocale locale = new ULocale(item.locale);
|
||||||
|
RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(locale, item.format);
|
||||||
|
rbnf.setContext(item.context);
|
||||||
|
String result = rbnf.format(item.value, rbnf.getDefaultRuleSetName());
|
||||||
|
if (!result.equals(item.expectedResult)) {
|
||||||
|
errln("Error for locale " + item.locale + ", context " + item.context + ", expected " + item.expectedResult + ", got " + result);
|
||||||
|
}
|
||||||
|
RuleBasedNumberFormat rbnfClone = (RuleBasedNumberFormat)rbnf.clone();
|
||||||
|
if (!rbnfClone.equals(rbnf)) {
|
||||||
|
errln("Error for locale " + item.locale + ", context " + item.context + ", rbnf.clone() != rbnf");
|
||||||
|
} else {
|
||||||
|
result = rbnfClone.format(item.value, rbnfClone.getDefaultRuleSetName());
|
||||||
|
if (!result.equals(item.expectedResult)) {
|
||||||
|
errln("Error with clone for locale " + item.locale + ", context " + item.context + ", expected " + item.expectedResult + ", got " + result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user