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