• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.unicode.cldr.util;
2 
3 import java.util.Arrays;
4 import java.util.Collection;
5 import java.util.Collections;
6 import java.util.Comparator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Map.Entry;
10 import java.util.Set;
11 import java.util.TreeMap;
12 import java.util.TreeSet;
13 import java.util.regex.Matcher;
14 import java.util.regex.Pattern;
15 import java.util.stream.Collectors;
16 
17 import org.unicode.cldr.util.UnitConverter.UnitSystem;
18 
19 import com.google.common.base.Joiner;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.common.collect.ImmutableSet;
22 import com.google.common.collect.ImmutableSortedSet;
23 import com.ibm.icu.util.Freezable;
24 import com.ibm.icu.util.Output;
25 
26 /**
27  * Get the info from supplemental data, eg CLDRConfig.getInstance().getSupplementalDataInfo().getGrammarInfo("fr"); Use hasGrammarInfo() to see which locales have it.
28  * @author markdavis
29  *
30  */
31 public class GrammarInfo implements Freezable<GrammarInfo>{
32 
33     public enum GrammaticalTarget {nominal}
34 
35     /**
36      * The ordering of these values is intended to put the default values first, and to group values together that tend to have similar forms for the most common cases,
37      * then have the rest in alphabetical order.
38      */
39     public enum CaseValues {nominative, vocative, accusative, oblique, genitive, dative, locative, instrumental, prepositional, ablative,
40         abessive, adessive, allative, causal, comitative, delative, elative, ergative, essive, illative, inessive, locativecopulative, partitive, sociative, sublative, superessive, terminative, translative;
41         public static Comparator<String> COMPARATOR = EnumComparator.create(CaseValues.class);
42     }
43     public enum GenderValues {neuter, masculine, inanimate, animate, common, personal, feminine;
44         public static Comparator<String> COMPARATOR = EnumComparator.create(GenderValues.class);
45     }
46     public enum DefinitenessValues {unspecified, indefinite, definite, construct;
47         public static Comparator<String> COMPARATOR = EnumComparator.create(DefinitenessValues.class);
48     }
49     public enum PluralValues {zero, one, two, few, many, other;
50         public static Comparator<String> COMPARATOR = EnumComparator.create(PluralValues.class);
51     }
52 
53     public enum GrammaticalFeature {
54         grammaticalNumber("plural", "Ⓟ", "other", PluralValues.COMPARATOR),
55         grammaticalCase("case", "Ⓒ", "nominative", CaseValues.COMPARATOR),
56         grammaticalDefiniteness("definiteness", "Ⓓ", "indefinite", DefinitenessValues.COMPARATOR),
57         grammaticalGender("gender", "Ⓖ", "neuter", GenderValues.COMPARATOR);
58 
59         private final String shortName;
60         private final String symbol;
61         private final String defaultValue;
62         private final Comparator<String> comparator;
63 
64         public static final Pattern PATH_HAS_FEATURE = Pattern.compile("\\[@(count|case|gender|definiteness)=");
65 
GrammaticalFeature(String shortName, String symbol, String defaultValue, Comparator<String> comparator)66         GrammaticalFeature(String shortName, String symbol, String defaultValue, Comparator<String> comparator) {
67             this.shortName = shortName;
68             this.symbol = symbol;
69             this.defaultValue = defaultValue;
70             this.comparator = comparator;
71         }
getShortName()72         public String getShortName() {
73             return shortName;
74         }
getSymbol()75         public CharSequence getSymbol() {
76             return symbol;
77         }
78         /**
79          * Gets the default value. The parameter only needs to be set for grammaticalGender
80          */
getDefault(Collection<String> featureValuesFromGrammaticalInfo)81         public String getDefault(Collection<String> featureValuesFromGrammaticalInfo) {
82             return this == grammaticalGender
83                 && featureValuesFromGrammaticalInfo != null
84                     && !featureValuesFromGrammaticalInfo.contains("neuter")
85                     ? "masculine"
86                         : defaultValue;
87         }
pathHasFeature(String path)88         public static Matcher pathHasFeature(String path) {
89             Matcher result = PATH_HAS_FEATURE.matcher(path);
90             return result.find() ? result : null;
91         }
92         static final Map<String, GrammaticalFeature> shortNameToEnum =
93             ImmutableMap.copyOf(Arrays.asList(GrammaticalFeature.values())
94                 .stream()
95                 .collect(Collectors.toMap(e -> e.shortName, e -> e)));
96 
fromName(String name)97         public static GrammaticalFeature fromName(String name) {
98             GrammaticalFeature result = shortNameToEnum.get(name);
99             return result != null ? result : valueOf(name);
100         }
getValueComparator()101         public Comparator getValueComparator() {
102             return comparator;
103         }
104     }
105 
106     public enum GrammaticalScope {general, units}
107 
108     private Map<GrammaticalTarget, Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>>> targetToFeatureToUsageToValues = new TreeMap<>();
109     private boolean frozen = false;
110 
111     /** Only internal */
112     @Deprecated
add(GrammaticalTarget target, GrammaticalFeature feature, GrammaticalScope usage, String value)113     public void add(GrammaticalTarget target, GrammaticalFeature feature, GrammaticalScope usage, String value) {
114         Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> featureToUsageToValues = targetToFeatureToUsageToValues.get(target);
115         if (featureToUsageToValues == null) {
116             targetToFeatureToUsageToValues.put(target, featureToUsageToValues = new TreeMap<>());
117         }
118         if (feature != null) {
119             Map<GrammaticalScope,Set<String>> usageToValues = featureToUsageToValues.get(feature);
120             if (usageToValues == null) {
121                 featureToUsageToValues.put(feature, usageToValues = new TreeMap<>());
122             }
123             Set<String> values = usageToValues.get(usage);
124             if (values == null) {
125                 usageToValues.put(usage, values = new TreeSet<>());
126             }
127             if (value != null) {
128                 values.add(value);
129             } else {
130                 int debug = 0;
131             }
132         }
133     }
134 
135     /** Only internal */
136     @Deprecated
add(GrammaticalTarget target, GrammaticalFeature feature, GrammaticalScope usage, Collection<String> valueSet)137     public void add(GrammaticalTarget target, GrammaticalFeature feature, GrammaticalScope usage, Collection<String> valueSet) {
138         Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> featureToUsageToValues = targetToFeatureToUsageToValues.get(target);
139         if (featureToUsageToValues == null) {
140             targetToFeatureToUsageToValues.put(target, featureToUsageToValues = new TreeMap<>());
141         }
142         if (feature != null) {
143             Map<GrammaticalScope,Set<String>> usageToValues = featureToUsageToValues.get(feature);
144             if (usageToValues == null) {
145                 featureToUsageToValues.put(feature, usageToValues = new TreeMap<>());
146             }
147             Set<String> values = usageToValues.get(usage);
148             if (values == null) {
149                 usageToValues.put(usage, values = new TreeSet<>());
150             }
151             validate(feature, valueSet);
152             values.addAll(valueSet);
153         }
154     }
155 
156 
validate(GrammaticalFeature feature, Collection<String> valueSet)157     private void validate(GrammaticalFeature feature, Collection<String> valueSet) {
158         for (String value : valueSet) {
159             validate(feature, value);
160         }
161     }
162 
validate(GrammaticalFeature feature, String value)163     private void validate(GrammaticalFeature feature, String value) {
164         switch (feature) {
165         case grammaticalCase: CaseValues.valueOf(value); break;
166         case grammaticalDefiniteness: DefinitenessValues.valueOf(value); break;
167         case grammaticalGender: GenderValues.valueOf(value); break;
168         case grammaticalNumber: PluralValues.valueOf(value); break;
169         }
170     }
171 
172     /**
173      * Note: when there is known to be no features, the featureRaw will be null
174      * Only internal */
175     @Deprecated
add(String targetsRaw, String featureRaw, String usagesRaw, String valuesRaw)176     public void add(String targetsRaw, String featureRaw, String usagesRaw, String valuesRaw) {
177         for (String targetString : SupplementalDataInfo.split_space.split(targetsRaw)) {
178             GrammaticalTarget target = GrammaticalTarget.valueOf(targetString);
179             if (featureRaw == null) {
180                 add(target, null, null, (String)null);
181             } else {
182                 final GrammaticalFeature feature = GrammaticalFeature.valueOf(featureRaw);
183 
184                 List<String> usages = usagesRaw == null ? Collections.singletonList(GrammaticalScope.general.toString()) : SupplementalDataInfo.split_space.splitToList(usagesRaw);
185 
186                 List<String> values = valuesRaw == null ? Collections.emptyList() : SupplementalDataInfo.split_space.splitToList(valuesRaw);
187                 for (String usageRaw : usages) {
188                     GrammaticalScope usage = GrammaticalScope.valueOf(usageRaw);
189                     add(target, feature, usage, values);
190                 }
191             }
192         }
193     }
194 
195     @Override
isFrozen()196     public boolean isFrozen() {
197         return frozen;
198     }
199 
200     @Override
freeze()201     public GrammarInfo freeze() {
202         if (!frozen) {
203             Map<GrammaticalTarget, Map<GrammaticalFeature, Map<GrammaticalScope, Set<String>>>> temp = CldrUtility.protectCollection(targetToFeatureToUsageToValues);
204             if (!temp.equals(targetToFeatureToUsageToValues)) {
205                 throw new IllegalArgumentException();
206             }
207             targetToFeatureToUsageToValues = temp;
208             frozen = true;
209         }
210         return this;
211     }
212 
213     @Override
cloneAsThawed()214     public GrammarInfo cloneAsThawed() {
215         GrammarInfo result = new GrammarInfo();
216         this.forEach3((t,f,u,v) -> result.add(t,f,u,v));
217         return result;
218     }
219 
220     static interface Handler4<T,F,U,V> {
apply(T t, F f, U u, V v)221         void apply(T t, F f, U u, V v);
222     }
223 
forEach(Handler4<GrammaticalTarget, GrammaticalFeature, GrammaticalScope, String> handler)224     public void forEach(Handler4<GrammaticalTarget, GrammaticalFeature, GrammaticalScope, String> handler) {
225         for (Entry<GrammaticalTarget, Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>>> entry1 : targetToFeatureToUsageToValues.entrySet()) {
226             GrammaticalTarget target = entry1.getKey();
227             final Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> featureToUsageToValues = entry1.getValue();
228             if (featureToUsageToValues.isEmpty()) {
229                 handler.apply(target, null, null, null);
230             } else
231                 for (Entry<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> entry2 : featureToUsageToValues.entrySet()) {
232                     GrammaticalFeature feature = entry2.getKey();
233                     for (Entry<GrammaticalScope, Set<String>> entry3 : entry2.getValue().entrySet()) {
234                         final GrammaticalScope usage = entry3.getKey();
235                         for (String value : entry3.getValue()) {
236                             handler.apply(target, feature, usage, value);
237                         }
238                     }
239                 }
240         }
241     }
242 
243     static interface Handler3<T,F,U, V> {
apply(T t, F f, U u, V v)244         void apply(T t, F f, U u, V v);
245     }
246 
forEach3(Handler3<GrammaticalTarget, GrammaticalFeature, GrammaticalScope, Collection<String>> handler)247     public void forEach3(Handler3<GrammaticalTarget, GrammaticalFeature, GrammaticalScope, Collection<String>> handler) {
248         for (Entry<GrammaticalTarget, Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>>> entry1 : targetToFeatureToUsageToValues.entrySet()) {
249             GrammaticalTarget target = entry1.getKey();
250             final Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> featureToUsageToValues = entry1.getValue();
251             if (featureToUsageToValues.isEmpty()) {
252                 handler.apply(target, null, null, null);
253             } else
254                 for (Entry<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> entry2 : featureToUsageToValues.entrySet()) {
255                     GrammaticalFeature feature = entry2.getKey();
256                     for (Entry<GrammaticalScope, Set<String>> entry3 : entry2.getValue().entrySet()) {
257                         final GrammaticalScope usage = entry3.getKey();
258                         final Collection<String> values = entry3.getValue();
259                         handler.apply(target, feature, usage, values);
260                     }
261                 }
262         }
263     }
264 
265     /** Returns null if there is no known information. Otherwise returns the information for the locale (which may be empty if there are no variants) */
get(GrammaticalTarget target, GrammaticalFeature feature, GrammaticalScope usage)266     public Collection<String> get(GrammaticalTarget target, GrammaticalFeature feature, GrammaticalScope usage) {
267         Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> featureToUsageToValues = targetToFeatureToUsageToValues.get(target);
268         if (featureToUsageToValues == null) {
269             return Collections.emptySet();
270         }
271         Map<GrammaticalScope,Set<String>> usageToValues = featureToUsageToValues.get(feature);
272         if (usageToValues == null) {
273             return Collections.emptySet();
274         }
275         Collection<String> result = usageToValues.get(usage);
276         return result == null
277             ? usageToValues.get(GrammaticalScope.general)
278                 : result;
279     }
280 
get(GrammaticalTarget target, GrammaticalFeature feature)281     public Map<GrammaticalScope, Set<String>> get(GrammaticalTarget target, GrammaticalFeature feature) {
282         Map<GrammaticalFeature, Map<GrammaticalScope,Set<String>>> featureToUsageToValues = targetToFeatureToUsageToValues.get(target);
283         if (featureToUsageToValues == null) {
284             return Collections.emptyMap();
285         }
286         Map<GrammaticalScope,Set<String>> usageToValues = featureToUsageToValues.get(feature);
287         if (usageToValues == null) {
288             return Collections.emptyMap();
289         }
290         return usageToValues;
291     }
292 
293 
hasInfo(GrammaticalTarget target)294     public boolean hasInfo(GrammaticalTarget target) {
295         return targetToFeatureToUsageToValues.containsKey(target);
296     }
297 
298     @Override
toString()299     public String toString() {
300         return toString("\n");
301     }
toString(String lineSep)302     public String toString(String lineSep) {
303         StringBuilder result = new StringBuilder();
304         this.forEach3((t,f,u, v) ->
305         {
306             result.append(lineSep);
307             result.append("{" + (t == null ? "" : t.toString()) + "}"
308                 + "\t{" + (f == null ? "" : f.toString()) + "}"
309                 + "\t{" +  (u == null ? "" : u.toString()) + "}"
310                 + "\t{" +  (v == null ? "" : Joiner.on(' ').join(v)) + "}");
311         });
312         return result.toString();
313     }
314 
getGrammaticalInfoAttributes(GrammarInfo grammarInfo, UnitPathType pathType, String plural, String gender, String caseVariant)315     static public String getGrammaticalInfoAttributes(GrammarInfo grammarInfo, UnitPathType pathType, String plural, String gender, String caseVariant) {
316         String grammaticalAttributes = "";
317         if (pathType.features.contains(GrammaticalFeature.grammaticalNumber)) { // count is special
318             grammaticalAttributes += "[@count=\"" + (plural == null ? "other" : plural) + "\"]";
319         }
320         if (grammarInfo != null && gender != null
321             && pathType.features.contains(GrammaticalFeature.grammaticalGender)
322             ) {
323             Collection<String> genders = grammarInfo.get(GrammaticalTarget.nominal, GrammaticalFeature.grammaticalGender, GrammaticalScope.units);
324             if (!gender.equals(GrammaticalFeature.grammaticalGender.getDefault(genders))) {
325                 grammaticalAttributes += "[@gender=\"" + gender + "\"]";
326             }
327         }
328         if (grammarInfo != null && caseVariant != null
329             && pathType.features.contains(GrammaticalFeature.grammaticalCase)
330             && !caseVariant.equals(GrammaticalFeature.grammaticalCase.getDefault(null))) {
331             grammaticalAttributes += "[@case=\"" + caseVariant + "\"]";
332         }
333         return grammaticalAttributes;
334     }
335 
336     /**
337      * TODO: change this to be data-file driven
338      */
339     private static final Set<String> CORE_UNITS_NEEDING_GRAMMAR = ImmutableSet.of(
340         // new in v38
341         "mass-grain",
342         "volume-dessert-spoon",
343         "volume-dessert-spoon-imperial",
344         "volume-drop",
345         "volume-dram",
346         "volume-jigger",
347         "volume-pinch",
348         "volume-quart-imperial",
349         // "volume-pint-imperial",
350 
351         "acceleration-meter-per-square-second", "area-acre", "area-hectare",
352         "area-square-centimeter", "area-square-foot", "area-square-kilometer", "area-square-mile", "concentr-percent", "consumption-mile-per-gallon",
353         "consumption-mile-per-gallon-imperial", "duration-day", "duration-hour", "duration-minute", "duration-month", "duration-second", "duration-week",
354         "duration-year", "energy-foodcalorie", "energy-kilocalorie", "length-centimeter", "length-foot", "length-inch", "length-kilometer", "length-meter",
355         "length-mile", "length-millimeter", "length-parsec", "length-picometer", "length-solar-radius", "length-yard", "light-solar-luminosity", "mass-dalton",
356         "mass-earth-mass", "mass-milligram", "mass-solar-mass", "pressure-kilopascal", "speed-kilometer-per-hour", "speed-meter-per-second", "speed-mile-per-hour",
357         "temperature-celsius", "temperature-fahrenheit", "temperature-generic", "temperature-kelvin", "acceleration-g-force", "consumption-liter-per-100-kilometer",
358         "mass-gram", "mass-kilogram", "mass-ounce", "mass-pound", "volume-centiliter", "volume-cubic-centimeter", "volume-cubic-foot", "volume-cubic-mile",
359         "volume-cup", "volume-deciliter", "volume-fluid-ounce", "volume-fluid-ounce-imperial", "volume-gallon", "volume-gallon", "volume-gallon-imperial",
360         "volume-liter", "volume-milliliter", "volume-pint", "volume-quart", "volume-tablespoon", "volume-teaspoon");
361     // compounds
362     // "kilogram-per-cubic-meter", "kilometer-per-liter", "concentr-gram-per-mole", "speed-mile-per-second", "volumetricflow-cubic-foot-per-second",
363     // "volumetricflow-cubic-meter-per-second", "gram-per-cubic-centimeter",
364 
365 
getSourceCaseAndPlural(String locale, String gender, String value, String desiredCase, String desiredPlural, Output<String> sourceCase, Output<String> sourcePlural)366     public void getSourceCaseAndPlural(String locale, String gender, String value, String desiredCase, String desiredPlural,
367         Output<String> sourceCase, Output<String> sourcePlural) {
368         switch(locale) {
369         case "pl":
370             getSourceCaseAndPluralPolish(gender, value, desiredCase, desiredPlural, sourceCase, sourcePlural);
371             break;
372         case "ru":
373             getSourceCaseAndPluralRussian(gender, value, desiredCase, desiredPlural, sourceCase, sourcePlural);
374             break;
375         default:
376             throw new UnsupportedOperationException(locale);
377         }
378     }
379 
380     /** Russian rules for paucal (few) and fractional (other)
381      * <pre>
382      * plural = other
383      * Nominative ⇒ genitive singular
384      * Accusative + masculine ⇒ genitive singular
385      * All other combinations of gender + case ⇒ same-case, plural
386      *
387      * Other
388      * genitive singular
389      *
390      * Plurals:
391      *   one,
392      *   few (2~4),
393      *   many, = plural
394      *   other (where other is 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0)
395      * </pre>
396      */
getSourceCaseAndPluralRussian(String gender, String value, String desiredCase, String desiredPlural, Output<String> sourceCase, Output<String> sourcePlural)397     private void getSourceCaseAndPluralRussian(String gender, String value,
398         String desiredCase, String desiredPlural,
399         Output<String> sourceCase, Output<String> sourcePlural) {
400         switch (desiredPlural) {
401         case "few":
402             // default source
403             sourceCase.value = desiredCase;
404             sourcePlural.value = "many";
405             // special cases
406             switch (desiredCase) {
407             case "nominative":
408                 sourceCase.value = "genitive";
409                 sourcePlural.value = "one";
410                 break;
411             case "accusative":
412                 switch (gender) {
413                 case "masculine":
414                     sourceCase.value = "genitive";
415                     sourcePlural.value = "one";
416                     break;
417                 }
418                 break;
419             }
420         case "other":
421             sourceCase.value = "genitive";
422             sourcePlural.value = "one";
423             return;
424         }
425     }
426 
427     /** Polish rules
428      * <pre>
429      * plural = few
430      *
431      * neuter + ending in -um + (nominative, accusative) ⇒ vocative plural
432      * Feminine||neuter + (nominative, accusative) ⇒ genitive singular
433      * Animate||inanimate + (nominative, accusative) ⇒ vocative plural
434      * Personal + nominative ⇒ vocative plural
435      * Personal + accusative ⇒ genitive plural
436      * All other combinations of gender + case ⇒ same-case, plural
437      *
438      * plural = other
439      * genitive singular
440      *
441      * Plurals:
442      *   one,
443      *   few (2~4),
444      *   many, = plural
445      *   other (where other is 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0)
446      * </pre>
447      */
getSourceCaseAndPluralPolish(String gender, String value, String desiredCase, String desiredPlural, Output<String> sourceCase, Output<String> sourcePlural)448     private void getSourceCaseAndPluralPolish(String gender, String value,
449         String desiredCase, String desiredPlural,
450         Output<String> sourceCase, Output<String> sourcePlural) {
451         switch (desiredPlural) {
452         case "few":
453             // default
454             sourceCase.value = desiredCase;
455             sourcePlural.value = "many";
456             // special cases
457             boolean isNominative = false;
458             switch (desiredCase) {
459             case "nominative":
460                 isNominative = true;
461             case "vocative":
462             case "accusative":
463                 switch (gender) {
464                 case "neuter":
465                     if (value.endsWith("um")) {
466                         sourceCase.value = "vocative";
467                         break;
468                     }
469                     // otherwise fall thorugh to feminine
470                 case "feminine":
471                     sourceCase.value = "nominative";
472                     sourcePlural.value = "few";
473                     break;
474                 case "animate":
475                 case "inanimate":
476                     sourceCase.value = "vocative";
477                     break;
478                 case "personal":
479                     sourceCase.value = isNominative ? "vocative" : "genitive";
480                     break;
481                 }
482                 break;
483             }
484             return;
485         case "other":
486             sourceCase.value = "genitive";
487             sourcePlural.value = "one";
488             return;
489         }
490     }
491 
492     /**
493      * Internal class for thread-safety
494      */
495     static class GrammarLocales {
496         static final Set<String> data = ImmutableSortedSet.copyOf(ImmutableSet.<String>builder()
497             .addAll(
498                 CLDRConfig.getInstance().getSupplementalDataInfo()
499                 .getLocalesWithFeatures(GrammaticalTarget.nominal, GrammaticalScope.units, GrammaticalFeature.grammaticalCase))
500             .addAll(
501                 CLDRConfig.getInstance().getSupplementalDataInfo()
502                 .getLocalesWithFeatures(GrammaticalTarget.nominal, GrammaticalScope.units, GrammaticalFeature.grammaticalGender)
503                 ).build());
504     }
505 
506     /**
507      * Return the locales that have either case or gender info for units (or both).
508      */
getGrammarLocales()509     public static Set<String> getGrammarLocales() {
510         return GrammarLocales.data;
511     }
512 
513     static final Set<String> INCLUDE_OTHER = ImmutableSet.of(
514         "g-force",
515         "arc-minute",
516         "arc-second",
517         "degree",
518         "revolution",
519         "bit",
520         "byte",
521         "week",
522         "calorie",
523         "pixel",
524         "generic",
525         "karat",
526         "percent",
527         "permille",
528         "permillion",
529         "permyriad",
530         "atmosphere",
531         "em",
532         "century",
533         "decade",
534         "month",
535         "year"
536         );
537     public static final boolean DEBUG = false;
538     /**
539      * Internal class for thread-safety
540      */
541     static class UnitsToAddGrammar {
542         static final Set<String> data;
543         static {
544             final CLDRConfig config = CLDRConfig.getInstance();
545             final UnitConverter converter = config.getSupplementalDataInfo().getUnitConverter();
546             Set<String> missing = new TreeSet<>();
547             Set<String> _data = new TreeSet<>();
548             for (String path : With.in(config.getRoot().iterator("//ldml/units/unitLength[@type=\"short\"]/unit"))) {
549                 XPathParts parts = XPathParts.getFrozenInstance(path);
550                 String unit = parts.getAttributeValue(3, "type");
551                 // Add simple units
552                 String shortUnit = converter.getShortId(unit);
553                 if (INCLUDE_OTHER.contains(shortUnit)) {
554                     _data.add(unit);
555                     continue;
556                 }
557                 Set<UnitSystem> systems = converter.getSystemsEnum(shortUnit);
558                 if (converter.isSimple(shortUnit)
559                     && !Collections.disjoint(systems, UnitSystem.SiOrMetric)) {
560                     _data.add(unit);
561                     continue;
562                 }
563                 missing.add(unit);
564             }
565             if (DEBUG) for (String unit : missing) {
566                 String shortUnit = converter.getShortId(unit);
567                 System.out.println("*Skipping\t" + unit
568                     + "\t" + converter.getQuantityFromUnit(shortUnit, false)
569                     + "\t" + converter.getSystemsEnum(shortUnit)
570                     + "\t" + (converter.isSimple(shortUnit) ? "SIMPLE" : ""));
571             }
572             data = ImmutableSet.copyOf(_data);
573         }
574     }
575 
576     /**
577      * Return the units that we should get grammar information for.
578      */
getUnitsToAddGrammar()579     public static Set<String> getUnitsToAddGrammar() {
580         return UnitsToAddGrammar.data;
581     }
582 }