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}