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.EnumMap; 7 import java.util.Map; 8 import java.util.MissingResourceException; 9 10 import ohos.global.icu.impl.CurrencyData; 11 import ohos.global.icu.impl.ICUData; 12 import ohos.global.icu.impl.ICUResourceBundle; 13 import ohos.global.icu.impl.SimpleFormatterImpl; 14 import ohos.global.icu.impl.StandardPlural; 15 import ohos.global.icu.impl.UResource; 16 import ohos.global.icu.impl.number.Modifier.Signum; 17 import ohos.global.icu.number.NumberFormatter.UnitWidth; 18 import ohos.global.icu.text.NumberFormat; 19 import ohos.global.icu.text.PluralRules; 20 import ohos.global.icu.util.Currency; 21 import ohos.global.icu.util.ICUException; 22 import ohos.global.icu.util.MeasureUnit; 23 import ohos.global.icu.util.ULocale; 24 import ohos.global.icu.util.UResourceBundle; 25 26 /** 27 * @hide exposed on OHOS 28 */ 29 public class LongNameHandler implements MicroPropsGenerator, ModifierStore { 30 31 private static final int DNAM_INDEX = StandardPlural.COUNT; 32 private static final int PER_INDEX = StandardPlural.COUNT + 1; 33 private static final int ARRAY_LENGTH = StandardPlural.COUNT + 2; 34 getIndex(String pluralKeyword)35 private static int getIndex(String pluralKeyword) { 36 // pluralKeyword can also be "dnam" or "per" 37 if (pluralKeyword.equals("dnam")) { 38 return DNAM_INDEX; 39 } else if (pluralKeyword.equals("per")) { 40 return PER_INDEX; 41 } else { 42 return StandardPlural.fromString(pluralKeyword).ordinal(); 43 } 44 } 45 getWithPlural(String[] strings, StandardPlural plural)46 private static String getWithPlural(String[] strings, StandardPlural plural) { 47 String result = strings[plural.ordinal()]; 48 if (result == null) { 49 result = strings[StandardPlural.OTHER.ordinal()]; 50 } 51 if (result == null) { 52 // There should always be data in the "other" plural variant. 53 throw new ICUException("Could not find data in 'other' plural variant"); 54 } 55 return result; 56 } 57 58 ////////////////////////// 59 /// BEGIN DATA LOADING /// 60 ////////////////////////// 61 62 private static final class PluralTableSink extends UResource.Sink { 63 64 String[] outArray; 65 PluralTableSink(String[] outArray)66 public PluralTableSink(String[] outArray) { 67 this.outArray = outArray; 68 } 69 70 @Override put(UResource.Key key, UResource.Value value, boolean noFallback)71 public void put(UResource.Key key, UResource.Value value, boolean noFallback) { 72 UResource.Table pluralsTable = value.getTable(); 73 for (int i = 0; pluralsTable.getKeyAndValue(i, key, value); ++i) { 74 int index = getIndex(key.toString()); 75 if (outArray[index] != null) { 76 continue; 77 } 78 String formatString = value.getString(); 79 outArray[index] = formatString; 80 } 81 } 82 } 83 84 // NOTE: outArray MUST have at least ARRAY_LENGTH entries. No bounds checking is performed. 85 getMeasureData( ULocale locale, MeasureUnit unit, UnitWidth width, String[] outArray)86 private static void getMeasureData( 87 ULocale locale, 88 MeasureUnit unit, 89 UnitWidth width, 90 String[] outArray) { 91 PluralTableSink sink = new PluralTableSink(outArray); 92 ICUResourceBundle resource; 93 resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, 94 locale); 95 StringBuilder key = new StringBuilder(); 96 key.append("units"); 97 if (width == UnitWidth.NARROW) { 98 key.append("Narrow"); 99 } else if (width == UnitWidth.SHORT) { 100 key.append("Short"); 101 } 102 key.append("/"); 103 key.append(unit.getType()); 104 key.append("/"); 105 106 // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ... 107 // TODO(ICU-20400): Get duration-*-person data properly with aliases. 108 if (unit.getSubtype().endsWith("-person")) { 109 key.append(unit.getSubtype(), 0, unit.getSubtype().length() - 7); 110 } else { 111 key.append(unit.getSubtype()); 112 } 113 114 try { 115 resource.getAllItemsWithFallback(key.toString(), sink); 116 } catch (MissingResourceException e) { 117 throw new IllegalArgumentException("No data for unit " + unit + ", width " + width, e); 118 } 119 } 120 getCurrencyLongNameData(ULocale locale, Currency currency, String[] outArray)121 private static void getCurrencyLongNameData(ULocale locale, Currency currency, String[] outArray) { 122 // In ICU4J, this method gets a CurrencyData from CurrencyData.provider. 123 // TODO(ICU4J): Implement this without going through CurrencyData, like in ICU4C? 124 Map<String, String> data = CurrencyData.provider.getInstance(locale, true).getUnitPatterns(); 125 for (Map.Entry<String, String> e : data.entrySet()) { 126 String pluralKeyword = e.getKey(); 127 int index = getIndex(pluralKeyword); 128 String longName = currency.getName(locale, Currency.PLURAL_LONG_NAME, pluralKeyword, null); 129 String simpleFormat = e.getValue(); 130 // Example pattern from data: "{0} {1}" 131 // Example output after find-and-replace: "{0} US dollars" 132 simpleFormat = simpleFormat.replace("{1}", longName); 133 // String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 134 // 1); 135 // SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY, false); 136 outArray[index] = simpleFormat; 137 } 138 } 139 getPerUnitFormat(ULocale locale, UnitWidth width)140 private static String getPerUnitFormat(ULocale locale, UnitWidth width) { 141 ICUResourceBundle resource; 142 resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, 143 locale); 144 StringBuilder key = new StringBuilder(); 145 key.append("units"); 146 if (width == UnitWidth.NARROW) { 147 key.append("Narrow"); 148 } else if (width == UnitWidth.SHORT) { 149 key.append("Short"); 150 } 151 key.append("/compound/per"); 152 try { 153 return resource.getStringWithFallback(key.toString()); 154 } catch (MissingResourceException e) { 155 throw new IllegalArgumentException( 156 "Could not find x-per-y format for " + locale + ", width " + width); 157 } 158 } 159 160 //////////////////////// 161 /// END DATA LOADING /// 162 //////////////////////// 163 164 private final Map<StandardPlural, SimpleModifier> modifiers; 165 private final PluralRules rules; 166 private final MicroPropsGenerator parent; 167 LongNameHandler( Map<StandardPlural, SimpleModifier> modifiers, PluralRules rules, MicroPropsGenerator parent)168 private LongNameHandler( 169 Map<StandardPlural, SimpleModifier> modifiers, 170 PluralRules rules, 171 MicroPropsGenerator parent) { 172 this.modifiers = modifiers; 173 this.rules = rules; 174 this.parent = parent; 175 } 176 getUnitDisplayName(ULocale locale, MeasureUnit unit, UnitWidth width)177 public static String getUnitDisplayName(ULocale locale, MeasureUnit unit, UnitWidth width) { 178 String[] measureData = new String[ARRAY_LENGTH]; 179 getMeasureData(locale, unit, width, measureData); 180 return measureData[DNAM_INDEX]; 181 } 182 forCurrencyLongNames( ULocale locale, Currency currency, PluralRules rules, MicroPropsGenerator parent)183 public static LongNameHandler forCurrencyLongNames( 184 ULocale locale, 185 Currency currency, 186 PluralRules rules, 187 MicroPropsGenerator parent) { 188 String[] simpleFormats = new String[ARRAY_LENGTH]; 189 getCurrencyLongNameData(locale, currency, simpleFormats); 190 // TODO(ICU4J): Reduce the number of object creations here? 191 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<>( 192 StandardPlural.class); 193 LongNameHandler result = new LongNameHandler(modifiers, rules, parent); 194 result.simpleFormatsToModifiers(simpleFormats, NumberFormat.Field.CURRENCY); 195 return result; 196 } 197 forMeasureUnit( ULocale locale, MeasureUnit unit, MeasureUnit perUnit, UnitWidth width, PluralRules rules, MicroPropsGenerator parent)198 public static LongNameHandler forMeasureUnit( 199 ULocale locale, 200 MeasureUnit unit, 201 MeasureUnit perUnit, 202 UnitWidth width, 203 PluralRules rules, 204 MicroPropsGenerator parent) { 205 if (perUnit != null) { 206 // Compound unit: first try to simplify (e.g., meters per second is its own unit). 207 MeasureUnit simplified = MeasureUnit.resolveUnitPerUnit(unit, perUnit); 208 if (simplified != null) { 209 unit = simplified; 210 } else { 211 // No simplified form is available. 212 return forCompoundUnit(locale, unit, perUnit, width, rules, parent); 213 } 214 } 215 216 String[] simpleFormats = new String[ARRAY_LENGTH]; 217 getMeasureData(locale, unit, width, simpleFormats); 218 // TODO(ICU4J): Reduce the number of object creations here? 219 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<>( 220 StandardPlural.class); 221 LongNameHandler result = new LongNameHandler(modifiers, rules, parent); 222 result.simpleFormatsToModifiers(simpleFormats, NumberFormat.Field.MEASURE_UNIT); 223 return result; 224 } 225 forCompoundUnit( ULocale locale, MeasureUnit unit, MeasureUnit perUnit, UnitWidth width, PluralRules rules, MicroPropsGenerator parent)226 private static LongNameHandler forCompoundUnit( 227 ULocale locale, 228 MeasureUnit unit, 229 MeasureUnit perUnit, 230 UnitWidth width, 231 PluralRules rules, 232 MicroPropsGenerator parent) { 233 String[] primaryData = new String[ARRAY_LENGTH]; 234 getMeasureData(locale, unit, width, primaryData); 235 String[] secondaryData = new String[ARRAY_LENGTH]; 236 getMeasureData(locale, perUnit, width, secondaryData); 237 String perUnitFormat; 238 if (secondaryData[PER_INDEX] != null) { 239 perUnitFormat = secondaryData[PER_INDEX]; 240 } else { 241 String rawPerUnitFormat = getPerUnitFormat(locale, width); 242 // rawPerUnitFormat is something like "{0}/{1}"; we need to substitute in the secondary unit. 243 // TODO: Lots of thrashing. Improve? 244 StringBuilder sb = new StringBuilder(); 245 String compiled = SimpleFormatterImpl 246 .compileToStringMinMaxArguments(rawPerUnitFormat, sb, 2, 2); 247 String secondaryFormat = getWithPlural(secondaryData, StandardPlural.ONE); 248 249 // Some "one" pattern may not contain "{0}". For example in "ar" or "ne" locale. 250 String secondaryCompiled = SimpleFormatterImpl 251 .compileToStringMinMaxArguments(secondaryFormat, sb, 0, 1); 252 String secondaryString = SimpleFormatterImpl.getTextWithNoArguments(secondaryCompiled) 253 .trim(); 254 perUnitFormat = SimpleFormatterImpl.formatCompiledPattern(compiled, "{0}", secondaryString); 255 } 256 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<>( 257 StandardPlural.class); 258 LongNameHandler result = new LongNameHandler(modifiers, rules, parent); 259 result.multiSimpleFormatsToModifiers(primaryData, perUnitFormat, NumberFormat.Field.MEASURE_UNIT); 260 return result; 261 } 262 simpleFormatsToModifiers( String[] simpleFormats, NumberFormat.Field field)263 private void simpleFormatsToModifiers( 264 String[] simpleFormats, 265 NumberFormat.Field field) { 266 StringBuilder sb = new StringBuilder(); 267 for (StandardPlural plural : StandardPlural.VALUES) { 268 String simpleFormat = getWithPlural(simpleFormats, plural); 269 String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 0, 1); 270 Modifier.Parameters parameters = new Modifier.Parameters(); 271 parameters.obj = this; 272 parameters.signum = null;// Signum ignored 273 parameters.plural = plural; 274 modifiers.put(plural, new SimpleModifier(compiled, field, false, parameters)); 275 } 276 } 277 multiSimpleFormatsToModifiers( String[] leadFormats, String trailFormat, NumberFormat.Field field)278 private void multiSimpleFormatsToModifiers( 279 String[] leadFormats, 280 String trailFormat, 281 NumberFormat.Field field) { 282 StringBuilder sb = new StringBuilder(); 283 String trailCompiled = SimpleFormatterImpl.compileToStringMinMaxArguments(trailFormat, sb, 1, 1); 284 for (StandardPlural plural : StandardPlural.VALUES) { 285 String leadFormat = getWithPlural(leadFormats, plural); 286 String compoundFormat = SimpleFormatterImpl.formatCompiledPattern(trailCompiled, leadFormat); 287 String compoundCompiled = SimpleFormatterImpl 288 .compileToStringMinMaxArguments(compoundFormat, sb, 0, 1); 289 Modifier.Parameters parameters = new Modifier.Parameters(); 290 parameters.obj = this; 291 parameters.signum = null; // Signum ignored 292 parameters.plural = plural; 293 modifiers.put(plural, new SimpleModifier(compoundCompiled, field, false, parameters)); 294 } 295 } 296 297 @Override processQuantity(DecimalQuantity quantity)298 public MicroProps processQuantity(DecimalQuantity quantity) { 299 MicroProps micros = parent.processQuantity(quantity); 300 StandardPlural pluralForm = RoundingUtils.getPluralSafe(micros.rounder, rules, quantity); 301 micros.modOuter = modifiers.get(pluralForm); 302 return micros; 303 } 304 305 @Override getModifier(Signum signum, StandardPlural plural)306 public Modifier getModifier(Signum signum, StandardPlural plural) { 307 // Signum ignored 308 return modifiers.get(plural); 309 } 310 } 311