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