1 // © 2018 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 package com.ibm.icu.impl.number.range; 4 5 import java.util.Collections; 6 import java.util.HashMap; 7 import java.util.Map; 8 9 import com.ibm.icu.impl.ICUData; 10 import com.ibm.icu.impl.ICUResourceBundle; 11 import com.ibm.icu.impl.StandardPlural; 12 import com.ibm.icu.impl.UResource; 13 import com.ibm.icu.util.ULocale; 14 import com.ibm.icu.util.UResourceBundle; 15 import com.ibm.icu.util.UResourceTypeMismatchException; 16 17 /** 18 * @author sffc 19 * 20 */ 21 public class StandardPluralRanges { 22 23 StandardPlural[] flatTriples; 24 int numTriples = 0; 25 26 /** 27 * An immutable map from language codes to set IDs. 28 * Pre-computed and cached in Java since it is used as a cache key for PluralRules. 29 */ 30 private static volatile Map<String, String> languageToSet; 31 32 /** An empty StandardPluralRanges instance. */ 33 public static final StandardPluralRanges DEFAULT = new StandardPluralRanges(); 34 35 //////////////////// 36 37 private static final class PluralRangeSetsDataSink extends UResource.Sink { 38 39 Map<String, String> output; 40 PluralRangeSetsDataSink(Map<String, String> output)41 PluralRangeSetsDataSink(Map<String, String> output) { 42 this.output = output; 43 } 44 45 @Override put(UResource.Key key, UResource.Value value, boolean noFallback)46 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 47 UResource.Table table = value.getTable(); 48 for (int i = 0; table.getKeyAndValue(i, key, value); ++i) { 49 // The data has only languages; no regions/scripts. If this changes, this 50 // code and languageToSet will need to change. 51 assert key.toString().equals(new ULocale(key.toString()).getLanguage()); 52 output.put(key.toString(), value.toString()); 53 } 54 } 55 } 56 getLanguageToSet()57 private static Map<String, String> getLanguageToSet() { 58 Map<String, String> candidate = languageToSet; 59 if (candidate == null) { 60 Map<String, String> map = new HashMap<String, String>(); 61 PluralRangeSetsDataSink sink = new PluralRangeSetsDataSink(map); 62 ICUResourceBundle resource = (ICUResourceBundle) 63 UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "pluralRanges"); 64 resource.getAllItemsWithFallback("locales", sink); 65 candidate = Collections.unmodifiableMap(map); 66 } 67 // Check if another thread set languageToSet in the mean time 68 if (languageToSet == null) { 69 languageToSet = candidate; 70 } 71 return languageToSet; 72 } 73 74 private static final class PluralRangesDataSink extends UResource.Sink { 75 76 StandardPluralRanges output; 77 PluralRangesDataSink(StandardPluralRanges output)78 PluralRangesDataSink(StandardPluralRanges output) { 79 this.output = output; 80 } 81 82 @Override put(UResource.Key key, UResource.Value value, boolean noFallback)83 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 84 UResource.Array entriesArray = value.getArray(); 85 output.setCapacity(entriesArray.getSize()); 86 for (int i = 0; entriesArray.getValue(i, value); ++i) { 87 UResource.Array pluralFormsArray = value.getArray(); 88 if (pluralFormsArray.getSize() != 3) { 89 throw new UResourceTypeMismatchException( 90 "Expected 3 elements in pluralRanges.txt array"); 91 } 92 pluralFormsArray.getValue(0, value); 93 StandardPlural first = StandardPlural.fromString(value.getString()); 94 pluralFormsArray.getValue(1, value); 95 StandardPlural second = StandardPlural.fromString(value.getString()); 96 pluralFormsArray.getValue(2, value); 97 StandardPlural result = StandardPlural.fromString(value.getString()); 98 output.addPluralRange(first, second, result); 99 } 100 } 101 } 102 getPluralRangesData( String set, StandardPluralRanges out)103 private static void getPluralRangesData( 104 String set, 105 StandardPluralRanges out) { 106 StringBuilder sb = new StringBuilder(); 107 ICUResourceBundle resource; 108 resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "pluralRanges"); 109 sb.setLength(0); 110 sb.append("rules/"); 111 sb.append(set); 112 String key = sb.toString(); 113 PluralRangesDataSink sink = new PluralRangesDataSink(out); 114 resource.getAllItemsWithFallback(key, sink); 115 } 116 117 //////////////////// 118 119 /** Create a StandardPluralRanges based on locale. */ forLocale(ULocale locale)120 public static StandardPluralRanges forLocale(ULocale locale) { 121 return forSet(getSetForLocale(locale)); 122 } 123 124 /** Create a StandardPluralRanges based on set name. */ forSet(String set)125 public static StandardPluralRanges forSet(String set) { 126 StandardPluralRanges result = new StandardPluralRanges(); 127 if (set == null) { 128 // Not all languages are covered: fail gracefully 129 return DEFAULT; 130 } 131 getPluralRangesData(set, result); 132 return result; 133 } 134 135 /** Get the set name from the locale. */ getSetForLocale(ULocale locale)136 public static String getSetForLocale(ULocale locale) { 137 return getLanguageToSet().get(locale.getLanguage()); 138 } 139 StandardPluralRanges()140 private StandardPluralRanges() { 141 } 142 143 /** Used for data loading. */ addPluralRange(StandardPlural first, StandardPlural second, StandardPlural result)144 private void addPluralRange(StandardPlural first, StandardPlural second, StandardPlural result) { 145 flatTriples[3 * numTriples] = first; 146 flatTriples[3 * numTriples + 1] = second; 147 flatTriples[3 * numTriples + 2] = result; 148 numTriples++; 149 } 150 151 /** Used for data loading. */ setCapacity(int length)152 private void setCapacity(int length) { 153 flatTriples = new StandardPlural[length*3]; 154 } 155 resolve(StandardPlural first, StandardPlural second)156 public StandardPlural resolve(StandardPlural first, StandardPlural second) { 157 for (int i = 0; i < numTriples; i++) { 158 if (first == flatTriples[3 * i] && second == flatTriples[3 * i + 1]) { 159 return flatTriples[3 * i + 2]; 160 } 161 } 162 // Default fallback 163 return StandardPlural.OTHER; 164 } 165 166 } 167