| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| /* |
| ******************************************************************************* |
| * Copyright (C) 2008-2015, Google, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.text; |
| |
| import java.util.Arrays; |
| import java.util.EnumSet; |
| |
| import com.ibm.icu.impl.StandardPlural; |
| import com.ibm.icu.util.Freezable; |
| import com.ibm.icu.util.Output; |
| |
| /** |
| * Utility class for returning the plural category for a range of numbers, such as 1–5, so that appropriate messages can |
| * be chosen. The rules for determining this value vary widely across locales. |
| * |
| * @author markdavis |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public final class PluralRanges implements Freezable<PluralRanges>, Comparable<PluralRanges> { |
| |
| private volatile boolean isFrozen; |
| private Matrix matrix = new Matrix(); |
| private boolean[] explicit = new boolean[StandardPlural.COUNT]; |
| |
| /** |
| * Constructor |
| * |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public PluralRanges() { |
| } |
| |
| /** |
| * Internal class for mapping from two StandardPluralCategories values to another. |
| */ |
| private static final class Matrix implements Comparable<Matrix>, Cloneable { |
| private byte[] data = new byte[StandardPlural.COUNT * StandardPlural.COUNT]; |
| { |
| for (int i = 0; i < data.length; ++i) { |
| data[i] = -1; |
| } |
| } |
| |
| Matrix() { |
| } |
| |
| /** |
| * Internal method for setting. |
| */ |
| @SuppressWarnings("unused") |
| void set(StandardPlural start, StandardPlural end, StandardPlural result) { |
| data[start.ordinal() * StandardPlural.COUNT + end.ordinal()] = result == null ? (byte) -1 |
| : (byte) result.ordinal(); |
| } |
| |
| /** |
| * Internal method for setting; throws exception if already set. |
| */ |
| void setIfNew(StandardPlural start, StandardPlural end, |
| StandardPlural result) { |
| byte old = data[start.ordinal() * StandardPlural.COUNT + end.ordinal()]; |
| if (old >= 0) { |
| throw new IllegalArgumentException("Previously set value for <" + start + ", " + end + ", " |
| + StandardPlural.VALUES.get(old) + ">"); |
| } |
| data[start.ordinal() * StandardPlural.COUNT + end.ordinal()] = result == null ? (byte) -1 |
| : (byte) result.ordinal(); |
| } |
| |
| /** |
| * Internal method for getting. |
| */ |
| StandardPlural get(StandardPlural start, StandardPlural end) { |
| byte result = data[start.ordinal() * StandardPlural.COUNT + end.ordinal()]; |
| return result < 0 ? null : StandardPlural.VALUES.get(result); |
| } |
| |
| /** |
| * Internal method to see if <*,end> values are all the same. |
| */ |
| @SuppressWarnings("unused") |
| StandardPlural endSame(StandardPlural end) { |
| StandardPlural first = null; |
| for (StandardPlural start : StandardPlural.VALUES) { |
| StandardPlural item = get(start, end); |
| if (item == null) { |
| continue; |
| } |
| if (first == null) { |
| first = item; |
| continue; |
| } |
| if (first != item) { |
| return null; |
| } |
| } |
| return first; |
| } |
| |
| /** |
| * Internal method to see if <start,*> values are all the same. |
| */ |
| @SuppressWarnings("unused") |
| StandardPlural startSame(StandardPlural start, |
| EnumSet<StandardPlural> endDone, Output<Boolean> emit) { |
| emit.value = false; |
| StandardPlural first = null; |
| for (StandardPlural end : StandardPlural.VALUES) { |
| StandardPlural item = get(start, end); |
| if (item == null) { |
| continue; |
| } |
| if (first == null) { |
| first = item; |
| continue; |
| } |
| if (first != item) { |
| return null; |
| } |
| // only emit if we didn't cover with the 'end' values |
| if (!endDone.contains(end)) { |
| emit.value = true; |
| } |
| } |
| return first; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = 0; |
| for (int i = 0; i < data.length; ++i) { |
| result = result * 37 + data[i]; |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (!(other instanceof Matrix)) { |
| return false; |
| } |
| return 0 == compareTo((Matrix) other); |
| } |
| |
| @Override |
| public int compareTo(Matrix o) { |
| for (int i = 0; i < data.length; ++i) { |
| int diff = data[i] - o.data[i]; |
| if (diff != 0) { |
| return diff; |
| } |
| } |
| return 0; |
| } |
| |
| @Override |
| public Matrix clone() { |
| Matrix result = new Matrix(); |
| result.data = data.clone(); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder result = new StringBuilder(); |
| for (StandardPlural i : StandardPlural.values()) { |
| for (StandardPlural j : StandardPlural.values()) { |
| StandardPlural x = get(i, j); |
| if (x != null) { |
| result.append(i + " & " + j + " → " + x + ";\n"); |
| } |
| } |
| } |
| return result.toString(); |
| } |
| } |
| |
| /** |
| * Internal method for building. If the start or end are null, it means everything of that type. |
| * |
| * @param rangeStart |
| * plural category for the start of the range |
| * @param rangeEnd |
| * plural category for the end of the range |
| * @param result |
| * the resulting plural category |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public void add(StandardPlural rangeStart, StandardPlural rangeEnd, |
| StandardPlural result) { |
| if (isFrozen) { |
| throw new UnsupportedOperationException(); |
| } |
| explicit[result.ordinal()] = true; |
| if (rangeStart == null) { |
| for (StandardPlural rs : StandardPlural.values()) { |
| if (rangeEnd == null) { |
| for (StandardPlural re : StandardPlural.values()) { |
| matrix.setIfNew(rs, re, result); |
| } |
| } else { |
| explicit[rangeEnd.ordinal()] = true; |
| matrix.setIfNew(rs, rangeEnd, result); |
| } |
| } |
| } else if (rangeEnd == null) { |
| explicit[rangeStart.ordinal()] = true; |
| for (StandardPlural re : StandardPlural.values()) { |
| matrix.setIfNew(rangeStart, re, result); |
| } |
| } else { |
| explicit[rangeStart.ordinal()] = true; |
| explicit[rangeEnd.ordinal()] = true; |
| matrix.setIfNew(rangeStart, rangeEnd, result); |
| } |
| } |
| |
| /** |
| * Returns the appropriate plural category for a range from start to end. If there is no available data, then |
| * 'end' is returned as an implicit value. (Such an implicit value can be tested for with {@link #isExplicit}.) |
| * |
| * @param start |
| * plural category for the start of the range |
| * @param end |
| * plural category for the end of the range |
| * @return the resulting plural category, or 'end' if there is no data. |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public StandardPlural get(StandardPlural start, StandardPlural end) { |
| StandardPlural result = matrix.get(start, end); |
| return result == null ? end : result; |
| } |
| |
| /** |
| * Returns whether the appropriate plural category for a range from start to end |
| * is explicitly in the data (vs given an implicit value). See also {@link #get}. |
| * |
| * @param start |
| * plural category for the start of the range |
| * @param end |
| * plural category for the end of the range |
| * @return whether the value for (start,end) is explicit or not. |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public boolean isExplicit(StandardPlural start, StandardPlural end) { |
| return matrix.get(start, end) != null; |
| } |
| |
| /** |
| * Internal method to determines whether the StandardPluralCategories was explicitly used in any add statement. |
| * |
| * @param count |
| * plural category to test |
| * @return true if set |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| public boolean isExplicitlySet(StandardPlural count) { |
| return explicit[count.ordinal()]; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Deprecated |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; |
| } |
| if (!(other instanceof PluralRanges)) { |
| return false; |
| } |
| PluralRanges otherPR = (PluralRanges)other; |
| return matrix.equals(otherPR.matrix) && Arrays.equals(explicit, otherPR.explicit); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Override |
| @Deprecated |
| public int hashCode() { |
| return matrix.hashCode(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Override |
| @Deprecated |
| public int compareTo(PluralRanges that) { |
| return matrix.compareTo(that.matrix); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Override |
| @Deprecated |
| public boolean isFrozen() { |
| return isFrozen; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Override |
| @Deprecated |
| public PluralRanges freeze() { |
| isFrozen = true; |
| return this; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Override |
| @Deprecated |
| public PluralRanges cloneAsThawed() { |
| PluralRanges result = new PluralRanges(); |
| result.explicit = explicit.clone(); |
| result.matrix = matrix.clone(); |
| return result; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * @internal |
| * @deprecated This API is ICU internal only. |
| */ |
| @Override |
| @Deprecated |
| public String toString() { |
| return matrix.toString(); |
| } |
| } |