14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm/* GENERATED SOURCE. DO NOT MODIFY. */ 24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm// © 2016 and later: Unicode, Inc. and others. 34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm// License & terms of use: http://www.unicode.org/copyright.html#License 44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm/* 54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ******************************************************************************* 64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Copyright (C) 2007-2016, International Business Machines Corporation and 74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * others. All Rights Reserved. 84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm ******************************************************************************* 94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmpackage android.icu.text; 124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.io.IOException; 144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.io.NotSerializableException; 154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.io.ObjectInputStream; 164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.io.ObjectOutputStream; 174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.io.ObjectStreamException; 184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.io.Serializable; 194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.text.ParseException; 204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.ArrayList; 214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.Collection; 224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.Collections; 234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.HashSet; 244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.Iterator; 254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.LinkedHashSet; 264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.List; 274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.Locale; 284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.Set; 294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.TreeSet; 304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport java.util.regex.Pattern; 314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport android.icu.impl.PluralRulesLoader; 334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport android.icu.util.Output; 344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport android.icu.util.ULocale; 354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm/** 374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Defines rules for mapping non-negative numeric values onto a small set of keywords. 394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Rules are constructed from a text description, consisting of a series of keywords and conditions. The {@link #select} 424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * method examines each condition in order and returns the keyword for the first condition that matches the number. If 434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * none match, {@link #KEYWORD_OTHER} is returned. 444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * A PluralRules object is immutable. It contains caches for sample values, but those are synchronized. 474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * PluralRules is Serializable so that it can be used in formatters, which are serializable. 494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * For more information, details, and tips for writing rules, see the <a 524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * href="http://www.unicode.org/draft/reports/tr35/tr35.html#Language_Plural_Rules">LDML spec, C.11 Language Plural 534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Rules</a> 544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Examples: 574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <pre> 604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * "one: n is 1; few: n in 2..4" 614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </pre> 624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * This defines two rules, for 'one' and 'few'. The condition for 'one' is "n is 1" which means that the number must be 644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * equal to 1 for this condition to pass. The condition for 'few' is "n in 2..4" which means that the number must be 654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * between 2 and 4 inclusive - and be an integer - for this condition to pass. All other numbers are assigned the 664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * keyword "other" by the default rule. 674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <pre> 704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * "zero: n is 0; one: n is 1; zero: n mod 100 in 1..19" 714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </pre> 724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * This illustrates that the same keyword can be defined multiple times. Each rule is examined in order, and the first 744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * keyword whose condition passes is the one returned. Also notes that a modulus is applied to n in the last rule. Thus 754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * its condition holds for 119, 219, 319... 764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <pre> 794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * "one: n is 1; few: n mod 10 in 2..4 and n mod 100 not in 12..14" 804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </pre> 814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * This illustrates conjunction and negation. The condition for 'few' has two parts, both of which must be met: 834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * "n mod 10 in 2..4" and "n mod 100 not in 12..14". The first part applies a modulus to n before the test as in the 844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * previous example. The second part applies a different modulus and also uses negation, thus it matches all numbers 854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * _not_ in 12, 13, 14, 112, 113, 114, 212, 213, 214... 864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Syntax: 894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <pre> 914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * rules = rule (';' rule)* 924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * rule = keyword ':' condition 934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * keyword = <identifier> 944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * condition = and_condition ('or' and_condition)* 954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * and_condition = relation ('and' relation)* 964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * relation = not? expr not? rel not? range_list 974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * expr = ('n' | 'i' | 'f' | 'v' | 't') (mod value)? 984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * not = 'not' | '!' 994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * rel = 'in' | 'is' | '=' | '≠' | 'within' 1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * mod = 'mod' | '%' 1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * range_list = (range | value) (',' range_list)* 1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * value = digit+ 1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * digit = 0|1|2|3|4|5|6|7|8|9 1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * range = value'..'value 1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </pre> 1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p>Each <b>not</b> term inverts the meaning; however, there should not be more than one of them.</p> 1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * The i, f, t, and v values are defined as follows: 1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <ul> 1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <li>i to be the integer digits.</li> 1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <li>f to be the visible decimal digits, as an integer.</li> 1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <li>t to be the visible decimal digits—without trailing zeros—as an integer.</li> 1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <li>v to be the number of visible fraction digits.</li> 1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <li>j is defined to only match integers. That is j is 3 fails if v != 0 (eg for 3.1 or 3.0).</li> 1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </ul> 1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Examples are in the following table: 1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <table border='1' style="border-collapse:collapse"> 1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tbody> 1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tr> 1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <th>n</th> 1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <th>i</th> 1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <th>f</th> 1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <th>v</th> 1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tr> 1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tr> 1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1.0</td> 1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td align="right">0</td> 1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tr> 1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tr> 1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1.00</td> 1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td align="right">0</td> 1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>2</td> 1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tr> 1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tr> 1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1.3</td> 1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td align="right">3</td> 1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tr> 1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tr> 1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1.03</td> 1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td align="right">3</td> 1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>2</td> 1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tr> 1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <tr> 1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1.23</td> 1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>1</td> 1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td align="right">23</td> 1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <td>2</td> 1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tr> 1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </tbody> 1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </table> 1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * An "identifier" is a sequence of characters that do not have the Unicode Pattern_Syntax or Pattern_White_Space 1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * properties. 1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * The difference between 'in' and 'within' is that 'in' only includes integers in the specified range, while 'within' 1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * includes all values. Using 'within' with a range_list consisting entirely of values is the same as using 'in' (it's 1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * not an error). 1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * </p> 1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmpublic class PluralRules implements Serializable { 1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm static final UnicodeSet ALLOWED_ID = new UnicodeSet("[a-z]").freeze(); 1724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm // TODO Remove RulesList by moving its API and fields into PluralRules. 1744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 1754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 1764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 1774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 1784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 1794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 1804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public static final String CATEGORY_SEPARATOR = "; "; 1814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 1824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 1834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 1844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 1854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 1864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 1874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public static final String KEYWORD_RULE_SEPARATOR = ": "; 1884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm private static final long serialVersionUID = 1; 1904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm private final RuleList rules; 1924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm private final transient Set<String> keywords; 1934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 1944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 1954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Provides a factory for returning plural rules 1964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 1974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 1984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 1994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 2004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 2014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 2024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public static abstract class Factory { 2034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 2044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Sole constructor 2054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 2064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 2074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 2084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 2094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 2104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm protected Factory() { 2114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm } 2124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 2144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Provides access to the predefined <code>PluralRules</code> for a given locale and the plural type. 2154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 2164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * <p> 2174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>. For these predefined 2184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * rules, see CLDR page at http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html 2194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 2204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @param locale 2214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * The locale for which a <code>PluralRules</code> object is returned. 2224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @param type 2234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * The plural type (e.g., cardinal or ordinal). 2244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @return The predefined <code>PluralRules</code> object for this locale. If there's no predefined rules for 2254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * this locale, the rules for the closest parent in the locale hierarchy that has one will be returned. 2264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * The final fallback always returns the default rules. 2274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 2284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 2294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 2304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 2314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 2324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public abstract PluralRules forLocale(ULocale locale, PluralType type); 2334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 2354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Utility for getting CARDINAL rules. 2364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @param locale the locale 2374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @return plural rules. 2384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 2394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 2404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 2414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 2424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 2434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public final PluralRules forLocale(ULocale locale) { 2444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm return forLocale(locale, PluralType.CARDINAL); 2454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm } 2464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 2484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Returns the locales for which there is plurals data. 2494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 2504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 2514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 2524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 2534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 2544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 2554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public abstract ULocale[] getAvailableULocales(); 2564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 2584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Returns the 'functionally equivalent' locale with respect to plural rules. Calling PluralRules.forLocale with 2594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * the functionally equivalent locale, and with the provided locale, returns rules that behave the same. <br> 2604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * All locales with the same functionally equivalent locale have plural rules that behave the same. This is not 2614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * exaustive; there may be other locales whose plural rules behave the same that do not have the same equivalent 2624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * locale. 2634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * 2644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @param locale 2654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * the locale to check 2664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @param isAvailable 2674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * if not null and of length > 0, this will hold 'true' at index 0 if locale is directly defined 2684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * (without fallback) as having plural rules 2694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @return the functionally-equivalent locale 2704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 2714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide original deprecated declaration 2724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @hide draft / provisional / internal are hidden on Android 2734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm */ 2744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm @Deprecated 2754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm public abstract ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable); 2764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm 2774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm /** 2784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * Returns the default factory. 2794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm * @deprecated This API is ICU internal only. 280 * @hide original deprecated declaration 281 * @hide draft / provisional / internal are hidden on Android 282 */ 283 @Deprecated 284 public static PluralRulesLoader getDefaultFactory() { 285 return PluralRulesLoader.loader; 286 } 287 288 /** 289 * Returns whether or not there are overrides. 290 * @deprecated This API is ICU internal only. 291 * @hide original deprecated declaration 292 * @hide draft / provisional / internal are hidden on Android 293 */ 294 @Deprecated 295 public abstract boolean hasOverride(ULocale locale); 296 } 297 // Standard keywords. 298 299 /** 300 * Common name for the 'zero' plural form. 301 */ 302 public static final String KEYWORD_ZERO = "zero"; 303 304 /** 305 * Common name for the 'singular' plural form. 306 */ 307 public static final String KEYWORD_ONE = "one"; 308 309 /** 310 * Common name for the 'dual' plural form. 311 */ 312 public static final String KEYWORD_TWO = "two"; 313 314 /** 315 * Common name for the 'paucal' or other special plural form. 316 */ 317 public static final String KEYWORD_FEW = "few"; 318 319 /** 320 * Common name for the arabic (11 to 99) plural form. 321 */ 322 public static final String KEYWORD_MANY = "many"; 323 324 /** 325 * Common name for the default plural form. This name is returned 326 * for values to which no other form in the rule applies. It 327 * can additionally be assigned rules of its own. 328 */ 329 public static final String KEYWORD_OTHER = "other"; 330 331 /** 332 * Value returned by {@link #getUniqueKeywordValue} when there is no 333 * unique value to return. 334 */ 335 public static final double NO_UNIQUE_VALUE = -0.00123456777; 336 337 /** 338 * Type of plurals and PluralRules. 339 */ 340 public enum PluralType { 341 /** 342 * Plural rules for cardinal numbers: 1 file vs. 2 files. 343 */ 344 CARDINAL, 345 /** 346 * Plural rules for ordinal numbers: 1st file, 2nd file, 3rd file, 4th file, etc. 347 */ 348 ORDINAL 349 }; 350 351 /* 352 * The default constraint that is always satisfied. 353 */ 354 private static final Constraint NO_CONSTRAINT = new Constraint() { 355 private static final long serialVersionUID = 9163464945387899416L; 356 357 @Override 358 public boolean isFulfilled(FixedDecimal n) { 359 return true; 360 } 361 362 @Override 363 public boolean isLimited(SampleType sampleType) { 364 return false; 365 } 366 367 @Override 368 public String toString() { 369 return ""; 370 } 371 }; 372 373 /** 374 * 375 */ 376 private static final Rule DEFAULT_RULE = new Rule("other", NO_CONSTRAINT, null, null); 377 378 /** 379 * Parses a plural rules description and returns a PluralRules. 380 * @param description the rule description. 381 * @throws ParseException if the description cannot be parsed. 382 * The exception index is typically not set, it will be -1. 383 */ 384 public static PluralRules parseDescription(String description) 385 throws ParseException { 386 387 description = description.trim(); 388 return description.length() == 0 ? DEFAULT : new PluralRules(parseRuleChain(description)); 389 } 390 391 /** 392 * Creates a PluralRules from a description if it is parsable, 393 * otherwise returns null. 394 * @param description the rule description. 395 * @return the PluralRules 396 */ 397 public static PluralRules createRules(String description) { 398 try { 399 return parseDescription(description); 400 } catch(Exception e) { 401 return null; 402 } 403 } 404 405 /** 406 * The default rules that accept any number and return 407 * {@link #KEYWORD_OTHER}. 408 */ 409 public static final PluralRules DEFAULT = new PluralRules(new RuleList().addRule(DEFAULT_RULE)); 410 411 private enum Operand { 412 n, 413 i, 414 f, 415 t, 416 v, 417 w, 418 /* deprecated */ 419 j; 420 } 421 422 /** 423 * @deprecated This API is ICU internal only. 424 * @hide original deprecated declaration 425 * @hide draft / provisional / internal are hidden on Android 426 */ 427 @Deprecated 428 public static class FixedDecimal extends Number implements Comparable<FixedDecimal> { 429 private static final long serialVersionUID = -4756200506571685661L; 430 /** 431 * @deprecated This API is ICU internal only. 432 * @hide original deprecated declaration 433 * @hide draft / provisional / internal are hidden on Android 434 */ 435 @Deprecated 436 public final double source; 437 /** 438 * @deprecated This API is ICU internal only. 439 * @hide original deprecated declaration 440 * @hide draft / provisional / internal are hidden on Android 441 */ 442 @Deprecated 443 public final int visibleDecimalDigitCount; 444 /** 445 * @deprecated This API is ICU internal only. 446 * @hide original deprecated declaration 447 * @hide draft / provisional / internal are hidden on Android 448 */ 449 @Deprecated 450 public final int visibleDecimalDigitCountWithoutTrailingZeros; 451 /** 452 * @deprecated This API is ICU internal only. 453 * @hide original deprecated declaration 454 * @hide draft / provisional / internal are hidden on Android 455 */ 456 @Deprecated 457 public final long decimalDigits; 458 /** 459 * @deprecated This API is ICU internal only. 460 * @hide original deprecated declaration 461 * @hide draft / provisional / internal are hidden on Android 462 */ 463 @Deprecated 464 public final long decimalDigitsWithoutTrailingZeros; 465 /** 466 * @deprecated This API is ICU internal only. 467 * @hide original deprecated declaration 468 * @hide draft / provisional / internal are hidden on Android 469 */ 470 @Deprecated 471 public final long integerValue; 472 /** 473 * @deprecated This API is ICU internal only. 474 * @hide original deprecated declaration 475 * @hide draft / provisional / internal are hidden on Android 476 */ 477 @Deprecated 478 public final boolean hasIntegerValue; 479 /** 480 * @deprecated This API is ICU internal only. 481 * @hide original deprecated declaration 482 * @hide draft / provisional / internal are hidden on Android 483 */ 484 @Deprecated 485 public final boolean isNegative; 486 private final int baseFactor; 487 488 /** 489 * @deprecated This API is ICU internal only. 490 * @hide original deprecated declaration 491 * @hide draft / provisional / internal are hidden on Android 492 */ 493 @Deprecated 494 public double getSource() { 495 return source; 496 } 497 498 /** 499 * @deprecated This API is ICU internal only. 500 * @hide original deprecated declaration 501 * @hide draft / provisional / internal are hidden on Android 502 */ 503 @Deprecated 504 public int getVisibleDecimalDigitCount() { 505 return visibleDecimalDigitCount; 506 } 507 508 /** 509 * @deprecated This API is ICU internal only. 510 * @hide original deprecated declaration 511 * @hide draft / provisional / internal are hidden on Android 512 */ 513 @Deprecated 514 public int getVisibleDecimalDigitCountWithoutTrailingZeros() { 515 return visibleDecimalDigitCountWithoutTrailingZeros; 516 } 517 518 /** 519 * @deprecated This API is ICU internal only. 520 * @hide original deprecated declaration 521 * @hide draft / provisional / internal are hidden on Android 522 */ 523 @Deprecated 524 public long getDecimalDigits() { 525 return decimalDigits; 526 } 527 528 /** 529 * @deprecated This API is ICU internal only. 530 * @hide original deprecated declaration 531 * @hide draft / provisional / internal are hidden on Android 532 */ 533 @Deprecated 534 public long getDecimalDigitsWithoutTrailingZeros() { 535 return decimalDigitsWithoutTrailingZeros; 536 } 537 538 /** 539 * @deprecated This API is ICU internal only. 540 * @hide original deprecated declaration 541 * @hide draft / provisional / internal are hidden on Android 542 */ 543 @Deprecated 544 public long getIntegerValue() { 545 return integerValue; 546 } 547 548 /** 549 * @deprecated This API is ICU internal only. 550 * @hide original deprecated declaration 551 * @hide draft / provisional / internal are hidden on Android 552 */ 553 @Deprecated 554 public boolean isHasIntegerValue() { 555 return hasIntegerValue; 556 } 557 558 /** 559 * @deprecated This API is ICU internal only. 560 * @hide original deprecated declaration 561 * @hide draft / provisional / internal are hidden on Android 562 */ 563 @Deprecated 564 public boolean isNegative() { 565 return isNegative; 566 } 567 568 /** 569 * @deprecated This API is ICU internal only. 570 * @hide original deprecated declaration 571 * @hide draft / provisional / internal are hidden on Android 572 */ 573 @Deprecated 574 public int getBaseFactor() { 575 return baseFactor; 576 } 577 578 static final long MAX = (long)1E18; 579 580 /** 581 * @deprecated This API is ICU internal only. 582 * @param n is the original number 583 * @param v number of digits to the right of the decimal place. e.g 1.00 = 2 25. = 0 584 * @param f Corresponds to f in the plural rules grammar. 585 * The digits to the right of the decimal place as an integer. e.g 1.10 = 10 586 * @hide original deprecated declaration 587 * @hide draft / provisional / internal are hidden on Android 588 */ 589 @Deprecated 590 public FixedDecimal(double n, int v, long f) { 591 isNegative = n < 0; 592 source = isNegative ? -n : n; 593 visibleDecimalDigitCount = v; 594 decimalDigits = f; 595 integerValue = n > MAX 596 ? MAX 597 : (long)n; 598 hasIntegerValue = source == integerValue; 599 // check values. TODO make into unit test. 600 // 601 // long visiblePower = (int) Math.pow(10, v); 602 // if (fractionalDigits > visiblePower) { 603 // throw new IllegalArgumentException(); 604 // } 605 // double fraction = intValue + (fractionalDigits / (double) visiblePower); 606 // if (fraction != source) { 607 // double diff = Math.abs(fraction - source)/(Math.abs(fraction) + Math.abs(source)); 608 // if (diff > 0.00000001d) { 609 // throw new IllegalArgumentException(); 610 // } 611 // } 612 if (f == 0) { 613 decimalDigitsWithoutTrailingZeros = 0; 614 visibleDecimalDigitCountWithoutTrailingZeros = 0; 615 } else { 616 long fdwtz = f; 617 int trimmedCount = v; 618 while ((fdwtz%10) == 0) { 619 fdwtz /= 10; 620 --trimmedCount; 621 } 622 decimalDigitsWithoutTrailingZeros = fdwtz; 623 visibleDecimalDigitCountWithoutTrailingZeros = trimmedCount; 624 } 625 baseFactor = (int) Math.pow(10, v); 626 } 627 628 /** 629 * @deprecated This API is ICU internal only. 630 * @hide original deprecated declaration 631 * @hide draft / provisional / internal are hidden on Android 632 */ 633 @Deprecated 634 public FixedDecimal(double n, int v) { 635 this(n,v,getFractionalDigits(n, v)); 636 } 637 638 private static int getFractionalDigits(double n, int v) { 639 if (v == 0) { 640 return 0; 641 } else { 642 if (n < 0) { 643 n = -n; 644 } 645 int baseFactor = (int) Math.pow(10, v); 646 long scaled = Math.round(n * baseFactor); 647 return (int) (scaled % baseFactor); 648 } 649 } 650 651 /** 652 * @deprecated This API is ICU internal only. 653 * @hide original deprecated declaration 654 * @hide draft / provisional / internal are hidden on Android 655 */ 656 @Deprecated 657 public FixedDecimal(double n) { 658 this(n, decimals(n)); 659 } 660 661 /** 662 * @deprecated This API is ICU internal only. 663 * @hide original deprecated declaration 664 * @hide draft / provisional / internal are hidden on Android 665 */ 666 @Deprecated 667 public FixedDecimal(long n) { 668 this(n,0); 669 } 670 671 private static final long MAX_INTEGER_PART = 1000000000; 672 /** 673 * Return a guess as to the number of decimals that would be displayed. This is only a guess; callers should 674 * always supply the decimals explicitly if possible. Currently, it is up to 6 decimals (without trailing zeros). 675 * Returns 0 for infinities and nans. 676 * @deprecated This API is ICU internal only. 677 * @hide original deprecated declaration 678 * @hide draft / provisional / internal are hidden on Android 679 * 680 */ 681 @Deprecated 682 public static int decimals(double n) { 683 // Ugly... 684 if (Double.isInfinite(n) || Double.isNaN(n)) { 685 return 0; 686 } 687 if (n < 0) { 688 n = -n; 689 } 690 if (n == Math.floor(n)) { 691 return 0; 692 } 693 if (n < MAX_INTEGER_PART) { 694 long temp = (long)(n * 1000000) % 1000000; // get 6 decimals 695 for (int mask = 10, digits = 6; digits > 0; mask *= 10, --digits) { 696 if ((temp % mask) != 0) { 697 return digits; 698 } 699 } 700 return 0; 701 } else { 702 String buf = String.format(Locale.ENGLISH, "%1.15e", n); 703 int ePos = buf.lastIndexOf('e'); 704 int expNumPos = ePos + 1; 705 if (buf.charAt(expNumPos) == '+') { 706 expNumPos++; 707 } 708 String exponentStr = buf.substring(expNumPos); 709 int exponent = Integer.parseInt(exponentStr); 710 int numFractionDigits = ePos - 2 - exponent; 711 if (numFractionDigits < 0) { 712 return 0; 713 } 714 for (int i=ePos-1; numFractionDigits > 0; --i) { 715 if (buf.charAt(i) != '0') { 716 break; 717 } 718 --numFractionDigits; 719 } 720 return numFractionDigits; 721 } 722 } 723 724 /** 725 * @deprecated This API is ICU internal only. 726 * @hide original deprecated declaration 727 * @hide draft / provisional / internal are hidden on Android 728 */ 729 @Deprecated 730 public FixedDecimal (String n) { 731 // Ugly, but for samples we don't care. 732 this(Double.parseDouble(n), getVisibleFractionCount(n)); 733 } 734 735 private static int getVisibleFractionCount(String value) { 736 value = value.trim(); 737 int decimalPos = value.indexOf('.') + 1; 738 if (decimalPos == 0) { 739 return 0; 740 } else { 741 return value.length() - decimalPos; 742 } 743 } 744 745 /** 746 * @deprecated This API is ICU internal only. 747 * @hide original deprecated declaration 748 * @hide draft / provisional / internal are hidden on Android 749 */ 750 @Deprecated 751 public double get(Operand operand) { 752 switch(operand) { 753 default: return source; 754 case i: return integerValue; 755 case f: return decimalDigits; 756 case t: return decimalDigitsWithoutTrailingZeros; 757 case v: return visibleDecimalDigitCount; 758 case w: return visibleDecimalDigitCountWithoutTrailingZeros; 759 } 760 } 761 762 /** 763 * @deprecated This API is ICU internal only. 764 * @hide original deprecated declaration 765 * @hide draft / provisional / internal are hidden on Android 766 */ 767 @Deprecated 768 public static Operand getOperand(String t) { 769 return Operand.valueOf(t); 770 } 771 772 /** 773 * We're not going to care about NaN. 774 * @deprecated This API is ICU internal only. 775 * @hide original deprecated declaration 776 * @hide draft / provisional / internal are hidden on Android 777 */ 778 @Override 779 @Deprecated 780 public int compareTo(FixedDecimal other) { 781 if (integerValue != other.integerValue) { 782 return integerValue < other.integerValue ? -1 : 1; 783 } 784 if (source != other.source) { 785 return source < other.source ? -1 : 1; 786 } 787 if (visibleDecimalDigitCount != other.visibleDecimalDigitCount) { 788 return visibleDecimalDigitCount < other.visibleDecimalDigitCount ? -1 : 1; 789 } 790 long diff = decimalDigits - other.decimalDigits; 791 if (diff != 0) { 792 return diff < 0 ? -1 : 1; 793 } 794 return 0; 795 } 796 797 /** 798 * @deprecated This API is ICU internal only. 799 * @hide original deprecated declaration 800 * @hide draft / provisional / internal are hidden on Android 801 */ 802 @Deprecated 803 @Override 804 public boolean equals(Object arg0) { 805 if (arg0 == null) { 806 return false; 807 } 808 if (arg0 == this) { 809 return true; 810 } 811 if (!(arg0 instanceof FixedDecimal)) { 812 return false; 813 } 814 FixedDecimal other = (FixedDecimal)arg0; 815 return source == other.source && visibleDecimalDigitCount == other.visibleDecimalDigitCount && decimalDigits == other.decimalDigits; 816 } 817 818 /** 819 * @deprecated This API is ICU internal only. 820 * @hide original deprecated declaration 821 * @hide draft / provisional / internal are hidden on Android 822 */ 823 @Deprecated 824 @Override 825 public int hashCode() { 826 // TODO Auto-generated method stub 827 return (int)(decimalDigits + 37 * (visibleDecimalDigitCount + (int)(37 * source))); 828 } 829 830 /** 831 * @deprecated This API is ICU internal only. 832 * @hide original deprecated declaration 833 * @hide draft / provisional / internal are hidden on Android 834 */ 835 @Deprecated 836 @Override 837 public String toString() { 838 return String.format("%." + visibleDecimalDigitCount + "f", source); 839 } 840 841 /** 842 * @deprecated This API is ICU internal only. 843 * @hide original deprecated declaration 844 * @hide draft / provisional / internal are hidden on Android 845 */ 846 @Deprecated 847 public boolean hasIntegerValue() { 848 return hasIntegerValue; 849 } 850 851 /** 852 * @deprecated This API is ICU internal only. 853 * @hide original deprecated declaration 854 * @hide draft / provisional / internal are hidden on Android 855 */ 856 @Deprecated 857 @Override 858 public int intValue() { 859 // TODO Auto-generated method stub 860 return (int)integerValue; 861 } 862 863 /** 864 * @deprecated This API is ICU internal only. 865 * @hide original deprecated declaration 866 * @hide draft / provisional / internal are hidden on Android 867 */ 868 @Deprecated 869 @Override 870 public long longValue() { 871 return integerValue; 872 } 873 874 /** 875 * @deprecated This API is ICU internal only. 876 * @hide original deprecated declaration 877 * @hide draft / provisional / internal are hidden on Android 878 */ 879 @Deprecated 880 @Override 881 public float floatValue() { 882 return (float) source; 883 } 884 885 /** 886 * @deprecated This API is ICU internal only. 887 * @hide original deprecated declaration 888 * @hide draft / provisional / internal are hidden on Android 889 */ 890 @Deprecated 891 @Override 892 public double doubleValue() { 893 return isNegative ? -source : source; 894 } 895 896 /** 897 * @deprecated This API is ICU internal only. 898 * @hide original deprecated declaration 899 * @hide draft / provisional / internal are hidden on Android 900 */ 901 @Deprecated 902 public long getShiftedValue() { 903 return integerValue * baseFactor + decimalDigits; 904 } 905 906 private void writeObject( 907 ObjectOutputStream out) 908 throws IOException { 909 throw new NotSerializableException(); 910 } 911 912 private void readObject(ObjectInputStream in 913 ) throws IOException, ClassNotFoundException { 914 throw new NotSerializableException(); 915 } 916 } 917 918 /** 919 * Selection parameter for either integer-only or decimal-only. 920 * @deprecated This API is ICU internal only. 921 * @hide original deprecated declaration 922 * @hide draft / provisional / internal are hidden on Android 923 */ 924 @Deprecated 925 public enum SampleType { 926 /** 927 * @deprecated This API is ICU internal only. 928 * @hide draft / provisional / internal are hidden on Android 929 */ 930 @Deprecated 931 INTEGER, 932 /** 933 * @deprecated This API is ICU internal only. 934 * @hide draft / provisional / internal are hidden on Android 935 */ 936 @Deprecated 937 DECIMAL 938 } 939 940 /** 941 * A range of NumberInfo that includes all values with the same visibleFractionDigitCount. 942 * @deprecated This API is ICU internal only. 943 * @hide original deprecated declaration 944 * @hide draft / provisional / internal are hidden on Android 945 */ 946 @Deprecated 947 public static class FixedDecimalRange { 948 /** 949 * @deprecated This API is ICU internal only. 950 * @hide original deprecated declaration 951 * @hide draft / provisional / internal are hidden on Android 952 */ 953 @Deprecated 954 public final FixedDecimal start; 955 /** 956 * @deprecated This API is ICU internal only. 957 * @hide original deprecated declaration 958 * @hide draft / provisional / internal are hidden on Android 959 */ 960 @Deprecated 961 public final FixedDecimal end; 962 /** 963 * @deprecated This API is ICU internal only. 964 * @hide original deprecated declaration 965 * @hide draft / provisional / internal are hidden on Android 966 */ 967 @Deprecated 968 public FixedDecimalRange(FixedDecimal start, FixedDecimal end) { 969 if (start.visibleDecimalDigitCount != end.visibleDecimalDigitCount) { 970 throw new IllegalArgumentException("Ranges must have the same number of visible decimals: " + start + "~" + end); 971 } 972 this.start = start; 973 this.end = end; 974 } 975 /** 976 * @deprecated This API is ICU internal only. 977 * @hide original deprecated declaration 978 * @hide draft / provisional / internal are hidden on Android 979 */ 980 @Deprecated 981 @Override 982 public String toString() { 983 return start + (end == start ? "" : "~" + end); 984 } 985 } 986 987 /** 988 * A list of NumberInfo that includes all values with the same visibleFractionDigitCount. 989 * @deprecated This API is ICU internal only. 990 * @hide original deprecated declaration 991 * @hide draft / provisional / internal are hidden on Android 992 */ 993 @Deprecated 994 public static class FixedDecimalSamples { 995 /** 996 * @deprecated This API is ICU internal only. 997 * @hide original deprecated declaration 998 * @hide draft / provisional / internal are hidden on Android 999 */ 1000 @Deprecated 1001 public final SampleType sampleType; 1002 /** 1003 * @deprecated This API is ICU internal only. 1004 * @hide original deprecated declaration 1005 * @hide draft / provisional / internal are hidden on Android 1006 */ 1007 @Deprecated 1008 public final Set<FixedDecimalRange> samples; 1009 /** 1010 * @deprecated This API is ICU internal only. 1011 * @hide original deprecated declaration 1012 * @hide draft / provisional / internal are hidden on Android 1013 */ 1014 @Deprecated 1015 public final boolean bounded; 1016 /** 1017 * The samples must be immutable. 1018 * @param sampleType 1019 * @param samples 1020 */ 1021 private FixedDecimalSamples(SampleType sampleType, Set<FixedDecimalRange> samples, boolean bounded) { 1022 super(); 1023 this.sampleType = sampleType; 1024 this.samples = samples; 1025 this.bounded = bounded; 1026 } 1027 /* 1028 * Parse a list of the form described in CLDR. The source must be trimmed. 1029 */ 1030 static FixedDecimalSamples parse(String source) { 1031 SampleType sampleType2; 1032 boolean bounded2 = true; 1033 boolean haveBound = false; 1034 Set<FixedDecimalRange> samples2 = new LinkedHashSet<FixedDecimalRange>(); 1035 1036 if (source.startsWith("integer")) { 1037 sampleType2 = SampleType.INTEGER; 1038 } else if (source.startsWith("decimal")) { 1039 sampleType2 = SampleType.DECIMAL; 1040 } else { 1041 throw new IllegalArgumentException("Samples must start with 'integer' or 'decimal'"); 1042 } 1043 source = source.substring(7).trim(); // remove both 1044 1045 for (String range : COMMA_SEPARATED.split(source)) { 1046 if (range.equals("…") || range.equals("...")) { 1047 bounded2 = false; 1048 haveBound = true; 1049 continue; 1050 } 1051 if (haveBound) { 1052 throw new IllegalArgumentException("Can only have … at the end of samples: " + range); 1053 } 1054 String[] rangeParts = TILDE_SEPARATED.split(range); 1055 switch (rangeParts.length) { 1056 case 1: 1057 FixedDecimal sample = new FixedDecimal(rangeParts[0]); 1058 checkDecimal(sampleType2, sample); 1059 samples2.add(new FixedDecimalRange(sample, sample)); 1060 break; 1061 case 2: 1062 FixedDecimal start = new FixedDecimal(rangeParts[0]); 1063 FixedDecimal end = new FixedDecimal(rangeParts[1]); 1064 checkDecimal(sampleType2, start); 1065 checkDecimal(sampleType2, end); 1066 samples2.add(new FixedDecimalRange(start, end)); 1067 break; 1068 default: throw new IllegalArgumentException("Ill-formed number range: " + range); 1069 } 1070 } 1071 return new FixedDecimalSamples(sampleType2, Collections.unmodifiableSet(samples2), bounded2); 1072 } 1073 1074 private static void checkDecimal(SampleType sampleType2, FixedDecimal sample) { 1075 if ((sampleType2 == SampleType.INTEGER) != (sample.getVisibleDecimalDigitCount() == 0)) { 1076 throw new IllegalArgumentException("Ill-formed number range: " + sample); 1077 } 1078 } 1079 1080 /** 1081 * @deprecated This API is ICU internal only. 1082 * @hide original deprecated declaration 1083 * @hide draft / provisional / internal are hidden on Android 1084 */ 1085 @Deprecated 1086 public Set<Double> addSamples(Set<Double> result) { 1087 for (FixedDecimalRange item : samples) { 1088 // we have to convert to longs so we don't get strange double issues 1089 long startDouble = item.start.getShiftedValue(); 1090 long endDouble = item.end.getShiftedValue(); 1091 1092 for (long d = startDouble; d <= endDouble; d += 1) { 1093 result.add(d/(double)item.start.baseFactor); 1094 } 1095 } 1096 return result; 1097 } 1098 1099 /** 1100 * @deprecated This API is ICU internal only. 1101 * @hide original deprecated declaration 1102 * @hide draft / provisional / internal are hidden on Android 1103 */ 1104 @Deprecated 1105 @Override 1106 public String toString() { 1107 StringBuilder b = new StringBuilder("@").append(sampleType.toString().toLowerCase(Locale.ENGLISH)); 1108 boolean first = true; 1109 for (FixedDecimalRange item : samples) { 1110 if (first) { 1111 first = false; 1112 } else { 1113 b.append(","); 1114 } 1115 b.append(' ').append(item); 1116 } 1117 if (!bounded) { 1118 b.append(", …"); 1119 } 1120 return b.toString(); 1121 } 1122 1123 /** 1124 * @deprecated This API is ICU internal only. 1125 * @hide original deprecated declaration 1126 * @hide draft / provisional / internal are hidden on Android 1127 */ 1128 @Deprecated 1129 public Set<FixedDecimalRange> getSamples() { 1130 return samples; 1131 } 1132 1133 /** 1134 * @deprecated This API is ICU internal only. 1135 * @hide original deprecated declaration 1136 * @hide draft / provisional / internal are hidden on Android 1137 */ 1138 @Deprecated 1139 public void getStartEndSamples(Set<FixedDecimal> target) { 1140 for (FixedDecimalRange item : samples) { 1141 target.add(item.start); 1142 target.add(item.end); 1143 } 1144 } 1145 } 1146 1147 /* 1148 * A constraint on a number. 1149 */ 1150 private interface Constraint extends Serializable { 1151 /* 1152 * Returns true if the number fulfills the constraint. 1153 * @param n the number to test, >= 0. 1154 */ 1155 boolean isFulfilled(FixedDecimal n); 1156 1157 /* 1158 * Returns false if an unlimited number of values fulfills the 1159 * constraint. 1160 */ 1161 boolean isLimited(SampleType sampleType); 1162 } 1163 1164 static class SimpleTokenizer { 1165 static final UnicodeSet BREAK_AND_IGNORE = new UnicodeSet(0x09, 0x0a, 0x0c, 0x0d, 0x20, 0x20).freeze(); 1166 static final UnicodeSet BREAK_AND_KEEP = new UnicodeSet('!', '!', '%', '%', ',', ',', '.', '.', '=', '=').freeze(); 1167 static String[] split(String source) { 1168 int last = -1; 1169 List<String> result = new ArrayList<String>(); 1170 for (int i = 0; i < source.length(); ++i) { 1171 char ch = source.charAt(i); 1172 if (BREAK_AND_IGNORE.contains(ch)) { 1173 if (last >= 0) { 1174 result.add(source.substring(last,i)); 1175 last = -1; 1176 } 1177 } else if (BREAK_AND_KEEP.contains(ch)) { 1178 if (last >= 0) { 1179 result.add(source.substring(last,i)); 1180 } 1181 result.add(source.substring(i,i+1)); 1182 last = -1; 1183 } else if (last < 0) { 1184 last = i; 1185 } 1186 } 1187 if (last >= 0) { 1188 result.add(source.substring(last)); 1189 } 1190 return result.toArray(new String[result.size()]); 1191 } 1192 } 1193 1194 /* 1195 * syntax: 1196 * condition : or_condition 1197 * and_condition 1198 * or_condition : and_condition 'or' condition 1199 * and_condition : relation 1200 * relation 'and' relation 1201 * relation : in_relation 1202 * within_relation 1203 * in_relation : not? expr not? in not? range 1204 * within_relation : not? expr not? 'within' not? range 1205 * not : 'not' 1206 * '!' 1207 * expr : 'n' 1208 * 'n' mod value 1209 * mod : 'mod' 1210 * '%' 1211 * in : 'in' 1212 * 'is' 1213 * '=' 1214 * '≠' 1215 * value : digit+ 1216 * digit : 0|1|2|3|4|5|6|7|8|9 1217 * range : value'..'value 1218 */ 1219 private static Constraint parseConstraint(String description) 1220 throws ParseException { 1221 1222 Constraint result = null; 1223 String[] or_together = OR_SEPARATED.split(description); 1224 for (int i = 0; i < or_together.length; ++i) { 1225 Constraint andConstraint = null; 1226 String[] and_together = AND_SEPARATED.split(or_together[i]); 1227 for (int j = 0; j < and_together.length; ++j) { 1228 Constraint newConstraint = NO_CONSTRAINT; 1229 1230 String condition = and_together[j].trim(); 1231 String[] tokens = SimpleTokenizer.split(condition); 1232 1233 int mod = 0; 1234 boolean inRange = true; 1235 boolean integersOnly = true; 1236 double lowBound = Long.MAX_VALUE; 1237 double highBound = Long.MIN_VALUE; 1238 long[] vals = null; 1239 1240 int x = 0; 1241 String t = tokens[x++]; 1242 boolean hackForCompatibility = false; 1243 Operand operand; 1244 try { 1245 operand = FixedDecimal.getOperand(t); 1246 } catch (Exception e) { 1247 throw unexpected(t, condition); 1248 } 1249 if (x < tokens.length) { 1250 t = tokens[x++]; 1251 if ("mod".equals(t) || "%".equals(t)) { 1252 mod = Integer.parseInt(tokens[x++]); 1253 t = nextToken(tokens, x++, condition); 1254 } 1255 if ("not".equals(t)) { 1256 inRange = !inRange; 1257 t = nextToken(tokens, x++, condition); 1258 if ("=".equals(t)) { 1259 throw unexpected(t, condition); 1260 } 1261 } else if ("!".equals(t)) { 1262 inRange = !inRange; 1263 t = nextToken(tokens, x++, condition); 1264 if (!"=".equals(t)) { 1265 throw unexpected(t, condition); 1266 } 1267 } 1268 if ("is".equals(t) || "in".equals(t) || "=".equals(t)) { 1269 hackForCompatibility = "is".equals(t); 1270 if (hackForCompatibility && !inRange) { 1271 throw unexpected(t, condition); 1272 } 1273 t = nextToken(tokens, x++, condition); 1274 } else if ("within".equals(t)) { 1275 integersOnly = false; 1276 t = nextToken(tokens, x++, condition); 1277 } else { 1278 throw unexpected(t, condition); 1279 } 1280 if ("not".equals(t)) { 1281 if (!hackForCompatibility && !inRange) { 1282 throw unexpected(t, condition); 1283 } 1284 inRange = !inRange; 1285 t = nextToken(tokens, x++, condition); 1286 } 1287 1288 List<Long> valueList = new ArrayList<Long>(); 1289 1290 // the token t is always one item ahead 1291 while (true) { 1292 long low = Long.parseLong(t); 1293 long high = low; 1294 if (x < tokens.length) { 1295 t = nextToken(tokens, x++, condition); 1296 if (t.equals(".")) { 1297 t = nextToken(tokens, x++, condition); 1298 if (!t.equals(".")) { 1299 throw unexpected(t, condition); 1300 } 1301 t = nextToken(tokens, x++, condition); 1302 high = Long.parseLong(t); 1303 if (x < tokens.length) { 1304 t = nextToken(tokens, x++, condition); 1305 if (!t.equals(",")) { // adjacent number: 1 2 1306 // no separator, fail 1307 throw unexpected(t, condition); 1308 } 1309 } 1310 } else if (!t.equals(",")) { // adjacent number: 1 2 1311 // no separator, fail 1312 throw unexpected(t, condition); 1313 } 1314 } 1315 // at this point, either we are out of tokens, or t is ',' 1316 if (low > high) { 1317 throw unexpected(low + "~" + high, condition); 1318 } else if (mod != 0 && high >= mod) { 1319 throw unexpected(high + ">mod=" + mod, condition); 1320 } 1321 valueList.add(low); 1322 valueList.add(high); 1323 lowBound = Math.min(lowBound, low); 1324 highBound = Math.max(highBound, high); 1325 if (x >= tokens.length) { 1326 break; 1327 } 1328 t = nextToken(tokens, x++, condition); 1329 } 1330 1331 if (t.equals(",")) { 1332 throw unexpected(t, condition); 1333 } 1334 1335 if (valueList.size() == 2) { 1336 vals = null; 1337 } else { 1338 vals = new long[valueList.size()]; 1339 for (int k = 0; k < vals.length; ++k) { 1340 vals[k] = valueList.get(k); 1341 } 1342 } 1343 1344 // Hack to exclude "is not 1,2" 1345 if (lowBound != highBound && hackForCompatibility && !inRange) { 1346 throw unexpected("is not <range>", condition); 1347 } 1348 1349 newConstraint = 1350 new RangeConstraint(mod, inRange, operand, integersOnly, lowBound, highBound, vals); 1351 } 1352 1353 if (andConstraint == null) { 1354 andConstraint = newConstraint; 1355 } else { 1356 andConstraint = new AndConstraint(andConstraint, 1357 newConstraint); 1358 } 1359 } 1360 1361 if (result == null) { 1362 result = andConstraint; 1363 } else { 1364 result = new OrConstraint(result, andConstraint); 1365 } 1366 } 1367 return result; 1368 } 1369 1370 static final Pattern AT_SEPARATED = Pattern.compile("\\s*\\Q\\E@\\s*"); 1371 static final Pattern OR_SEPARATED = Pattern.compile("\\s*or\\s*"); 1372 static final Pattern AND_SEPARATED = Pattern.compile("\\s*and\\s*"); 1373 static final Pattern COMMA_SEPARATED = Pattern.compile("\\s*,\\s*"); 1374 static final Pattern DOTDOT_SEPARATED = Pattern.compile("\\s*\\Q..\\E\\s*"); 1375 static final Pattern TILDE_SEPARATED = Pattern.compile("\\s*~\\s*"); 1376 static final Pattern SEMI_SEPARATED = Pattern.compile("\\s*;\\s*"); 1377 1378 1379 /* Returns a parse exception wrapping the token and context strings. */ 1380 private static ParseException unexpected(String token, String context) { 1381 return new ParseException("unexpected token '" + token + 1382 "' in '" + context + "'", -1); 1383 } 1384 1385 /* 1386 * Returns the token at x if available, else throws a parse exception. 1387 */ 1388 private static String nextToken(String[] tokens, int x, String context) 1389 throws ParseException { 1390 if (x < tokens.length) { 1391 return tokens[x]; 1392 } 1393 throw new ParseException("missing token at end of '" + context + "'", -1); 1394 } 1395 1396 /* 1397 * Syntax: 1398 * rule : keyword ':' condition 1399 * keyword: <identifier> 1400 */ 1401 private static Rule parseRule(String description) throws ParseException { 1402 if (description.length() == 0) { 1403 return DEFAULT_RULE; 1404 } 1405 1406 description = description.toLowerCase(Locale.ENGLISH); 1407 1408 int x = description.indexOf(':'); 1409 if (x == -1) { 1410 throw new ParseException("missing ':' in rule description '" + 1411 description + "'", 0); 1412 } 1413 1414 String keyword = description.substring(0, x).trim(); 1415 if (!isValidKeyword(keyword)) { 1416 throw new ParseException("keyword '" + keyword + 1417 " is not valid", 0); 1418 } 1419 1420 description = description.substring(x+1).trim(); 1421 String[] constraintOrSamples = AT_SEPARATED.split(description); 1422 boolean sampleFailure = false; 1423 FixedDecimalSamples integerSamples = null, decimalSamples = null; 1424 switch (constraintOrSamples.length) { 1425 case 1: break; 1426 case 2: 1427 integerSamples = FixedDecimalSamples.parse(constraintOrSamples[1]); 1428 if (integerSamples.sampleType == SampleType.DECIMAL) { 1429 decimalSamples = integerSamples; 1430 integerSamples = null; 1431 } 1432 break; 1433 case 3: 1434 integerSamples = FixedDecimalSamples.parse(constraintOrSamples[1]); 1435 decimalSamples = FixedDecimalSamples.parse(constraintOrSamples[2]); 1436 if (integerSamples.sampleType != SampleType.INTEGER || decimalSamples.sampleType != SampleType.DECIMAL) { 1437 throw new IllegalArgumentException("Must have @integer then @decimal in " + description); 1438 } 1439 break; 1440 default: 1441 throw new IllegalArgumentException("Too many samples in " + description); 1442 } 1443 if (sampleFailure) { 1444 throw new IllegalArgumentException("Ill-formed samples—'@' characters."); 1445 } 1446 1447 // 'other' is special, and must have no rules; all other keywords must have rules. 1448 boolean isOther = keyword.equals("other"); 1449 if (isOther != (constraintOrSamples[0].length() == 0)) { 1450 throw new IllegalArgumentException("The keyword 'other' must have no constraints, just samples."); 1451 } 1452 1453 Constraint constraint; 1454 if (isOther) { 1455 constraint = NO_CONSTRAINT; 1456 } else { 1457 constraint = parseConstraint(constraintOrSamples[0]); 1458 } 1459 return new Rule(keyword, constraint, integerSamples, decimalSamples); 1460 } 1461 1462 1463 /* 1464 * Syntax: 1465 * rules : rule 1466 * rule ';' rules 1467 */ 1468 private static RuleList parseRuleChain(String description) 1469 throws ParseException { 1470 RuleList result = new RuleList(); 1471 // remove trailing ; 1472 if (description.endsWith(";")) { 1473 description = description.substring(0,description.length()-1); 1474 } 1475 String[] rules = SEMI_SEPARATED.split(description); 1476 for (int i = 0; i < rules.length; ++i) { 1477 Rule rule = parseRule(rules[i].trim()); 1478 result.hasExplicitBoundingInfo |= rule.integerSamples != null || rule.decimalSamples != null; 1479 result.addRule(rule); 1480 } 1481 return result.finish(); 1482 } 1483 1484 /* 1485 * An implementation of Constraint representing a modulus, 1486 * a range of values, and include/exclude. Provides lots of 1487 * convenience factory methods. 1488 */ 1489 private static class RangeConstraint implements Constraint, Serializable { 1490 private static final long serialVersionUID = 1; 1491 1492 private final int mod; 1493 private final boolean inRange; 1494 private final boolean integersOnly; 1495 private final double lowerBound; 1496 private final double upperBound; 1497 private final long[] range_list; 1498 private final Operand operand; 1499 1500 RangeConstraint(int mod, boolean inRange, Operand operand, boolean integersOnly, 1501 double lowBound, double highBound, long[] vals) { 1502 this.mod = mod; 1503 this.inRange = inRange; 1504 this.integersOnly = integersOnly; 1505 this.lowerBound = lowBound; 1506 this.upperBound = highBound; 1507 this.range_list = vals; 1508 this.operand = operand; 1509 } 1510 1511 @Override 1512 public boolean isFulfilled(FixedDecimal number) { 1513 double n = number.get(operand); 1514 if ((integersOnly && (n - (long)n) != 0.0 1515 || operand == Operand.j && number.visibleDecimalDigitCount != 0)) { 1516 return !inRange; 1517 } 1518 if (mod != 0) { 1519 n = n % mod; // java % handles double numerator the way we want 1520 } 1521 boolean test = n >= lowerBound && n <= upperBound; 1522 if (test && range_list != null) { 1523 test = false; 1524 for (int i = 0; !test && i < range_list.length; i += 2) { 1525 test = n >= range_list[i] && n <= range_list[i+1]; 1526 } 1527 } 1528 return inRange == test; 1529 } 1530 1531 @Override 1532 public boolean isLimited(SampleType sampleType) { 1533 boolean valueIsZero = lowerBound == upperBound && lowerBound == 0d; 1534 boolean hasDecimals = 1535 (operand == Operand.v || operand == Operand.w || operand == Operand.f || operand == Operand.t) 1536 && inRange != valueIsZero; // either NOT f = zero or f = non-zero 1537 switch (sampleType) { 1538 case INTEGER: 1539 return hasDecimals // will be empty 1540 || (operand == Operand.n || operand == Operand.i || operand == Operand.j) 1541 && mod == 0 1542 && inRange; 1543 1544 case DECIMAL: 1545 return (!hasDecimals || operand == Operand.n || operand == Operand.j) 1546 && (integersOnly || lowerBound == upperBound) 1547 && mod == 0 1548 && inRange; 1549 } 1550 return false; 1551 } 1552 1553 @Override 1554 public String toString() { 1555 StringBuilder result = new StringBuilder(); 1556 result.append(operand); 1557 if (mod != 0) { 1558 result.append(" % ").append(mod); 1559 } 1560 boolean isList = lowerBound != upperBound; 1561 result.append( 1562 !isList ? (inRange ? " = " : " != ") 1563 : integersOnly ? (inRange ? " = " : " != ") 1564 : (inRange ? " within " : " not within ") 1565 ); 1566 if (range_list != null) { 1567 for (int i = 0; i < range_list.length; i += 2) { 1568 addRange(result, range_list[i], range_list[i+1], i != 0); 1569 } 1570 } else { 1571 addRange(result, lowerBound, upperBound, false); 1572 } 1573 return result.toString(); 1574 } 1575 } 1576 1577 private static void addRange(StringBuilder result, double lb, double ub, boolean addSeparator) { 1578 if (addSeparator) { 1579 result.append(","); 1580 } 1581 if (lb == ub) { 1582 result.append(format(lb)); 1583 } else { 1584 result.append(format(lb) + ".." + format(ub)); 1585 } 1586 } 1587 1588 private static String format(double lb) { 1589 long lbi = (long) lb; 1590 return lb == lbi ? String.valueOf(lbi) : String.valueOf(lb); 1591 } 1592 1593 /* Convenience base class for and/or constraints. */ 1594 private static abstract class BinaryConstraint implements Constraint, 1595 Serializable { 1596 private static final long serialVersionUID = 1; 1597 protected final Constraint a; 1598 protected final Constraint b; 1599 1600 protected BinaryConstraint(Constraint a, Constraint b) { 1601 this.a = a; 1602 this.b = b; 1603 } 1604 } 1605 1606 /* A constraint representing the logical and of two constraints. */ 1607 private static class AndConstraint extends BinaryConstraint { 1608 private static final long serialVersionUID = 7766999779862263523L; 1609 1610 AndConstraint(Constraint a, Constraint b) { 1611 super(a, b); 1612 } 1613 1614 @Override 1615 public boolean isFulfilled(FixedDecimal n) { 1616 return a.isFulfilled(n) 1617 && b.isFulfilled(n); 1618 } 1619 1620 @Override 1621 public boolean isLimited(SampleType sampleType) { 1622 // we ignore the case where both a and b are unlimited but no values 1623 // satisfy both-- we still consider this 'unlimited' 1624 return a.isLimited(sampleType) 1625 || b.isLimited(sampleType); 1626 } 1627 1628 @Override 1629 public String toString() { 1630 return a.toString() + " and " + b.toString(); 1631 } 1632 } 1633 1634 /* A constraint representing the logical or of two constraints. */ 1635 private static class OrConstraint extends BinaryConstraint { 1636 private static final long serialVersionUID = 1405488568664762222L; 1637 1638 OrConstraint(Constraint a, Constraint b) { 1639 super(a, b); 1640 } 1641 1642 @Override 1643 public boolean isFulfilled(FixedDecimal n) { 1644 return a.isFulfilled(n) 1645 || b.isFulfilled(n); 1646 } 1647 1648 @Override 1649 public boolean isLimited(SampleType sampleType) { 1650 return a.isLimited(sampleType) 1651 && b.isLimited(sampleType); 1652 } 1653 1654 @Override 1655 public String toString() { 1656 return a.toString() + " or " + b.toString(); 1657 } 1658 } 1659 1660 /* 1661 * Implementation of Rule that uses a constraint. 1662 * Provides 'and' and 'or' to combine constraints. Immutable. 1663 */ 1664 private static class Rule implements Serializable { 1665 // TODO - Findbugs: Class android.icu.text.PluralRules$Rule defines non-transient 1666 // non-serializable instance field integerSamples. See ticket#10494. 1667 private static final long serialVersionUID = 1; 1668 private final String keyword; 1669 private final Constraint constraint; 1670 private final FixedDecimalSamples integerSamples; 1671 private final FixedDecimalSamples decimalSamples; 1672 1673 public Rule(String keyword, Constraint constraint, FixedDecimalSamples integerSamples, FixedDecimalSamples decimalSamples) { 1674 this.keyword = keyword; 1675 this.constraint = constraint; 1676 this.integerSamples = integerSamples; 1677 this.decimalSamples = decimalSamples; 1678 } 1679 1680 @SuppressWarnings("unused") 1681 public Rule and(Constraint c) { 1682 return new Rule(keyword, new AndConstraint(constraint, c), integerSamples, decimalSamples); 1683 } 1684 1685 @SuppressWarnings("unused") 1686 public Rule or(Constraint c) { 1687 return new Rule(keyword, new OrConstraint(constraint, c), integerSamples, decimalSamples); 1688 } 1689 1690 public String getKeyword() { 1691 return keyword; 1692 } 1693 1694 public boolean appliesTo(FixedDecimal n) { 1695 return constraint.isFulfilled(n); 1696 } 1697 1698 public boolean isLimited(SampleType sampleType) { 1699 return constraint.isLimited(sampleType); 1700 } 1701 1702 @Override 1703 public String toString() { 1704 return keyword + ": " + constraint.toString() 1705 + (integerSamples == null ? "" : " " + integerSamples.toString()) 1706 + (decimalSamples == null ? "" : " " + decimalSamples.toString()); 1707 } 1708 1709 /** 1710 * @deprecated This API is ICU internal only. 1711 * @hide draft / provisional / internal are hidden on Android 1712 */ 1713 @Deprecated 1714 @Override 1715 public int hashCode() { 1716 return keyword.hashCode() ^ constraint.hashCode(); 1717 } 1718 1719 public String getConstraint() { 1720 return constraint.toString(); 1721 } 1722 } 1723 1724 private static class RuleList implements Serializable { 1725 private boolean hasExplicitBoundingInfo = false; 1726 private static final long serialVersionUID = 1; 1727 private final List<Rule> rules = new ArrayList<Rule>(); 1728 1729 public RuleList addRule(Rule nextRule) { 1730 String keyword = nextRule.getKeyword(); 1731 for (Rule rule : rules) { 1732 if (keyword.equals(rule.getKeyword())) { 1733 throw new IllegalArgumentException("Duplicate keyword: " + keyword); 1734 } 1735 } 1736 rules.add(nextRule); 1737 return this; 1738 } 1739 1740 public RuleList finish() throws ParseException { 1741 // make sure that 'other' is present, and at the end. 1742 Rule otherRule = null; 1743 for (Iterator<Rule> it = rules.iterator(); it.hasNext();) { 1744 Rule rule = it.next(); 1745 if ("other".equals(rule.getKeyword())) { 1746 otherRule = rule; 1747 it.remove(); 1748 } 1749 } 1750 if (otherRule == null) { 1751 otherRule = parseRule("other:"); // make sure we have always have an 'other' a rule 1752 } 1753 rules.add(otherRule); 1754 return this; 1755 } 1756 1757 private Rule selectRule(FixedDecimal n) { 1758 for (Rule rule : rules) { 1759 if (rule.appliesTo(n)) { 1760 return rule; 1761 } 1762 } 1763 return null; 1764 } 1765 1766 public String select(FixedDecimal n) { 1767 if (Double.isInfinite(n.source) || Double.isNaN(n.source)) { 1768 return KEYWORD_OTHER; 1769 } 1770 Rule r = selectRule(n); 1771 return r.getKeyword(); 1772 } 1773 1774 public Set<String> getKeywords() { 1775 Set<String> result = new LinkedHashSet<String>(); 1776 for (Rule rule : rules) { 1777 result.add(rule.getKeyword()); 1778 } 1779 // since we have explict 'other', we don't need this. 1780 //result.add(KEYWORD_OTHER); 1781 return result; 1782 } 1783 1784 public boolean isLimited(String keyword, SampleType sampleType) { 1785 if (hasExplicitBoundingInfo) { 1786 FixedDecimalSamples mySamples = getDecimalSamples(keyword, sampleType); 1787 return mySamples == null ? true : mySamples.bounded; 1788 } 1789 1790 return computeLimited(keyword, sampleType); 1791 } 1792 1793 public boolean computeLimited(String keyword, SampleType sampleType) { 1794 // if all rules with this keyword are limited, it's limited, 1795 // and if there's no rule with this keyword, it's unlimited 1796 boolean result = false; 1797 for (Rule rule : rules) { 1798 if (keyword.equals(rule.getKeyword())) { 1799 if (!rule.isLimited(sampleType)) { 1800 return false; 1801 } 1802 result = true; 1803 } 1804 } 1805 return result; 1806 } 1807 1808 @Override 1809 public String toString() { 1810 StringBuilder builder = new StringBuilder(); 1811 for (Rule rule : rules) { 1812 if (builder.length() != 0) { 1813 builder.append(CATEGORY_SEPARATOR); 1814 } 1815 builder.append(rule); 1816 } 1817 return builder.toString(); 1818 } 1819 1820 public String getRules(String keyword) { 1821 for (Rule rule : rules) { 1822 if (rule.getKeyword().equals(keyword)) { 1823 return rule.getConstraint(); 1824 } 1825 } 1826 return null; 1827 } 1828 1829 public boolean select(FixedDecimal sample, String keyword) { 1830 for (Rule rule : rules) { 1831 if (rule.getKeyword().equals(keyword) && rule.appliesTo(sample)) { 1832 return true; 1833 } 1834 } 1835 return false; 1836 } 1837 1838 public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) { 1839 for (Rule rule : rules) { 1840 if (rule.getKeyword().equals(keyword)) { 1841 return sampleType == SampleType.INTEGER ? rule.integerSamples : rule.decimalSamples; 1842 } 1843 } 1844 return null; 1845 } 1846 } 1847 1848 @SuppressWarnings("unused") 1849 private boolean addConditional(Set<FixedDecimal> toAddTo, Set<FixedDecimal> others, double trial) { 1850 boolean added; 1851 FixedDecimal toAdd = new FixedDecimal(trial); 1852 if (!toAddTo.contains(toAdd) && !others.contains(toAdd)) { 1853 others.add(toAdd); 1854 added = true; 1855 } else { 1856 added = false; 1857 } 1858 return added; 1859 } 1860 1861 1862 1863 // ------------------------------------------------------------------------- 1864 // Static class methods. 1865 // ------------------------------------------------------------------------- 1866 1867 /** 1868 * Provides access to the predefined cardinal-number <code>PluralRules</code> for a given 1869 * locale. 1870 * Same as forLocale(locale, PluralType.CARDINAL). 1871 * 1872 * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>. 1873 * For these predefined rules, see CLDR page at 1874 * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html 1875 * 1876 * @param locale The locale for which a <code>PluralRules</code> object is 1877 * returned. 1878 * @return The predefined <code>PluralRules</code> object for this locale. 1879 * If there's no predefined rules for this locale, the rules 1880 * for the closest parent in the locale hierarchy that has one will 1881 * be returned. The final fallback always returns the default 1882 * rules. 1883 */ 1884 public static PluralRules forLocale(ULocale locale) { 1885 return Factory.getDefaultFactory().forLocale(locale, PluralType.CARDINAL); 1886 } 1887 1888 /** 1889 * Provides access to the predefined cardinal-number <code>PluralRules</code> for a given 1890 * {@link java.util.Locale}. 1891 * Same as forLocale(locale, PluralType.CARDINAL). 1892 * 1893 * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>. 1894 * For these predefined rules, see CLDR page at 1895 * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html 1896 * 1897 * @param locale The locale for which a <code>PluralRules</code> object is 1898 * returned. 1899 * @return The predefined <code>PluralRules</code> object for this locale. 1900 * If there's no predefined rules for this locale, the rules 1901 * for the closest parent in the locale hierarchy that has one will 1902 * be returned. The final fallback always returns the default 1903 * rules. 1904 */ 1905 public static PluralRules forLocale(Locale locale) { 1906 return forLocale(ULocale.forLocale(locale)); 1907 } 1908 1909 /** 1910 * Provides access to the predefined <code>PluralRules</code> for a given 1911 * locale and the plural type. 1912 * 1913 * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>. 1914 * For these predefined rules, see CLDR page at 1915 * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html 1916 * 1917 * @param locale The locale for which a <code>PluralRules</code> object is 1918 * returned. 1919 * @param type The plural type (e.g., cardinal or ordinal). 1920 * @return The predefined <code>PluralRules</code> object for this locale. 1921 * If there's no predefined rules for this locale, the rules 1922 * for the closest parent in the locale hierarchy that has one will 1923 * be returned. The final fallback always returns the default 1924 * rules. 1925 */ 1926 public static PluralRules forLocale(ULocale locale, PluralType type) { 1927 return Factory.getDefaultFactory().forLocale(locale, type); 1928 } 1929 1930 /** 1931 * Provides access to the predefined <code>PluralRules</code> for a given 1932 * {@link java.util.Locale} and the plural type. 1933 * 1934 * <p>ICU defines plural rules for many locales based on CLDR <i>Language Plural Rules</i>. 1935 * For these predefined rules, see CLDR page at 1936 * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html 1937 * 1938 * @param locale The locale for which a <code>PluralRules</code> object is 1939 * returned. 1940 * @param type The plural type (e.g., cardinal or ordinal). 1941 * @return The predefined <code>PluralRules</code> object for this locale. 1942 * If there's no predefined rules for this locale, the rules 1943 * for the closest parent in the locale hierarchy that has one will 1944 * be returned. The final fallback always returns the default 1945 * rules. 1946 */ 1947 public static PluralRules forLocale(Locale locale, PluralType type) { 1948 return forLocale(ULocale.forLocale(locale), type); 1949 } 1950 1951 /* 1952 * Checks whether a token is a valid keyword. 1953 * 1954 * @param token the token to be checked 1955 * @return true if the token is a valid keyword. 1956 */ 1957 private static boolean isValidKeyword(String token) { 1958 return ALLOWED_ID.containsAll(token); 1959 } 1960 1961 /* 1962 * Creates a new <code>PluralRules</code> object. Immutable. 1963 */ 1964 private PluralRules(RuleList rules) { 1965 this.rules = rules; 1966 this.keywords = Collections.unmodifiableSet(rules.getKeywords()); 1967 } 1968 1969 /** 1970 * @deprecated This API is ICU internal only. 1971 * @hide original deprecated declaration 1972 * @hide draft / provisional / internal are hidden on Android 1973 */ 1974 @Deprecated 1975 @Override 1976 public int hashCode() { 1977 return rules.hashCode(); 1978 } 1979 /** 1980 * Given a number, returns the keyword of the first rule that applies to 1981 * the number. 1982 * 1983 * @param number The number for which the rule has to be determined. 1984 * @return The keyword of the selected rule. 1985 */ 1986 public String select(double number) { 1987 return rules.select(new FixedDecimal(number)); 1988 } 1989 1990 /** 1991 * Given a number, returns the keyword of the first rule that applies to 1992 * the number. 1993 * 1994 * @param number The number for which the rule has to be determined. 1995 * @return The keyword of the selected rule. 1996 * @deprecated This API is ICU internal only. 1997 * @hide original deprecated declaration 1998 * @hide draft / provisional / internal are hidden on Android 1999 */ 2000 @Deprecated 2001 public String select(double number, int countVisibleFractionDigits, long fractionaldigits) { 2002 return rules.select(new FixedDecimal(number, countVisibleFractionDigits, fractionaldigits)); 2003 } 2004 2005 /** 2006 * Given a number information, returns the keyword of the first rule that applies to 2007 * the number. 2008 * 2009 * @param number The number information for which the rule has to be determined. 2010 * @return The keyword of the selected rule. 2011 * @deprecated This API is ICU internal only. 2012 * @hide original deprecated declaration 2013 * @hide draft / provisional / internal are hidden on Android 2014 */ 2015 @Deprecated 2016 public String select(FixedDecimal number) { 2017 return rules.select(number); 2018 } 2019 2020 /** 2021 * Given a number information, and keyword, return whether the keyword would match the number. 2022 * 2023 * @param sample The number information for which the rule has to be determined. 2024 * @param keyword The keyword to filter on 2025 * @deprecated This API is ICU internal only. 2026 * @hide original deprecated declaration 2027 * @hide draft / provisional / internal are hidden on Android 2028 */ 2029 @Deprecated 2030 public boolean matches(FixedDecimal sample, String keyword) { 2031 return rules.select(sample, keyword); 2032 } 2033 2034 /** 2035 * Returns a set of all rule keywords used in this <code>PluralRules</code> 2036 * object. The rule "other" is always present by default. 2037 * 2038 * @return The set of keywords. 2039 */ 2040 public Set<String> getKeywords() { 2041 return keywords; 2042 } 2043 2044 /** 2045 * Returns the unique value that this keyword matches, or {@link #NO_UNIQUE_VALUE} 2046 * if the keyword matches multiple values or is not defined for this PluralRules. 2047 * 2048 * @param keyword the keyword to check for a unique value 2049 * @return The unique value for the keyword, or NO_UNIQUE_VALUE. 2050 */ 2051 public double getUniqueKeywordValue(String keyword) { 2052 Collection<Double> values = getAllKeywordValues(keyword); 2053 if (values != null && values.size() == 1) { 2054 return values.iterator().next(); 2055 } 2056 return NO_UNIQUE_VALUE; 2057 } 2058 2059 /** 2060 * Returns all the values that trigger this keyword, or null if the number of such 2061 * values is unlimited. 2062 * 2063 * @param keyword the keyword 2064 * @return the values that trigger this keyword, or null. The returned collection 2065 * is immutable. It will be empty if the keyword is not defined. 2066 */ 2067 public Collection<Double> getAllKeywordValues(String keyword) { 2068 return getAllKeywordValues(keyword, SampleType.INTEGER); 2069 } 2070 2071 /** 2072 * Returns all the values that trigger this keyword, or null if the number of such 2073 * values is unlimited. 2074 * 2075 * @param keyword the keyword 2076 * @param type the type of samples requested, INTEGER or DECIMAL 2077 * @return the values that trigger this keyword, or null. The returned collection 2078 * is immutable. It will be empty if the keyword is not defined. 2079 * 2080 * @deprecated This API is ICU internal only. 2081 * @hide original deprecated declaration 2082 * @hide draft / provisional / internal are hidden on Android 2083 */ 2084 @Deprecated 2085 public Collection<Double> getAllKeywordValues(String keyword, SampleType type) { 2086 if (!isLimited(keyword, type)) { 2087 return null; 2088 } 2089 Collection<Double> samples = getSamples(keyword, type); 2090 return samples == null ? null : Collections.unmodifiableCollection(samples); 2091 } 2092 2093 /** 2094 * Returns a list of integer values for which select() would return that keyword, 2095 * or null if the keyword is not defined. The returned collection is unmodifiable. 2096 * The returned list is not complete, and there might be additional values that 2097 * would return the keyword. 2098 * 2099 * @param keyword the keyword to test 2100 * @return a list of values matching the keyword. 2101 */ 2102 public Collection<Double> getSamples(String keyword) { 2103 return getSamples(keyword, SampleType.INTEGER); 2104 } 2105 2106 /** 2107 * Returns a list of values for which select() would return that keyword, 2108 * or null if the keyword is not defined. 2109 * The returned collection is unmodifiable. 2110 * The returned list is not complete, and there might be additional values that 2111 * would return the keyword. The keyword might be defined, and yet have an empty set of samples, 2112 * IF there are samples for the other sampleType. 2113 * 2114 * @param keyword the keyword to test 2115 * @param sampleType the type of samples requested, INTEGER or DECIMAL 2116 * @return a list of values matching the keyword. 2117 * @deprecated ICU internal only 2118 * @hide original deprecated declaration 2119 * @hide draft / provisional / internal are hidden on Android 2120 */ 2121 @Deprecated 2122 public Collection<Double> getSamples(String keyword, SampleType sampleType) { 2123 if (!keywords.contains(keyword)) { 2124 return null; 2125 } 2126 Set<Double> result = new TreeSet<Double>(); 2127 2128 if (rules.hasExplicitBoundingInfo) { 2129 FixedDecimalSamples samples = rules.getDecimalSamples(keyword, sampleType); 2130 return samples == null ? Collections.unmodifiableSet(result) 2131 : Collections.unmodifiableSet(samples.addSamples(result)); 2132 } 2133 2134 // hack in case the rule is created without explicit samples 2135 int maxCount = isLimited(keyword, sampleType) ? Integer.MAX_VALUE : 20; 2136 2137 switch (sampleType) { 2138 case INTEGER: 2139 for (int i = 0; i < 200; ++i) { 2140 if (!addSample(keyword, i, maxCount, result)) { 2141 break; 2142 } 2143 } 2144 addSample(keyword, 1000000, maxCount, result); // hack for Welsh 2145 break; 2146 case DECIMAL: 2147 for (int i = 0; i < 2000; ++i) { 2148 if (!addSample(keyword, new FixedDecimal(i/10d, 1), maxCount, result)) { 2149 break; 2150 } 2151 } 2152 addSample(keyword, new FixedDecimal(1000000d, 1), maxCount, result); // hack for Welsh 2153 break; 2154 } 2155 return result.size() == 0 ? null : Collections.unmodifiableSet(result); 2156 } 2157 2158 /** 2159 * @deprecated This API is ICU internal only. 2160 * @hide original deprecated declaration 2161 * @hide draft / provisional / internal are hidden on Android 2162 */ 2163 @Deprecated 2164 public boolean addSample(String keyword, Number sample, int maxCount, Set<Double> result) { 2165 String selectedKeyword = sample instanceof FixedDecimal ? select((FixedDecimal)sample) : select(sample.doubleValue()); 2166 if (selectedKeyword.equals(keyword)) { 2167 result.add(sample.doubleValue()); 2168 if (--maxCount < 0) { 2169 return false; 2170 } 2171 } 2172 return true; 2173 } 2174 2175 /** 2176 * Returns a list of values for which select() would return that keyword, 2177 * or null if the keyword is not defined or no samples are available. 2178 * The returned collection is unmodifiable. 2179 * The returned list is not complete, and there might be additional values that 2180 * would return the keyword. 2181 * 2182 * @param keyword the keyword to test 2183 * @param sampleType the type of samples requested, INTEGER or DECIMAL 2184 * @return a list of values matching the keyword. 2185 * @deprecated This API is ICU internal only. 2186 * @hide original deprecated declaration 2187 * @hide draft / provisional / internal are hidden on Android 2188 */ 2189 @Deprecated 2190 public FixedDecimalSamples getDecimalSamples(String keyword, SampleType sampleType) { 2191 return rules.getDecimalSamples(keyword, sampleType); 2192 } 2193 2194 /** 2195 * Returns the set of locales for which PluralRules are known. 2196 * @return the set of locales for which PluralRules are known, as a list 2197 * @hide draft / provisional / internal are hidden on Android 2198 */ 2199 public static ULocale[] getAvailableULocales() { 2200 return Factory.getDefaultFactory().getAvailableULocales(); 2201 } 2202 2203 /** 2204 * Returns the 'functionally equivalent' locale with respect to 2205 * plural rules. Calling PluralRules.forLocale with the functionally equivalent 2206 * locale, and with the provided locale, returns rules that behave the same. 2207 * <br> 2208 * All locales with the same functionally equivalent locale have 2209 * plural rules that behave the same. This is not exaustive; 2210 * there may be other locales whose plural rules behave the same 2211 * that do not have the same equivalent locale. 2212 * 2213 * @param locale the locale to check 2214 * @param isAvailable if not null and of length > 0, this will hold 'true' at 2215 * index 0 if locale is directly defined (without fallback) as having plural rules 2216 * @return the functionally-equivalent locale 2217 * @hide draft / provisional / internal are hidden on Android 2218 */ 2219 public static ULocale getFunctionalEquivalent(ULocale locale, boolean[] isAvailable) { 2220 return Factory.getDefaultFactory().getFunctionalEquivalent(locale, isAvailable); 2221 } 2222 2223 /** 2224 * {@inheritDoc} 2225 */ 2226 @Override 2227 public String toString() { 2228 return rules.toString(); 2229 } 2230 2231 /** 2232 * {@inheritDoc} 2233 */ 2234 @Override 2235 public boolean equals(Object rhs) { 2236 return rhs instanceof PluralRules && equals((PluralRules)rhs); 2237 } 2238 2239 /** 2240 * Returns true if rhs is equal to this. 2241 * @param rhs the PluralRules to compare to. 2242 * @return true if this and rhs are equal. 2243 */ 2244 // TODO Optimize this 2245 public boolean equals(PluralRules rhs) { 2246 return rhs != null && toString().equals(rhs.toString()); 2247 } 2248 2249 /** 2250 * Status of the keyword for the rules, given a set of explicit values. 2251 * 2252 * @hide draft / provisional / internal are hidden on Android 2253 */ 2254 public enum KeywordStatus { 2255 /** 2256 * The keyword is not valid for the rules. 2257 * 2258 * @hide draft / provisional / internal are hidden on Android 2259 */ 2260 INVALID, 2261 /** 2262 * The keyword is valid, but unused (it is covered by the explicit values, OR has no values for the given {@link SampleType}). 2263 * 2264 * @hide draft / provisional / internal are hidden on Android 2265 */ 2266 SUPPRESSED, 2267 /** 2268 * The keyword is valid, used, and has a single possible value (before considering explicit values). 2269 * 2270 * @hide draft / provisional / internal are hidden on Android 2271 */ 2272 UNIQUE, 2273 /** 2274 * The keyword is valid, used, not unique, and has a finite set of values. 2275 * 2276 * @hide draft / provisional / internal are hidden on Android 2277 */ 2278 BOUNDED, 2279 /** 2280 * The keyword is valid but not bounded; there indefinitely many matching values. 2281 * 2282 * @hide draft / provisional / internal are hidden on Android 2283 */ 2284 UNBOUNDED 2285 } 2286 2287 /** 2288 * Find the status for the keyword, given a certain set of explicit values. 2289 * 2290 * @param keyword 2291 * the particular keyword (call rules.getKeywords() to get the valid ones) 2292 * @param offset 2293 * the offset used, or 0.0d if not. Internally, the offset is subtracted from each explicit value before 2294 * checking against the keyword values. 2295 * @param explicits 2296 * a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null. 2297 * @param uniqueValue 2298 * If non null, set to the unique value. 2299 * @return the KeywordStatus 2300 * @hide draft / provisional / internal are hidden on Android 2301 */ 2302 public KeywordStatus getKeywordStatus(String keyword, int offset, Set<Double> explicits, 2303 Output<Double> uniqueValue) { 2304 return getKeywordStatus(keyword, offset, explicits, uniqueValue, SampleType.INTEGER); 2305 } 2306 /** 2307 * Find the status for the keyword, given a certain set of explicit values. 2308 * 2309 * @param keyword 2310 * the particular keyword (call rules.getKeywords() to get the valid ones) 2311 * @param offset 2312 * the offset used, or 0.0d if not. Internally, the offset is subtracted from each explicit value before 2313 * checking against the keyword values. 2314 * @param explicits 2315 * a set of Doubles that are used explicitly (eg [=0], "[=1]"). May be empty or null. 2316 * @param sampleType 2317 * request KeywordStatus relative to INTEGER or DECIMAL values 2318 * @param uniqueValue 2319 * If non null, set to the unique value. 2320 * @return the KeywordStatus 2321 * @deprecated This API is ICU internal only. 2322 * @hide original deprecated declaration 2323 * @hide draft / provisional / internal are hidden on Android 2324 */ 2325 @Deprecated 2326 public KeywordStatus getKeywordStatus(String keyword, int offset, Set<Double> explicits, 2327 Output<Double> uniqueValue, SampleType sampleType) { 2328 if (uniqueValue != null) { 2329 uniqueValue.value = null; 2330 } 2331 2332 if (!keywords.contains(keyword)) { 2333 return KeywordStatus.INVALID; 2334 } 2335 2336 if (!isLimited(keyword, sampleType)) { 2337 return KeywordStatus.UNBOUNDED; 2338 } 2339 2340 Collection<Double> values = getSamples(keyword, sampleType); 2341 2342 int originalSize = values.size(); 2343 2344 if (explicits == null) { 2345 explicits = Collections.emptySet(); 2346 } 2347 2348 // Quick check on whether there are multiple elements 2349 2350 if (originalSize > explicits.size()) { 2351 if (originalSize == 1) { 2352 if (uniqueValue != null) { 2353 uniqueValue.value = values.iterator().next(); 2354 } 2355 return KeywordStatus.UNIQUE; 2356 } 2357 return KeywordStatus.BOUNDED; 2358 } 2359 2360 // Compute if the quick test is insufficient. 2361 2362 HashSet<Double> subtractedSet = new HashSet<Double>(values); 2363 for (Double explicit : explicits) { 2364 subtractedSet.remove(explicit - offset); 2365 } 2366 if (subtractedSet.size() == 0) { 2367 return KeywordStatus.SUPPRESSED; 2368 } 2369 2370 if (uniqueValue != null && subtractedSet.size() == 1) { 2371 uniqueValue.value = subtractedSet.iterator().next(); 2372 } 2373 2374 return originalSize == 1 ? KeywordStatus.UNIQUE : KeywordStatus.BOUNDED; 2375 } 2376 2377 /** 2378 * @deprecated This API is ICU internal only. 2379 * @hide original deprecated declaration 2380 * @hide draft / provisional / internal are hidden on Android 2381 */ 2382 @Deprecated 2383 public String getRules(String keyword) { 2384 return rules.getRules(keyword); 2385 } 2386 2387 private void writeObject( 2388 ObjectOutputStream out) 2389 throws IOException { 2390 throw new NotSerializableException(); 2391 } 2392 2393 private void readObject(ObjectInputStream in 2394 ) throws IOException, ClassNotFoundException { 2395 throw new NotSerializableException(); 2396 } 2397 2398 private Object writeReplace() throws ObjectStreamException { 2399 return new PluralRulesSerialProxy(toString()); 2400 } 2401 2402 /** 2403 * @deprecated internal 2404 * @hide original deprecated declaration 2405 * @hide draft / provisional / internal are hidden on Android 2406 */ 2407 @Deprecated 2408 public int compareTo(PluralRules other) { 2409 return toString().compareTo(other.toString()); 2410 } 2411 2412 /** 2413 * @deprecated internal 2414 * @hide original deprecated declaration 2415 * @hide draft / provisional / internal are hidden on Android 2416 */ 2417 @Deprecated 2418 public Boolean isLimited(String keyword) { 2419 return rules.isLimited(keyword, SampleType.INTEGER); 2420 } 2421 2422 /** 2423 * @deprecated internal 2424 * @hide original deprecated declaration 2425 * @hide draft / provisional / internal are hidden on Android 2426 */ 2427 @Deprecated 2428 public boolean isLimited(String keyword, SampleType sampleType) { 2429 return rules.isLimited(keyword, sampleType); 2430 } 2431 2432 /** 2433 * @deprecated internal 2434 * @hide original deprecated declaration 2435 * @hide draft / provisional / internal are hidden on Android 2436 */ 2437 @Deprecated 2438 public boolean computeLimited(String keyword, SampleType sampleType) { 2439 return rules.computeLimited(keyword, sampleType); 2440 } 2441} 2442