1/*
2 *******************************************************************************
3 * Copyright (C) 2008-2014, Google, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.text;
8
9import java.util.Arrays;
10import java.util.EnumSet;
11
12import com.ibm.icu.text.PluralRules.StandardPluralCategories;
13import com.ibm.icu.util.Freezable;
14import com.ibm.icu.util.Output;
15
16/**
17 * Utility class for returning the plural category for a range of numbers, such as 1–5, so that appropriate messages can
18 * be chosen. The rules for determining this value vary widely across locales.
19 *
20 * @author markdavis
21 * @internal
22 * @deprecated This API is ICU internal only.
23 */
24@Deprecated
25public final class PluralRanges implements Freezable<PluralRanges>, Comparable<PluralRanges> {
26
27    private volatile boolean isFrozen;
28    private Matrix matrix = new Matrix();
29    private boolean[] explicit = new boolean[StandardPluralCategories.COUNT];
30
31    /**
32     * Constructor
33     *
34     * @internal
35     * @deprecated This API is ICU internal only.
36     */
37    @Deprecated
38    public PluralRanges() {
39    }
40
41    /**
42     * Internal class for mapping from two StandardPluralCategories values to another.
43     */
44    private static final class Matrix implements Comparable<Matrix>, Cloneable {
45        private byte[] data = new byte[StandardPluralCategories.COUNT * StandardPluralCategories.COUNT];
46        {
47            for (int i = 0; i < data.length; ++i) {
48                data[i] = -1;
49            }
50        }
51
52        Matrix() {
53        }
54
55        /**
56         * Internal method for setting.
57         */
58        @SuppressWarnings("unused")
59        void set(StandardPluralCategories start, StandardPluralCategories end, StandardPluralCategories result) {
60            data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()] = result == null ? (byte) -1
61                    : (byte) result.ordinal();
62        }
63
64        /**
65         * Internal method for setting; throws exception if already set.
66         */
67        void setIfNew(StandardPluralCategories start, StandardPluralCategories end,
68                StandardPluralCategories result) {
69            byte old = data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()];
70            if (old >= 0) {
71                throw new IllegalArgumentException("Previously set value for <" + start + ", " + end + ", "
72                        + StandardPluralCategories.VALUES.get(old) + ">");
73            }
74            data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()] = result == null ? (byte) -1
75                    : (byte) result.ordinal();
76        }
77
78        /**
79         * Internal method for getting.
80         */
81        StandardPluralCategories get(StandardPluralCategories start, StandardPluralCategories end) {
82            byte result = data[start.ordinal() * StandardPluralCategories.COUNT + end.ordinal()];
83            return result < 0 ? null : StandardPluralCategories.VALUES.get(result);
84        }
85
86        /**
87         * Internal method to see if <*,end> values are all the same.
88         */
89        @SuppressWarnings("unused")
90        StandardPluralCategories endSame(StandardPluralCategories end) {
91            StandardPluralCategories first = null;
92            for (StandardPluralCategories start : StandardPluralCategories.VALUES) {
93                StandardPluralCategories item = get(start, end);
94                if (item == null) {
95                    continue;
96                }
97                if (first == null) {
98                    first = item;
99                    continue;
100                }
101                if (first != item) {
102                    return null;
103                }
104            }
105            return first;
106        }
107
108        /**
109         * Internal method to see if <start,*> values are all the same.
110         */
111        @SuppressWarnings("unused")
112        StandardPluralCategories startSame(StandardPluralCategories start,
113                EnumSet<StandardPluralCategories> endDone, Output<Boolean> emit) {
114            emit.value = false;
115            StandardPluralCategories first = null;
116            for (StandardPluralCategories end : StandardPluralCategories.VALUES) {
117                StandardPluralCategories item = get(start, end);
118                if (item == null) {
119                    continue;
120                }
121                if (first == null) {
122                    first = item;
123                    continue;
124                }
125                if (first != item) {
126                    return null;
127                }
128                // only emit if we didn't cover with the 'end' values
129                if (!endDone.contains(end)) {
130                    emit.value = true;
131                }
132            }
133            return first;
134        }
135
136        @Override
137        public int hashCode() {
138            int result = 0;
139            for (int i = 0; i < data.length; ++i) {
140                result = result * 37 + data[i];
141            }
142            return result;
143        }
144
145        @Override
146        public boolean equals(Object other) {
147            if (!(other instanceof Matrix)) {
148                return false;
149            }
150            return 0 == compareTo((Matrix) other);
151        }
152
153        public int compareTo(Matrix o) {
154            for (int i = 0; i < data.length; ++i) {
155                int diff = data[i] - o.data[i];
156                if (diff != 0) {
157                    return diff;
158                }
159            }
160            return 0;
161        }
162
163        @Override
164        public Matrix clone() {
165            Matrix result = new Matrix();
166            result.data = data.clone();
167            return result;
168        }
169
170        @Override
171        public String toString() {
172            StringBuilder result = new StringBuilder();
173            for (StandardPluralCategories i : StandardPluralCategories.values()) {
174                for (StandardPluralCategories j : StandardPluralCategories.values()) {
175                    StandardPluralCategories x = get(i, j);
176                    if (x != null) {
177                        result.append(i + " & " + j + " → " + x + ";\n");
178                    }
179                }
180            }
181            return result.toString();
182        }
183    }
184
185    /**
186     * Internal method for building. If the start or end are null, it means everything of that type.
187     *
188     * @param rangeStart
189     *            plural category for the start of the range
190     * @param rangeEnd
191     *            plural category for the end of the range
192     * @param result
193     *            the resulting plural category
194     * @internal
195     * @deprecated This API is ICU internal only.
196     */
197    @Deprecated
198    public void add(StandardPluralCategories rangeStart, StandardPluralCategories rangeEnd,
199            StandardPluralCategories result) {
200        if (isFrozen) {
201            throw new UnsupportedOperationException();
202        }
203        explicit[result.ordinal()] = true;
204        if (rangeStart == null) {
205            for (StandardPluralCategories rs : StandardPluralCategories.values()) {
206                if (rangeEnd == null) {
207                    for (StandardPluralCategories re : StandardPluralCategories.values()) {
208                        matrix.setIfNew(rs, re, result);
209                    }
210                } else {
211                    explicit[rangeEnd.ordinal()] = true;
212                    matrix.setIfNew(rs, rangeEnd, result);
213                }
214            }
215        } else if (rangeEnd == null) {
216            explicit[rangeStart.ordinal()] = true;
217            for (StandardPluralCategories re : StandardPluralCategories.values()) {
218                matrix.setIfNew(rangeStart, re, result);
219            }
220        } else {
221            explicit[rangeStart.ordinal()] = true;
222            explicit[rangeEnd.ordinal()] = true;
223            matrix.setIfNew(rangeStart, rangeEnd, result);
224        }
225    }
226
227    /**
228     * Returns the appropriate plural category for a range from start to end. If there is no available data, then
229     * 'end' is returned as an implicit value. (Such an implicit value can be tested for with {@link #isExplicit}.)
230     *
231     * @param start
232     *            plural category for the start of the range
233     * @param end
234     *            plural category for the end of the range
235     * @return the resulting plural category, or 'end' if there is no data.
236     * @internal
237     * @deprecated This API is ICU internal only.
238     */
239    @Deprecated
240    public StandardPluralCategories get(StandardPluralCategories start, StandardPluralCategories end) {
241        StandardPluralCategories result = matrix.get(start, end);
242        return result == null ? end : result;
243    }
244
245    /**
246     * Returns whether the appropriate plural category for a range from start to end
247     * is explicitly in the data (vs given an implicit value). See also {@link #get}.
248     *
249     * @param start
250     *            plural category for the start of the range
251     * @param end
252     *            plural category for the end of the range
253     * @return whether the value for (start,end) is explicit or not.
254     * @internal
255     * @deprecated This API is ICU internal only.
256     */
257    @Deprecated
258    public boolean isExplicit(StandardPluralCategories start, StandardPluralCategories end) {
259        return matrix.get(start, end) != null;
260    }
261
262    /**
263     * Internal method to determines whether the StandardPluralCategories was explicitly used in any add statement.
264     *
265     * @param count
266     *            plural category to test
267     * @return true if set
268     * @internal
269     * @deprecated This API is ICU internal only.
270     */
271    @Deprecated
272    public boolean isExplicitlySet(StandardPluralCategories count) {
273        return explicit[count.ordinal()];
274    }
275
276    /**
277     * {@inheritDoc}
278     * @internal
279     * @deprecated This API is ICU internal only.
280     */
281    @Deprecated
282    @Override
283    public boolean equals(Object other) {
284        if (this == other) {
285            return true;
286        }
287        if (!(other instanceof PluralRanges)) {
288            return false;
289        }
290        PluralRanges otherPR = (PluralRanges)other;
291        return matrix.equals(otherPR.matrix) && Arrays.equals(explicit, otherPR.explicit);
292    }
293
294    /**
295     * {@inheritDoc}
296     * @internal
297     * @deprecated This API is ICU internal only.
298     */
299    @Override
300    @Deprecated
301    public int hashCode() {
302        return matrix.hashCode();
303    }
304
305    /**
306     * {@inheritDoc}
307     * @internal
308     * @deprecated This API is ICU internal only.
309     */
310    @Deprecated
311    public int compareTo(PluralRanges that) {
312        return matrix.compareTo(that.matrix);
313    }
314
315    /**
316     * {@inheritDoc}
317     * @internal
318     * @deprecated This API is ICU internal only.
319     */
320    @Deprecated
321    public boolean isFrozen() {
322        return isFrozen;
323    }
324
325    /**
326     * {@inheritDoc}
327     * @internal
328     * @deprecated This API is ICU internal only.
329     */
330    @Deprecated
331    public PluralRanges freeze() {
332        isFrozen = true;
333        return this;
334    }
335
336    /**
337     * {@inheritDoc}
338     * @internal
339     * @deprecated This API is ICU internal only.
340     */
341    @Deprecated
342    public PluralRanges cloneAsThawed() {
343        PluralRanges result = new PluralRanges();
344        result.explicit = explicit.clone();
345        result.matrix = matrix.clone();
346        return result;
347    }
348
349    /**
350     * {@inheritDoc}
351     * @internal
352     * @deprecated This API is ICU internal only.
353     */
354    @Override
355    @Deprecated
356    public String toString() {
357        return matrix.toString();
358    }
359}