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.number; | 3 package com.ibm.icu.number; |
4 | 4 |
5 import java.math.BigDecimal; | 5 import java.math.BigDecimal; |
6 import java.math.MathContext; | 6 import java.math.MathContext; |
7 | 7 |
8 import com.ibm.icu.impl.StandardPlural; | |
9 import com.ibm.icu.impl.number.AffixPatternProvider; | 8 import com.ibm.icu.impl.number.AffixPatternProvider; |
10 import com.ibm.icu.impl.number.AffixUtils; | 9 import com.ibm.icu.impl.number.CurrencyPluralInfoAffixProvider; |
11 import com.ibm.icu.impl.number.CustomSymbolCurrency; | 10 import com.ibm.icu.impl.number.CustomSymbolCurrency; |
12 import com.ibm.icu.impl.number.DecimalFormatProperties; | 11 import com.ibm.icu.impl.number.DecimalFormatProperties; |
13 import com.ibm.icu.impl.number.MacroProps; | 12 import com.ibm.icu.impl.number.MacroProps; |
14 import com.ibm.icu.impl.number.MultiplierImpl; | 13 import com.ibm.icu.impl.number.MultiplierImpl; |
15 import com.ibm.icu.impl.number.Padder; | 14 import com.ibm.icu.impl.number.Padder; |
16 import com.ibm.icu.impl.number.PatternStringParser; | 15 import com.ibm.icu.impl.number.PatternStringParser; |
17 import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo; | 16 import com.ibm.icu.impl.number.PropertiesAffixPatternProvider; |
18 import com.ibm.icu.impl.number.RoundingUtils; | 17 import com.ibm.icu.impl.number.RoundingUtils; |
19 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay; | 18 import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay; |
20 import com.ibm.icu.number.NumberFormatter.SignDisplay; | 19 import com.ibm.icu.number.NumberFormatter.SignDisplay; |
21 import com.ibm.icu.number.Rounder.FractionRounderImpl; | 20 import com.ibm.icu.number.Rounder.FractionRounderImpl; |
22 import com.ibm.icu.number.Rounder.IncrementRounderImpl; | 21 import com.ibm.icu.number.Rounder.IncrementRounderImpl; |
23 import com.ibm.icu.number.Rounder.SignificantRounderImpl; | 22 import com.ibm.icu.number.Rounder.SignificantRounderImpl; |
24 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; | 23 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; |
25 import com.ibm.icu.text.CurrencyPluralInfo; | |
26 import com.ibm.icu.text.DecimalFormatSymbols; | 24 import com.ibm.icu.text.DecimalFormatSymbols; |
27 import com.ibm.icu.util.Currency; | 25 import com.ibm.icu.util.Currency; |
28 import com.ibm.icu.util.Currency.CurrencyUsage; | 26 import com.ibm.icu.util.Currency.CurrencyUsage; |
29 import com.ibm.icu.util.ULocale; | 27 import com.ibm.icu.util.ULocale; |
30 | 28 |
31 /** | 29 /** |
32 * <p> | 30 * <p> |
33 * This class, as well as NumberFormatterImpl, could go into the impl package, b
ut they depend on too many | 31 * This class, as well as NumberFormatterImpl, could go into the impl package, b
ut they depend on too |
34 * package-private members of the public APIs. | 32 * many package-private members of the public APIs. |
35 */ | 33 */ |
36 final class NumberPropertyMapper { | 34 final class NumberPropertyMapper { |
37 | 35 |
38 /** Convenience method to create a NumberFormatter directly from Properties.
*/ | 36 /** Convenience method to create a NumberFormatter directly from Properties.
*/ |
39 public static UnlocalizedNumberFormatter create(DecimalFormatProperties prop
erties, DecimalFormatSymbols symbols) { | 37 public static UnlocalizedNumberFormatter create( |
| 38 DecimalFormatProperties properties, |
| 39 DecimalFormatSymbols symbols) { |
40 MacroProps macros = oldToNew(properties, symbols, null); | 40 MacroProps macros = oldToNew(properties, symbols, null); |
41 return NumberFormatter.with().macros(macros); | 41 return NumberFormatter.with().macros(macros); |
42 } | 42 } |
43 | 43 |
44 /** | 44 /** |
45 * Convenience method to create a NumberFormatter directly from a pattern st
ring. Something like this could become | 45 * Convenience method to create a NumberFormatter directly from a pattern st
ring. Something like this |
46 * public API if there is demand. | 46 * could become public API if there is demand. |
47 */ | 47 */ |
48 public static UnlocalizedNumberFormatter create(String pattern, DecimalForma
tSymbols symbols) { | 48 public static UnlocalizedNumberFormatter create(String pattern, DecimalForma
tSymbols symbols) { |
49 DecimalFormatProperties properties = PatternStringParser.parseToProperti
es(pattern); | 49 DecimalFormatProperties properties = PatternStringParser.parseToProperti
es(pattern); |
50 return create(properties, symbols); | 50 return create(properties, symbols); |
51 } | 51 } |
52 | 52 |
53 /** | 53 /** |
54 * Creates a new {@link MacroProps} object based on the content of a {@link
DecimalFormatProperties} object. In | 54 * Creates a new {@link MacroProps} object based on the content of a {@link
DecimalFormatProperties} |
55 * other words, maps Properties to MacroProps. This function is used by the
JDK-compatibility API to call into the | 55 * object. In other words, maps Properties to MacroProps. This function is u
sed by the |
56 * ICU 60 fluent number formatting pipeline. | 56 * JDK-compatibility API to call into the ICU 60 fluent number formatting pi
peline. |
57 * | 57 * |
58 * @param properties | 58 * @param properties |
59 * The property bag to be mapped. | 59 * The property bag to be mapped. |
60 * @param symbols | 60 * @param symbols |
61 * The symbols associated with the property bag. | 61 * The symbols associated with the property bag. |
62 * @param exportedProperties | 62 * @param exportedProperties |
63 * A property bag in which to store validated properties. | 63 * A property bag in which to store validated properties. Used by
some DecimalFormat |
| 64 * getters. |
64 * @return A new MacroProps containing all of the information in the Propert
ies. | 65 * @return A new MacroProps containing all of the information in the Propert
ies. |
65 */ | 66 */ |
66 public static MacroProps oldToNew(DecimalFormatProperties properties, Decima
lFormatSymbols symbols, | 67 public static MacroProps oldToNew( |
| 68 DecimalFormatProperties properties, |
| 69 DecimalFormatSymbols symbols, |
67 DecimalFormatProperties exportedProperties) { | 70 DecimalFormatProperties exportedProperties) { |
68 MacroProps macros = new MacroProps(); | 71 MacroProps macros = new MacroProps(); |
69 ULocale locale = symbols.getULocale(); | 72 ULocale locale = symbols.getULocale(); |
70 | 73 |
71 ///////////// | 74 ///////////// |
72 // SYMBOLS // | 75 // SYMBOLS // |
73 ///////////// | 76 ///////////// |
74 | 77 |
75 macros.symbols = symbols; | 78 macros.symbols = symbols; |
76 | 79 |
(...skipping 12 matching lines...) Expand all Loading... |
89 affixProvider = new PropertiesAffixPatternProvider(properties); | 92 affixProvider = new PropertiesAffixPatternProvider(properties); |
90 } else { | 93 } else { |
91 affixProvider = new CurrencyPluralInfoAffixProvider(properties.getCu
rrencyPluralInfo()); | 94 affixProvider = new CurrencyPluralInfoAffixProvider(properties.getCu
rrencyPluralInfo()); |
92 } | 95 } |
93 macros.affixProvider = affixProvider; | 96 macros.affixProvider = affixProvider; |
94 | 97 |
95 /////////// | 98 /////////// |
96 // UNITS // | 99 // UNITS // |
97 /////////// | 100 /////////// |
98 | 101 |
99 boolean useCurrency = ((properties.getCurrency() != null) || properties.
getCurrencyPluralInfo() != null | 102 boolean useCurrency = ((properties.getCurrency() != null) |
100 || properties.getCurrencyUsage() != null || affixProvider.hasCur
rencySign()); | 103 || properties.getCurrencyPluralInfo() != null |
| 104 || properties.getCurrencyUsage() != null |
| 105 || affixProvider.hasCurrencySign()); |
101 Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(
), locale, symbols); | 106 Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(
), locale, symbols); |
102 CurrencyUsage currencyUsage = properties.getCurrencyUsage(); | 107 CurrencyUsage currencyUsage = properties.getCurrencyUsage(); |
103 boolean explicitCurrencyUsage = currencyUsage != null; | 108 boolean explicitCurrencyUsage = currencyUsage != null; |
104 if (!explicitCurrencyUsage) { | 109 if (!explicitCurrencyUsage) { |
105 currencyUsage = CurrencyUsage.STANDARD; | 110 currencyUsage = CurrencyUsage.STANDARD; |
106 } | 111 } |
107 if (useCurrency) { | 112 if (useCurrency) { |
108 macros.unit = currency; | 113 macros.unit = currency; |
109 } | 114 } |
110 | 115 |
111 /////////////////////// | 116 /////////////////////// |
112 // ROUNDING STRATEGY // | 117 // ROUNDING STRATEGY // |
113 /////////////////////// | 118 /////////////////////// |
114 | 119 |
115 int maxInt = properties.getMaximumIntegerDigits(); | 120 int maxInt = properties.getMaximumIntegerDigits(); |
116 int minInt = properties.getMinimumIntegerDigits(); | 121 int minInt = properties.getMinimumIntegerDigits(); |
117 int maxFrac = properties.getMaximumFractionDigits(); | 122 int maxFrac = properties.getMaximumFractionDigits(); |
118 int minFrac = properties.getMinimumFractionDigits(); | 123 int minFrac = properties.getMinimumFractionDigits(); |
119 int minSig = properties.getMinimumSignificantDigits(); | 124 int minSig = properties.getMinimumSignificantDigits(); |
120 int maxSig = properties.getMaximumSignificantDigits(); | 125 int maxSig = properties.getMaximumSignificantDigits(); |
121 BigDecimal roundingIncrement = properties.getRoundingIncrement(); | 126 BigDecimal roundingIncrement = properties.getRoundingIncrement(); |
122 MathContext mathContext = RoundingUtils.getMathContextOrUnlimited(proper
ties); | 127 MathContext mathContext = RoundingUtils.getMathContextOrUnlimited(proper
ties); |
123 boolean explicitMinMaxFrac = minFrac != -1 || maxFrac != -1; | 128 boolean explicitMinMaxFrac = minFrac != -1 || maxFrac != -1; |
124 boolean explicitMinMaxSig = minSig != -1 || maxSig != -1; | 129 boolean explicitMinMaxSig = minSig != -1 || maxSig != -1; |
125 // Resolve min/max frac for currencies, required for the validation logi
c and for when minFrac or maxFrac was | 130 // Resolve min/max frac for currencies, required for the validation logi
c and for when minFrac or |
| 131 // maxFrac was |
126 // set (but not both) on a currency instance. | 132 // set (but not both) on a currency instance. |
127 // NOTE: Increments are handled in "Rounder.constructCurrency()". | 133 // NOTE: Increments are handled in "Rounder.constructCurrency()". |
128 if (useCurrency) { | 134 if (useCurrency) { |
129 if (minFrac == -1 && maxFrac == -1) { | 135 if (minFrac == -1 && maxFrac == -1) { |
130 minFrac = currency.getDefaultFractionDigits(currencyUsage); | 136 minFrac = currency.getDefaultFractionDigits(currencyUsage); |
131 maxFrac = currency.getDefaultFractionDigits(currencyUsage); | 137 maxFrac = currency.getDefaultFractionDigits(currencyUsage); |
132 } else if (minFrac == -1) { | 138 } else if (minFrac == -1) { |
133 minFrac = Math.min(maxFrac, currency.getDefaultFractionDigits(cu
rrencyUsage)); | 139 minFrac = Math.min(maxFrac, currency.getDefaultFractionDigits(cu
rrencyUsage)); |
134 } else if (maxFrac == -1) { | 140 } else if (maxFrac == -1) { |
135 maxFrac = Math.max(minFrac, currency.getDefaultFractionDigits(cu
rrencyUsage)); | 141 maxFrac = Math.max(minFrac, currency.getDefaultFractionDigits(cu
rrencyUsage)); |
136 } else { | 142 } else { |
137 // No-op: user override for both minFrac and maxFrac | 143 // No-op: user override for both minFrac and maxFrac |
138 } | 144 } |
139 } | 145 } |
140 // Validate min/max int/frac. | 146 // Validate min/max int/frac. |
141 // For backwards compatibility, minimum overrides maximum if the two con
flict. | 147 // For backwards compatibility, minimum overrides maximum if the two con
flict. |
142 // The following logic ensures that there is always a minimum of at leas
t one digit. | 148 // The following logic ensures that there is always a minimum of at leas
t one digit. |
143 if (minInt == 0 && maxFrac != 0) { | 149 if (minInt == 0 && maxFrac != 0) { |
144 // Force a digit after the decimal point. | 150 // Force a digit after the decimal point. |
145 minFrac = minFrac <= 0 ? 1 : minFrac; | 151 minFrac = minFrac <= 0 ? 1 : minFrac; |
146 maxFrac = maxFrac < 0 ? Integer.MAX_VALUE : maxFrac < minFrac ? minF
rac : maxFrac; | 152 maxFrac = maxFrac < 0 ? Integer.MAX_VALUE : maxFrac < minFrac ? minF
rac : maxFrac; |
147 minInt = 0; | 153 minInt = 0; |
148 maxInt = maxInt < 0 ? -1 : maxInt > RoundingUtils.MAX_INT_FRAC_SIG ?
-1 : maxInt; | 154 maxInt = maxInt < 0 ? -1 : maxInt > RoundingUtils.MAX_INT_FRAC_SIG ?
-1 : maxInt; |
149 } else { | 155 } else { |
150 // Force a digit before the decimal point. | 156 // Force a digit before the decimal point. |
151 minFrac = minFrac < 0 ? 0 : minFrac; | 157 minFrac = minFrac < 0 ? 0 : minFrac; |
152 maxFrac = maxFrac < 0 ? Integer.MAX_VALUE : maxFrac < minFrac ? minF
rac : maxFrac; | 158 maxFrac = maxFrac < 0 ? Integer.MAX_VALUE : maxFrac < minFrac ? minF
rac : maxFrac; |
153 minInt = minInt <= 0 ? 1 : minInt > RoundingUtils.MAX_INT_FRAC_SIG ?
1 : minInt; | 159 minInt = minInt <= 0 ? 1 : minInt > RoundingUtils.MAX_INT_FRAC_SIG ?
1 : minInt; |
154 maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > Round
ingUtils.MAX_INT_FRAC_SIG ? -1 : maxInt; | 160 maxInt = maxInt < 0 ? -1 |
| 161 : maxInt < minInt ? minInt : maxInt > RoundingUtils.MAX_INT_
FRAC_SIG ? -1 : maxInt; |
155 } | 162 } |
156 Rounder rounding = null; | 163 Rounder rounding = null; |
157 if (explicitCurrencyUsage) { | 164 if (explicitCurrencyUsage) { |
158 rounding = Rounder.constructCurrency(currencyUsage).withCurrency(cur
rency); | 165 rounding = Rounder.constructCurrency(currencyUsage).withCurrency(cur
rency); |
159 } else if (roundingIncrement != null) { | 166 } else if (roundingIncrement != null) { |
160 rounding = Rounder.constructIncrement(roundingIncrement); | 167 rounding = Rounder.constructIncrement(roundingIncrement); |
161 } else if (explicitMinMaxSig) { | 168 } else if (explicitMinMaxSig) { |
162 minSig = minSig < 1 ? 1 : minSig > RoundingUtils.MAX_INT_FRAC_SIG ?
RoundingUtils.MAX_INT_FRAC_SIG : minSig; | 169 minSig = minSig < 1 ? 1 |
| 170 : minSig > RoundingUtils.MAX_INT_FRAC_SIG ? RoundingUtils.MA
X_INT_FRAC_SIG : minSig; |
163 maxSig = maxSig < 0 ? RoundingUtils.MAX_INT_FRAC_SIG | 171 maxSig = maxSig < 0 ? RoundingUtils.MAX_INT_FRAC_SIG |
164 : maxSig < minSig ? minSig | 172 : maxSig < minSig ? minSig |
165 : maxSig > RoundingUtils.MAX_INT_FRAC_SIG ? Rounding
Utils.MAX_INT_FRAC_SIG : maxSig; | 173 : maxSig > RoundingUtils.MAX_INT_FRAC_SIG ? Rounding
Utils.MAX_INT_FRAC_SIG |
| 174 : maxSig; |
166 rounding = Rounder.constructSignificant(minSig, maxSig); | 175 rounding = Rounder.constructSignificant(minSig, maxSig); |
167 } else if (explicitMinMaxFrac) { | 176 } else if (explicitMinMaxFrac) { |
168 rounding = Rounder.constructFraction(minFrac, maxFrac); | 177 rounding = Rounder.constructFraction(minFrac, maxFrac); |
169 } else if (useCurrency) { | 178 } else if (useCurrency) { |
170 rounding = Rounder.constructCurrency(currencyUsage); | 179 rounding = Rounder.constructCurrency(currencyUsage); |
171 } | 180 } |
172 if (rounding != null) { | 181 if (rounding != null) { |
173 rounding = rounding.withMode(mathContext); | 182 rounding = rounding.withMode(mathContext); |
174 macros.rounder = rounding; | 183 macros.rounder = rounding; |
175 } | 184 } |
176 | 185 |
177 /////////////////// | 186 /////////////////// |
178 // INTEGER WIDTH // | 187 // INTEGER WIDTH // |
179 /////////////////// | 188 /////////////////// |
180 | 189 |
181 macros.integerWidth = IntegerWidth.zeroFillTo(minInt).truncateAt(maxInt)
; | 190 macros.integerWidth = IntegerWidth.zeroFillTo(minInt).truncateAt(maxInt)
; |
182 | 191 |
183 /////////////////////// | 192 /////////////////////// |
184 // GROUPING STRATEGY // | 193 // GROUPING STRATEGY // |
185 /////////////////////// | 194 /////////////////////// |
186 | 195 |
187 int grouping1 = properties.getGroupingSize(); | 196 macros.grouper = Grouper.defaults().withProperties(properties); |
188 int grouping2 = properties.getSecondaryGroupingSize(); | |
189 int minGrouping = properties.getMinimumGroupingDigits(); | |
190 assert grouping1 >= -2; // value of -2 means to forward no grouping info
rmation | |
191 grouping1 = grouping1 > 0 ? grouping1 : grouping2 > 0 ? grouping2 : grou
ping1; | |
192 grouping2 = grouping2 > 0 ? grouping2 : grouping1; | |
193 // TODO: Is it important to handle minGrouping > 2? | |
194 macros.grouper = Grouper.getInstance((byte) grouping1, (byte) grouping2,
minGrouping == 2); | |
195 | 197 |
196 ///////////// | 198 ///////////// |
197 // PADDING // | 199 // PADDING // |
198 ///////////// | 200 ///////////// |
199 | 201 |
200 if (properties.getFormatWidth() != -1) { | 202 if (properties.getFormatWidth() != -1) { |
201 macros.padder = new Padder(properties.getPadString(), properties.get
FormatWidth(), | 203 macros.padder = new Padder(properties.getPadString(), |
| 204 properties.getFormatWidth(), |
202 properties.getPadPosition()); | 205 properties.getPadPosition()); |
203 } | 206 } |
204 | 207 |
205 /////////////////////////////// | 208 /////////////////////////////// |
206 // DECIMAL MARK ALWAYS SHOWN // | 209 // DECIMAL MARK ALWAYS SHOWN // |
207 /////////////////////////////// | 210 /////////////////////////////// |
208 | 211 |
209 macros.decimal = properties.getDecimalSeparatorAlwaysShown() ? DecimalSe
paratorDisplay.ALWAYS | 212 macros.decimal = properties.getDecimalSeparatorAlwaysShown() ? DecimalSe
paratorDisplay.ALWAYS |
210 : DecimalSeparatorDisplay.AUTO; | 213 : DecimalSeparatorDisplay.AUTO; |
211 | 214 |
(...skipping 27 matching lines...) Expand all Loading... |
239 engineering, | 242 engineering, |
240 // Enforce minimum integer digits (for patterns like "000.00
E0"): | 243 // Enforce minimum integer digits (for patterns like "000.00
E0"): |
241 (engineering == minInt), | 244 (engineering == minInt), |
242 // Minimum exponent digits: | 245 // Minimum exponent digits: |
243 properties.getMinimumExponentDigits(), | 246 properties.getMinimumExponentDigits(), |
244 // Exponent sign always shown: | 247 // Exponent sign always shown: |
245 properties.getExponentSignAlwaysShown() ? SignDisplay.ALWAYS
: SignDisplay.AUTO); | 248 properties.getExponentSignAlwaysShown() ? SignDisplay.ALWAYS
: SignDisplay.AUTO); |
246 // Scientific notation also involves overriding the rounding mode. | 249 // Scientific notation also involves overriding the rounding mode. |
247 // TODO: Overriding here is a bit of a hack. Should this logic go ea
rlier? | 250 // TODO: Overriding here is a bit of a hack. Should this logic go ea
rlier? |
248 if (macros.rounder instanceof FractionRounder) { | 251 if (macros.rounder instanceof FractionRounder) { |
249 // For the purposes of rounding, get the original min/max int/fr
ac, since the local variables | 252 // For the purposes of rounding, get the original min/max int/fr
ac, since the local |
| 253 // variables |
250 // have been manipulated for display purposes. | 254 // have been manipulated for display purposes. |
251 int minInt_ = properties.getMinimumIntegerDigits(); | 255 int minInt_ = properties.getMinimumIntegerDigits(); |
252 int minFrac_ = properties.getMinimumFractionDigits(); | 256 int minFrac_ = properties.getMinimumFractionDigits(); |
253 int maxFrac_ = properties.getMaximumFractionDigits(); | 257 int maxFrac_ = properties.getMaximumFractionDigits(); |
254 if (minInt_ == 0 && maxFrac_ == 0) { | 258 if (minInt_ == 0 && maxFrac_ == 0) { |
255 // Patterns like "#E0" and "##E0", which mean no rounding! | 259 // Patterns like "#E0" and "##E0", which mean no rounding! |
256 macros.rounder = Rounder.constructInfinite().withMode(mathCo
ntext); | 260 macros.rounder = Rounder.constructInfinite().withMode(mathCo
ntext); |
257 } else if (minInt_ == 0 && minFrac_ == 0) { | 261 } else if (minInt_ == 0 && minFrac_ == 0) { |
258 // Patterns like "#.##E0" (no zeros in the mantissa), which
mean round to maxFrac+1 | 262 // Patterns like "#.##E0" (no zeros in the mantissa), which
mean round to maxFrac+1 |
259 macros.rounder = Rounder.constructSignificant(1, maxFrac_ +
1).withMode(mathContext); | 263 macros.rounder = Rounder.constructSignificant(1, maxFrac_ +
1).withMode(mathContext); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 | 331 |
328 exportedProperties.setMinimumFractionDigits(minFrac_); | 332 exportedProperties.setMinimumFractionDigits(minFrac_); |
329 exportedProperties.setMaximumFractionDigits(maxFrac_); | 333 exportedProperties.setMaximumFractionDigits(maxFrac_); |
330 exportedProperties.setMinimumSignificantDigits(minSig_); | 334 exportedProperties.setMinimumSignificantDigits(minSig_); |
331 exportedProperties.setMaximumSignificantDigits(maxSig_); | 335 exportedProperties.setMaximumSignificantDigits(maxSig_); |
332 exportedProperties.setRoundingIncrement(increment_); | 336 exportedProperties.setRoundingIncrement(increment_); |
333 } | 337 } |
334 | 338 |
335 return macros; | 339 return macros; |
336 } | 340 } |
337 | |
338 private static class PropertiesAffixPatternProvider implements AffixPatternP
rovider { | |
339 private final String posPrefix; | |
340 private final String posSuffix; | |
341 private final String negPrefix; | |
342 private final String negSuffix; | |
343 | |
344 public PropertiesAffixPatternProvider(DecimalFormatProperties properties
) { | |
345 // There are two ways to set affixes in DecimalFormat: via the patte
rn string (applyPattern), and via the | |
346 // explicit setters (setPositivePrefix and friends). The way to res
olve the settings is as follows: | |
347 // | |
348 // 1) If the explicit setting is present for the field, use it. | |
349 // 2) Otherwise, follows UTS 35 rules based on the pattern string. | |
350 // | |
351 // Importantly, the explicit setters affect only the one field they
override. If you set the positive | |
352 // prefix, that should not affect the negative prefix. Since it is
impossible for the user of this class | |
353 // to know whether the origin for a string was the override or the p
attern, we have to say that we always | |
354 // have a negative subpattern and perform all resolution logic here. | |
355 | |
356 // Convenience: Extract the properties into local variables. | |
357 // Variables are named with three chars: [p/n][p/s][o/p] | |
358 // [p/n] => p for positive, n for negative | |
359 // [p/s] => p for prefix, s for suffix | |
360 // [o/p] => o for escaped custom override string, p for pattern stri
ng | |
361 String ppo = AffixUtils.escape(properties.getPositivePrefix()); | |
362 String pso = AffixUtils.escape(properties.getPositiveSuffix()); | |
363 String npo = AffixUtils.escape(properties.getNegativePrefix()); | |
364 String nso = AffixUtils.escape(properties.getNegativeSuffix()); | |
365 String ppp = properties.getPositivePrefixPattern(); | |
366 String psp = properties.getPositiveSuffixPattern(); | |
367 String npp = properties.getNegativePrefixPattern(); | |
368 String nsp = properties.getNegativeSuffixPattern(); | |
369 | |
370 if (ppo != null) { | |
371 posPrefix = ppo; | |
372 } else if (ppp != null) { | |
373 posPrefix = ppp; | |
374 } else { | |
375 // UTS 35: Default positive prefix is empty string. | |
376 posPrefix = ""; | |
377 } | |
378 | |
379 if (pso != null) { | |
380 posSuffix = pso; | |
381 } else if (psp != null) { | |
382 posSuffix = psp; | |
383 } else { | |
384 // UTS 35: Default positive suffix is empty string. | |
385 posSuffix = ""; | |
386 } | |
387 | |
388 if (npo != null) { | |
389 negPrefix = npo; | |
390 } else if (npp != null) { | |
391 negPrefix = npp; | |
392 } else { | |
393 // UTS 35: Default negative prefix is "-" with positive prefix. | |
394 // Important: We prepend the "-" to the pattern, not the overrid
e! | |
395 negPrefix = ppp == null ? "-" : "-" + ppp; | |
396 } | |
397 | |
398 if (nso != null) { | |
399 negSuffix = nso; | |
400 } else if (nsp != null) { | |
401 negSuffix = nsp; | |
402 } else { | |
403 // UTS 35: Default negative prefix is the positive prefix. | |
404 negSuffix = psp == null ? "" : psp; | |
405 } | |
406 } | |
407 | |
408 @Override | |
409 public char charAt(int flags, int i) { | |
410 return getStringForFlags(flags).charAt(i); | |
411 } | |
412 | |
413 @Override | |
414 public int length(int flags) { | |
415 return getStringForFlags(flags).length(); | |
416 } | |
417 | |
418 private String getStringForFlags(int flags) { | |
419 boolean prefix = (flags & Flags.PREFIX) != 0; | |
420 boolean negative = (flags & Flags.NEGATIVE_SUBPATTERN) != 0; | |
421 if (prefix && negative) { | |
422 return negPrefix; | |
423 } else if (prefix) { | |
424 return posPrefix; | |
425 } else if (negative) { | |
426 return negSuffix; | |
427 } else { | |
428 return posSuffix; | |
429 } | |
430 } | |
431 | |
432 @Override | |
433 public boolean positiveHasPlusSign() { | |
434 return AffixUtils.containsType(posPrefix, AffixUtils.TYPE_PLUS_SIGN) | |
435 || AffixUtils.containsType(posSuffix, AffixUtils.TYPE_PLUS_S
IGN); | |
436 } | |
437 | |
438 @Override | |
439 public boolean hasNegativeSubpattern() { | |
440 // See comments in the constructor for more information on why this
is always true. | |
441 return true; | |
442 } | |
443 | |
444 @Override | |
445 public boolean negativeHasMinusSign() { | |
446 return AffixUtils.containsType(negPrefix, AffixUtils.TYPE_MINUS_SIGN
) | |
447 || AffixUtils.containsType(negSuffix, AffixUtils.TYPE_MINUS_
SIGN); | |
448 } | |
449 | |
450 @Override | |
451 public boolean hasCurrencySign() { | |
452 return AffixUtils.hasCurrencySymbols(posPrefix) || AffixUtils.hasCur
rencySymbols(posSuffix) | |
453 || AffixUtils.hasCurrencySymbols(negPrefix) || AffixUtils.ha
sCurrencySymbols(negSuffix); | |
454 } | |
455 | |
456 @Override | |
457 public boolean containsSymbolType(int type) { | |
458 return AffixUtils.containsType(posPrefix, type) || AffixUtils.contai
nsType(posSuffix, type) | |
459 || AffixUtils.containsType(negPrefix, type) || AffixUtils.co
ntainsType(negSuffix, type); | |
460 } | |
461 } | |
462 | |
463 private static class CurrencyPluralInfoAffixProvider implements AffixPattern
Provider { | |
464 private final AffixPatternProvider[] affixesByPlural; | |
465 | |
466 public CurrencyPluralInfoAffixProvider(CurrencyPluralInfo cpi) { | |
467 affixesByPlural = new ParsedPatternInfo[StandardPlural.COUNT]; | |
468 for (StandardPlural plural : StandardPlural.VALUES) { | |
469 affixesByPlural[plural.ordinal()] = PatternStringParser | |
470 .parseToPatternInfo(cpi.getCurrencyPluralPattern(plural.
getKeyword())); | |
471 } | |
472 } | |
473 | |
474 @Override | |
475 public char charAt(int flags, int i) { | |
476 int pluralOrdinal = (flags & Flags.PLURAL_MASK); | |
477 return affixesByPlural[pluralOrdinal].charAt(flags, i); | |
478 } | |
479 | |
480 @Override | |
481 public int length(int flags) { | |
482 int pluralOrdinal = (flags & Flags.PLURAL_MASK); | |
483 return affixesByPlural[pluralOrdinal].length(flags); | |
484 } | |
485 | |
486 @Override | |
487 public boolean positiveHasPlusSign() { | |
488 return affixesByPlural[StandardPlural.OTHER.ordinal()].positiveHasPl
usSign(); | |
489 } | |
490 | |
491 @Override | |
492 public boolean hasNegativeSubpattern() { | |
493 return affixesByPlural[StandardPlural.OTHER.ordinal()].hasNegativeSu
bpattern(); | |
494 } | |
495 | |
496 @Override | |
497 public boolean negativeHasMinusSign() { | |
498 return affixesByPlural[StandardPlural.OTHER.ordinal()].negativeHasMi
nusSign(); | |
499 } | |
500 | |
501 @Override | |
502 public boolean hasCurrencySign() { | |
503 return affixesByPlural[StandardPlural.OTHER.ordinal()].hasCurrencySi
gn(); | |
504 } | |
505 | |
506 @Override | |
507 public boolean containsSymbolType(int type) { | |
508 return affixesByPlural[StandardPlural.OTHER.ordinal()].containsSymbo
lType(type); | |
509 } | |
510 } | |
511 } | 341 } |
LEFT | RIGHT |