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:
Peter Edberg 2014-03-03 19:48:34 +00:00
parent 22316fe74f
commit 62c9048bc0
3 changed files with 150 additions and 9 deletions

View File

@ -1047,7 +1047,8 @@ public abstract class NumberFormat extends UFormat {
&& minimumFractionDigits == other.minimumFractionDigits
&& groupingUsed == other.groupingUsed
&& parseIntegerOnly == other.parseIntegerOnly
&& parseStrict == other.parseStrict;
&& parseStrict == other.parseStrict
&& capitalizationSetting == other.capitalizationSetting;
}
/**
@ -1528,6 +1529,10 @@ public abstract class NumberFormat extends UFormat {
maximumFractionDigits = maxFractionDigits;
minimumFractionDigits = minFractionDigits;
}
if (serialVersionOnStream < 2) {
// Didn't have capitalizationSetting, set it to default
capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
}
///CLOVER:ON
/*Bug 4185761
Validate the min and max fields [Richard/GCL]
@ -1714,7 +1719,7 @@ public abstract class NumberFormat extends UFormat {
*/
private Currency currency;
static final int currentSerialVersion = 1;
static final int currentSerialVersion = 2;
/**
* 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,
* and the <code>int</code> fields such as <code>maximumIntegerDigits</code>
* are used instead.
*
* <li><b>2</b>: adds capitalizationSetting.
* </ul>
* When streaming out a <code>NumberFormat</code>, the most recent format
* (corresponding to the highest allowable <code>serialVersionOnStream</code>)
@ -1755,9 +1762,10 @@ public abstract class NumberFormat extends UFormat {
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

View File

@ -20,6 +20,9 @@ import java.util.Set;
import com.ibm.icu.impl.ICUDebug;
import com.ibm.icu.impl.ICUResourceBundle;
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.Category;
import com.ibm.icu.util.UResourceBundle;
@ -600,6 +603,15 @@ public class RuleBasedNumberFormat extends NumberFormat {
*/
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");
//-----------------------------------------------------------------------
@ -833,6 +845,9 @@ public class RuleBasedNumberFormat extends NumberFormat {
public boolean equals(Object that) {
// if the other object isn't a RuleBasedNumberFormat, that's
// 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)) {
return false;
} else {
@ -1061,7 +1076,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
if (ruleSet.startsWith("%%")) {
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("%%")) {
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
// have a way to select the rule set to use, it just uses the
// 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;
}
@ -1121,7 +1142,12 @@ public class RuleBasedNumberFormat extends NumberFormat {
// 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
// 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;
}
@ -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
//-----------------------------------------------------------------------
@ -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.,
* 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
* if this formatter doesn't have a rule set with that name.

View File

@ -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. *
*******************************************************************************
*/
@ -15,6 +15,7 @@ import java.util.Random;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.RuleBasedNumberFormat;
import com.ibm.icu.util.ULocale;
@ -1302,4 +1303,51 @@ public class RbnfTest extends TestFmwk {
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);
}
}
}
}
}