1aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin/* GENERATED SOURCE. DO NOT MODIFY. */
2f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert// © 2016 and later: Unicode, Inc. and others.
3f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
4aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin/*
5aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin *******************************************************************************
6aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin * Copyright (C) 2007-2015, International Business Machines Corporation and
7aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin * others. All Rights Reserved.
8aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin *******************************************************************************
9aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin */
10aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinpackage android.icu.dev.test.format;
11aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
12aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.io.ByteArrayInputStream;
13aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.io.ByteArrayOutputStream;
14aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.io.IOException;
15aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.io.ObjectInputStream;
16aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.io.ObjectOutputStream;
17aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.io.Serializable;
18aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.text.ParseException;
19aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.ArrayList;
20aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Arrays;
21aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Collection;
22aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Collections;
23aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Comparator;
24aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.EnumSet;
25aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.HashMap;
26aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.HashSet;
27aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.LinkedHashSet;
28aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.List;
29aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Locale;
30aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Map;
31aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Map.Entry;
32aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.Set;
33aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.TreeMap;
34aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport java.util.TreeSet;
35aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
36f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubertimport org.junit.Test;
37f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
38aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.dev.test.TestFmwk;
39f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubertimport android.icu.dev.test.serializable.SerializableTestUtility;
40aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.dev.util.CollectionUtilities;
41aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.impl.Relation;
42aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.impl.Utility;
43aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.NumberFormat;
44aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules;
45aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules.FixedDecimal;
46aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules.FixedDecimalRange;
47aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules.FixedDecimalSamples;
48aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules.KeywordStatus;
49aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules.PluralType;
50aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.PluralRules.SampleType;
51aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.text.UFieldPosition;
52aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.util.Output;
53aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinimport android.icu.util.ULocale;
54aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
55aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin/**
56aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin * @author dougfelt (Doug Felt)
57aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin * @author markdavis (Mark Davis) [for fractional support]
58aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin */
59aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffinpublic class PluralRulesTest extends TestFmwk {
60aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
61aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    PluralRulesFactory factory = PluralRulesFactory.NORMAL;
62aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
63f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
64aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testOverUnderflow() {
65aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        logln(String.valueOf(Long.MAX_VALUE + 1d));
66aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (double[] testDouble : new double[][] {
67aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { 1E18, 0, 0, 1E18 }, // check overflow
68aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { 10000000000000.1d, 1, 1, 10000000000000d }, { -0.00001d, 1, 5, 0 }, { 1d, 0, 0, 1 },
69aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { 1.1d, 1, 1, 1 }, { 12345d, 0, 0, 12345 }, { 12345.678912d, 678912, 6, 12345 },
70aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { 12345.6789123d, 678912, 6, 12345 }, // we only go out 6 digits
71aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { 1E18, 0, 0, 1E18 }, // check overflow
72aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { 1E19, 0, 0, 1E18 }, // check overflow
73aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }) {
74aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            FixedDecimal fd = new FixedDecimal(testDouble[0]);
75aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(testDouble[0] + "=doubleValue()", testDouble[0], fd.doubleValue());
76aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(testDouble[0] + " decimalDigits", (int) testDouble[1], fd.decimalDigits);
77aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(testDouble[0] + " visibleDecimalDigitCount", (int) testDouble[2], fd.visibleDecimalDigitCount);
78aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(testDouble[0] + " decimalDigitsWithoutTrailingZeros", (int) testDouble[1],
79aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    fd.decimalDigitsWithoutTrailingZeros);
80aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(testDouble[0] + " visibleDecimalDigitCountWithoutTrailingZeros", (int) testDouble[2],
81aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    fd.visibleDecimalDigitCountWithoutTrailingZeros);
82aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(testDouble[0] + " integerValue", (long) testDouble[3], fd.integerValue);
83aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
84aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
85aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (ULocale locale : new ULocale[] { ULocale.ENGLISH, new ULocale("cy"), new ULocale("ar") }) {
86aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules rules = factory.forLocale(locale);
87aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
88aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(locale + " NaN", "other", rules.select(Double.NaN));
89aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(locale + " ∞", "other", rules.select(Double.POSITIVE_INFINITY));
90aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals(locale + " -∞", "other", rules.select(Double.NEGATIVE_INFINITY));
91aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
92aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
93aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
94f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
95aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testSyntaxRestrictions() {
96aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Object[][] shouldFail = {
97aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n in 3..10,13..19" },
98aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
99aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // = and != always work
100aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n=1" },
101aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n=1,3" },
102aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n!=1" },
103aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n!=1,3" },
104aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
105aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // with spacing
106aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n = 1" },
107aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n = 1, 3" },
108aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n != 1" },
109aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n != 1, 3" },
110aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n ! = 1" },
111aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n ! = 1, 3" },
112aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n = 1 , 3" },
113aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n != 1 , 3" },
114aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n ! = 1 , 3" },
115aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n = 1 .. 3" },
116aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n != 1 .. 3" },
117aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n ! = 1 .. 3" },
118aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
119aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // more complicated
120aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n in 3 .. 10 , 13 .. 19" },
121aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
122aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // singles have special exceptions
123aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n is 1" },
124aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n is not 1" },
125aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not is 1", ParseException.class }, // hacked to fail
126aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n in 1" },
127aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not in 1" },
128aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
129aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // multiples also have special exceptions
130aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // TODO enable the following once there is an update to CLDR
131aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // {"a: n is 1,3", ParseException.class},
132aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n is not 1,3", ParseException.class }, // hacked to fail
133aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not is 1,3", ParseException.class }, // hacked to fail
134aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n in 1,3" },
135aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not in 1,3" },
136aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
137aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // disallow not with =
138aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not= 1", ParseException.class }, // hacked to fail
139aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not= 1,3", ParseException.class }, // hacked to fail
140aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
141aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // disallow double negatives
142aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n ! is not 1", ParseException.class },
143aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n ! is not 1", ParseException.class },
144aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n not not in 1", ParseException.class },
145aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n is not not 1", NumberFormatException.class },
146aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
147aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // disallow screwy cases
148aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { null, NullPointerException.class }, { "djkl;", ParseException.class },
149aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n = 1 .", ParseException.class }, { "a: n = 1 ..", ParseException.class },
150aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a: n = 1 2", ParseException.class }, { "a: n = 1 ,", ParseException.class },
151aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { "a:n in 3 .. 10 , 13 .. 19 ,", ParseException.class }, };
152aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (Object[] shouldFailTest : shouldFail) {
153aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String rules = (String) shouldFailTest[0];
154aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Class exception = shouldFailTest.length < 2 ? null : (Class) shouldFailTest[1];
155aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Class actualException = null;
156aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            try {
157aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules.parseDescription(rules);
158aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            } catch (Exception e) {
159aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                actualException = e.getClass();
160aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
161aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals("Exception " + rules, exception, actualException);
162aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
163aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
164aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
165f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
166aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testSamples() {
167aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String description = "one: n is 3 or f is 5 @integer  3,19, @decimal 3.50 ~ 3.53,   …; other:  @decimal 99.0~99.2, 999.0, …";
168aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules test = PluralRules.createRules(description);
169aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
170aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkNewSamples(description, test, "one", PluralRules.SampleType.INTEGER, "@integer 3, 19", true,
171aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                new FixedDecimal(3));
172aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkNewSamples(description, test, "one", PluralRules.SampleType.DECIMAL, "@decimal 3.50~3.53, …", false,
173aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                new FixedDecimal(3.5, 2));
174aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkOldSamples(description, test, "one", SampleType.INTEGER, 3d, 19d);
175aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkOldSamples(description, test, "one", SampleType.DECIMAL, 3.5d, 3.51d, 3.52d, 3.53d);
176aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
177aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkNewSamples(description, test, "other", PluralRules.SampleType.INTEGER, "", true, null);
178aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkNewSamples(description, test, "other", PluralRules.SampleType.DECIMAL, "@decimal 99.0~99.2, 999.0, …",
179aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                false, new FixedDecimal(99d, 1));
180aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkOldSamples(description, test, "other", SampleType.INTEGER);
181aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkOldSamples(description, test, "other", SampleType.DECIMAL, 99d, 99.1, 99.2d, 999d);
182aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
183aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
184aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void checkOldSamples(String description, PluralRules rules, String keyword, SampleType sampleType,
185aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Double... expected) {
186aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Collection<Double> oldSamples = rules.getSamples(keyword, sampleType);
187aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        if (!assertEquals("getOldSamples; " + keyword + "; " + description, new HashSet(Arrays.asList(expected)),
188aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                oldSamples)) {
189aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            rules.getSamples(keyword, sampleType);
190aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
191aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
192aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
193aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void checkNewSamples(String description, PluralRules test, String keyword, SampleType sampleType,
194aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String samplesString, boolean isBounded, FixedDecimal firstInRange) {
195aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String title = description + ", " + sampleType;
196aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        FixedDecimalSamples samples = test.getDecimalSamples(keyword, sampleType);
197aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        if (samples != null) {
198aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals("samples; " + title, samplesString, samples.toString());
199aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals("bounded; " + title, isBounded, samples.bounded);
200aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals("first; " + title, firstInRange, samples.samples.iterator().next().start);
201aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
202aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("limited: " + title, isBounded, test.isLimited(keyword, sampleType));
203aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
204aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
205aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private static final String[] parseTestData = { "a: n is 1", "a:1", "a: n mod 10 is 2", "a:2,12,22",
206aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n is not 1", "a:0,2,3,4,5", "a: n mod 3 is not 1", "a:0,2,3,5,6,8,9", "a: n in 2..5", "a:2,3,4,5",
207aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n within 2..5", "a:2,3,4,5", "a: n not in 2..5", "a:0,1,6,7,8", "a: n not within 2..5", "a:0,1,6,7,8",
208aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n mod 10 in 2..5", "a:2,3,4,5,12,13,14,15,22,23,24,25", "a: n mod 10 within 2..5",
209aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a:2,3,4,5,12,13,14,15,22,23,24,25", "a: n mod 10 is 2 and n is not 12", "a:2,22,32,42",
210aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n mod 10 in 2..3 or n mod 10 is 5", "a:2,3,5,12,13,15,22,23,25",
211aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n mod 10 within 2..3 or n mod 10 is 5", "a:2,3,5,12,13,15,22,23,25", "a: n is 1 or n is 4 or n is 23",
212aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a:1,4,23", "a: n mod 2 is 1 and n is not 3 and n in 1..11", "a:1,5,7,9,11",
213aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n mod 2 is 1 and n is not 3 and n within 1..11", "a:1,5,7,9,11",
214aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n mod 2 is 1 or n mod 5 is 1 and n is not 6", "a:1,3,5,7,9,11,13,15,16",
215aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n in 2..5; b: n in 5..8; c: n mod 2 is 1", "a:2,3,4,5;b:6,7,8;c:1,9,11",
216aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n within 2..5; b: n within 5..8; c: n mod 2 is 1", "a:2,3,4,5;b:6,7,8;c:1,9,11",
217aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n in 2,4..6; b: n within 7..9,11..12,20", "a:2,4,5,6;b:7,8,9,11,12,20",
218aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a: n in 2..8,12 and n not in 4..6", "a:2,3,7,8,12", "a: n mod 10 in 2,3,5..7 and n is not 12",
219aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "a:2,3,5,6,7,13,15,16,17", "a: n in 2..6,3..7", "a:2,3,4,5,6,7", };
220aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
221aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private String[] getTargetStrings(String targets) {
222aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        List list = new ArrayList(50);
223aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String[] valSets = Utility.split(targets, ';');
224aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < valSets.length; ++i) {
225aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String[] temp = Utility.split(valSets[i], ':');
226aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String key = temp[0].trim();
227aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String[] vals = Utility.split(temp[1], ',');
228aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (int j = 0; j < vals.length; ++j) {
229aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String valString = vals[j].trim();
230aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                int val = Integer.parseInt(valString);
231aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                while (list.size() <= val) {
232aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    list.add(null);
233aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
234aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (list.get(val) != null) {
235aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    fail("test data error, key: " + list.get(val) + " already set for: " + val);
236aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
237aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                list.set(val, key);
238aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
239aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
240aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
241aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String[] result = (String[]) list.toArray(new String[list.size()]);
242aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < result.length; ++i) {
243aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (result[i] == null) {
244aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                result[i] = "other";
245aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
246aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
247aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        return result;
248aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
249aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
250aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void checkTargets(PluralRules rules, String[] targets) {
251aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < targets.length; ++i) {
252aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals("value " + i, targets[i], rules.select(i));
253aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
254aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
255aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
256f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
257aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testParseEmpty() throws ParseException {
258aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules rules = PluralRules.parseDescription("a:n");
259aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("empty", "a", rules.select(0));
260aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
261aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
262f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
263aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testParsing() {
264aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < parseTestData.length; i += 2) {
265aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String pattern = parseTestData[i];
266aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String expected = parseTestData[i + 1];
267aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
268aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            logln("pattern[" + i + "] " + pattern);
269aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            try {
270aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules rules = PluralRules.createRules(pattern);
271aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String[] targets = getTargetStrings(expected);
272aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                checkTargets(rules, targets);
273aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            } catch (Exception e) {
274aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                e.printStackTrace();
275aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                throw new RuntimeException(e.getMessage());
276aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
277aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
278aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
279aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
280aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private static String[][] operandTestData = { { "a: n 3", "FAIL" },
281aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: n=1,2; b: n != 3..5; c:n!=5", "a:1,2; b:6,7; c:3,4" },
282aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: n=1,2; b: n!=3..5; c:n!=5", "a:1,2; b:6,7; c:3,4" },
283aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: t is 1", "a:1.1,1.1000,99.100; other:1.2,1.0" }, { "a: f is 1", "a:1.1; other:1.1000,99.100" },
284aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: i is 2; b:i is 3", "b: 3.5; a: 2.5" }, { "a: f is 0; b:f is 50", "a: 1.00; b: 1.50" },
285aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: v is 1; b:v is 2", "a: 1.0; b: 1.00" }, { "one: n is 1 AND v is 0", "one: 1 ; other: 1.00,1.0" }, // English
286aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                                                                                                    // rules
287aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "one: v is 0 and i mod 10 is 1 or f mod 10 is 1", "one: 1, 1.1, 3.1; other: 1.0, 3.2, 5" }, // Last
288aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                                                                                          // visible
289aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                                                                                          // digit
290aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "one: j is 0", "one: 0; other: 0.0, 1.0, 3" }, // Last visible digit
291aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // one → n is 1; few → n in 2..4;
292aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    };
293aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
294f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
295aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testOperands() {
296aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (String[] pair : operandTestData) {
297aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String pattern = pair[0].trim();
298aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String categoriesAndExpected = pair[1].trim();
299aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
300aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // logln("pattern[" + i + "] " + pattern);
301aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            boolean FAIL_EXPECTED = categoriesAndExpected.equalsIgnoreCase("fail");
302aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            try {
303aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                logln(pattern);
304aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules rules = PluralRules.createRules(pattern);
305aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (FAIL_EXPECTED) {
306aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    assertNull("Should fail with 'null' return.", rules);
307aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                } else {
308aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    logln(rules == null ? "null rules" : rules.toString());
309aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    checkCategoriesAndExpected(pattern, categoriesAndExpected, rules);
310aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
311aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            } catch (Exception e) {
312aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (!FAIL_EXPECTED) {
313aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    e.printStackTrace();
314aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    throw new RuntimeException(e.getMessage());
315aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
316aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
317aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
318aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
319aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
320f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
321aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testUniqueRules() {
322aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        main: for (ULocale locale : factory.getAvailableULocales()) {
323aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules rules = factory.forLocale(locale);
324aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Map<String, PluralRules> keywordToRule = new HashMap<String, PluralRules>();
325aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Collection<FixedDecimalSamples> samples = new LinkedHashSet<FixedDecimalSamples>();
326aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
327aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String keyword : rules.getKeywords()) {
328aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (SampleType sampleType : SampleType.values()) {
329aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    FixedDecimalSamples samples2 = rules.getDecimalSamples(keyword, sampleType);
330aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    if (samples2 != null) {
331aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        samples.add(samples2);
332aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
333aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
334aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (keyword.equals("other")) {
335aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    continue;
336aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
337aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String rules2 = keyword + ":" + rules.getRules(keyword);
338aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules singleRule = PluralRules.createRules(rules2);
339aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (singleRule == null) {
340aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    errln("Can't generate single rule for " + rules2);
341aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    PluralRules.createRules(rules2); // for debugging
342aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    continue main;
343aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
344aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                keywordToRule.put(keyword, singleRule);
345aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
346aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Map<FixedDecimal, String> collisionTest = new TreeMap();
347aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (FixedDecimalSamples sample3 : samples) {
348aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Set<FixedDecimalRange> samples2 = sample3.getSamples();
349aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (samples2 == null) {
350aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    continue;
351aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
352aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (FixedDecimalRange sample : samples2) {
353aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    for (int i = 0; i < 1; ++i) {
354aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        FixedDecimal item = i == 0 ? sample.start : sample.end;
355aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        collisionTest.clear();
356aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        for (Entry<String, PluralRules> entry : keywordToRule.entrySet()) {
357aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            PluralRules rule = entry.getValue();
358aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            String foundKeyword = rule.select(item);
359aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            if (foundKeyword.equals("other")) {
360aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                continue;
361aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            }
362aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            String old = collisionTest.get(item);
363aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            if (old != null) {
364aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                errln(locale + "\tNon-unique rules: " + item + " => " + old + " & " + foundKeyword);
365aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                rule.select(item);
366aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            } else {
367aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                collisionTest.put(item, foundKeyword);
368aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            }
369aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        }
370aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
371aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
372aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
373aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
374aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
375aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
376aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void checkCategoriesAndExpected(String title1, String categoriesAndExpected, PluralRules rules) {
377aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (String categoryAndExpected : categoriesAndExpected.split("\\s*;\\s*")) {
378aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String[] categoryFromExpected = categoryAndExpected.split("\\s*:\\s*");
379aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String expected = categoryFromExpected[0];
380aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String value : categoryFromExpected[1].split("\\s*,\\s*")) {
381aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (value.startsWith("@") || value.equals("…") || value.equals("null")) {
382aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    continue;
383aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
384aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String[] values = value.split("\\s*~\\s*");
385aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                checkValue(title1, rules, expected, values[0]);
386aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (values.length > 1) {
387aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    checkValue(title1, rules, expected, values[1]);
388aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
389aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
390aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
391aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
392aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
393aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void checkValue(String title1, PluralRules rules, String expected, String value) {
394aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        double number = Double.parseDouble(value);
395aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        int decimalPos = value.indexOf('.') + 1;
396aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        int countVisibleFractionDigits;
397aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        int fractionaldigits;
398aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        if (decimalPos == 0) {
399aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            countVisibleFractionDigits = fractionaldigits = 0;
400aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        } else {
401aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            countVisibleFractionDigits = value.length() - decimalPos;
402aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            fractionaldigits = Integer.parseInt(value.substring(decimalPos));
403aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
404aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String result = rules.select(number, countVisibleFractionDigits, fractionaldigits);
405aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale locale = null;
406aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals(getAssertMessage(title1, locale, rules, expected) + "; value: " + value, expected, result);
407aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
408aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
409aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private static String[][] equalityTestData = {
410aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // once we add fractions, we had to retract the "test all possibilities" for equality,
411aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // so we only have a limited set of equality tests now.
412aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "c: n%11!=5", "c: n mod 11 is not 5" }, { "c: n is not 7", "c: n != 7" }, { "a:n in 2;", "a: n = 2" },
413aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "b:n not in 5;", "b: n != 5" },
414aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
415aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // { "a: n is 5",
416aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // "a: n in 2..6 and n not in 2..4 and n is not 6" },
417aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // { "a: n in 2..3",
418aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // "a: n is 2 or n is 3",
419aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // "a: n is 3 and n in 2..5 or n is 2" },
420aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // { "a: n is 12; b:n mod 10 in 2..3",
421aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // "b: n mod 10 in 2..3 and n is not 12; a: n in 12..12",
422aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    // "b: n is 13; a: n is 12; b: n mod 10 is 2 or n mod 10 is 3" },
423aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    };
424aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
425aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private static String[][] inequalityTestData = { { "a: n mod 8 is 3", "a: n mod 7 is 3" },
426aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: n mod 3 is 2 and n is not 5", "a: n mod 6 is 2 or n is 8 or n is 11" },
427aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // the following are currently inequal, but we may make them equal in the future.
428aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            { "a: n in 2..5", "a: n in 2..4,5" }, };
429aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
430aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void compareEquality(String id, Object[] objects, boolean shouldBeEqual) {
431aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < objects.length; ++i) {
432aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Object lhs = objects[i];
433aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            int start = shouldBeEqual ? i : i + 1;
434aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (int j = start; j < objects.length; ++j) {
435aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Object rhs = objects[j];
436aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (rhs == null || shouldBeEqual != lhs.equals(rhs)) {
437aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    String msg = shouldBeEqual ? "should be equal" : "should not be equal";
438aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    fail(id + " " + msg + " (" + i + ", " + j + "):\n    " + lhs + "\n    " + rhs);
439aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
440aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // assertEquals("obj " + i + " and " + j, lhs, rhs);
441aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
442aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
443aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
444aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
445aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void compareEqualityTestSets(String[][] sets, boolean shouldBeEqual) {
446aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < sets.length; ++i) {
447aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String[] patterns = sets[i];
448aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules[] rules = new PluralRules[patterns.length];
449aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (int j = 0; j < patterns.length; ++j) {
450aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                rules[j] = PluralRules.createRules(patterns[j]);
451aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
452aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            compareEquality("test " + i, rules, shouldBeEqual);
453aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
454aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
455aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
456f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
457aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testEquality() {
458aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        compareEqualityTestSets(equalityTestData, true);
459aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
460aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
461f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
462aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testInequality() {
463aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        compareEqualityTestSets(inequalityTestData, false);
464aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
465aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
466f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
467aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testBuiltInRules() {
468aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // spot check
469aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules rules = factory.forLocale(ULocale.US);
470aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("us 0", PluralRules.KEYWORD_OTHER, rules.select(0));
471aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("us 1", PluralRules.KEYWORD_ONE, rules.select(1));
472aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("us 2", PluralRules.KEYWORD_OTHER, rules.select(2));
473aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
474aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        rules = factory.forLocale(ULocale.JAPAN);
475aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ja 0", PluralRules.KEYWORD_OTHER, rules.select(0));
476aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ja 1", PluralRules.KEYWORD_OTHER, rules.select(1));
477aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ja 2", PluralRules.KEYWORD_OTHER, rules.select(2));
478aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
479aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        rules = factory.forLocale(ULocale.createCanonical("ru"));
480aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ru 0", PluralRules.KEYWORD_MANY, rules.select(0));
481aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ru 1", PluralRules.KEYWORD_ONE, rules.select(1));
482aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ru 2", PluralRules.KEYWORD_FEW, rules.select(2));
483aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
484aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
485f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
486aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testFunctionalEquivalent() {
487aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // spot check
488aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale unknown = ULocale.createCanonical("zz_ZZ");
489aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale un_equiv = PluralRules.getFunctionalEquivalent(unknown, null);
490aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("unknown locales have root", ULocale.ROOT, un_equiv);
491aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
492aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale jp_equiv = PluralRules.getFunctionalEquivalent(ULocale.JAPAN, null);
493aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale cn_equiv = PluralRules.getFunctionalEquivalent(ULocale.CHINA, null);
494aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("japan and china equivalent locales", jp_equiv, cn_equiv);
495aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
496aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        boolean[] available = new boolean[1];
497aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale russia = ULocale.createCanonical("ru_RU");
498aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale ru_ru_equiv = PluralRules.getFunctionalEquivalent(russia, available);
499aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertFalse("ru_RU not listed", available[0]);
500aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
501aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale russian = ULocale.createCanonical("ru");
502aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale ru_equiv = PluralRules.getFunctionalEquivalent(russian, available);
503aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertTrue("ru listed", available[0]);
504aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("ru and ru_RU equivalent locales", ru_ru_equiv, ru_equiv);
505aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
506aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
507f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
508aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testAvailableULocales() {
509aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale[] locales = factory.getAvailableULocales();
510aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Set localeSet = new HashSet();
511aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        localeSet.addAll(Arrays.asList(locales));
512aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
513aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("locales are unique in list", locales.length, localeSet.size());
514aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
515aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
516aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /*
517aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * Test the method public static PluralRules parseDescription(String description)
518aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
519f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
520aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestParseDescription() {
521aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        try {
522aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (PluralRules.DEFAULT != PluralRules.parseDescription("")) {
523aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                errln("PluralRules.parseDescription(String) was suppose "
524aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        + "to return PluralRules.DEFAULT when String is of " + "length 0.");
525aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
526aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        } catch (ParseException e) {
527aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            errln("PluralRules.parseDescription(String) was not suppose " + "to return an exception.");
528aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
529aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
530aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
531aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /*
532aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * Tests the method public static PluralRules createRules(String description)
533aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
534f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
535aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestCreateRules() {
536aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        try {
537aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (PluralRules.createRules(null) != null) {
538aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                errln("PluralRules.createRules(String) was suppose to "
539aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        + "return null for an invalid String descrtiption.");
540aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
541aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        } catch (Exception e) {
542aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
543aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
544aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
545aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /*
546aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * Tests the method public int hashCode()
547aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
548f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
549aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestHashCode() {
550aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // Bad test, breaks whenever PluralRules implementation changes.
551aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // PluralRules pr = PluralRules.DEFAULT;
552aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // if (106069776 != pr.hashCode()) {
553aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // errln("PluralRules.hashCode() was suppose to return 106069776 " + "when PluralRules.DEFAULT.");
554aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // }
555aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
556aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
557aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /*
558aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * Tests the method public boolean equals(PluralRules rhs)
559aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
560f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
561aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestEquals() {
562aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules pr = PluralRules.DEFAULT;
563aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
564aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        if (pr.equals((PluralRules) null)) {
565aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            errln("PluralRules.equals(PluralRules) was supposed to return false " + "when passing null.");
566aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
567aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
568aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
569aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void assertRuleValue(String rule, double value) {
570aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleKeyValue("a:" + rule, "a", value);
571aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
572aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
573aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void assertRuleKeyValue(String rule, String key, double value) {
574aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules pr = PluralRules.createRules(rule);
575aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals(rule, value, pr.getUniqueKeywordValue(key));
576aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
577aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
578aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /*
579aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * Tests getUniqueKeywordValue()
580aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
581f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
582aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestGetUniqueKeywordValue() {
583aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleKeyValue("a: n is 1", "not_defined", PluralRules.NO_UNIQUE_VALUE); // key not defined
584aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n within 2..2", 2);
585aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 1", 1);
586aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n in 2..2", 2);
587aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n in 3..4", PluralRules.NO_UNIQUE_VALUE);
588aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n within 3..4", PluralRules.NO_UNIQUE_VALUE);
589aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 2 or n is 2", 2);
590aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 2 and n is 2", 2);
591aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 2 or n is 3", PluralRules.NO_UNIQUE_VALUE);
592aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 2 and n is 3", PluralRules.NO_UNIQUE_VALUE);
593aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 2 or n in 2..3", PluralRules.NO_UNIQUE_VALUE);
594aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n is 2 and n in 2..3", 2);
595aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleKeyValue("a: n is 1", "other", PluralRules.NO_UNIQUE_VALUE); // key matches default rule
596aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n in 2,3", PluralRules.NO_UNIQUE_VALUE);
597aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertRuleValue("n in 2,3..6 and n not in 2..3,5..6", 4);
598aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
599aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
600aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /**
601aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * The version in PluralFormatUnitTest is not really a test, and it's in the wrong place anyway, so I'm putting a
602aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * variant of it here.
603aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
604f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
605aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestGetSamples() {
606aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Set<ULocale> uniqueRuleSet = new HashSet<ULocale>();
607aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (ULocale locale : factory.getAvailableULocales()) {
608aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            uniqueRuleSet.add(PluralRules.getFunctionalEquivalent(locale, null));
609aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
610aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (ULocale locale : uniqueRuleSet) {
611aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules rules = factory.forLocale(locale);
612aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            logln("\nlocale: " + (locale == ULocale.ROOT ? "root" : locale.toString()) + ", rules: " + rules);
613aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Set<String> keywords = rules.getKeywords();
614aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String keyword : keywords) {
615aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Collection<Double> list = rules.getSamples(keyword);
616aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                logln("keyword: " + keyword + ", samples: " + list);
617aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // with fractions, the samples can be empty and thus the list null. In that case, however, there will be
618aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // FixedDecimal values.
619aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // So patch the test for that.
620aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (list.size() == 0) {
621aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    // when the samples (meaning integer samples) are null, then then integerSamples must be, and the
622aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    // decimalSamples must not be
623aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    FixedDecimalSamples integerSamples = rules.getDecimalSamples(keyword, SampleType.INTEGER);
624aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    FixedDecimalSamples decimalSamples = rules.getDecimalSamples(keyword, SampleType.DECIMAL);
625aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    assertTrue(getAssertMessage("List is not null", locale, rules, keyword), integerSamples == null
626aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            && decimalSamples != null && decimalSamples.samples.size() != 0);
627aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                } else {
628aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    if (!assertTrue(getAssertMessage("Test getSamples.isEmpty", locale, rules, keyword),
629aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            !list.isEmpty())) {
630aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        rules.getSamples(keyword);
631aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
632aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    if (rules.toString().contains(": j")) {
633aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        // hack until we remove j
634aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    } else {
635aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        for (double value : list) {
636aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            assertEquals(getAssertMessage("Match keyword", locale, rules, keyword) + "; value '"
637aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                    + value + "'", keyword, rules.select(value));
638aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        }
639aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
640aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
641aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
642aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
643aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertNull(locale + ", list is null", rules.getSamples("@#$%^&*"));
644aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertNull(locale + ", list is null", rules.getSamples("@#$%^&*", SampleType.DECIMAL));
645aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
646aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
647aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
648aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public String getAssertMessage(String message, ULocale locale, PluralRules rules, String keyword) {
649aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String ruleString = "";
650aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        if (keyword != null) {
651aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (keyword.equals("other")) {
652aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (String keyword2 : rules.getKeywords()) {
653aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    ruleString += " NOR " + rules.getRules(keyword2).split("@")[0];
654aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
655aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            } else {
656aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String rule = rules.getRules(keyword);
657aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                ruleString = rule == null ? null : rule.split("@")[0];
658aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
659aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ruleString = "; rule: '" + keyword + ": " + ruleString + "'";
660aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // !keyword.equals("other") ? "'; keyword: '" + keyword + "'; rule: '" + rules.getRules(keyword) + "'"
661aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // : "'; keyword: '" + keyword + "'; rules: '" + rules.toString() + "'";
662aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
663aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        return message + (locale == null ? "" : "; locale: '" + locale + "'") + ruleString;
664aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
665aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
666aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /**
667aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * Returns the empty set if the keyword is not defined, null if there are an unlimited number of values for the
668aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * keyword, or the set of values that trigger the keyword.
669aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
670f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
671aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestGetAllKeywordValues() {
672aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        // data is pairs of strings, the rule, and the expected values as arguments
673aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String[] data = {
674aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "other: ; a: n mod 3 is 0",
675aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: null",
676aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n in 2..5 and n within 5..8",
677aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: 5",
678aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n in 2..5",
679aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: 2,3,4,5; other: null",
680aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n not in 2..5",
681aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: null; other: null",
682aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n within 2..5",
683aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: 2,3,4,5; other: null",
684aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n not within 2..5",
685aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: null; other: null",
686aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n in 2..5 or n within 6..8",
687aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: 2,3,4,5,6,7,8", // ignore 'other' here on out, always null
688aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n in 2..5 and n within 6..8",
689aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: null",
690aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // we no longer support 'degenerate' rules
691aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // "a: n within 2..5 and n within 6..8", "a:", // our sampling catches these
692aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // "a: n within 2..5 and n within 5..8", "a: 5", // ''
693aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5", "a: 2,4",
694aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // "a: n mod 3 is 0 and n within 0..5", "a: 0,3",
695aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n within 1..2 and n within 2..3 or n within 3..4 and n within 4..5 or n within 5..6 and n within 6..7",
696aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: 2,4,6", // but not this...
697aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n mod 3 is 0 and n within 1..2", "a: null", "a: n mod 3 is 0 and n within 0..6", "a: 0,3,6",
698aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                "a: n mod 3 is 0 and n in 3..12", "a: 3,6,9,12", "a: n in 2,4..6 and n is not 5", "a: 2,4,6", };
699aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < data.length; i += 2) {
700aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String ruleDescription = data[i];
701aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String result = data[i + 1];
702aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
703aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules p = PluralRules.createRules(ruleDescription);
704aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (p == null) { // for debugging
705aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules.createRules(ruleDescription);
706aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
707aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String ruleResult : result.split(";")) {
708aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String[] ruleAndValues = ruleResult.split(":");
709aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String keyword = ruleAndValues[0].trim();
710aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String valueList = ruleAndValues.length < 2 ? null : ruleAndValues[1];
711aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (valueList != null) {
712aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    valueList = valueList.trim();
713aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
714aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Collection<Double> values;
715aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (valueList == null || valueList.length() == 0) {
716aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    values = Collections.EMPTY_SET;
717aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                } else if ("null".equals(valueList)) {
718aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    values = null;
719aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                } else {
720aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    values = new TreeSet<Double>();
721aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    for (String value : valueList.split(",")) {
722aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        values.add(Double.parseDouble(value));
723aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
724aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
725aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
726aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Collection<Double> results = p.getAllKeywordValues(keyword);
727aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertEquals(keyword + " in " + ruleDescription, values, results == null ? null : new HashSet(results));
728aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
729aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (results != null) {
730aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    try {
731aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        results.add(PluralRules.NO_UNIQUE_VALUE);
732aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        fail("returned set is modifiable");
733aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    } catch (UnsupportedOperationException e) {
734aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        // pass
735aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
736aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
737aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
738aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
739aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
740aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
741f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
742aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestOrdinal() {
743aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules pr = factory.forLocale(ULocale.ENGLISH, PluralType.ORDINAL);
744aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("PluralRules(en-ordinal).select(2)", "two", pr.select(2));
745aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
746aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
747f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
748aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestBasicFraction() {
749aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        String[][] tests = { { "en", "one: j is 1" }, { "1", "0", "1", "one" }, { "1", "2", "1.00", "other" }, };
750aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        ULocale locale = null;
751aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        NumberFormat nf = null;
752aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules pr = null;
753aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
754aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (String[] row : tests) {
755aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            switch (row.length) {
756aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            case 2:
757aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                locale = ULocale.forLanguageTag(row[0]);
758aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                nf = NumberFormat.getInstance(locale);
759aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                pr = PluralRules.createRules(row[1]);
760aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                break;
761aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            case 4:
762aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                double n = Double.parseDouble(row[0]);
763aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                int minFracDigits = Integer.parseInt(row[1]);
764aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                nf.setMinimumFractionDigits(minFracDigits);
765aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String expectedFormat = row[2];
766aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String expectedKeyword = row[3];
767aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
768aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                UFieldPosition pos = new UFieldPosition();
769aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String formatted = nf.format(1.0, new StringBuffer(), pos).toString();
770aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                int countVisibleFractionDigits = pos.getCountVisibleFractionDigits();
771aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                long fractionDigits = pos.getFractionDigits();
772aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String keyword = pr.select(n, countVisibleFractionDigits, fractionDigits);
773aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertEquals("Formatted " + n + "\t" + minFracDigits, expectedFormat, formatted);
774aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertEquals("Keyword " + n + "\t" + minFracDigits, expectedKeyword, keyword);
775aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                break;
776aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            default:
777aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                throw new RuntimeException();
778aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
779aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
780aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
781aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
782f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
783aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestLimitedAndSamplesConsistency() {
784aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (ULocale locale : PluralRules.getAvailableULocales()) {
785aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ULocale loc2 = PluralRules.getFunctionalEquivalent(locale, null);
786aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (!loc2.equals(locale)) {
787aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                continue; // only need "unique" rules
788aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
789aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (PluralType type : PluralType.values()) {
790aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules rules = PluralRules.forLocale(locale, type);
791aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (SampleType sampleType : SampleType.values()) {
792aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    if (type == PluralType.ORDINAL) {
793aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        logKnownIssue("10783", "Fix issues with isLimited vs computeLimited on ordinals");
794aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        continue;
795aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
796aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    for (String keyword : rules.getKeywords()) {
797aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        boolean isLimited = rules.isLimited(keyword, sampleType);
798aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        boolean computeLimited = rules.computeLimited(keyword, sampleType);
799aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        if (!keyword.equals("other")) {
800aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            assertEquals(getAssertMessage("computeLimited == isLimited", locale, rules, keyword),
801aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                    computeLimited, isLimited);
802aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        }
803aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        Collection<Double> samples = rules.getSamples(keyword, sampleType);
804aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        assertNotNull(getAssertMessage("Samples must not be null", locale, rules, keyword), samples);
805aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        /* FixedDecimalSamples decimalSamples = */rules.getDecimalSamples(keyword, sampleType);
806aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        // assertNotNull(getAssertMessage("Decimal samples must be null if unlimited", locale, rules,
807aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        // keyword), decimalSamples);
808aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
809aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
810aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
811aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
812aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
813aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
814f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
815aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestKeywords() {
816aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Set<String> possibleKeywords = new LinkedHashSet(Arrays.asList("zero", "one", "two", "few", "many", "other"));
817aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Object[][][] tests = {
818aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // format is locale, explicits, then triples of keyword, status, unique value.
819aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { { "en", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "other", KeywordStatus.UNBOUNDED, null } },
820aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                { { "pl", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "few", KeywordStatus.UNBOUNDED, null },
821aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        { "many", KeywordStatus.UNBOUNDED, null },
822aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        { "other", KeywordStatus.SUPPRESSED, null, KeywordStatus.UNBOUNDED, null } // note that it is
823aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                                                                                   // suppressed in
824aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                                                                                   // INTEGER but not
825aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                                                                                                   // DECIMAL
826aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }, { { "en", new HashSet<Double>(Arrays.asList(1.0d)) }, // check that 1 is suppressed
827aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        { "one", KeywordStatus.SUPPRESSED, null }, { "other", KeywordStatus.UNBOUNDED, null } }, };
828aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Output<Double> uniqueValue = new Output<Double>();
829aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (Object[][] test : tests) {
830aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ULocale locale = new ULocale((String) test[0][0]);
831aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // NumberType numberType = (NumberType) test[1];
832aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Set<Double> explicits = (Set<Double>) test[0][1];
833aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules pluralRules = factory.forLocale(locale);
834aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            LinkedHashSet<String> remaining = new LinkedHashSet(possibleKeywords);
835aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (int i = 1; i < test.length; ++i) {
836aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Object[] row = test[i];
837aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                String keyword = (String) row[0];
838aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                KeywordStatus statusExpected = (KeywordStatus) row[1];
839aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Double uniqueExpected = (Double) row[2];
840aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                remaining.remove(keyword);
841aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue);
842aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertEquals(getAssertMessage("Unique Value", locale, pluralRules, keyword), uniqueExpected,
843aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        uniqueValue.value);
844aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertEquals(getAssertMessage("Keyword Status", locale, pluralRules, keyword), statusExpected, status);
845aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (row.length > 3) {
846aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    statusExpected = (KeywordStatus) row[3];
847aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    uniqueExpected = (Double) row[4];
848aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue, SampleType.DECIMAL);
849aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    assertEquals(getAssertMessage("Unique Value - decimal", locale, pluralRules, keyword),
850aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            uniqueExpected, uniqueValue.value);
851aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    assertEquals(getAssertMessage("Keyword Status - decimal", locale, pluralRules, keyword),
852aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            statusExpected, status);
853aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
854aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
855aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String keyword : remaining) {
856aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, null, uniqueValue);
857aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertEquals("Invalid keyword " + keyword, status, KeywordStatus.INVALID);
858aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                assertNull("Invalid keyword " + keyword, uniqueValue.value);
859aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
860aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
861aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
862aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
863aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    enum StandardPluralCategories {
864aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        zero, one, two, few, many, other;
865aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        /**
866aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         *
867aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         */
868aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        private static final Set<StandardPluralCategories> ALL = Collections.unmodifiableSet(EnumSet
869aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                .allOf(StandardPluralCategories.class));
870aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
871aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        /**
872aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         * Return a mutable set
873aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         *
874aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         * @param source
875aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         * @return
876aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin         */
877aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        static final EnumSet<StandardPluralCategories> getSet(Collection<String> source) {
878aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            EnumSet<StandardPluralCategories> result = EnumSet.noneOf(StandardPluralCategories.class);
879aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String s : source) {
880aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                result.add(StandardPluralCategories.valueOf(s));
881aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
882aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            return result;
883aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
884aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
885aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        static final Comparator<Set<StandardPluralCategories>> SHORTEST_FIRST = new Comparator<Set<StandardPluralCategories>>() {
886aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            public int compare(Set<StandardPluralCategories> arg0, Set<StandardPluralCategories> arg1) {
887aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                int diff = arg0.size() - arg1.size();
888aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (diff != 0) {
889aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    return diff;
890aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
891aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // otherwise first...
892aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // could be optimized, but we don't care here.
893aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (StandardPluralCategories value : ALL) {
894aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    if (arg0.contains(value)) {
895aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        if (!arg1.contains(value)) {
896aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                            return 1;
897aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        }
898aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    } else if (arg1.contains(value)) {
899aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                        return -1;
900aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    }
901aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
902aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
903aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                return 0;
904aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
905aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
906aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        };
907aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
908aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
909f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
910aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestLocales() {
911aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        if (false) {
912aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            generateLOCALE_SNAPSHOT();
913aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
914aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (String test : LOCALE_SNAPSHOT) {
915aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            test = test.trim();
916aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            String[] parts = test.split("\\s*;\\s*");
917aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (String localeString : parts[0].split("\\s*,\\s*")) {
918aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                ULocale locale = new ULocale(localeString);
919aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                if (factory.hasOverride(locale)) {
920aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    continue; // skip for now
921aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
922aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules rules = factory.forLocale(locale);
923aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (int i = 1; i < parts.length; ++i) {
924aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    checkCategoriesAndExpected(localeString, parts[i], rules);
925aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
926aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
927aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
928aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
929aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
930aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private static final Comparator<PluralRules> PLURAL_RULE_COMPARATOR = new Comparator<PluralRules>() {
931aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        public int compare(PluralRules o1, PluralRules o2) {
932aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            return o1.compareTo(o2);
933aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
934aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    };
935aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
936aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private void generateLOCALE_SNAPSHOT() {
937aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Comparator c = new CollectionUtilities.CollectionComparator<Comparable>();
938aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Relation<Set<StandardPluralCategories>, PluralRules> setsToRules = Relation.of(
939aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                new TreeMap<Set<StandardPluralCategories>, Set<PluralRules>>(c), TreeSet.class, PLURAL_RULE_COMPARATOR);
940aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Relation<PluralRules, ULocale> data = Relation.of(
941aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                new TreeMap<PluralRules, Set<ULocale>>(PLURAL_RULE_COMPARATOR), TreeSet.class);
942aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (ULocale locale : PluralRules.getAvailableULocales()) {
943aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules pr = PluralRules.forLocale(locale);
944aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            EnumSet<StandardPluralCategories> set = getCanonicalSet(pr.getKeywords());
945aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            setsToRules.put(set, pr);
946aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            data.put(pr, locale);
947aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
948aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (Entry<Set<StandardPluralCategories>, Set<PluralRules>> entry1 : setsToRules.keyValuesSet()) {
949aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Set<StandardPluralCategories> set = entry1.getKey();
950aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Set<PluralRules> rules = entry1.getValue();
951aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            System.out.println("\n        // " + set);
952aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            for (PluralRules rule : rules) {
953aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                Set<ULocale> locales = data.get(rule);
954aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                System.out.print("        \"" + CollectionUtilities.join(locales, ","));
955aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                for (StandardPluralCategories spc : set) {
956aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    String keyword = spc.toString();
957aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    FixedDecimalSamples samples = rule.getDecimalSamples(keyword, SampleType.INTEGER);
958aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    System.out.print("; " + spc + ": " + samples);
959aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                }
960aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                System.out.println("\",");
961aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
962aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
963aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
964aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
965aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /**
966aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * @param keywords
967aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * @return
968aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
969aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private EnumSet<StandardPluralCategories> getCanonicalSet(Set<String> keywords) {
970aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        EnumSet<StandardPluralCategories> result = EnumSet.noneOf(StandardPluralCategories.class);
971aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (String s : keywords) {
972aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            result.add(StandardPluralCategories.valueOf(s));
973aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
974aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        return result;
975aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
976aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
977aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    static final String[] LOCALE_SNAPSHOT = {
978aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [other]
979aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "bm,bo,dz,id,ig,ii,in,ja,jbo,jv,jw,kde,kea,km,ko,lkt,lo,ms,my,nqo,root,sah,ses,sg,th,to,vi,wo,yo,zh; other: @integer 0~15, 100, 1000, 10000, 100000, 1000000, …",
980aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
981aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, other]
982aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "am,bn,fa,gu,hi,kn,mr,zu; one: @integer 0, 1; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, …",
983aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ff,fr,hy,kab; one: @integer 0, 1; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, …",
984aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ast,ca,de,en,et,fi,fy,gl,it,ji,nl,sv,sw,ur,yi; one: @integer 1; other: @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …",
985aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "pt; one: @integer 1; other: @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …",
986aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "si; one: @integer 0, 1; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, …",
987aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ak,bh,guw,ln,mg,nso,pa,ti,wa; one: @integer 0, 1; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, …",
988aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "tzm; one: @integer 0, 1, 11~24; other: @integer 2~10, 100~106, 1000, 10000, 100000, 1000000, …",
989aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "af,asa,az,bem,bez,bg,brx,cgg,chr,ckb,dv,ee,el,eo,es,eu,fo,fur,gsw,ha,haw,hu,jgo,jmc,ka,kaj,kcg,kk,kkj,kl,ks,ksb,ku,ky,lb,lg,mas,mgo,ml,mn,nah,nb,nd,ne,nn,nnh,no,nr,ny,nyn,om,or,os,pap,ps,rm,rof,rwk,saq,seh,sn,so,sq,ss,ssy,st,syr,ta,te,teo,tig,tk,tn,tr,ts,ug,uz,ve,vo,vun,wae,xh,xog; one: @integer 1; other: @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …",
990aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "pt_PT; one: @integer 1; other: @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …",
991aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "da; one: @integer 1; other: @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …",
992aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "is; one: @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …; other: @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, …",
993aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "mk; one: @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, …; other: @integer 0, 2~10, 12~17, 100, 1000, 10000, 100000, 1000000, …",
994aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "fil,tl; one: @integer 0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, …; other: @integer 4, 6, 9, 14, 16, 19, 24, 26, 104, 1004, …",
995aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
996aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [zero, one, other]
997aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "lag; zero: @integer 0; one: @integer 1; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, …",
998aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "lv,prg; zero: @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, …; one: @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …; other: @integer 2~9, 22~29, 102, 1002, …",
999aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ksh; zero: @integer 0; one: @integer 1; other: @integer 2~17, 100, 1000, 10000, 100000, 1000000, …",
1000aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1001aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, two, other]
1002aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "iu,kw,naq,se,sma,smi,smj,smn,sms; one: @integer 1; two: @integer 2; other: @integer 0, 3~17, 100, 1000, 10000, 100000, 1000000, …",
1003aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1004aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, few, other]
1005aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "shi; one: @integer 0, 1; few: @integer 2~10; other: @integer 11~26, 100, 1000, 10000, 100000, 1000000, …",
1006aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "mo,ro; one: @integer 1; few: @integer 0, 2~16, 101, 1001, …; other: @integer 20~35, 100, 1000, 10000, 100000, 1000000, …",
1007aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "bs,hr,sh,sr; one: @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …; few: @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …; other: @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …",
1008aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1009aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, two, few, other]
1010aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "gd; one: @integer 1, 11; two: @integer 2, 12; few: @integer 3~10, 13~19; other: @integer 0, 20~34, 100, 1000, 10000, 100000, 1000000, …",
1011aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "sl; one: @integer 1, 101, 201, 301, 401, 501, 601, 701, 1001, …; two: @integer 2, 102, 202, 302, 402, 502, 602, 702, 1002, …; few: @integer 3, 4, 103, 104, 203, 204, 303, 304, 403, 404, 503, 504, 603, 604, 703, 704, 1003, …; other: @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …",
1012aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1013aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, two, many, other]
1014aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "he,iw; one: @integer 1; two: @integer 2; many: @integer 20, 30, 40, 50, 60, 70, 80, 90, 100, 1000, 10000, 100000, 1000000, …; other: @integer 0, 3~17, 101, 1001, …",
1015aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1016aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, few, many, other]
1017aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "cs,sk; one: @integer 1; few: @integer 2~4; many: null; other: @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …",
1018aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "be; one: @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …; few: @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …; many: @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …; other: null",
1019aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "lt; one: @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …; few: @integer 2~9, 22~29, 102, 1002, …; many: null; other: @integer 0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, …",
1020aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "mt; one: @integer 1; few: @integer 0, 2~10, 102~107, 1002, …; many: @integer 11~19, 111~117, 1011, …; other: @integer 20~35, 100, 1000, 10000, 100000, 1000000, …",
1021aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "pl; one: @integer 1; few: @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …; many: @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …; other: null",
1022aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ru,uk; one: @integer 1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, …; few: @integer 2~4, 22~24, 32~34, 42~44, 52~54, 62, 102, 1002, …; many: @integer 0, 5~19, 100, 1000, 10000, 100000, 1000000, …; other: null",
1023aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1024aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [one, two, few, many, other]
1025aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "br; one: @integer 1, 21, 31, 41, 51, 61, 81, 101, 1001, …; two: @integer 2, 22, 32, 42, 52, 62, 82, 102, 1002, …; few: @integer 3, 4, 9, 23, 24, 29, 33, 34, 39, 43, 44, 49, 103, 1003, …; many: @integer 1000000, …; other: @integer 0, 5~8, 10~20, 100, 1000, 10000, 100000, …",
1026aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ga; one: @integer 1; two: @integer 2; few: @integer 3~6; many: @integer 7~10; other: @integer 0, 11~25, 100, 1000, 10000, 100000, 1000000, …",
1027aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "gv; one: @integer 1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, …; two: @integer 2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, …; few: @integer 0, 20, 40, 60, 80, 100, 120, 140, 1000, 10000, 100000, 1000000, …; many: null; other: @integer 3~10, 13~19, 23, 103, 1003, …",
1028aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1029aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            // [zero, one, two, few, many, other]
1030aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "ar; zero: @integer 0; one: @integer 1; two: @integer 2; few: @integer 3~10, 103~110, 1003, …; many: @integer 11~26, 111, 1011, …; other: @integer 100~102, 200~202, 300~302, 400~402, 500~502, 600, 1000, 10000, 100000, 1000000, …",
1031aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            "cy; zero: @integer 0; one: @integer 1; two: @integer 2; few: @integer 3; many: @integer 6; other: @integer 4, 5, 7~20, 100, 1000, 10000, 100000, 1000000, …", };
1032aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1033aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private <T extends Serializable> T serializeAndDeserialize(T original, Output<Integer> size) {
1034aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        try {
1035aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ByteArrayOutputStream baos = new ByteArrayOutputStream();
1036aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ObjectOutputStream ostream = new ObjectOutputStream(baos);
1037aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ostream.writeObject(original);
1038aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ostream.flush();
1039aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            byte bytes[] = baos.toByteArray();
1040aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            size.value = bytes.length;
1041aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ObjectInputStream istream = new ObjectInputStream(new ByteArrayInputStream(bytes));
1042aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            T reconstituted = (T) istream.readObject();
1043aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            return reconstituted;
1044aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        } catch (IOException e) {
1045aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            throw new RuntimeException(e);
1046aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        } catch (ClassNotFoundException e) {
1047aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            throw new RuntimeException(e);
1048aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
1049aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1050aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1051f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
1052aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestSerialization() {
1053aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        Output<Integer> size = new Output<Integer>();
1054aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        int max = 0;
1055aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (ULocale locale : PluralRules.getAvailableULocales()) {
1056aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules item = PluralRules.forLocale(locale);
1057aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            PluralRules item2 = serializeAndDeserialize(item, size);
1058aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            logln(locale + "\tsize:\t" + size.value);
1059aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            max = Math.max(max, size.value);
1060aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (!assertEquals(locale + "\tPlural rules before and after serialization", item, item2)) {
1061aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                // for debugging
1062aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                PluralRules item3 = serializeAndDeserialize(item, size);
1063aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                item.equals(item3);
1064aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
1065aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
1066aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        logln("max \tsize:\t" + max);
1067aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1068aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1069f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    public static class FixedDecimalHandler implements SerializableTestUtility.Handler {
1070aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        public Object[] getTestObjects() {
1071aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            FixedDecimal items[] = { new FixedDecimal(3d), new FixedDecimal(3d, 2), new FixedDecimal(3.1d, 1),
1072aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                    new FixedDecimal(3.1d, 2), };
1073aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            return items;
1074aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
1075aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1076aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        public boolean hasSameBehavior(Object a, Object b) {
1077aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            FixedDecimal a1 = (FixedDecimal) a;
1078aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            FixedDecimal b1 = (FixedDecimal) b;
1079aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            return a1.equals(b1);
1080aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
1081aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1082aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1083f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
1084aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void TestSerial() {
1085aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules s = PluralRules.forLocale(ULocale.ENGLISH);
1086aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        checkStreamingEquality(s);
1087aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1088aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1089aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void checkStreamingEquality(PluralRules s) {
1090aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        try {
1091aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
1092aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOut);
1093aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            objectOutputStream.writeObject(s);
1094aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            objectOutputStream.close();
1095aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            byte[] contents = byteOut.toByteArray();
1096aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            logln(s.getClass() + ": " + showBytes(contents));
1097aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ByteArrayInputStream byteIn = new ByteArrayInputStream(contents);
1098aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            ObjectInputStream objectInputStream = new ObjectInputStream(byteIn);
1099aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            Object obj = objectInputStream.readObject();
1100aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertEquals("Streamed Object equals ", s, obj);
1101aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        } catch (Exception e) {
1102aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            assertNull("TestSerial", e);
1103aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
1104aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1105aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1106aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    /**
1107aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * @param contents
1108aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     * @return
1109aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin     */
1110aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    private String showBytes(byte[] contents) {
1111aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        StringBuilder b = new StringBuilder('[');
1112aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        for (int i = 0; i < contents.length; ++i) {
1113aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            int item = contents[i] & 0xFF;
1114aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            if (item >= 0x20 && item <= 0x7F) {
1115aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                b.append((char) item);
1116aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            } else {
1117aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin                b.append('(').append(Utility.hex(item, 2)).append(')');
1118aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin            }
1119aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        }
1120aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        return b.append(']').toString();
1121aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1122aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1123f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Test
1124aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    public void testJavaLocaleFactory() {
1125aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules rulesU0 = PluralRules.forLocale(ULocale.FRANCE);
1126aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules rulesJ0 = PluralRules.forLocale(Locale.FRANCE);
1127aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("forLocale()", rulesU0, rulesJ0);
1128aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin
1129aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules rulesU1 = PluralRules.forLocale(ULocale.FRANCE, PluralType.ORDINAL);
1130aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        PluralRules rulesJ1 = PluralRules.forLocale(Locale.FRANCE, PluralType.ORDINAL);
1131aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin        assertEquals("forLocale() with type", rulesU1, rulesJ1);
1132aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin    }
1133aefe4d1f8f1773ead1a52f7a5d2c9e0009353600Paul Duffin}
1134