1 package org.unicode.cldr.util;
2 
3 import java.util.EnumSet;
4 
5 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo.Count;
6 
7 import com.ibm.icu.impl.Utility;
8 import com.ibm.icu.util.Freezable;
9 import com.ibm.icu.util.Output;
10 import com.ibm.icu.util.ULocale;
11 
12 /**
13  * Utility class for returning the plural category for a range of numbers, such as 1–5, so that appropriate messages can be chosen.
14  * The rules for determining this value vary widely across locales.
15  * @author markdavis
16  */
17 public final class PluralRanges implements Comparable<PluralRanges>, Freezable<PluralRanges> {
18 
19     /**
20      * Internal class for mapping from two Count values to another.
21      * @internal
22      * @deprecated
23      */
24     public static final class Matrix implements Comparable<Matrix>, Cloneable {
25         private byte[] data = new byte[Count.LENGTH * Count.LENGTH];
26         {
27             for (int i = 0; i < data.length; ++i) {
28                 data[i] = -1;
29             }
30         }
31 
32         /**
33          * Internal method for setting.
34          * @internal
35          * @deprecated
36          */
set(Count start, Count end, Count result)37         public void set(Count start, Count end, Count result) {
38             data[start.ordinal() * Count.LENGTH + end.ordinal()] = result == null ? (byte) -1 : (byte) result.ordinal();
39         }
40 
41         /**
42          * Internal method for setting; throws exception if already set.
43          * @internal
44          * @deprecated
45          */
setIfNew(Count start, Count end, Count result)46         public void setIfNew(Count start, Count end, Count result) {
47             byte old = data[start.ordinal() * Count.LENGTH + end.ordinal()];
48             if (old >= 0) {
49                 throw new IllegalArgumentException("Previously set value for <" +
50                     start + ", " + end + ", " + Count.VALUES.get(old) + ">");
51             }
52             data[start.ordinal() * Count.LENGTH + end.ordinal()] = result == null ? (byte) -1 : (byte) result.ordinal();
53         }
54 
55         /**
56          * Internal method for getting.
57          * @internal
58          * @deprecated
59          */
get(Count start, Count end)60         public Count get(Count start, Count end) {
61             byte result = data[start.ordinal() * Count.LENGTH + end.ordinal()];
62             return result < 0 ? null : Count.VALUES.get(result);
63         }
64 
65         /**
66          * Internal method to see if <*,end> values are all the same.
67          * @internal
68          * @deprecated
69          */
endSame(Count end)70         public Count endSame(Count end) {
71             Count first = null;
72             for (Count start : Count.VALUES) {
73                 Count item = get(start, end);
74                 if (item == null) {
75                     continue;
76                 }
77                 if (first == null) {
78                     first = item;
79                     continue;
80                 }
81                 if (first != item) {
82                     return null;
83                 }
84             }
85             return first;
86         }
87 
88         /**
89          * Internal method to see if <start,*> values are all the same.
90          * @internal
91          * @deprecated
92          */
startSame(Count start, EnumSet<Count> endDone, Output<Boolean> emit)93         public Count startSame(Count start, EnumSet<Count> endDone, Output<Boolean> emit) {
94             emit.value = false;
95             Count first = null;
96             for (Count end : Count.VALUES) {
97                 Count item = get(start, end);
98                 if (item == null) {
99                     continue;
100                 }
101                 if (first == null) {
102                     first = item;
103                     continue;
104                 }
105                 if (first != item) {
106                     return null;
107                 }
108                 // only emit if we didn't cover with the 'end' values
109                 if (!endDone.contains(end)) {
110                     emit.value = true;
111                 }
112             }
113             return first;
114         }
115 
116         @Override
hashCode()117         public int hashCode() {
118             int result = 0;
119             for (int i = 0; i < data.length; ++i) {
120                 result = result * 37 + data[i];
121             }
122             return result;
123         }
124 
125         @Override
equals(Object other)126         public boolean equals(Object other) {
127             if (!(other instanceof Matrix)) {
128                 return false;
129             }
130             return 0 == compareTo((Matrix) other);
131         }
132 
133         @Override
compareTo(Matrix o)134         public int compareTo(Matrix o) {
135             for (int i = 0; i < data.length; ++i) {
136                 int diff = data[i] - o.data[i];
137                 if (diff != 0) {
138                     return diff;
139                 }
140             }
141             return 0;
142         }
143 
144         @Override
clone()145         public Matrix clone() {
146             Matrix result = new Matrix();
147             result.data = data.clone();
148             return result;
149         }
150     }
151 
152     private Matrix matrix = new Matrix();
153     private boolean[] explicit = new boolean[Count.LENGTH];
154 
155     /**
156      * Returns a
157      * @return
158      */
getInstance(ULocale locale)159     public static PluralRanges getInstance(ULocale locale) {
160         return null;
161     }
162 
163     /**
164      * Internal method for building. If the start or end are null, it means everything of that type.
165      * @param rangeStart
166      * @param rangeEnd
167      * @param result
168      * @internal
169      * @deprecated
170      */
add(Count rangeStart, Count rangeEnd, Count result)171     public void add(Count rangeStart, Count rangeEnd, Count result) {
172         explicit[result.ordinal()] = true;
173         if (rangeStart == null) {
174             for (Count rs : Count.values()) {
175                 if (rangeEnd == null) {
176                     for (Count re : Count.values()) {
177                         matrix.setIfNew(rs, re, result);
178                     }
179                 } else {
180                     explicit[rangeEnd.ordinal()] = true;
181                     matrix.setIfNew(rs, rangeEnd, result);
182                 }
183             }
184         } else if (rangeEnd == null) {
185             explicit[rangeStart.ordinal()] = true;
186             for (Count re : Count.values()) {
187                 matrix.setIfNew(rangeStart, re, result);
188             }
189         } else {
190             explicit[rangeStart.ordinal()] = true;
191             explicit[rangeEnd.ordinal()] = true;
192             matrix.setIfNew(rangeStart, rangeEnd, result);
193         }
194     }
195 
196     /**
197      * Internal method to show a range in XML format.
198      * @param start
199      * @param end
200      * @param result
201      * @return
202      * @internal
203      * @deprecated
204      */
showRange(Count start, Count end, Count result)205     public static String showRange(Count start, Count end, Count result) {
206         String startEnd = "start=\"" + start + "\"" + Utility.repeat(" ", 5 - start.toString().length())
207             + " end=\"" + end + "\"" + Utility.repeat(" ", 5 - end.toString().length());
208         return result == null
209             ? "<!--         " + startEnd + " result=? -->"
210             : "<pluralRange " + startEnd + " result=\"" + result + "\"/>";
211     }
212 
213     /**
214      * Returns the appropriate plural category for a range from start to end.
215      * If there is no available data, then 'other' is returned.
216      * @param start
217      * @param end
218      * @return
219      */
get(Count start, Count end)220     public Count get(Count start, Count end) {
221         Count result = matrix.get(start, end);
222         return result == null ? Count.other : result;
223     }
224 
225     /**
226      * Returns the appropriate plural category for a range from start to end. If the combination does not
227      * explicitly occur in the data, returns null.
228      * @param start
229      * @param end
230      * @return
231      */
getExplicit(Count start, Count end)232     public Count getExplicit(Count start, Count end) {
233         return matrix.get(start, end);
234     }
235 
236     /**
237      * Internal method to determines whether the Count was explicitly used in any add statement.
238      * @param count
239      * @return
240      * @internal
241      * @deprecated
242      */
isExplicitlySet(Count count)243     public boolean isExplicitlySet(Count count) {
244         return explicit[count.ordinal()];
245     }
246 
247     @Override
equals(Object other)248     public boolean equals(Object other) {
249         return other instanceof PluralRanges ? matrix.equals((PluralRanges) other) : false;
250     }
251 
252     @Override
hashCode()253     public int hashCode() {
254         return matrix.hashCode();
255     }
256 
257     @Override
compareTo(PluralRanges that)258     public int compareTo(PluralRanges that) {
259         return matrix.compareTo(that.matrix);
260     }
261 
262     volatile boolean isFrozen;
263 
264     @Override
isFrozen()265     public boolean isFrozen() {
266         return isFrozen;
267     }
268 
269     @Override
freeze()270     public PluralRanges freeze() {
271         isFrozen = true;
272         return this;
273     }
274 
275     @Override
cloneAsThawed()276     public PluralRanges cloneAsThawed() {
277         PluralRanges result = new PluralRanges();
278         result.explicit = explicit.clone();
279         result.matrix = matrix.clone();
280         return result;
281     }
282 }