1 package org.unicode.cldr.test;
2 
3 import java.util.HashMap;
4 import java.util.Iterator;
5 import java.util.Locale;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.regex.Pattern;
9 
10 import org.unicode.cldr.util.CLDRFile;
11 import org.unicode.cldr.util.ICUServiceBuilder;
12 import org.unicode.cldr.util.PatternCache;
13 import org.unicode.cldr.util.SupplementalDataInfo;
14 import org.unicode.cldr.util.XPathParts;
15 
16 import com.ibm.icu.impl.number.DecimalFormatProperties;
17 import com.ibm.icu.text.CompactDecimalFormat;
18 import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
19 import com.ibm.icu.text.DecimalFormat.PropertySetter;
20 import com.ibm.icu.util.Currency;
21 import com.ibm.icu.util.ULocale;
22 
23 @SuppressWarnings("deprecation")
24 public class BuildIcuCompactDecimalFormat {
25     private static boolean DEBUG = false;
26     static SupplementalDataInfo sdi = SupplementalDataInfo.getInstance();
27     static final int MINIMUM_ARRAY_LENGTH = 15;
28     static final Pattern PATTERN = PatternCache.get("([^0,]*)([0]+)([.]0+)?([^0]*)");
29     static final Pattern TYPE = PatternCache.get("1([0]*)");
30 
31     public enum CurrencyStyle {
32         PLAIN, CURRENCY, LONG_CURRENCY, ISO_CURRENCY, UNIT
33     }
34 
35     /**
36      * JUST FOR DEVELOPMENT
37      *
38      * @param currencyStyle
39      * @param currencyCode
40      */
build(CLDRFile resolvedCldrFile, Set<String> debugCreationErrors, String[] debugOriginals, CompactStyle style, ULocale locale, CurrencyStyle currencyStyle, String currencyCodeOrUnit)41     public static final CompactDecimalFormat build(CLDRFile resolvedCldrFile,
42         Set<String> debugCreationErrors, String[] debugOriginals,
43         CompactStyle style, ULocale locale, CurrencyStyle currencyStyle, String currencyCodeOrUnit) {
44 
45         final Map<String, Map<String, String>> customData = new HashMap<String, Map<String, String>>();
46 //        Map<String,String> inner = new HashMap<String,String>();
47 //        inner.put("one", "0 qwerty");
48 //        inner.put("other", "0 dvorak");
49 //        customData.put("1000", inner);
50 
51 //        Map<String, String[][]> affixes = new HashMap<String, String[][]>();
52 //        Map<String, String[]> unitPrefixes = new HashMap<String, String[]>();
53 //
54 //        // String[] prefix = new String[CompactDecimalFormat.MINIMUM_ARRAY_LENGTH];
55 //        // String[] suffix = new String[CompactDecimalFormat.MINIMUM_ARRAY_LENGTH];
56 //        long[] divisor = new long[MINIMUM_ARRAY_LENGTH];
57 //        // get the pattern details from the locale
58 //        PluralInfo pluralInfo = sdi.getPlurals(PluralType.cardinal, locale.toString());
59 //
60 //        // fix low numbers
61 //        Set<String> canonicalKeywords = pluralInfo.getCanonicalKeywords();
62 //        for (String key : canonicalKeywords) {
63 //            String[][] affix = new String[MINIMUM_ARRAY_LENGTH][];
64 //            for (int i = 0; i < 3; ++i) {
65 //                affix[i] = new String[] { "", "" };
66 //                divisor[i] = 1;
67 //            }
68 //            affixes.put(key, affix);
69 //        }
70 //        Matcher patternMatcher = PATTERN.matcher("");
71 //        Matcher typeMatcher = TYPE.matcher("");
72 //        XPathParts parts = new XPathParts();
73         // for (String path :
74         // With.in(resolvedCldrFile.iterator("//ldml/numbers/decimalFormats/decimalFormatLength[@type=\"short\"]/decimalFormat[@type=\"standard\"]/")))
75         // {
76         String prefix = currencyStyle == CurrencyStyle.PLAIN ? "//ldml/numbers/decimalFormats[@numberSystem=\"latn\"]/decimalFormatLength"
77             : "//ldml/numbers/currencyFormats[@numberSystem=\"latn\"]/currencyFormatLength";
78 
79         Iterator<String> it = resolvedCldrFile.iterator(prefix);
80 
81         String styleString = style.toString().toLowerCase(Locale.ENGLISH);
82         while (it.hasNext()) {
83             String path = it.next();
84             if (path.endsWith("/alias")) {
85                 continue;
86             }
87             // String sourceLocale = resolvedCldrFile.getSourceLocaleID(path, null);
88             // if ("root".equals(sourceLocale)) {
89             // continue;
90             // }
91             // if (!path.contains("decimalFormatLength")) {
92             // continue;
93             // }
94             XPathParts parts = XPathParts.getFrozenInstance(path);
95             String stype = parts.getAttributeValue(3, "type");
96             if (!styleString.equals(stype)) {
97                 continue;
98             }
99             String type = parts.getAttributeValue(-1, "type");
100             String key = parts.getAttributeValue(-1, "count");
101             String pattern = resolvedCldrFile.getStringValue(path);
102 
103             /*
104                     <pattern type="1000" count="one">0K</pattern>
105              */
106 
107             add(customData, type, key, pattern);
108 
109 //            if (DEBUG && path.contains("miliony")) {
110 //                System.out.println(key + ", " + path);
111 //            }
112 //            String[][] affix = affixes.get(key);
113 //
114 //            if (!typeMatcher.reset(type).matches()) {
115 //                debugCreationErrors.add("type (" + type +
116 //                    ") doesn't match expected " + TYPE.pattern());
117 //                continue;
118 //            }
119 //
120 //            String pattern = resolvedCldrFile.getStringValue(path);
121 //
122 //            int typeZeroCount = typeMatcher.end(1) - typeMatcher.start(1);
123 //            // for debugging
124 //            if (debugOriginals != null) {
125 //                debugOriginals[typeZeroCount] = pattern;
126 //            }
127 //
128 //            // special pattern for unused
129 //            if (pattern.equals("0")) {
130 //                affix[typeZeroCount] = new String[] { "", "" };
131 //                divisor[typeZeroCount] = 1;
132 //                continue;
133 //            }
134 //
135 //            if (!patternMatcher.reset(pattern).matches()) {
136 //                debugCreationErrors.add("pattern (" + pattern +
137 //                    ") doesn't match expected " + PATTERN.pattern());
138 //                continue;
139 //            }
140 //
141 //            // HACK '.' just in case.
142 //            affix[typeZeroCount] = new String[] {
143 //                escape(patternMatcher.group(1).replace("'.'", ".")),
144 //                escape(patternMatcher.group(4).replace("'.'", "."))
145 //
146 ////                patternMatcher.group(1).replace("'.'", "."),
147 ////                patternMatcher.group(4).replace("'.'", ".")
148 //                };
149 //            if (DEBUG && key.equals("one")) {
150 //                System.out.println(key + ", " + typeZeroCount + ", " + Arrays.asList(affix[typeZeroCount]));
151 //            }
152 //            int zeroCount = patternMatcher.end(2) - patternMatcher.start(2) - 1;
153 //            divisor[typeZeroCount] = (long) Math.pow(10.0, typeZeroCount - zeroCount);
154         }
155 
156         // DecimalFormat format = (DecimalFormat) (currencyStyle == CurrencyStyle.PLAIN
157         // ? NumberFormat.getInstance(new ULocale(resolvedCldrFile.getLocaleID()))
158         // : NumberFormat.getCurrencyInstance(new ULocale(resolvedCldrFile.getLocaleID())));
159 
160 //        ICUServiceBuilder builder = new ICUServiceBuilder().setCldrFile(resolvedCldrFile);
161 ////        final DecimalFormat format = builder.getNumberFormat(1);
162 //        switch (currencyStyle) {
163 //        case PLAIN:
164 //        default:
165 //            break;
166 //        case LONG_CURRENCY:
167 //            // if the long form, modify the patterns
168 //            CurrencyInfo names = new CurrencyInfo(resolvedCldrFile, canonicalKeywords,
169 //                currencyCodeOrUnit, parts);
170 //            if (!names.isEmpty()) {
171 //                for (String count : canonicalKeywords) {
172 //                    String unitPattern = names.getUnitPattern(count);
173 //                    int pos = unitPattern.indexOf("{0}");
174 //                    String prefixUnit = unitPattern.substring(0, pos);
175 //                    String suffixUnit = unitPattern.substring(pos + 3);
176 //                    String currencyName = names.getCurrencyName(count);
177 //                    addPrefixSuffixInfo(unitPrefixes, count,
178 //                        MessageFormat.format(prefixUnit, null, currencyName),
179 //                        MessageFormat.format(suffixUnit, null, currencyName));
180 //                }
181 //                break;
182 //            }
183 //            // otherwise fallthru
184 //        case CURRENCY:
185 //            DecimalFormat format2 = builder.getCurrencyFormat(currencyCodeOrUnit);
186 //            String prefix1 = format2.getPositivePrefix();
187 //            String suffix2 = format2.getPositiveSuffix();
188 //            for (String count : canonicalKeywords) {
189 //                addPrefixSuffixInfo(unitPrefixes, count, prefix1, suffix2);
190 //            }
191 //            break;
192 //        case ISO_CURRENCY:
193 //            throw new IllegalArgumentException();
194 //        case UNIT:
195 //            String unit = currencyCodeOrUnit == null ? "mass-kilogram" : currencyCodeOrUnit;
196 //            String otherValue = getUnitString(resolvedCldrFile, unit, "other");
197 //            for (String count : canonicalKeywords) {
198 //                String value = getUnitString(resolvedCldrFile, unit, count);
199 //                if (value == null) {
200 //                    value = otherValue;
201 //                }
202 //                int pos = value.indexOf("{0}");
203 //                addPrefixSuffixInfo(unitPrefixes, count, value.substring(0, pos), value.substring(pos + 3));
204 //            }
205 //            break;
206 //        }
207 
208         // DecimalFormat currencyFormat = new
209         // ICUServiceBuilder().setCldrFile(resolvedCldrFile).getCurrencyFormat("USD");
210         // for (String s : With.in(resolvedCldrFile.iterator("//ldml/numbers/currencyFormats/"))) {
211         // System.out.println(s + "\t" + resolvedCldrFile.getStringValue(s));
212         // }
213         // DecimalFormat currencyFormat = new DecimalFormat(pattern);
214         // do this by hand, because the DecimalFormat pattern parser does too much.
215         // String pattern =
216         // resolvedCldrFile.getWinningValue("//ldml/numbers/currencyFormats/currencyFormatLength/currencyFormat[@type=\"standard\"]/pattern[@type=\""
217         // +
218         // "standard" +
219         // "\"]");
220         // String[] currencyAffixes = new String[CompactDecimalFormat.AFFIX_SIZE];
221         // String[] patterns = pattern.split(";");
222         // final Matcher matcher = CURRENCY_PATTERN.matcher(patterns[0]);
223         // if (!matcher.matches()) {
224         // throw new IllegalArgumentException("Can't match currency pattern");
225         // }
226         // currencyAffixes[CompactDecimalFormat.POSITIVE_PREFIX] = matcher.group(1);
227         // currencyAffixes[CompactDecimalFormat.POSITIVE_SUFFIX] = matcher.group(2);
228         //
229 //        if (DEBUG) {
230 //            for (Entry<String, String[][]> keyList : affixes.entrySet()) {
231 //                System.out.println("*\t" + keyList.getKey() + "\t" + CldrUtility.toString(keyList));
232 //            }
233 //        }
234 
235         // TODO fix to get right symbol for the count
236         // String pattern = format.toPattern();
237 
238         // if (style == Style.LONG) {
239         // pattern = pattern.replace("¤", "¤¤¤");
240         // }
241 
242         // JCE 2017-03-28 - This constructor was removed in ICU 59.  Shane is working on a
243         // workaround, but until one is done, we can't use it in its current state.
244         // TODO: Put it back once the fix is in place, See Ticket #10166
245         //        try {
246 //            return new CompactDecimalFormat(
247 //                pattern, format.getDecimalFormatSymbols(),
248 //                style, pluralInfo.getPluralRules(),
249 //                divisor, affixes, unitPrefixes,
250 //                debugCreationErrors);
251 //        } catch (Exception e) {
252 //            debugCreationErrors.add(e.getMessage());
253 //            return null;
254 
255         CompactDecimalFormat cdf = CompactDecimalFormat.getInstance(locale, style);
256         ICUServiceBuilder builder = new ICUServiceBuilder().setCldrFile(resolvedCldrFile);
257 
258         cdf.setDecimalFormatSymbols(builder.getDecimalFormatSymbols("latn"));
259         cdf.setProperties(new PropertySetter() {
260             @Override
261             public void set(DecimalFormatProperties props) {
262                 props.setCompactCustomData(customData);
263             }
264         });
265         return cdf;
266 
267 //        debugCreationErrors.add("Can't create due to lack of 'from scratch' constructor for CompactDecimalFormat");
268 //        return null;
269 //        }
270         /*
271          *                 divisor, prefixes, suffixes,
272             unitPrefixes, unitSuffixes,
273             currencyAffixes, new CompactDecimalFormatTest.MyCurrencySymbolDisplay(resolvedCldrFile),
274             debugCreationErrors
275 
276          */
277         //        CompactDecimalFormat cdf = new CompactDecimalFormat(
278         //                "#,###.00",
279         //                DecimalFormatSymbols.getInstance(new ULocale("fr")),
280         //                CompactStyle.SHORT, PluralRules.createRules("one: j is 1 or f is 1"),
281         //                divisors, affixes, null,
282         //                debugCreationErrors
283         //                );
284 
285     }
286 
add(Map<A, Map<B, C>> customData, A a, B b, C c)287     private static <A, B, C> void add(Map<A, Map<B, C>> customData, A a, B b, C c) {
288         Map<B, C> inner = customData.get(a);
289         if (inner == null) {
290             customData.put(a, inner = new HashMap<>());
291         }
292         inner.put(b, c);
293     }
294 
addPrefixSuffixInfo(Map<String, String[]> unitPrefixes, String count, final String prefix, final String suffix)295     private static String[] addPrefixSuffixInfo(Map<String, String[]> unitPrefixes, String count,
296         final String prefix, final String suffix) {
297         return unitPrefixes.put(count, new String[] { escape(prefix), escape(suffix) });
298     }
299 
escape(String prefix)300     private static String escape(String prefix) {
301         return prefix.isEmpty() ? prefix : "'" + prefix.replace("'", "''") + "'";
302     }
303 
getUnitString(CLDRFile resolvedCldrFile, String unit, String count)304     private static String getUnitString(CLDRFile resolvedCldrFile, String unit, String count) {
305         return resolvedCldrFile.getStringValue("//ldml/units/unitLength[@type=\"short\"]/unit[@type=\""
306             + unit + "\"]/unitPattern[@count=\"" +
307             count +
308             "\"]");
309     }
310 
311     private static class CurrencyInfo {
312         private HashMap<String, String> currencyNames = new HashMap<String, String>();
313         private HashMap<String, String> unitPatterns = new HashMap<String, String>();
314 
CurrencyInfo(CLDRFile resolvedCldrFile, Set<String> canonicalKeywords, String currencyCode, XPathParts parts)315         CurrencyInfo(CLDRFile resolvedCldrFile, Set<String> canonicalKeywords, String currencyCode, XPathParts parts) {
316             Iterator<String> it;
317             it = resolvedCldrFile.iterator(
318                 "//ldml/numbers/currencies/currency[@type=\"" +
319                     currencyCode +
320                     "\"]/displayName");
321             // //ldml/numbers/currencies/currency[@type="SRD"]/symbol
322             while (it.hasNext()) {
323                 String path = it.next();
324                 parts.set(path);
325                 String key = parts.getAttributeValue(-1, "count");
326                 if (key == null) {
327                     key = "default";
328                 }
329                 currencyNames.put(key, resolvedCldrFile.getStringValue(path));
330             }
331 
332             it = resolvedCldrFile.iterator(
333                 "//ldml/numbers/currencyFormats/unitPattern");
334             while (it.hasNext()) {
335                 String path = it.next();
336                 parts.set(path);
337                 String key = parts.getAttributeValue(-1, "count");
338                 unitPatterns.put(key, resolvedCldrFile.getStringValue(path));
339             }
340             // <displayName count="one" draft="contributed">evro</displayName>
341             // flesh out missing
342         }
343 
isEmpty()344         public boolean isEmpty() {
345             return currencyNames.isEmpty();
346         }
347 
getUnitPattern(String count)348         String getUnitPattern(String count) {
349             String result = unitPatterns.get(count);
350             if (result == null) {
351                 result = unitPatterns.get("other");
352             }
353             return result;
354         }
355 
getCurrencyName(String count)356         String getCurrencyName(String count) {
357             String result = currencyNames.get(count);
358             if (result != null) {
359                 return result;
360             }
361             if (!count.equals("other")) {
362                 result = currencyNames.get("other");
363                 if (result != null) {
364                     return result;
365                 }
366             }
367             result = currencyNames.get("default");
368             return result;
369         }
370     }
371 
372     // <currencyFormats numberSystem="latn">
373     // <currencyFormatLength>
374     // <currencyFormat>
375     // <pattern>¤#,##0.00;(¤#,##0.00)</pattern>
376     // </currencyFormat>
377     // </currencyFormatLength>
378     // <unitPattern count="one">{0} {1}</unitPattern>
379     // <unitPattern count="other">{0} {1}</unitPattern>
380     // </currencyFormats>
381 
382     static class MyCurrencySymbolDisplay {
383         CLDRFile cldrFile;
384 
MyCurrencySymbolDisplay(CLDRFile cldrFile)385         public MyCurrencySymbolDisplay(CLDRFile cldrFile) {
386             this.cldrFile = cldrFile;
387         }
388 
getName(Currency currency, int count)389         public String getName(Currency currency, int count) {
390             final String currencyCode = currency.getCurrencyCode();
391             if (count > 1) {
392                 return currencyCode;
393             }
394             String prefix = "//ldml/numbers/currencies/currency[@type=\"" + currencyCode + "\"]/";
395             String currencySymbol = cldrFile.getWinningValue(prefix + "symbol");
396             return currencySymbol != null ? currencySymbol : currencyCode;
397         }
398     };
399 
400 }
401