xref: /aosp_15_r20/external/cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/PluralRulesUtil.java (revision 912701f9769bb47905792267661f0baf2b85bed5)
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