• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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