OLD | NEW |
1 // © 2018 and later: Unicode, Inc. and others. | 1 // © 2018 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.parse; | 3 package com.ibm.icu.impl.number.parse; |
4 | 4 |
5 import java.util.Iterator; | 5 import java.util.Iterator; |
6 | 6 |
| 7 import com.ibm.icu.impl.StandardPlural; |
7 import com.ibm.icu.impl.StringSegment; | 8 import com.ibm.icu.impl.StringSegment; |
8 import com.ibm.icu.impl.TextTrieMap; | 9 import com.ibm.icu.impl.TextTrieMap; |
9 import com.ibm.icu.text.DecimalFormatSymbols; | 10 import com.ibm.icu.text.DecimalFormatSymbols; |
10 import com.ibm.icu.util.Currency; | 11 import com.ibm.icu.util.Currency; |
11 import com.ibm.icu.util.Currency.CurrencyStringInfo; | 12 import com.ibm.icu.util.Currency.CurrencyStringInfo; |
12 | 13 |
13 /** | 14 /** |
14 * Matches a currency, either a custom currency or one from the data bundle. The
class is called | 15 * Matches a currency, either a custom currency or one from the data bundle. The
class is called |
15 * "combined" to emphasize that the currency string may come from one of multipl
e sources. | 16 * "combined" to emphasize that the currency string may come from one of multipl
e sources. |
16 * | 17 * |
17 * Will match currency spacing either before or after the number depending on wh
ether we are currently in | 18 * Will match currency spacing either before or after the number depending on wh
ether we are currently in |
18 * the prefix or suffix. | 19 * the prefix or suffix. |
19 * | 20 * |
20 * The implementation of this class is slightly different between J and C. See #
13584 for a follow-up. | 21 * The implementation of this class is slightly different between J and C. See #
13584 for a follow-up. |
21 * | 22 * |
22 * @author sffc | 23 * @author sffc |
23 */ | 24 */ |
24 public class CombinedCurrencyMatcher implements NumberParseMatcher { | 25 public class CombinedCurrencyMatcher implements NumberParseMatcher { |
25 | 26 |
26 private final String isoCode; | 27 private final String isoCode; |
27 private final String currency1; | 28 private final String currency1; |
28 private final String currency2; | 29 private final String currency2; |
29 | 30 |
| 31 private final String[] localLongNames; |
| 32 |
30 private final String afterPrefixInsert; | 33 private final String afterPrefixInsert; |
31 private final String beforeSuffixInsert; | 34 private final String beforeSuffixInsert; |
32 | 35 |
33 private final TextTrieMap<CurrencyStringInfo> longNameTrie; | 36 private final TextTrieMap<CurrencyStringInfo> longNameTrie; |
34 private final TextTrieMap<CurrencyStringInfo> symbolTrie; | 37 private final TextTrieMap<CurrencyStringInfo> symbolTrie; |
35 | 38 |
36 // TODO: See comments in constructor. | 39 // TODO: See comments in constructor. |
37 // private final UnicodeSet leadCodePoints; | 40 // private final UnicodeSet leadCodePoints; |
38 | 41 |
39 public static CombinedCurrencyMatcher getInstance(Currency currency, Decimal
FormatSymbols dfs) { | 42 public static CombinedCurrencyMatcher getInstance(Currency currency, Decimal
FormatSymbols dfs, int parseFlags) { |
40 // TODO: Cache these instances. They are somewhat expensive. | 43 // TODO: Cache these instances. They are somewhat expensive. |
41 return new CombinedCurrencyMatcher(currency, dfs); | 44 return new CombinedCurrencyMatcher(currency, dfs, parseFlags); |
42 } | 45 } |
43 | 46 |
44 private CombinedCurrencyMatcher(Currency currency, DecimalFormatSymbols dfs)
{ | 47 private CombinedCurrencyMatcher(Currency currency, DecimalFormatSymbols dfs,
int parseFlags) { |
45 this.isoCode = currency.getSubtype(); | 48 this.isoCode = currency.getSubtype(); |
46 this.currency1 = currency.getSymbol(dfs.getULocale()); | 49 this.currency1 = currency.getSymbol(dfs.getULocale()); |
47 this.currency2 = currency.getCurrencyCode(); | 50 this.currency2 = currency.getCurrencyCode(); |
48 | 51 |
49 afterPrefixInsert = dfs.getPatternForCurrencySpacing(DecimalFormatSymbol
s.CURRENCY_SPC_INSERT, | 52 afterPrefixInsert = dfs.getPatternForCurrencySpacing(DecimalFormatSymbol
s.CURRENCY_SPC_INSERT, |
50 false); | 53 false); |
51 beforeSuffixInsert = dfs.getPatternForCurrencySpacing(DecimalFormatSymbo
ls.CURRENCY_SPC_INSERT, | 54 beforeSuffixInsert = dfs.getPatternForCurrencySpacing(DecimalFormatSymbo
ls.CURRENCY_SPC_INSERT, |
52 true); | 55 true); |
53 | 56 |
54 // TODO: Currency trie does not currently have an option for case foldin
g. It defaults to use | 57 if (0 == (parseFlags & ParsingUtils.PARSE_FLAG_NO_FOREIGN_CURRENCIES)) { |
55 // case folding on long-names but not symbols. | 58 // TODO: Currency trie does not currently have an option for case fo
lding. It defaults to use |
56 longNameTrie = Currency.getParsingTrie(dfs.getULocale(), Currency.LONG_N
AME); | 59 // case folding on long-names but not symbols. |
57 symbolTrie = Currency.getParsingTrie(dfs.getULocale(), Currency.SYMBOL_N
AME); | 60 longNameTrie = Currency.getParsingTrie(dfs.getULocale(), Currency.LO
NG_NAME); |
| 61 symbolTrie = Currency.getParsingTrie(dfs.getULocale(), Currency.SYMB
OL_NAME); |
| 62 localLongNames = null; |
| 63 |
| 64 } else { |
| 65 longNameTrie = null; |
| 66 symbolTrie = null; |
| 67 localLongNames = new String[StandardPlural.COUNT]; |
| 68 for (int i = 0; i < StandardPlural.COUNT; i++) { |
| 69 String pluralKeyword = StandardPlural.VALUES.get(i).getKeyword()
; |
| 70 localLongNames[i] = currency |
| 71 .getName(dfs.getLocale(), Currency.PLURAL_LONG_NAME, plu
ralKeyword, null); |
| 72 } |
| 73 } |
58 | 74 |
59 // TODO: Figure out how to make this faster and re-enable. | 75 // TODO: Figure out how to make this faster and re-enable. |
60 // Computing the "lead code points" set for fastpathing is too slow to u
se in production. | 76 // Computing the "lead code points" set for fastpathing is too slow to u
se in production. |
61 // See http://bugs.icu-project.org/trac/ticket/13584 | 77 // See http://bugs.icu-project.org/trac/ticket/13584 |
62 // // Compute the full set of characters that could be the first in a cu
rrency to allow for | 78 // // Compute the full set of characters that could be the first in a cu
rrency to allow for |
63 // // efficient smoke test. | 79 // // efficient smoke test. |
64 // leadCodePoints = new UnicodeSet(); | 80 // leadCodePoints = new UnicodeSet(); |
65 // leadCodePoints.add(currency1.codePointAt(0)); | 81 // leadCodePoints.add(currency1.codePointAt(0)); |
66 // leadCodePoints.add(currency2.codePointAt(0)); | 82 // leadCodePoints.add(currency2.codePointAt(0)); |
67 // leadCodePoints.add(beforeSuffixInsert.codePointAt(0)); | 83 // leadCodePoints.add(beforeSuffixInsert.codePointAt(0)); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
105 // Note: let currency spacing be a weak match. Don't update char
s consumed. | 121 // Note: let currency spacing be a weak match. Don't update char
s consumed. |
106 } | 122 } |
107 maybeMore = maybeMore || overlap == segment.length(); | 123 maybeMore = maybeMore || overlap == segment.length(); |
108 } | 124 } |
109 | 125 |
110 return maybeMore; | 126 return maybeMore; |
111 } | 127 } |
112 | 128 |
113 /** Matches the currency string without concern for currency spacing. */ | 129 /** Matches the currency string without concern for currency spacing. */ |
114 private boolean matchCurrency(StringSegment segment, ParsedNumber result) { | 130 private boolean matchCurrency(StringSegment segment, ParsedNumber result) { |
| 131 boolean maybeMore = false; |
| 132 |
115 int overlap1 = segment.getCaseSensitivePrefixLength(currency1); | 133 int overlap1 = segment.getCaseSensitivePrefixLength(currency1); |
| 134 maybeMore = maybeMore || overlap1 == segment.length(); |
116 if (overlap1 == currency1.length()) { | 135 if (overlap1 == currency1.length()) { |
117 result.currencyCode = isoCode; | 136 result.currencyCode = isoCode; |
118 segment.adjustOffset(overlap1); | 137 segment.adjustOffset(overlap1); |
119 result.setCharsConsumed(segment); | 138 result.setCharsConsumed(segment); |
120 return segment.length() == 0; | 139 return segment.length() == 0; |
121 } | 140 } |
122 | 141 |
123 int overlap2 = segment.getCaseSensitivePrefixLength(currency2); | 142 int overlap2 = segment.getCaseSensitivePrefixLength(currency2); |
| 143 maybeMore = maybeMore || overlap2 == segment.length(); |
124 if (overlap2 == currency2.length()) { | 144 if (overlap2 == currency2.length()) { |
125 result.currencyCode = isoCode; | 145 result.currencyCode = isoCode; |
126 segment.adjustOffset(overlap2); | 146 segment.adjustOffset(overlap2); |
127 result.setCharsConsumed(segment); | 147 result.setCharsConsumed(segment); |
128 return segment.length() == 0; | 148 return segment.length() == 0; |
129 } | 149 } |
130 | 150 |
131 TextTrieMap.Output trieOutput = new TextTrieMap.Output(); | 151 if (longNameTrie != null) { |
132 Iterator<CurrencyStringInfo> values = longNameTrie.get(segment, 0, trieO
utput); | 152 // Use the full currency data. |
133 if (values == null) { | 153 TextTrieMap.Output trieOutput = new TextTrieMap.Output(); |
134 values = symbolTrie.get(segment, 0, trieOutput); | 154 Iterator<CurrencyStringInfo> values = longNameTrie.get(segment, 0, t
rieOutput); |
135 } | 155 maybeMore = maybeMore || trieOutput.partialMatch; |
136 if (values != null) { | 156 if (values == null) { |
137 result.currencyCode = values.next().getISOCode(); | 157 values = symbolTrie.get(segment, 0, trieOutput); |
138 segment.adjustOffset(trieOutput.matchLength); | 158 maybeMore = maybeMore || trieOutput.partialMatch; |
139 result.setCharsConsumed(segment); | 159 } |
| 160 if (values != null) { |
| 161 result.currencyCode = values.next().getISOCode(); |
| 162 segment.adjustOffset(trieOutput.matchLength); |
| 163 result.setCharsConsumed(segment); |
| 164 return maybeMore; |
| 165 } |
| 166 |
| 167 } else { |
| 168 // Use the locale long names. |
| 169 int longestFullMatch = 0; |
| 170 for (int i=0; i<StandardPlural.COUNT; i++) { |
| 171 String name = localLongNames[i]; |
| 172 int overlap = segment.getCommonPrefixLength(name); |
| 173 if (overlap == name.length() && name.length() > longestFullMatch
) { |
| 174 longestFullMatch = name.length(); |
| 175 } |
| 176 maybeMore = maybeMore || overlap > 0; |
| 177 } |
| 178 if (longestFullMatch > 0) { |
| 179 result.currencyCode = isoCode; |
| 180 segment.adjustOffset(longestFullMatch); |
| 181 result.setCharsConsumed(segment); |
| 182 return maybeMore; |
| 183 } |
140 } | 184 } |
141 | 185 |
142 return overlap1 == segment.length() || overlap2 == segment.length() || t
rieOutput.partialMatch; | 186 // No match found. |
| 187 return maybeMore; |
143 } | 188 } |
144 | 189 |
145 @Override | 190 @Override |
146 public boolean smokeTest(StringSegment segment) { | 191 public boolean smokeTest(StringSegment segment) { |
147 // TODO: See constructor | 192 // TODO: See constructor |
148 return true; | 193 return true; |
149 // return segment.startsWith(leadCodePoints); | 194 // return segment.startsWith(leadCodePoints); |
150 } | 195 } |
151 | 196 |
152 @Override | 197 @Override |
153 public void postProcess(ParsedNumber result) { | 198 public void postProcess(ParsedNumber result) { |
154 // No-op | 199 // No-op |
155 } | 200 } |
156 | 201 |
157 @Override | 202 @Override |
158 public String toString() { | 203 public String toString() { |
159 return "<CombinedCurrencyMatcher " + isoCode + ">"; | 204 return "<CombinedCurrencyMatcher " + isoCode + ">"; |
160 } | 205 } |
161 | 206 |
162 } | 207 } |
OLD | NEW |