| // © 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; |
| |
| |
| /** |
| * <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 |
| * @stable ICU 4.2 |
| */ |
| public class NumberingSystem { |
| private static final String[] OTHER_NS_KEYWORDS = { "native", "traditional", "finance" }; |
| |
| /** |
| * For convenience, an instance representing the <em>latn</em> 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 <code>FORMAT</code> 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<String> 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<String, NumberingSystem, LocaleLookupData> cachedLocaleData = |
| new SoftCache<String, NumberingSystem, LocaleLookupData>() { |
| @Override |
| protected NumberingSystem createInstance(String key, LocaleLookupData localeLookupData) { |
| return lookupInstanceByLocale(localeLookupData); |
| } |
| }; |
| |
| /** |
| * Cache to hold the NumberingSystems by name. |
| */ |
| private static CacheBase<String, NumberingSystem, Void> cachedStringData = |
| new SoftCache<String, NumberingSystem, Void>() { |
| @Override |
| protected NumberingSystem createInstance(String key, Void unused) { |
| return lookupInstanceByName(key); |
| } |
| }; |
| } |