1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.impl.number; 5 6 import java.util.Arrays; 7 import java.util.Map; 8 import java.util.Set; 9 10 import ohos.global.icu.impl.ICUData; 11 import ohos.global.icu.impl.ICUResourceBundle; 12 import ohos.global.icu.impl.StandardPlural; 13 import ohos.global.icu.impl.UResource; 14 import ohos.global.icu.text.CompactDecimalFormat.CompactStyle; 15 import ohos.global.icu.util.ICUException; 16 import ohos.global.icu.util.ULocale; 17 import ohos.global.icu.util.UResourceBundle; 18 19 /** 20 * Datatype for compact notation data. Includes logic for data loading. 21 * @hide exposed on OHOS 22 */ 23 public class CompactData implements MultiplierProducer { 24 25 /** 26 * @hide exposed on OHOS 27 */ 28 public enum CompactType { 29 DECIMAL, CURRENCY 30 } 31 32 // A dummy object used when a "0" compact decimal entry is encountered. This is necessary 33 // in order to prevent falling back to root. Object equality ("==") is intended. 34 private static final String USE_FALLBACK = "<USE FALLBACK>"; 35 36 private final String[] patterns; 37 private final byte[] multipliers; 38 private byte largestMagnitude; 39 private boolean isEmpty; 40 41 private static final int COMPACT_MAX_DIGITS = 15; 42 CompactData()43 public CompactData() { 44 patterns = new String[(CompactData.COMPACT_MAX_DIGITS + 1) * StandardPlural.COUNT]; 45 multipliers = new byte[CompactData.COMPACT_MAX_DIGITS + 1]; 46 largestMagnitude = 0; 47 isEmpty = true; 48 } 49 populate( ULocale locale, String nsName, CompactStyle compactStyle, CompactType compactType)50 public void populate( 51 ULocale locale, 52 String nsName, 53 CompactStyle compactStyle, 54 CompactType compactType) { 55 assert isEmpty; 56 CompactDataSink sink = new CompactDataSink(this); 57 ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle 58 .getBundleInstance(ICUData.ICU_BASE_NAME, locale); 59 60 boolean nsIsLatn = nsName.equals("latn"); 61 boolean compactIsShort = compactStyle == CompactStyle.SHORT; 62 63 // Fall back to latn numbering system and/or short compact style. 64 StringBuilder resourceKey = new StringBuilder(); 65 getResourceBundleKey(nsName, compactStyle, compactType, resourceKey); 66 rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink); 67 if (isEmpty && !nsIsLatn) { 68 getResourceBundleKey("latn", compactStyle, compactType, resourceKey); 69 rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink); 70 } 71 if (isEmpty && !compactIsShort) { 72 getResourceBundleKey(nsName, CompactStyle.SHORT, compactType, resourceKey); 73 rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink); 74 } 75 if (isEmpty && !nsIsLatn && !compactIsShort) { 76 getResourceBundleKey("latn", CompactStyle.SHORT, compactType, resourceKey); 77 rb.getAllItemsWithFallbackNoFail(resourceKey.toString(), sink); 78 } 79 80 // The last fallback should be guaranteed to return data. 81 if (isEmpty) { 82 throw new ICUException("Could not load compact decimal data for locale " + locale); 83 } 84 } 85 86 /** Produces a string like "NumberElements/latn/patternsShort/decimalFormat". */ getResourceBundleKey( String nsName, CompactStyle compactStyle, CompactType compactType, StringBuilder sb)87 private static void getResourceBundleKey( 88 String nsName, 89 CompactStyle compactStyle, 90 CompactType compactType, 91 StringBuilder sb) { 92 sb.setLength(0); 93 sb.append("NumberElements/"); 94 sb.append(nsName); 95 sb.append(compactStyle == CompactStyle.SHORT ? "/patternsShort" : "/patternsLong"); 96 sb.append(compactType == CompactType.DECIMAL ? "/decimalFormat" : "/currencyFormat"); 97 } 98 99 /** Java-only method used by CLDR tooling. */ populate(Map<String, Map<String, String>> powersToPluralsToPatterns)100 public void populate(Map<String, Map<String, String>> powersToPluralsToPatterns) { 101 assert isEmpty; 102 for (Map.Entry<String, Map<String, String>> magnitudeEntry : powersToPluralsToPatterns 103 .entrySet()) { 104 byte magnitude = (byte) (magnitudeEntry.getKey().length() - 1); 105 for (Map.Entry<String, String> pluralEntry : magnitudeEntry.getValue().entrySet()) { 106 StandardPlural plural = StandardPlural.fromString(pluralEntry.getKey().toString()); 107 String patternString = pluralEntry.getValue().toString(); 108 patterns[getIndex(magnitude, plural)] = patternString; 109 int numZeros = countZeros(patternString); 110 if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun" 111 // Save the multiplier. 112 multipliers[magnitude] = (byte) (numZeros - magnitude - 1); 113 if (magnitude > largestMagnitude) { 114 largestMagnitude = magnitude; 115 } 116 isEmpty = false; 117 } 118 } 119 } 120 } 121 122 @Override getMultiplier(int magnitude)123 public int getMultiplier(int magnitude) { 124 if (magnitude < 0) { 125 return 0; 126 } 127 if (magnitude > largestMagnitude) { 128 magnitude = largestMagnitude; 129 } 130 return multipliers[magnitude]; 131 } 132 getPattern(int magnitude, StandardPlural plural)133 public String getPattern(int magnitude, StandardPlural plural) { 134 if (magnitude < 0) { 135 return null; 136 } 137 if (magnitude > largestMagnitude) { 138 magnitude = largestMagnitude; 139 } 140 String patternString = patterns[getIndex(magnitude, plural)]; 141 if (patternString == null && plural != StandardPlural.OTHER) { 142 // Fall back to "other" plural variant 143 patternString = patterns[getIndex(magnitude, StandardPlural.OTHER)]; 144 } 145 if (patternString == USE_FALLBACK) { // == is intended 146 // Return null if USE_FALLBACK is present 147 patternString = null; 148 } 149 return patternString; 150 } 151 getUniquePatterns(Set<String> output)152 public void getUniquePatterns(Set<String> output) { 153 assert output.isEmpty(); 154 // NOTE: In C++, this is done more manually with a UVector. 155 // In Java, we can take advantage of JDK HashSet. 156 output.addAll(Arrays.asList(patterns)); 157 output.remove(USE_FALLBACK); 158 output.remove(null); 159 } 160 161 private static final class CompactDataSink extends UResource.Sink { 162 163 CompactData data; 164 CompactDataSink(CompactData data)165 public CompactDataSink(CompactData data) { 166 this.data = data; 167 } 168 169 @Override put(UResource.Key key, UResource.Value value, boolean isRoot)170 public void put(UResource.Key key, UResource.Value value, boolean isRoot) { 171 // traverse into the table of powers of ten 172 UResource.Table powersOfTenTable = value.getTable(); 173 for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { 174 175 // Assumes that the keys are always of the form "10000" where the magnitude is the 176 // length of the key minus one. We expect magnitudes to be less than MAX_DIGITS. 177 byte magnitude = (byte) (key.length() - 1); 178 byte multiplier = data.multipliers[magnitude]; 179 assert magnitude < COMPACT_MAX_DIGITS; 180 181 // Iterate over the plural variants ("one", "other", etc) 182 UResource.Table pluralVariantsTable = value.getTable(); 183 for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { 184 185 // Skip this magnitude/plural if we already have it from a child locale. 186 // Note: This also skips USE_FALLBACK entries. 187 StandardPlural plural = StandardPlural.fromString(key.toString()); 188 if (data.patterns[getIndex(magnitude, plural)] != null) { 189 continue; 190 } 191 192 // The value "0" means that we need to use the default pattern and not fall back 193 // to parent locales. Example locale where this is relevant: 'it'. 194 String patternString = value.toString(); 195 if (patternString.equals("0")) { 196 patternString = USE_FALLBACK; 197 } 198 199 // Save the pattern string. We will parse it lazily. 200 data.patterns[getIndex(magnitude, plural)] = patternString; 201 202 // If necessary, compute the multiplier: the difference between the magnitude 203 // and the number of zeros in the pattern. 204 if (multiplier == 0) { 205 int numZeros = countZeros(patternString); 206 if (numZeros > 0) { // numZeros==0 in certain cases, like Somali "Kun" 207 multiplier = (byte) (numZeros - magnitude - 1); 208 } 209 } 210 } 211 212 // Save the multiplier. 213 if (data.multipliers[magnitude] == 0) { 214 data.multipliers[magnitude] = multiplier; 215 if (magnitude > data.largestMagnitude) { 216 data.largestMagnitude = magnitude; 217 } 218 data.isEmpty = false; 219 } else { 220 assert data.multipliers[magnitude] == multiplier; 221 } 222 } 223 } 224 } 225 226 private static final int getIndex(int magnitude, StandardPlural plural) { 227 return magnitude * StandardPlural.COUNT + plural.ordinal(); 228 } 229 230 private static final int countZeros(String patternString) { 231 // NOTE: This strategy for computing the number of zeros is a hack for efficiency. 232 // It could break if there are any 0s that aren't part of the main pattern. 233 int numZeros = 0; 234 for (int i = 0; i < patternString.length(); i++) { 235 if (patternString.charAt(i) == '0') { 236 numZeros++; 237 } else if (numZeros > 0) { 238 break; // zeros should always be contiguous 239 } 240 } 241 return numZeros; 242 } 243 } 244