com.ibm.icu.text.NumberingSystem Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2009-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.text;
import java.util.ArrayList;
import java.util.Locale;
import java.util.MissingResourceException;
import com.ibm.icu.impl.CacheBase;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SoftCache;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
import com.ibm.icu.util.UResourceBundle;
import com.ibm.icu.util.UResourceBundleIterator;
/**
* NumberingSystem
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
* @stable ICU 4.2
*/
public class NumberingSystem {
private static final String[] OTHER_NS_KEYWORDS = { "native", "traditional", "finance" };
/**
* For convenience, an instance representing the latn numbering system, which
* corresponds to digits in the ASCII range '0' through '9'.
*
* @stable ICU 60
*/
public static final NumberingSystem LATIN = lookupInstanceByName("latn");
/**
* Default constructor. Returns a numbering system that uses the Latin-script decimal
* digits 0 through 9. This should be equivalent to NumberingSystem.LATIN.
*
* @stable ICU 4.2
*/
public NumberingSystem() {
radix = 10;
algorithmic = false;
desc = "0123456789";
name = "latn";
}
/**
* Factory method for creating a numbering system.
* @param radix_in The radix for this numbering system. ICU currently
* supports only numbering systems whose radix is 10.
* @param isAlgorithmic_in Specifies whether the numbering system is algorithmic
* (true) or numeric (false).
* @param desc_in String used to describe the characteristics of the numbering
* system. For numeric systems, this string contains the digits used by the
* numbering system, in order, starting from zero. For algorithmic numbering
* systems, the string contains the name of the RBNF ruleset in the locale's
* NumberingSystemRules section that will be used to format numbers using
* this numbering system.
* @stable ICU 4.2
*/
public static NumberingSystem getInstance(int radix_in, boolean isAlgorithmic_in, String desc_in ) {
return getInstance(null,radix_in,isAlgorithmic_in,desc_in);
}
/**
* Factory method for creating a numbering system.
* @param name_in The string representing the name of the numbering system.
* @param radix_in The radix for this numbering system. ICU currently
* supports only numbering systems whose radix is 10.
* @param isAlgorithmic_in Specifies whether the numbering system is algorithmic
* (true) or numeric (false).
* @param desc_in String used to describe the characteristics of the numbering
* system. For numeric systems, this string contains the digits used by the
* numbering system, in order, starting from zero. For algorithmic numbering
* systems, the string contains the name of the RBNF ruleset in the locale's
* NumberingSystemRules section that will be used to format numbers using
* this numbering system.
* @stable ICU 4.6
*/
private static NumberingSystem getInstance(String name_in, 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.codePointCount(0, 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;
ns.name = name_in;
return ns;
}
/**
* Returns the default numbering system for the specified locale.
* @stable ICU 4.2
*/
public static NumberingSystem getInstance(Locale inLocale) {
return getInstance(ULocale.forLocale(inLocale));
}
/**
* Returns the default numbering system for the specified ULocale.
* @stable ICU 4.2
*/
public static NumberingSystem getInstance(ULocale locale) {
// Check for @numbers
boolean nsResolved = true;
String numbersKeyword = locale.getKeywordValue("numbers");
if (numbersKeyword != null ) {
for ( String keyword : OTHER_NS_KEYWORDS ) {
if ( numbersKeyword.equals(keyword)) {
nsResolved = false;
break;
}
}
} else {
numbersKeyword = "default";
nsResolved = false;
}
if (nsResolved) {
NumberingSystem ns = getInstanceByName(numbersKeyword);
if (ns != null) {
return ns;
}
// If the @numbers keyword points to a bogus numbering system name,
// we return the default for the locale.
numbersKeyword = "default";
}
// Attempt to get the numbering system from the cache
String baseName = locale.getBaseName();
// TODO: Caching by locale+numbersKeyword could yield a large cache.
// Try to load for each locale the mappings from OTHER_NS_KEYWORDS and default
// to real numbering system names; can we get those from supplemental data?
// Then look up those mappings for the locale and resolve the keyword.
String key = baseName+"@numbers="+numbersKeyword;
LocaleLookupData localeLookupData = new LocaleLookupData(locale, numbersKeyword);
return cachedLocaleData.getInstance(key, localeLookupData);
}
private static class LocaleLookupData {
public final ULocale locale;
public final String numbersKeyword;
LocaleLookupData(ULocale locale, String numbersKeyword) {
this.locale = locale;
this.numbersKeyword = numbersKeyword;
}
}
static NumberingSystem lookupInstanceByLocale(LocaleLookupData localeLookupData) {
ULocale locale = localeLookupData.locale;
ICUResourceBundle rb;
try {
rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
rb = rb.getWithFallback("NumberElements");
} catch (MissingResourceException ex) {
return new NumberingSystem();
}
String numbersKeyword = localeLookupData.numbersKeyword;
String resolvedNumberingSystem = null;
for (;;) {
try {
resolvedNumberingSystem = rb.getStringWithFallback(numbersKeyword);
break;
} catch (MissingResourceException ex) { // Fall back behavior as defined in TR35
if (numbersKeyword.equals("native") || numbersKeyword.equals("finance")) {
numbersKeyword = "default";
} else if (numbersKeyword.equals("traditional")) {
numbersKeyword = "native";
} else {
break;
}
}
}
NumberingSystem ns = null;
if (resolvedNumberingSystem != null) {
ns = getInstanceByName(resolvedNumberingSystem);
}
if (ns == null) {
ns = new NumberingSystem();
}
return ns;
}
/**
* Returns the default numbering system for the default FORMAT
locale.
* @see Category#FORMAT
* @stable ICU 4.2
*/
public static NumberingSystem getInstance() {
return getInstance(ULocale.getDefault(Category.FORMAT));
}
/**
* Returns a numbering system from one of the predefined numbering systems
* known to ICU. Numbering system names are based on the numbering systems
* defined in CLDR. To get a list of available numbering systems, use the
* getAvailableNames method.
* @param name The name of the desired numbering system. Numbering system
* names often correspond with the name of the script they are associated
* with. For example, "thai" for Thai digits, "hebr" for Hebrew numerals.
* @return The NumberingSystem instance, or null if not available.
* @stable ICU 4.2
*/
public static NumberingSystem getInstanceByName(String name) {
// Get the numbering system from the cache.
return cachedStringData.getInstance(name, null /* unused */);
}
private static NumberingSystem lookupInstanceByName(String name) {
int radix;
boolean isAlgorithmic;
String description;
try {
UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUData.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(name, radix, isAlgorithmic, description);
}
/**
* Returns a string array containing a list of the names of numbering systems
* currently known to ICU.
*
* @return An array of strings in alphabetical (invariant) order.
* @stable ICU 4.2
*/
public static String [] getAvailableNames() {
UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "numberingSystems");
UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems");
UResourceBundle temp;
String nsName;
ArrayList output = new ArrayList<>();
UResourceBundleIterator it = nsCurrent.getIterator();
while (it.hasNext()) {
temp = it.next();
nsName = temp.getKey();
output.add(nsName);
}
return output.toArray(new String[output.size()]);
}
/**
* Convenience method to determine if a given digit string is valid for use as a
* descriptor of a numeric ( non-algorithmic ) numbering system. In order for
* a digit string to be valid, it must contain exactly ten Unicode code points.
* @stable ICU 4.2
*/
public static boolean isValidDigitString(String str) {
int numCodepoints = str.codePointCount(0, str.length());
return (numCodepoints == 10);
}
/**
* Returns the radix of the current numbering system.
* @stable ICU 4.2
*/
public int getRadix() {
return radix;
}
/**
* Returns the description string of the current numbering system.
* The description string describes the characteristics of the numbering
* system. For numeric systems, this string contains the digits used by the
* numbering system, in order, starting from zero. For algorithmic numbering
* systems, the string contains the name of the RBNF ruleset in the locale's
* NumberingSystemRules section that will be used to format numbers using
* this numbering system.
* @stable ICU 4.2
*/
public String getDescription() {
return desc;
}
/**
* Returns the string representing the name of the numbering system.
* @stable ICU 4.6
*/
public String getName() {
return name;
}
/**
* Returns the numbering system's algorithmic status. If true,
* the numbering system is algorithmic and uses an RBNF formatter to
* format numerals. If false, the numbering system is numeric and
* uses a fixed set of digits.
* @stable ICU 4.2
*/
public boolean isAlgorithmic() {
return algorithmic;
}
private String desc;
private int radix;
private boolean algorithmic;
private String name;
/**
* Cache to hold the NumberingSystems by Locale.
*/
private static CacheBase cachedLocaleData =
new SoftCache() {
@Override
protected NumberingSystem createInstance(String key, LocaleLookupData localeLookupData) {
return lookupInstanceByLocale(localeLookupData);
}
};
/**
* Cache to hold the NumberingSystems by name.
*/
private static CacheBase cachedStringData =
new SoftCache() {
@Override
protected NumberingSystem createInstance(String key, Void unused) {
return lookupInstanceByName(key);
}
};
}