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