1 package org.unicode.cldr.util; 2 3 import com.google.common.collect.ImmutableSet; 4 import com.ibm.icu.text.PluralRules; 5 import java.util.Collection; 6 import java.util.Collections; 7 import java.util.HashSet; 8 import java.util.Set; 9 10 public class PluralRulesUtil { 11 /** Status of the keyword for the rules, given a set of explicit values. */ 12 public enum KeywordStatus { 13 /** The keyword is not valid for the rules. */ 14 INVALID, 15 /** The keyword is valid, but unused (it is covered by the explicit values). */ 16 SUPPRESSED, 17 /** The keyword is valid and used, but completely covered by the explicit values. */ 18 UNIQUE, 19 /** The keyword is valid, used, not suppressed, and has a finite set of values. */ 20 BOUNDED, 21 /** The keyword is valid but not bounded; there are indefinitely many matching values. */ 22 UNBOUNDED 23 } 24 25 /** 26 * Find the status for the keyword, given a certain set of explicit values. 27 * 28 * @param rules the PluralRules 29 * @param keyword the particular keyword (call rules.getKeywords() to get the valid ones) 30 * @param offset the offset used, or 0.0d if not. Internally, the offset is subtracted from each 31 * explicit value before checking against the keyword values. 32 * @param explicits a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or 33 * null. 34 * @param integerOnly In circumstances where the values are known to be integers, this parameter 35 * can be set to true. Examples: "There are 3 people in..." (integerOnly=true) vs. "There 36 * are 1.2 people per household (integerOnly=false). This may produce different results in 37 * languages where fractions have the same format as integers for some keywords. 38 * @return the KeywordStatus 39 * <p>NOTE: For testing, this is a static with the first parameter being the rules. Those 40 * will disappear. 41 */ getKeywordStatus( PluralRules rules, String keyword, int offset, Set<Double> explicits, boolean integerOnly)42 public static KeywordStatus getKeywordStatus( 43 PluralRules rules, 44 String keyword, 45 int offset, 46 Set<Double> explicits, 47 boolean integerOnly) { 48 if (!rules.getKeywords().contains(keyword)) { 49 return KeywordStatus.INVALID; 50 } 51 Collection<Double> values = rules.getAllKeywordValues(keyword); 52 if (values == null) { 53 return KeywordStatus.UNBOUNDED; 54 } 55 int originalSize = values.size(); 56 57 // Quick check on whether there are multiple elements 58 59 if (explicits == null) { 60 explicits = Collections.emptySet(); 61 } 62 if (originalSize > explicits.size()) { 63 return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED; 64 } 65 66 // Compute if the quick test is insufficient. 67 68 HashSet<Double> subtractedSet = new HashSet<>(values); 69 for (Double explicit : explicits) { 70 // int rounded = (int) Math.round(explicit*1000000); 71 subtractedSet.remove(explicit - offset); 72 } 73 if (subtractedSet.size() == 0) { 74 return KeywordStatus.SUPPRESSED; 75 } 76 77 return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED; 78 } 79 80 /** 81 * Locales where 'many' is optional. TODO get ICU to add a method that determines if a plural 82 * keyword's rule is only true if the compact operand is set. 83 */ 84 public static final ImmutableSet<String> LOCALES_WITH_OPTIONAL_MANY = 85 ImmutableSet.of("ca", "es", "fr", "it", "pt", "pt_PT"); 86 87 // static final Map<String,Set<String>> locale2keywords = new HashMap<String,Set<String>>(); 88 // static final Map<String,PluralRules> locale2pluralRules = new HashMap<String,PluralRules>(); 89 // static final Set<Double> explicits = new HashSet<Double>(); 90 // static { 91 // explicits.add(0.0d); 92 // explicits.add(1.0d); 93 // } 94 // public static Set<String> getCanonicalKeywords(String locale) { 95 // synchronized (locale2keywords) { 96 // Set<String> result = locale2keywords.get(locale); 97 // if (result != null) { 98 // return result; 99 // } 100 // // special caching because locales don't differ 101 // int pos = locale.indexOf('_'); 102 // String lang = pos < 0 ? locale : locale.substring(0,pos); 103 // if (pos >= 0) { 104 // result = locale2keywords.get(locale); 105 // if (result != null) { 106 // locale2keywords.put(locale, result); 107 // return result; 108 // } 109 // } 110 // PluralInfo pluralInfo = 111 // SupplementalDataInfo.getInstance().getPlurals(SupplementalDataInfo.PluralType.cardinal, 112 // lang); 113 // PluralRules pluralRules = PluralRules.createRules(pluralInfo.getRules()); 114 // locale2pluralRules.put(lang, pluralRules); 115 // result = new HashSet(); 116 // for (String keyword : pluralRules.getKeywords()) { 117 // KeywordStatus status = getKeywordStatus(pluralRules, keyword, 0, explicits, true); 118 // if (status != KeywordStatus.SUPPRESSED) { 119 // result.add(keyword); 120 // } 121 // } 122 // result = Collections.unmodifiableSet(result); 123 // locale2keywords.put(locale, result); 124 // if (pos >= 0) { 125 // locale2keywords.put(lang, result); 126 // } 127 // return result; 128 // } 129 // 130 // } 131 } 132