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 }