LEFT | RIGHT |
(no file at all) | |
1 // © 2017 and later: Unicode, Inc. and others. | 1 // © 2017 and later: Unicode, Inc. and others. |
2 // License & terms of use: http://www.unicode.org/copyright.html#License | 2 // License & terms of use: http://www.unicode.org/copyright.html#License |
3 package com.ibm.icu.impl.number; | 3 package com.ibm.icu.impl.number; |
4 | 4 |
5 import java.util.EnumMap; | 5 import java.util.EnumMap; |
6 import java.util.Map; | 6 import java.util.Map; |
| 7 import java.util.MissingResourceException; |
7 | 8 |
8 import com.ibm.icu.impl.CurrencyData; | 9 import com.ibm.icu.impl.CurrencyData; |
9 import com.ibm.icu.impl.ICUData; | 10 import com.ibm.icu.impl.ICUData; |
10 import com.ibm.icu.impl.ICUResourceBundle; | 11 import com.ibm.icu.impl.ICUResourceBundle; |
11 import com.ibm.icu.impl.SimpleFormatterImpl; | 12 import com.ibm.icu.impl.SimpleFormatterImpl; |
12 import com.ibm.icu.impl.StandardPlural; | 13 import com.ibm.icu.impl.StandardPlural; |
13 import com.ibm.icu.impl.UResource; | 14 import com.ibm.icu.impl.UResource; |
14 import com.ibm.icu.number.NumberFormatter.UnitWidth; | 15 import com.ibm.icu.number.NumberFormatter.UnitWidth; |
15 import com.ibm.icu.text.NumberFormat; | 16 import com.ibm.icu.text.NumberFormat; |
16 import com.ibm.icu.text.PluralRules; | 17 import com.ibm.icu.text.PluralRules; |
17 import com.ibm.icu.util.Currency; | 18 import com.ibm.icu.util.Currency; |
18 import com.ibm.icu.util.ICUException; | 19 import com.ibm.icu.util.ICUException; |
19 import com.ibm.icu.util.MeasureUnit; | 20 import com.ibm.icu.util.MeasureUnit; |
20 import com.ibm.icu.util.ULocale; | 21 import com.ibm.icu.util.ULocale; |
21 import com.ibm.icu.util.UResourceBundle; | 22 import com.ibm.icu.util.UResourceBundle; |
22 | 23 |
23 public class LongNameHandler implements MicroPropsGenerator { | 24 public class LongNameHandler implements MicroPropsGenerator { |
24 | 25 |
| 26 private static final int DNAM_INDEX = StandardPlural.COUNT; |
| 27 private static final int PER_INDEX = StandardPlural.COUNT + 1; |
| 28 private static final int ARRAY_LENGTH = StandardPlural.COUNT + 2; |
| 29 |
| 30 private static int getIndex(String pluralKeyword) { |
| 31 // pluralKeyword can also be "dnam" or "per" |
| 32 if (pluralKeyword.equals("dnam")) { |
| 33 return DNAM_INDEX; |
| 34 } else if (pluralKeyword.equals("per")) { |
| 35 return PER_INDEX; |
| 36 } else { |
| 37 return StandardPlural.fromString(pluralKeyword).ordinal(); |
| 38 } |
| 39 } |
| 40 |
| 41 private static String getWithPlural(String[] strings, StandardPlural plural)
{ |
| 42 String result = strings[plural.ordinal()]; |
| 43 if (result == null) { |
| 44 result = strings[StandardPlural.OTHER.ordinal()]; |
| 45 } |
| 46 if (result == null) { |
| 47 // There should always be data in the "other" plural variant. |
| 48 throw new ICUException("Could not find data in 'other' plural varian
t"); |
| 49 } |
| 50 return result; |
| 51 } |
| 52 |
25 ////////////////////////// | 53 ////////////////////////// |
26 /// BEGIN DATA LOADING /// | 54 /// BEGIN DATA LOADING /// |
27 ////////////////////////// | 55 ////////////////////////// |
28 | 56 |
29 private static final class PluralTableSink extends UResource.Sink { | 57 private static final class PluralTableSink extends UResource.Sink { |
30 | 58 |
31 Map<StandardPlural, String> output; | 59 String[] outArray; |
32 | 60 |
33 public PluralTableSink(Map<StandardPlural, String> output) { | 61 public PluralTableSink(String[] outArray) { |
34 this.output = output; | 62 this.outArray = outArray; |
35 } | 63 } |
36 | 64 |
37 @Override | 65 @Override |
38 public void put(UResource.Key key, UResource.Value value, boolean noFall
back) { | 66 public void put(UResource.Key key, UResource.Value value, boolean noFall
back) { |
39 UResource.Table pluralsTable = value.getTable(); | 67 UResource.Table pluralsTable = value.getTable(); |
40 for (int i = 0; pluralsTable.getKeyAndValue(i, key, value); ++i) { | 68 for (int i = 0; pluralsTable.getKeyAndValue(i, key, value); ++i) { |
41 if (key.contentEquals("dnam") || key.contentEquals("per")) { | 69 int index = getIndex(key.toString()); |
42 continue; | 70 if (outArray[index] != null) { |
43 } | |
44 StandardPlural plural = StandardPlural.fromString(key); | |
45 if (output.containsKey(plural)) { | |
46 continue; | 71 continue; |
47 } | 72 } |
48 String formatString = value.getString(); | 73 String formatString = value.getString(); |
49 output.put(plural, formatString); | 74 outArray[index] = formatString; |
50 } | 75 } |
51 } | 76 } |
52 } | 77 } |
53 | 78 |
54 private static void getMeasureData(ULocale locale, MeasureUnit unit, UnitWid
th width, | 79 // NOTE: outArray MUST have at least ARRAY_LENGTH entries. No bounds checkin
g is performed. |
55 Map<StandardPlural, String> output) { | 80 |
56 PluralTableSink sink = new PluralTableSink(output); | 81 private static void getMeasureData( |
| 82 ULocale locale, |
| 83 MeasureUnit unit, |
| 84 UnitWidth width, |
| 85 String[] outArray) { |
| 86 PluralTableSink sink = new PluralTableSink(outArray); |
57 ICUResourceBundle resource; | 87 ICUResourceBundle resource; |
58 resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData
.ICU_UNIT_BASE_NAME, locale); | 88 resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData
.ICU_UNIT_BASE_NAME, |
| 89 locale); |
59 StringBuilder key = new StringBuilder(); | 90 StringBuilder key = new StringBuilder(); |
60 key.append("units"); | 91 key.append("units"); |
61 if (width == UnitWidth.NARROW) { | 92 if (width == UnitWidth.NARROW) { |
62 key.append("Narrow"); | 93 key.append("Narrow"); |
63 } else if (width == UnitWidth.SHORT) { | 94 } else if (width == UnitWidth.SHORT) { |
64 key.append("Short"); | 95 key.append("Short"); |
65 } | 96 } |
66 key.append("/"); | 97 key.append("/"); |
67 key.append(unit.getType()); | 98 key.append(unit.getType()); |
68 key.append("/"); | 99 key.append("/"); |
69 key.append(unit.getSubtype()); | 100 key.append(unit.getSubtype()); |
70 resource.getAllItemsWithFallback(key.toString(), sink); | 101 try { |
71 } | 102 resource.getAllItemsWithFallback(key.toString(), sink); |
72 | 103 } catch (MissingResourceException e) { |
73 private static void getCurrencyLongNameData(ULocale locale, Currency currenc
y, Map<StandardPlural, String> output) { | 104 throw new IllegalArgumentException("No data for unit " + unit + ", w
idth " + width, e); |
| 105 } |
| 106 } |
| 107 |
| 108 private static void getCurrencyLongNameData(ULocale locale, Currency currenc
y, String[] outArray) { |
74 // In ICU4J, this method gets a CurrencyData from CurrencyData.provider. | 109 // In ICU4J, this method gets a CurrencyData from CurrencyData.provider. |
75 // TODO(ICU4J): Implement this without going through CurrencyData, like
in ICU4C? | 110 // TODO(ICU4J): Implement this without going through CurrencyData, like
in ICU4C? |
76 Map<String, String> data = CurrencyData.provider.getInstance(locale, tru
e).getUnitPatterns(); | 111 Map<String, String> data = CurrencyData.provider.getInstance(locale, tru
e).getUnitPatterns(); |
77 for (Map.Entry<String, String> e : data.entrySet()) { | 112 for (Map.Entry<String, String> e : data.entrySet()) { |
78 String pluralKeyword = e.getKey(); | 113 String pluralKeyword = e.getKey(); |
79 StandardPlural plural = StandardPlural.fromString(e.getKey()); | 114 int index = getIndex(pluralKeyword); |
80 String longName = currency.getName(locale, Currency.PLURAL_LONG_NAME
, pluralKeyword, null); | 115 String longName = currency.getName(locale, Currency.PLURAL_LONG_NAME
, pluralKeyword, null); |
81 String simpleFormat = e.getValue(); | 116 String simpleFormat = e.getValue(); |
82 // Example pattern from data: "{0} {1}" | 117 // Example pattern from data: "{0} {1}" |
83 // Example output after find-and-replace: "{0} US dollars" | 118 // Example output after find-and-replace: "{0} US dollars" |
84 simpleFormat = simpleFormat.replace("{1}", longName); | 119 simpleFormat = simpleFormat.replace("{1}", longName); |
85 // String compiled = SimpleFormatterImpl.compileToStringMinMaxArgume
nts(simpleFormat, sb, 1, 1); | 120 // String compiled = SimpleFormatterImpl.compileToStringMinMaxArgume
nts(simpleFormat, sb, 1, |
| 121 // 1); |
86 // SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY,
false); | 122 // SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY,
false); |
87 output.put(plural, simpleFormat); | 123 outArray[index] = simpleFormat; |
| 124 } |
| 125 } |
| 126 |
| 127 private static String getPerUnitFormat(ULocale locale, UnitWidth width) { |
| 128 ICUResourceBundle resource; |
| 129 resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData
.ICU_UNIT_BASE_NAME, |
| 130 locale); |
| 131 StringBuilder key = new StringBuilder(); |
| 132 key.append("units"); |
| 133 if (width == UnitWidth.NARROW) { |
| 134 key.append("Narrow"); |
| 135 } else if (width == UnitWidth.SHORT) { |
| 136 key.append("Short"); |
| 137 } |
| 138 key.append("/compound/per"); |
| 139 try { |
| 140 return resource.getStringWithFallback(key.toString()); |
| 141 } catch (MissingResourceException e) { |
| 142 throw new IllegalArgumentException( |
| 143 "Could not find x-per-y format for " + locale + ", width " +
width); |
88 } | 144 } |
89 } | 145 } |
90 | 146 |
91 //////////////////////// | 147 //////////////////////// |
92 /// END DATA LOADING /// | 148 /// END DATA LOADING /// |
93 //////////////////////// | 149 //////////////////////// |
94 | 150 |
95 private final Map<StandardPlural, SimpleModifier> modifiers; | 151 private final Map<StandardPlural, SimpleModifier> modifiers; |
96 private final PluralRules rules; | 152 private final PluralRules rules; |
97 private final MicroPropsGenerator parent; | 153 private final MicroPropsGenerator parent; |
98 | 154 |
99 private LongNameHandler(Map<StandardPlural, SimpleModifier> modifiers, Plura
lRules rules, | 155 private LongNameHandler( |
| 156 Map<StandardPlural, SimpleModifier> modifiers, |
| 157 PluralRules rules, |
100 MicroPropsGenerator parent) { | 158 MicroPropsGenerator parent) { |
101 this.modifiers = modifiers; | 159 this.modifiers = modifiers; |
102 this.rules = rules; | 160 this.rules = rules; |
103 this.parent = parent; | 161 this.parent = parent; |
104 } | 162 } |
105 | 163 |
106 public static LongNameHandler forCurrencyLongNames(ULocale locale, Currency
currency, PluralRules rules, | 164 public static LongNameHandler forCurrencyLongNames( |
107 MicroPropsGenerator parent) { | 165 ULocale locale, |
108 Map<StandardPlural, String> simpleFormats = new EnumMap<StandardPlural,
String>(StandardPlural.class); | 166 Currency currency, |
| 167 PluralRules rules, |
| 168 MicroPropsGenerator parent) { |
| 169 String[] simpleFormats = new String[ARRAY_LENGTH]; |
109 getCurrencyLongNameData(locale, currency, simpleFormats); | 170 getCurrencyLongNameData(locale, currency, simpleFormats); |
110 // TODO(ICU4J): Reduce the number of object creations here? | 171 // TODO(ICU4J): Reduce the number of object creations here? |
111 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlur
al, SimpleModifier>( | 172 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlur
al, SimpleModifier>( |
112 StandardPlural.class); | 173 StandardPlural.class); |
113 simpleFormatsToModifiers(simpleFormats, null, modifiers); | 174 simpleFormatsToModifiers(simpleFormats, null, modifiers); |
114 return new LongNameHandler(modifiers, rules, parent); | 175 return new LongNameHandler(modifiers, rules, parent); |
115 } | 176 } |
116 | 177 |
117 public static LongNameHandler forMeasureUnit(ULocale locale, MeasureUnit uni
t, UnitWidth width, PluralRules rules, | 178 public static LongNameHandler forMeasureUnit( |
118 MicroPropsGenerator parent) { | 179 ULocale locale, |
119 Map<StandardPlural, String> simpleFormats = new EnumMap<StandardPlural,
String>(StandardPlural.class); | 180 MeasureUnit unit, |
| 181 MeasureUnit perUnit, |
| 182 UnitWidth width, |
| 183 PluralRules rules, |
| 184 MicroPropsGenerator parent) { |
| 185 if (perUnit != null) { |
| 186 // Compound unit: first try to simplify (e.g., meters per second is
its own unit). |
| 187 MeasureUnit simplified = MeasureUnit.resolveUnitPerUnit(unit, perUni
t); |
| 188 if (simplified != null) { |
| 189 unit = simplified; |
| 190 } else { |
| 191 // No simplified form is available. |
| 192 return forCompoundUnit(locale, unit, perUnit, width, rules, pare
nt); |
| 193 } |
| 194 } |
| 195 |
| 196 String[] simpleFormats = new String[ARRAY_LENGTH]; |
120 getMeasureData(locale, unit, width, simpleFormats); | 197 getMeasureData(locale, unit, width, simpleFormats); |
121 // TODO: What field to use for units? | 198 // TODO: What field to use for units? |
122 // TODO(ICU4J): Reduce the number of object creations here? | 199 // TODO(ICU4J): Reduce the number of object creations here? |
123 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlur
al, SimpleModifier>( | 200 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlur
al, SimpleModifier>( |
124 StandardPlural.class); | 201 StandardPlural.class); |
125 simpleFormatsToModifiers(simpleFormats, null, modifiers); | 202 simpleFormatsToModifiers(simpleFormats, null, modifiers); |
126 return new LongNameHandler(modifiers, rules, parent); | 203 return new LongNameHandler(modifiers, rules, parent); |
127 } | 204 } |
128 | 205 |
129 private static void simpleFormatsToModifiers(Map<StandardPlural, String> sim
pleFormats, NumberFormat.Field field, | 206 private static LongNameHandler forCompoundUnit( |
| 207 ULocale locale, |
| 208 MeasureUnit unit, |
| 209 MeasureUnit perUnit, |
| 210 UnitWidth width, |
| 211 PluralRules rules, |
| 212 MicroPropsGenerator parent) { |
| 213 String[] primaryData = new String[ARRAY_LENGTH]; |
| 214 getMeasureData(locale, unit, width, primaryData); |
| 215 String[] secondaryData = new String[ARRAY_LENGTH]; |
| 216 getMeasureData(locale, perUnit, width, secondaryData); |
| 217 String perUnitFormat; |
| 218 if (secondaryData[PER_INDEX] != null) { |
| 219 perUnitFormat = secondaryData[PER_INDEX]; |
| 220 } else { |
| 221 String rawPerUnitFormat = getPerUnitFormat(locale, width); |
| 222 // rawPerUnitFormat is something like "{0}/{1}"; we need to substitu
te in the secondary unit. |
| 223 // TODO: Lots of thrashing. Improve? |
| 224 StringBuilder sb = new StringBuilder(); |
| 225 String compiled = SimpleFormatterImpl |
| 226 .compileToStringMinMaxArguments(rawPerUnitFormat, sb, 2, 2); |
| 227 String secondaryFormat = getWithPlural(secondaryData, StandardPlural
.ONE); |
| 228 String secondaryCompiled = SimpleFormatterImpl |
| 229 .compileToStringMinMaxArguments(secondaryFormat, sb, 1, 1); |
| 230 String secondaryString = SimpleFormatterImpl.getTextWithNoArguments(
secondaryCompiled) |
| 231 .trim(); |
| 232 perUnitFormat = SimpleFormatterImpl.formatCompiledPattern(compiled,
"{0}", secondaryString); |
| 233 } |
| 234 // TODO: What field to use for units? |
| 235 Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlur
al, SimpleModifier>( |
| 236 StandardPlural.class); |
| 237 multiSimpleFormatsToModifiers(primaryData, perUnitFormat, null, modifier
s); |
| 238 return new LongNameHandler(modifiers, rules, parent); |
| 239 } |
| 240 |
| 241 private static void simpleFormatsToModifiers( |
| 242 String[] simpleFormats, |
| 243 NumberFormat.Field field, |
130 Map<StandardPlural, SimpleModifier> output) { | 244 Map<StandardPlural, SimpleModifier> output) { |
131 StringBuilder sb = new StringBuilder(); | 245 StringBuilder sb = new StringBuilder(); |
132 for (StandardPlural plural : StandardPlural.VALUES) { | 246 for (StandardPlural plural : StandardPlural.VALUES) { |
133 String simpleFormat = simpleFormats.get(plural); | 247 String simpleFormat = getWithPlural(simpleFormats, plural); |
134 if (simpleFormat == null) { | |
135 simpleFormat = simpleFormats.get(StandardPlural.OTHER); | |
136 } | |
137 if (simpleFormat == null) { | |
138 // There should always be data in the "other" plural variant. | |
139 throw new ICUException("Could not find data in 'other' plural va
riant with field " + field); | |
140 } | |
141 String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments
(simpleFormat, sb, 1, 1); | 248 String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments
(simpleFormat, sb, 1, 1); |
142 output.put(plural, new SimpleModifier(compiled, null, false)); | 249 output.put(plural, new SimpleModifier(compiled, field, false)); |
| 250 } |
| 251 } |
| 252 |
| 253 private static void multiSimpleFormatsToModifiers( |
| 254 String[] leadFormats, |
| 255 String trailFormat, |
| 256 NumberFormat.Field field, |
| 257 Map<StandardPlural, SimpleModifier> output) { |
| 258 StringBuilder sb = new StringBuilder(); |
| 259 String trailCompiled = SimpleFormatterImpl.compileToStringMinMaxArgument
s(trailFormat, sb, 1, 1); |
| 260 for (StandardPlural plural : StandardPlural.VALUES) { |
| 261 String leadFormat = getWithPlural(leadFormats, plural); |
| 262 String compoundFormat = SimpleFormatterImpl.formatCompiledPattern(tr
ailCompiled, leadFormat); |
| 263 String compoundCompiled = SimpleFormatterImpl |
| 264 .compileToStringMinMaxArguments(compoundFormat, sb, 1, 1); |
| 265 output.put(plural, new SimpleModifier(compoundCompiled, field, false
)); |
143 } | 266 } |
144 } | 267 } |
145 | 268 |
146 @Override | 269 @Override |
147 public MicroProps processQuantity(DecimalQuantity quantity) { | 270 public MicroProps processQuantity(DecimalQuantity quantity) { |
148 MicroProps micros = parent.processQuantity(quantity); | 271 MicroProps micros = parent.processQuantity(quantity); |
149 // TODO: Avoid the copy here? | 272 // TODO: Avoid the copy here? |
150 DecimalQuantity copy = quantity.createCopy(); | 273 DecimalQuantity copy = quantity.createCopy(); |
151 micros.rounding.apply(copy); | 274 micros.rounding.apply(copy); |
152 micros.modOuter = modifiers.get(copy.getStandardPlural(rules)); | 275 micros.modOuter = modifiers.get(copy.getStandardPlural(rules)); |
153 return micros; | 276 return micros; |
154 } | 277 } |
155 } | 278 } |
LEFT | RIGHT |