ICU-6694 NumberingSystem implementation for ICU4J
X-SVN-Rev: 25319
This commit is contained in:
parent
926677c828
commit
6d3ab9535f
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -357,6 +357,7 @@ icu4j/src/com/ibm/icu/dev/tool/docs/icu4j401.api.gz -text
|
||||
icu4j/src/com/ibm/icu/dev/tool/tzu/icu.gif -text
|
||||
icu4j/src/com/ibm/icu/impl/data/icudata.jar -text
|
||||
icu4j/src/com/ibm/icu/text/CurrencyPluralInfo.java -text
|
||||
icu4j/src/com/ibm/icu/text/NumberingSystem.java -text
|
||||
icu4j/src/com/ibm/richtext/textapps/resources/unicode.arabic.red -text
|
||||
icu4j/src/com/ibm/richtext/textapps/resources/unicode.hebrew.red -text
|
||||
tools/release/java/.classpath -text
|
||||
|
@ -1211,6 +1211,28 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test alternate numbering systems
|
||||
*/
|
||||
public void TestNumberingSystems() {
|
||||
|
||||
ULocale loc1 = new ULocale("en_US@numbers=thai");
|
||||
ULocale loc2 = new ULocale("en_US@numbers=hebrew");
|
||||
ULocale loc3 = new ULocale("en_US@numbers=persian");
|
||||
ULocale loc4 = new ULocale("hi_IN@numbers=foobar");
|
||||
|
||||
NumberFormat fmt1 = NumberFormat.getInstance(loc1);
|
||||
NumberFormat fmt2 = NumberFormat.getInstance(loc2);
|
||||
NumberFormat fmt3 = NumberFormat.getInstance(loc3);
|
||||
NumberFormat fmt4 = NumberFormat.getInstance(loc4);
|
||||
|
||||
expect2(fmt1,1234.567,"\u0e51,\u0e52\u0e53\u0e54.\u0e55\u0e56\u0e57");
|
||||
expect3(fmt2,5678.0,"\u05d4\u05f3\u05ea\u05e8\u05e2\u05f4\u05d7");
|
||||
expect2(fmt3,1234.567,"\u06f1,\u06f2\u06f3\u06f4.\u06f5\u06f6\u06f7");
|
||||
expect2(fmt4,1234.567,"\u0967,\u0968\u0969\u096a.\u096b\u096c\u096d");
|
||||
|
||||
}
|
||||
|
||||
public void TestThreadedFormat() {
|
||||
|
||||
class FormatTask implements Runnable {
|
||||
@ -1647,16 +1669,30 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
expect(fmt, n, exp, false);
|
||||
expect(fmt, exp, n);
|
||||
}
|
||||
// Format-Parse test
|
||||
public void expect3(NumberFormat fmt, Number n, String exp) {
|
||||
// Don't round-trip format test, since we explicitly do it
|
||||
expect_rbnf(fmt, n, exp, false);
|
||||
expect_rbnf(fmt, exp, n);
|
||||
}
|
||||
|
||||
// Format-Parse test (convenience)
|
||||
public void expect2(NumberFormat fmt, double n, String exp) {
|
||||
expect2(fmt, new Double(n), exp);
|
||||
}
|
||||
// Format-Parse test (convenience)
|
||||
public void expect3(NumberFormat fmt, double n, String exp) {
|
||||
expect3(fmt, new Double(n), exp);
|
||||
}
|
||||
|
||||
// Format-Parse test (convenience)
|
||||
public void expect2(NumberFormat fmt, long n, String exp) {
|
||||
expect2(fmt, new Long(n), exp);
|
||||
}
|
||||
// Format-Parse test (convenience)
|
||||
public void expect3(NumberFormat fmt, long n, String exp) {
|
||||
expect3(fmt, new Long(n), exp);
|
||||
}
|
||||
|
||||
// Format test
|
||||
public void expect(NumberFormat fmt, Number n, String exp, boolean rt) {
|
||||
@ -1691,6 +1727,36 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
saw + "\", expected \"" + exp + "\"");
|
||||
}
|
||||
}
|
||||
// Format test
|
||||
public void expect_rbnf(NumberFormat fmt, Number n, String exp, boolean rt) {
|
||||
StringBuffer saw = new StringBuffer();
|
||||
FieldPosition pos = new FieldPosition(0);
|
||||
fmt.format(n, saw, pos);
|
||||
if (saw.toString().equals(exp)) {
|
||||
logln("Ok " + n + " = \"" +
|
||||
saw + "\"");
|
||||
// We should be able to round-trip the formatted string =>
|
||||
// number => string (but not the other way around: number
|
||||
// => string => number2, might have number2 != number):
|
||||
if (rt) {
|
||||
try {
|
||||
Number n2 = fmt.parse(exp);
|
||||
StringBuffer saw2 = new StringBuffer();
|
||||
fmt.format(n2, saw2, pos);
|
||||
if (!saw2.toString().equals(exp)) {
|
||||
errln("FAIL \"" + exp + "\" => " + n2 +
|
||||
" => \"" + saw2 + '"');
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
errln(e.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
errln("FAIL " + n + " = \"" +
|
||||
saw + "\", expected \"" + exp + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// Format test (convenience)
|
||||
public void expect(NumberFormat fmt, Number n, String exp) {
|
||||
@ -1730,6 +1796,26 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse test
|
||||
public void expect_rbnf(NumberFormat fmt, String str, Number n) {
|
||||
Number num = null;
|
||||
try {
|
||||
num = (Number) fmt.parse(str);
|
||||
} catch (ParseException e) {
|
||||
errln(e.getMessage());
|
||||
return;
|
||||
}
|
||||
// A little tricky here -- make sure Double(12345.0) and
|
||||
// Long(12345) match.
|
||||
if (num.equals(n) || num.doubleValue() == n.doubleValue()) {
|
||||
logln("Ok \"" + str + " = " +
|
||||
num);
|
||||
} else {
|
||||
errln("FAIL \"" + str + " = " +
|
||||
num + ", expected " + n);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse test (convenience)
|
||||
public void expect(NumberFormat fmt, String str, double n) {
|
||||
expect(fmt, str, new Double(n));
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1996-2007, International Business Machines Corporation and *
|
||||
* Copyright (C) 1996-2009, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -157,6 +157,18 @@ public class RbnfRoundTripTest extends TestFmwk {
|
||||
doTest(formatter, 0, 12345678);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an exhaustive round-trip test on the Greek spellout rules
|
||||
*/
|
||||
public void TestHebrewNumberingRT() {
|
||||
RuleBasedNumberFormat formatter
|
||||
= new RuleBasedNumberFormat(new Locale("he", "IL",
|
||||
""), RuleBasedNumberFormat.NUMBERING_SYSTEM);
|
||||
|
||||
formatter.setDefaultRuleSet("%hebrew");
|
||||
doTest(formatter, 0, 12345678);
|
||||
}
|
||||
|
||||
void doTest(RuleBasedNumberFormat formatter, long lowLimit,
|
||||
long highLimit) {
|
||||
try {
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:395969705d3ec27761bfabd301b09444aad168747c91b3742f21bde77c21cd36
|
||||
size 6638663
|
||||
oid sha256:bbc8472971db3431bbff7018c370518987388f2de3ba89ec4386d9933c7a3b9e
|
||||
size 6640115
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1996-2008, International Business Machines Corporation and *
|
||||
* Copyright (C) 1996-2009, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -701,6 +701,13 @@ public class DecimalFormatSymbols implements Cloneable, Serializable {
|
||||
padEscape = DecimalFormat.PATTERN_PAD_ESCAPE;
|
||||
sigDigit = DecimalFormat.PATTERN_SIGNIFICANT_DIGIT;
|
||||
|
||||
// Attempt to set the zero digit based on the numbering system for the locale requested
|
||||
//
|
||||
NumberingSystem ns = NumberingSystem.getInstance(locale);
|
||||
if ( ns != null && ns.getRadix() == 10 && !ns.isAlgorithmic()) {
|
||||
zeroDigit = ns.getDescription().charAt(0);
|
||||
}
|
||||
|
||||
// Obtain currency data from the currency API. This is strictly
|
||||
// for backward compatibility; we don't use DecimalFormatSymbols
|
||||
// for currency data anymore.
|
||||
|
@ -1320,20 +1320,33 @@ public abstract class NumberFormat extends UFormat {
|
||||
pattern = Utility.replace(pattern, "\u00A4", doubleCurrencyStr);
|
||||
}
|
||||
|
||||
DecimalFormat format = new DecimalFormat(pattern, symbols, choice);
|
||||
// System.out.println("loc: " + desiredLocale + " choice: " + choice + " pat: " + pattern + " sym: " + symbols + " result: " + format);
|
||||
|
||||
/*Bug 4408066
|
||||
Add codes for the new method getIntegerInstance() [Richard/GCL]
|
||||
*/
|
||||
// TODO: revisit this -- this is almost certainly not the way we want
|
||||
// to do this. aliu 1/6/2004
|
||||
if (choice == INTEGERSTYLE) {
|
||||
format.setMaximumFractionDigits(0);
|
||||
format.setDecimalSeparatorAlwaysShown(false);
|
||||
format.setParseIntegerOnly(true);
|
||||
NumberingSystem ns = NumberingSystem.getInstance(desiredLocale);
|
||||
if ( ns == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
NumberFormat format;
|
||||
|
||||
if ( ns != null && ns.isAlgorithmic()) {
|
||||
RuleBasedNumberFormat r = new RuleBasedNumberFormat(desiredLocale,RuleBasedNumberFormat.NUMBERING_SYSTEM);
|
||||
r.setDefaultRuleSet(ns.getDescription());
|
||||
format = r;
|
||||
} else {
|
||||
DecimalFormat f = new DecimalFormat(pattern, symbols, choice);
|
||||
// System.out.println("loc: " + desiredLocale + " choice: " + choice + " pat: " + pattern + " sym: " + symbols + " result: " + format);
|
||||
|
||||
/*Bug 4408066
|
||||
Add codes for the new method getIntegerInstance() [Richard/GCL]
|
||||
*/
|
||||
// TODO: revisit this -- this is almost certainly not the way we want
|
||||
// to do this. aliu 1/6/2004
|
||||
if (choice == INTEGERSTYLE) {
|
||||
f.setMaximumFractionDigits(0);
|
||||
f.setDecimalSeparatorAlwaysShown(false);
|
||||
f.setParseIntegerOnly(true);
|
||||
}
|
||||
format = f;
|
||||
}
|
||||
// TODO: the actual locale of the *pattern* may differ from that
|
||||
// for the *symbols*. For now, we use the data for the symbols.
|
||||
// Revisit this.
|
||||
|
189
icu4j/src/com/ibm/icu/text/NumberingSystem.java
Normal file
189
icu4j/src/com/ibm/icu/text/NumberingSystem.java
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2009, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.text.Format;
|
||||
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
import com.ibm.icu.util.UResourceBundleIterator;
|
||||
import com.ibm.icu.text.UCharacterIterator;
|
||||
|
||||
/**
|
||||
* <code>NumberingSystem</code> is the base class for all number
|
||||
* systems. This class provides the interface for setting different numbering
|
||||
* system types, whether it be a simple alternate digit system such as
|
||||
* Thai digits or Devanagari digits, or an algorithmic numbering system such
|
||||
* as Hebrew numbering or Chinese numbering.
|
||||
*
|
||||
* @author John Emmons
|
||||
* @draft ICU 4.2
|
||||
*/
|
||||
|
||||
public class NumberingSystem {
|
||||
|
||||
public NumberingSystem() {
|
||||
radix = 10;
|
||||
algorithmic = false;
|
||||
desc = "0123456789";
|
||||
}
|
||||
|
||||
public static NumberingSystem getInstance(int radix_in, boolean isAlgorithmic_in, String desc_in ) {
|
||||
if ( radix_in < 2 ) {
|
||||
throw new IllegalArgumentException("Invalid radix for numbering system");
|
||||
}
|
||||
|
||||
if ( !isAlgorithmic_in ) {
|
||||
if ( desc_in.length() != radix_in || !isValidDigitString(desc_in)) {
|
||||
throw new IllegalArgumentException("Invalid digit string for numbering system");
|
||||
}
|
||||
}
|
||||
NumberingSystem ns = new NumberingSystem();
|
||||
ns.radix = radix_in;
|
||||
ns.algorithmic = isAlgorithmic_in;
|
||||
ns.desc = desc_in;
|
||||
return ns;
|
||||
}
|
||||
|
||||
public static NumberingSystem getInstance(Locale inLocale) {
|
||||
return getInstance(ULocale.forLocale(inLocale));
|
||||
}
|
||||
|
||||
public static NumberingSystem getInstance(ULocale locale) {
|
||||
|
||||
String numbersKeyword = locale.getKeywordValue("numbers");
|
||||
if (numbersKeyword != null) {
|
||||
NumberingSystem ns = getInstanceByName(numbersKeyword);
|
||||
if ( ns != null ) {
|
||||
return ns;
|
||||
}
|
||||
}
|
||||
|
||||
String defaultNumberingSystem;
|
||||
|
||||
try {
|
||||
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,locale);
|
||||
defaultNumberingSystem = rb.getString("defaultNumberingSystem");
|
||||
} catch (MissingResourceException ex) {
|
||||
return new NumberingSystem();
|
||||
}
|
||||
|
||||
NumberingSystem ns = getInstanceByName(defaultNumberingSystem);
|
||||
if ( ns != null ) {
|
||||
return ns;
|
||||
}
|
||||
|
||||
return new NumberingSystem();
|
||||
}
|
||||
|
||||
public static NumberingSystem getInstance() {
|
||||
return getInstance(ULocale.getDefault());
|
||||
}
|
||||
|
||||
public static NumberingSystem getInstanceByName(String name) {
|
||||
int radix;
|
||||
boolean isAlgorithmic;
|
||||
String description;
|
||||
try {
|
||||
UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "numberingSystems");
|
||||
UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems");
|
||||
UResourceBundle nsTop = nsCurrent.get(name);
|
||||
|
||||
description = nsTop.getString("desc");
|
||||
UResourceBundle nsRadixBundle = nsTop.get("radix");
|
||||
UResourceBundle nsAlgBundle = nsTop.get("algorithmic");
|
||||
radix = nsRadixBundle.getInt();
|
||||
int algorithmic = nsAlgBundle.getInt();
|
||||
|
||||
isAlgorithmic = ( algorithmic == 1 );
|
||||
|
||||
} catch (MissingResourceException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getInstance(radix,isAlgorithmic,description);
|
||||
}
|
||||
|
||||
public static String [] getAvailableNames() {
|
||||
|
||||
UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "numberingSystems");
|
||||
UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems");
|
||||
UResourceBundle temp;
|
||||
|
||||
StringBuffer availableNamesString = new StringBuffer(256);
|
||||
String nsName;
|
||||
int count = 0;
|
||||
|
||||
UResourceBundleIterator it = nsCurrent.getIterator();
|
||||
while (it.hasNext()) {
|
||||
temp = it.next();
|
||||
nsName = temp.getKey();
|
||||
if (count > 0) {
|
||||
availableNamesString.append(":");
|
||||
}
|
||||
availableNamesString.append(nsName);
|
||||
count++;
|
||||
}
|
||||
return availableNamesString.toString().split(":");
|
||||
}
|
||||
|
||||
public static boolean isValidDigitString(String str) {
|
||||
|
||||
int c;
|
||||
int prev = 0;
|
||||
int i = 0;
|
||||
UCharacterIterator it = UCharacterIterator.getInstance(str);
|
||||
|
||||
it.setToStart();
|
||||
while ( (c = it.nextCodePoint()) != UCharacterIterator.DONE) {
|
||||
if ( UCharacter.digit(c) != i ) { // Digits outside the Unicode decimal digit class are not currently supported
|
||||
return false;
|
||||
}
|
||||
if ( prev != 0 && c != prev + 1 ) { // Non-contiguous digits are not currently supported
|
||||
return false;
|
||||
}
|
||||
if ( UCharacter.isSupplementary(c)) { // Digits outside the BMP are not currently supported
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
prev = c;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getRadix() {
|
||||
return radix;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public boolean isAlgorithmic() {
|
||||
return algorithmic;
|
||||
}
|
||||
|
||||
|
||||
private String desc;
|
||||
private int radix;
|
||||
private boolean algorithmic;
|
||||
|
||||
}
|
@ -509,6 +509,12 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
*/
|
||||
public static final int DURATION = 3;
|
||||
|
||||
/**
|
||||
* Selector code that tells the constructor to create a numbering system formatter
|
||||
* @draft ICU 4.2
|
||||
*/
|
||||
public static final int NUMBERING_SYSTEM = 4;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// data members
|
||||
//-----------------------------------------------------------------------
|
||||
@ -719,10 +725,12 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
* and duration.
|
||||
* @param locale The locale for the formatter.
|
||||
* @param format A selector code specifying which kind of formatter to create for that
|
||||
* locale. There are three legal values: SPELLOUT, which creates a formatter that
|
||||
* locale. There are four legal values: SPELLOUT, which creates a formatter that
|
||||
* spells out a value in words in the desired language, ORDINAL, which attaches
|
||||
* an ordinal suffix from the desired language to the end of a number (e.g. "123rd"),
|
||||
* and DURATION, which formats a duration in seconds as hours, minutes, and seconds.
|
||||
* DURATION, which formats a duration in seconds as hours, minutes, and seconds, and
|
||||
* NUMBERING_SYSTEM, which is used to invoke rules for alternate numbering
|
||||
* systems such as the Hebrew numbering system, or for Roman numerals, etc..
|
||||
* @stable ICU 3.2
|
||||
*/
|
||||
public RuleBasedNumberFormat(ULocale locale, int format) {
|
||||
@ -771,10 +779,10 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
}
|
||||
|
||||
private static final String[] rulenames = {
|
||||
"SpelloutRules", "OrdinalRules", "DurationRules",
|
||||
"SpelloutRules", "OrdinalRules", "DurationRules", "NumberingSystemRules",
|
||||
};
|
||||
private static final String[] locnames = {
|
||||
"SpelloutLocalizations", "OrdinalLocalizations", "DurationLocalizations",
|
||||
"SpelloutLocalizations", "OrdinalLocalizations", "DurationLocalizations", "NumberingSystemLocalizations",
|
||||
};
|
||||
|
||||
/**
|
||||
@ -785,6 +793,7 @@ public class RuleBasedNumberFormat extends NumberFormat {
|
||||
* out a value in words in the default locale's langyage, ORDINAL, which attaches
|
||||
* an ordinal suffix from the default locale's language to a numeral, and
|
||||
* DURATION, which formats a duration in seconds as hours, minutes, and seconds.
|
||||
* or NUMBERING_SYSTEM, which is used for alternate numbering systems such as Hebrew.
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
public RuleBasedNumberFormat(int format) {
|
||||
|
Loading…
Reference in New Issue
Block a user