/*
 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/**
 * @test
 * @bug 4048446 4051866 4053636 4054238 4054734 4054736 4058613 4059820 4060154
 *      4062418 4065540 4066189 4066696 4076676 4078588 4079231 4081866 4087241
 *      4087243 4092260 4095316 4101940 4103436 4114076 4114077 4124632 4132736
 *      4133509 4139572 4141640 4179126 4179686 4244884 4663220
 * @library /java/text/testlib
 * @summary Regression tests for Collation and associated classes
 * @modules jdk.localedata
 * @run junit Regression
 */
/*
(C) Copyright Taligent, Inc. 1996 - All Rights Reserved
(C) Copyright IBM Corp. 1996 - All Rights Reserved

  The original version of this source code and documentation is copyrighted and
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
provided under terms of a License Agreement between Taligent and Sun. This
technology is protected by multiple US and International patents. This notice and
attribution to Taligent may not be removed.
  Taligent is a registered trademark of Taligent, Inc.
*/

import java.text.*;
import java.util.Locale;
import java.util.Vector;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.fail;


public class Regression {

    // CollationElementIterator.reset() doesn't work
    //
    @Test
    public void Test4048446() {
        CollationElementIterator i1 = en_us.getCollationElementIterator(test1);
        CollationElementIterator i2 = en_us.getCollationElementIterator(test1);

        while ( i1.next() != CollationElementIterator.NULLORDER ) {
        }
        i1.reset();

        TestUtils.compareCollationElementIters(i1, i2);
    }


    // Collator -> rules -> Collator round-trip broken for expanding characters
    //
    @Test
    public void Test4051866() throws ParseException {
        // Build a collator containing expanding characters
        RuleBasedCollator c1 = new RuleBasedCollator("< o "
                                                    +"& oe ,o\u3080"
                                                    +"& oe ,\u1530 ,O"
                                                    +"& OE ,O\u3080"
                                                    +"& OE ,\u1520"
                                                    +"< p ,P");

        // Build another using the rules from  the first
        RuleBasedCollator c2 = new RuleBasedCollator(c1.getRules());

        // Make sure they're the same
        if (!c1.getRules().equals(c2.getRules())) {
            fail("Rules are not equal");
        }
    }

    // Collator thinks "black-bird" == "black"
    //
    @Test
    public void Test4053636() {
        if (en_us.equals("black-bird","black")) {
            fail("black-bird == black");
        }
    }


    // CollationElementIterator will not work correctly if the associated
    // Collator object's mode is changed
    //
    @Test
    public void Test4054238() {
        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();

        c.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
        CollationElementIterator i1 = en_us.getCollationElementIterator(test3);

        c.setDecomposition(Collator.NO_DECOMPOSITION);
        CollationElementIterator i2 = en_us.getCollationElementIterator(test3);

        // At this point, BOTH iterators should use NO_DECOMPOSITION, since the
        // collator itself is in that mode
        TestUtils.compareCollationElementIters(i1, i2);
    }

    // Collator.IDENTICAL documented but not implemented
    //
    @Test
    public void Test4054734() {
        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        try {
            c.setStrength(Collator.IDENTICAL);
        }
        catch (Exception e) {
            fail("Caught " + e.toString() + " setting Collator.IDENTICAL");
        }

        String[] decomp = {
            "\u0001",   "<",    "\u0002",
            "\u0001",   "=",    "\u0001",
            "A\u0001",  ">",    "~\u0002",      // Ensure A and ~ are not compared bitwise
            "\u00C0",   "=",    "A\u0300"       // Decomp should make these equal
        };
        c.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
        compareArray(c, decomp);

        String[] nodecomp = {
            "\u00C0",   ">",    "A\u0300"       // A-grave vs. A combining-grave
        };
        c.setDecomposition(Collator.NO_DECOMPOSITION);
        compareArray(c, nodecomp);
    }

    // Full Decomposition mode not implemented
    //
    @Test
    public void Test4054736() {
        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setDecomposition(Collator.FULL_DECOMPOSITION);

        String[] tests = {
            "\uFB4f", "=", "\u05D0\u05DC",  // Alef-Lamed vs. Alef, Lamed
        };

        compareArray(c, tests);
    }

    // Collator.getInstance() causes an ArrayIndexOutofBoundsException for Korean
    //
    @Test
    public void Test4058613() {
        // Creating a default collator doesn't work when Korean is the default
        // locale

        Locale oldDefault = Locale.getDefault();

        Locale.setDefault( Locale.KOREAN );
        try {
            Collator c = Collator.getInstance();

            // Since the fix to this bug was to turn of decomposition for Korean collators,
            // ensure that's what we got
            if (c.getDecomposition() != Collator.NO_DECOMPOSITION) {
              fail("Decomposition is not set to NO_DECOMPOSITION");
            }
        }
        finally {
            Locale.setDefault(oldDefault);
        }
    }

    // RuleBasedCollator.getRules does not return the exact pattern as input
    // for expanding character sequences
    //
    @Test
    public void Test4059820() {
        RuleBasedCollator c = null;
        try {
            c = new RuleBasedCollator("< a < b , c/a < d < z");
        } catch (ParseException e) {
            fail("Exception building collator: " + e.toString());
            return;
        }
        if ( c.getRules().indexOf("c/a") == -1) {
            fail("returned rules do not contain 'c/a'");
        }
    }

    // MergeCollation::fixEntry broken for "& H < \u0131, \u0130, i, I"
    //
    @Test
    public void Test4060154() {
        RuleBasedCollator c = null;
        try {
            c = new RuleBasedCollator("< g, G < h, H < i, I < j, J"
                                      + " & H < \u0131, \u0130, i, I" );
        } catch (ParseException e) {
            fail("Exception building collator: " + e.toString());
            return;
        }
        c.setDecomposition(Collator.CANONICAL_DECOMPOSITION);

        String[] tertiary = {
            "A",        "<",    "B",
            "H",        "<",    "\u0131",
            "H",        "<",    "I",
            "\u0131",   "<",    "\u0130",
            "\u0130",   "<",    "i",
            "\u0130",   ">",    "H",
        };
        c.setStrength(Collator.TERTIARY);
        compareArray(c, tertiary);

        String[] secondary = {
            "H",        "<",    "I",
            "\u0131",   "=",    "\u0130",
        };
        c.setStrength(Collator.PRIMARY);
        compareArray(c, secondary);
    };

    // Secondary/Tertiary comparison incorrect in French Secondary
    //
    @Test
    public void Test4062418() throws ParseException {
        RuleBasedCollator c = (RuleBasedCollator) Collator.getInstance(Locale.FRANCE);
        c.setStrength(Collator.SECONDARY);

        String[] tests = {
                "p\u00eache",    "<",    "p\u00e9ch\u00e9",    // Comparing accents from end, p\u00e9ch\u00e9 is greater
        };

        compareArray(c, tests);
    }

    // Collator.compare() method broken if either string contains spaces
    //
    @Test
    public void Test4065540() {
        if (en_us.compare("abcd e", "abcd f") == 0) {
            fail("'abcd e' == 'abcd f'");
        }
    }

    // Unicode characters need to be recursively decomposed to get the
    // correct result. For example,
    // u1EB1 -> \u0103 + \u0300 -> a + \u0306 + \u0300.
    //
    @Test
    public void Test4066189() {
        String test1 = "\u1EB1";
        String test2 = "a\u0306\u0300";

        RuleBasedCollator c1 = (RuleBasedCollator) en_us.clone();
        c1.setDecomposition(Collator.FULL_DECOMPOSITION);
        CollationElementIterator i1 = en_us.getCollationElementIterator(test1);

        RuleBasedCollator c2 = (RuleBasedCollator) en_us.clone();
        c2.setDecomposition(Collator.NO_DECOMPOSITION);
        CollationElementIterator i2 = en_us.getCollationElementIterator(test2);

        TestUtils.compareCollationElementIters(i1, i2);
    }

    // French secondary collation checking at the end of compare iteration fails
    //
    @Test
    public void Test4066696() {
        RuleBasedCollator c = (RuleBasedCollator) Collator.getInstance(Locale.FRANCE);
        c.setStrength(Collator.SECONDARY);

        String[] tests = {
            "\u00e0",   "<",     "\u01fa",       // a-grave <  A-ring-acute
        };

        compareArray(c, tests);
    }


    // Bad canonicalization of same-class combining characters
    //
    @Test
    public void Test4076676() {
        // These combining characters are all in the same class, so they should not
        // be reordered, and they should compare as unequal.
        String s1 = "A\u0301\u0302\u0300";
        String s2 = "A\u0302\u0300\u0301";

        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setStrength(Collator.TERTIARY);

        if (c.compare(s1,s2) == 0) {
            fail("Same-class combining chars were reordered");
        }
    }


    // RuleBasedCollator.equals(null) throws NullPointerException
    //
    @Test
    public void Test4079231() {
        try {
            if (en_us.equals(null)) {
                fail("en_us.equals(null) returned true");
            }
        }
        catch (Exception e) {
            fail("en_us.equals(null) threw " + e.toString());
        }
    }

    // RuleBasedCollator breaks on "< a < bb" rule
    //
    @Test
    public void Test4078588() throws ParseException {
        RuleBasedCollator rbc=new RuleBasedCollator("< a < bb");

        int result = rbc.compare("a","bb");

        if (result != -1) {
            fail("Compare(a,bb) returned " + result + "; expected -1");
        }
    }

    // Combining characters in different classes not reordered properly.
    //
    @Test
    public void Test4081866() throws ParseException {
        // These combining characters are all in different classes,
        // so they should be reordered and the strings should compare as equal.
        String s1 = "A\u0300\u0316\u0327\u0315";
        String s2 = "A\u0327\u0316\u0315\u0300";

        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setStrength(Collator.TERTIARY);

        // Now that the default collators are set to NO_DECOMPOSITION
        // (as a result of fixing bug 4114077), we must set it explicitly
        // when we're testing reordering behavior.  -- lwerner, 5/5/98
        c.setDecomposition(Collator.CANONICAL_DECOMPOSITION);

        if (c.compare(s1,s2) != 0) {
            fail("Combining chars were not reordered");
        }
    }

    // string comparison errors in Scandinavian collators
    //
    @Test
    public void Test4087241() {
        RuleBasedCollator c = (RuleBasedCollator) Collator.getInstance(
                                                        Locale.of("da", "DK"));
        c.setStrength(Collator.SECONDARY);

        String[] tests = {
            "\u007a",   "<",    "\u00e6",       // z        < ae
            "a\u0308",  "<",    "a\u030a",      // a-unlaut < a-ring
            "Y",        "<",    "u\u0308",      // Y        < u-umlaut
        };

        compareArray(c, tests);
    }

    // CollationKey takes ignorable strings into account when it shouldn't
    //
    @Test
    public void Test4087243() {
        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setStrength(Collator.TERTIARY);

        String[] tests = {
            "123",      "=",    "123\u0001",        // 1 2 3  =  1 2 3 ctrl-A
        };

        compareArray(c, tests);
    }

    // Mu/micro conflict
    // Micro symbol and greek lowercase letter Mu should sort identically
    //
    @Test
    public void Test4092260() {
        Collator c = Collator.getInstance(Locale.of("el"));

        // will only be equal when FULL_DECOMPOSITION is used
        c.setDecomposition(Collator.FULL_DECOMPOSITION);

        String[] tests = {
            "\u00B5",      "=",    "\u03BC",
        };

        compareArray(c, tests);
    }

    void Test4095316() {
        Collator c = Collator.getInstance(Locale.of("el", "GR"));
        c.setStrength(Collator.TERTIARY);
        // javadocs for RuleBasedCollator clearly specify that characters containing compatability
        // chars MUST use FULL_DECOMPOSITION to get accurate comparisons.
        c.setDecomposition(Collator.FULL_DECOMPOSITION);

        String[] tests = {
            "\u03D4",      "=",    "\u03AB",
        };

        compareArray(c, tests);
    }

    @Test
    public void Test4101940() {
        try {
            RuleBasedCollator c = new RuleBasedCollator("< a < b");
            CollationElementIterator i = c.getCollationElementIterator("");
            i.reset();

            if (i.next() != i.NULLORDER) {
                fail("next did not return NULLORDER");
            }
        }
        catch (Exception e) {
            fail("Caught " + e );
        }
    }

    // Collator.compare not handling spaces properly
    //
    @Test
    public void Test4103436() {
        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setStrength(Collator.TERTIARY);

        String[] tests = {
            "file",      "<",    "file access",
            "file",      "<",    "fileaccess",
        };

        compareArray(c, tests);
    }

    // Collation not Unicode conformant with Hangul syllables
    //
    @Test
    public void Test4114076() {
        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setStrength(Collator.TERTIARY);

        //
        // With Canonical decomposition, Hangul syllables should get decomposed
        // into Jamo, but Jamo characters should not be decomposed into
        // conjoining Jamo
        //
        c.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
        String[] test1 = {
            "\ud4db",   "=",    "\u1111\u1171\u11b6",
        };
        compareArray(c, test1);

        // Full decomposition result should be the same as canonical decomposition
        // for all hangul.
        c.setDecomposition(Collator.FULL_DECOMPOSITION);
        compareArray(c, test1);

    }


    // Collator.getCollationKey was hanging on certain character sequences
    //
    @Test
    public void Test4124632() throws Exception {
        Collator coll = Collator.getInstance(Locale.JAPAN);

        try {
            coll.getCollationKey("A\u0308bc");
        } catch (OutOfMemoryError e) {
            fail("Ran out of memory -- probably an infinite loop");
        }
    }

    // sort order of french words with multiple accents has errors
    //
    @Test
    public void Test4132736() {
        Collator c = Collator.getInstance(Locale.FRANCE);

        String[] test1 = {
            "e\u0300e\u0301",   "<",    "e\u0301e\u0300",
            "e\u0300\u0301",    ">",    "e\u0301\u0300",
        };
        compareArray(c, test1);
    }

    // The sorting using java.text.CollationKey is not in the exact order
    //
    @Test
    public void Test4133509() {
        String[] test1 = {
            "Exception",    "<",    "ExceptionInInitializerError",
            "Graphics",     "<",    "GraphicsEnvironment",
            "String",       "<",    "StringBuffer",
        };
        compareArray(en_us, test1);
    }

    // Collation with decomposition off doesn't work for Europe
    //
    @Test
    public void Test4114077() {
        // Ensure that we get the same results with decomposition off
        // as we do with it on....

        RuleBasedCollator c = (RuleBasedCollator) en_us.clone();
        c.setStrength(Collator.TERTIARY);

        String[] test1 = {
            "\u00C0",        "=", "A\u0300",        // Should be equivalent
            "p\u00eache",         ">", "p\u00e9ch\u00e9",
            "\u0204",        "=", "E\u030F",
            "\u01fa",        "=", "A\u030a\u0301",  // a-ring-acute -> a-ring, acute
                                                    //   -> a, ring, acute
            "A\u0300\u0316", "<", "A\u0316\u0300",  // No reordering --> unequal
        };
        c.setDecomposition(Collator.NO_DECOMPOSITION);
        compareArray(c, test1);

        String[] test2 = {
            "A\u0300\u0316", "=", "A\u0316\u0300",      // Reordering --> equal
        };
        c.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
        compareArray(c, test2);
    }

    // Support for Swedish gone in 1.1.6 (Can't create Swedish collator)
    //
    @Test
    public void Test4141640() {
        //
        // Rather than just creating a Swedish collator, we might as well
        // try to instantiate one for every locale available on the system
        // in order to prevent this sort of bug from cropping up in the future
        //
        Locale[] locales = Collator.getAvailableLocales();

        for (int i = 0; i < locales.length; i++) {
            try {
                Collator c = Collator.getInstance(locales[i]);
            } catch (Exception e) {
                fail("Caught " + e + " creating collator for " + locales[i]);
            }
        }
    }

    // getCollationKey throws exception for spanish text
    // Cannot reproduce this bug on 1.2, however it DOES fail on 1.1.6
    //
    @Test
    public void Test4139572() {
        //
        // Code pasted straight from the bug report
        //
        // create spanish locale and collator
        Locale l = Locale.of("es", "es");
        Collator col = Collator.getInstance(l);

        // this spanish phrase kills it!
        col.getCollationKey("Nombre De Objeto");
    }

    // RuleBasedCollator doesn't use getCollationElementIterator internally
    //
    @Test
    public void Test4146160() throws ParseException {
        //
        // Use a custom collator class whose getCollationElementIterator
        // methods increment a count....
        //
        My4146160Collator.count = 0;
        new My4146160Collator().getCollationKey("1");
        if (My4146160Collator.count < 1) {
            fail("getCollationElementIterator not called");
        }

        My4146160Collator.count = 0;
        new My4146160Collator().compare("1", "2");
        if (My4146160Collator.count < 1) {
            fail("getCollationElementIterator not called");
        }
    }

    static class My4146160Collator extends RuleBasedCollator {
        public My4146160Collator() throws ParseException {
            super(Regression.en_us.getRules());
        }

        public CollationElementIterator getCollationElementIterator(
                                            String text) {
            count++;
            return super.getCollationElementIterator(text);
        }
        public CollationElementIterator getCollationElementIterator(
                                            CharacterIterator text) {
            count++;
            return super.getCollationElementIterator(text);
        }

        public static int count = 0;
    };

    // CollationElementIterator.previous broken for expanding char sequences
    //
    @Test
    public void Test4179686() throws ParseException {

        // Create a collator with a few expanding character sequences in it....
        RuleBasedCollator coll = new RuleBasedCollator(en_us.getRules()
                                                    + " & ae ; \u00e4 & AE ; \u00c4"
                                                    + " & oe ; \u00f6 & OE ; \u00d6"
                                                    + " & ue ; \u00fc & UE ; \u00dc");

        String text = "T\u00f6ne"; // o-umlaut

        CollationElementIterator iter = coll.getCollationElementIterator(text);
        Vector elements = new Vector();
        int elem;

        // Iterate forward and collect all of the elements into a Vector
        while ((elem = iter.next()) != iter.NULLORDER) {
            elements.addElement(new Integer(elem));
        }

        // Now iterate backward and make sure they're the same
        int index = elements.size() - 1;
        while ((elem = iter.previous()) != iter.NULLORDER) {
            int expect = ((Integer)elements.elementAt(index)).intValue();

            if (elem != expect) {
                fail("Mismatch at index " + index
                      + ": got " + Integer.toString(elem,16)
                      + ", expected " + Integer.toString(expect,16));
            }
            index--;
        }
    }

    @Test
    public void Test4244884() throws ParseException {
        RuleBasedCollator coll = (RuleBasedCollator)Collator.getInstance(Locale.US);
        coll = new RuleBasedCollator(coll.getRules()
                + " & C < ch , cH , Ch , CH < cat < crunchy");

        String[] testStrings = new String[] {
            "car",
            "cave",
            "clamp",
            "cramp",
            "czar",
            "church",
            "catalogue",
            "crunchy",
            "dog"
        };

        for (int i = 1; i < testStrings.length; i++) {
            if (coll.compare(testStrings[i - 1], testStrings[i]) >= 0) {
                fail("error: \"" + testStrings[i - 1]
                    + "\" is greater than or equal to \"" + testStrings[i]
                    + "\".");
            }
        }
    }

    @Test
    public void Test4179216() throws ParseException {
        // you can position a CollationElementIterator in the middle of
        // a contracting character sequence, yielding a bogus collation
        // element
        RuleBasedCollator coll = (RuleBasedCollator)Collator.getInstance(Locale.US);
        coll = new RuleBasedCollator(coll.getRules()
                + " & C < ch , cH , Ch , CH < cat < crunchy");
        String testText = "church church catcatcher runcrunchynchy";
        CollationElementIterator iter = coll.getCollationElementIterator(
                testText);

        // test that the "ch" combination works properly
        iter.setOffset(4);
        int elt4 = CollationElementIterator.primaryOrder(iter.next());

        iter.reset();
        int elt0 = CollationElementIterator.primaryOrder(iter.next());

        iter.setOffset(5);
        int elt5 = CollationElementIterator.primaryOrder(iter.next());

        if (elt4 != elt0 || elt5 != elt0)
            fail("The collation elements at positions 0 (" + elt0 + "), 4 ("
                    + elt4 + "), and 5 (" + elt5 + ") don't match.");

        // test that the "cat" combination works properly
        iter.setOffset(14);
        int elt14 = CollationElementIterator.primaryOrder(iter.next());

        iter.setOffset(15);
        int elt15 = CollationElementIterator.primaryOrder(iter.next());

        iter.setOffset(16);
        int elt16 = CollationElementIterator.primaryOrder(iter.next());

        iter.setOffset(17);
        int elt17 = CollationElementIterator.primaryOrder(iter.next());

        iter.setOffset(18);
        int elt18 = CollationElementIterator.primaryOrder(iter.next());

        iter.setOffset(19);
        int elt19 = CollationElementIterator.primaryOrder(iter.next());

        if (elt14 != elt15 || elt14 != elt16 || elt14 != elt17
                || elt14 != elt18 || elt14 != elt19)
            fail("\"cat\" elements don't match: elt14 = " + elt14 + ", elt15 = "
            + elt15 + ", elt16 = " + elt16 + ", elt17 = " + elt17
            + ", elt18 = " + elt18 + ", elt19 = " + elt19);

        // now generate a complete list of the collation elements,
        // first using next() and then using setOffset(), and
        // make sure both interfaces return the same set of elements
        iter.reset();

        int elt = iter.next();
        int count = 0;
        while (elt != CollationElementIterator.NULLORDER) {
            ++count;
            elt = iter.next();
        }

        String[] nextElements = new String[count];
        String[] setOffsetElements = new String[count];
        int lastPos = 0;

        iter.reset();
        elt = iter.next();
        count = 0;
        while (elt != CollationElementIterator.NULLORDER) {
            nextElements[count++] = testText.substring(lastPos, iter.getOffset());
            lastPos = iter.getOffset();
            elt = iter.next();
        }
        count = 0;
        for (int i = 0; i < testText.length(); ) {
            iter.setOffset(i);
            lastPos = iter.getOffset();
            elt = iter.next();
            setOffsetElements[count++] = testText.substring(lastPos, iter.getOffset());
            i = iter.getOffset();
        }
        for (int i = 0; i < nextElements.length; i++) {
            if (nextElements[i].equals(setOffsetElements[i])) {
                System.out.println(nextElements[i]);
            } else {
                fail("Error: next() yielded " + nextElements[i] + ", but setOffset() yielded "
                    + setOffsetElements[i]);
            }
        }
    }

    @Test
    public void Test4216006() throws Exception {
        // rule parser barfs on "<\u00e0=a\u0300", and on other cases
        // where the same token (after normalization) appears twice in a row
        boolean caughtException = false;
        try {
            RuleBasedCollator dummy = new RuleBasedCollator("\u00e0<a\u0300");
        }
        catch (ParseException e) {
            caughtException = true;
        }
        if (!caughtException) {
            throw new Exception("\"a<a\" collation sequence didn't cause parse error!");
        }

        RuleBasedCollator collator = new RuleBasedCollator("<\u00e0=a\u0300");
        collator.setDecomposition(Collator.FULL_DECOMPOSITION);
        collator.setStrength(Collator.IDENTICAL);

        String[] tests = {
            "a\u0300", "=", "\u00e0",
            "\u00e0",  "=", "a\u0300"
        };

        compareArray(collator, tests);
    }

    @Test
    public void Test4171974() {
        // test French accent ordering more thoroughly
        String[] frenchList = {
            "\u0075\u0075",     // u u
            "\u00fc\u0075",     // u-umlaut u
            "\u01d6\u0075",     // u-umlaut-macron u
            "\u016b\u0075",     // u-macron u
            "\u1e7b\u0075",     // u-macron-umlaut u
            "\u0075\u00fc",     // u u-umlaut
            "\u00fc\u00fc",     // u-umlaut u-umlaut
            "\u01d6\u00fc",     // u-umlaut-macron u-umlaut
            "\u016b\u00fc",     // u-macron u-umlaut
            "\u1e7b\u00fc",     // u-macron-umlaut u-umlaut
            "\u0075\u01d6",     // u u-umlaut-macron
            "\u00fc\u01d6",     // u-umlaut u-umlaut-macron
            "\u01d6\u01d6",     // u-umlaut-macron u-umlaut-macron
            "\u016b\u01d6",     // u-macron u-umlaut-macron
            "\u1e7b\u01d6",     // u-macron-umlaut u-umlaut-macron
            "\u0075\u016b",     // u u-macron
            "\u00fc\u016b",     // u-umlaut u-macron
            "\u01d6\u016b",     // u-umlaut-macron u-macron
            "\u016b\u016b",     // u-macron u-macron
            "\u1e7b\u016b",     // u-macron-umlaut u-macron
            "\u0075\u1e7b",     // u u-macron-umlaut
            "\u00fc\u1e7b",     // u-umlaut u-macron-umlaut
            "\u01d6\u1e7b",     // u-umlaut-macron u-macron-umlaut
            "\u016b\u1e7b",     // u-macron u-macron-umlaut
            "\u1e7b\u1e7b"      // u-macron-umlaut u-macron-umlaut
        };
        Collator french = Collator.getInstance(Locale.FRENCH);

        System.out.println("Testing French order...");
        checkListOrder(frenchList, french);

        System.out.println("Testing French order without decomposition...");
        french.setDecomposition(Collator.NO_DECOMPOSITION);
        checkListOrder(frenchList, french);

        String[] englishList = {
            "\u0075\u0075",     // u u
            "\u0075\u00fc",     // u u-umlaut
            "\u0075\u01d6",     // u u-umlaut-macron
            "\u0075\u016b",     // u u-macron
            "\u0075\u1e7b",     // u u-macron-umlaut
            "\u00fc\u0075",     // u-umlaut u
            "\u00fc\u00fc",     // u-umlaut u-umlaut
            "\u00fc\u01d6",     // u-umlaut u-umlaut-macron
            "\u00fc\u016b",     // u-umlaut u-macron
            "\u00fc\u1e7b",     // u-umlaut u-macron-umlaut
            "\u01d6\u0075",     // u-umlaut-macron u
            "\u01d6\u00fc",     // u-umlaut-macron u-umlaut
            "\u01d6\u01d6",     // u-umlaut-macron u-umlaut-macron
            "\u01d6\u016b",     // u-umlaut-macron u-macron
            "\u01d6\u1e7b",     // u-umlaut-macron u-macron-umlaut
            "\u016b\u0075",     // u-macron u
            "\u016b\u00fc",     // u-macron u-umlaut
            "\u016b\u01d6",     // u-macron u-umlaut-macron
            "\u016b\u016b",     // u-macron u-macron
            "\u016b\u1e7b",     // u-macron u-macron-umlaut
            "\u1e7b\u0075",     // u-macron-umlaut u
            "\u1e7b\u00fc",     // u-macron-umlaut u-umlaut
            "\u1e7b\u01d6",     // u-macron-umlaut u-umlaut-macron
            "\u1e7b\u016b",     // u-macron-umlaut u-macron
            "\u1e7b\u1e7b"      // u-macron-umlaut u-macron-umlaut
        };
        Collator english = Collator.getInstance(Locale.ENGLISH);

        System.out.println("Testing English order...");
        checkListOrder(englishList, english);

        System.out.println("Testing English order without decomposition...");
        english.setDecomposition(Collator.NO_DECOMPOSITION);
        checkListOrder(englishList, english);
    }

    private void checkListOrder(String[] sortedList, Collator c) {
        // this function uses the specified Collator to make sure the
        // passed-in list is already sorted into ascending order
        for (int i = 0; i < sortedList.length - 1; i++) {
            if (c.compare(sortedList[i], sortedList[i + 1]) >= 0) {
                fail("List out of order at element #" + i + ": "
                        + TestUtils.prettify(sortedList[i]) + " >= "
                        + TestUtils.prettify(sortedList[i + 1]));
            }
        }
    }

    // CollationElementIterator set doesn't work propertly with next/prev
    @Test
    public void Test4663220() {
        RuleBasedCollator collator = (RuleBasedCollator)Collator.getInstance(Locale.US);
        CharacterIterator stringIter = new StringCharacterIterator("fox");
        CollationElementIterator iter = collator.getCollationElementIterator(stringIter);

        int[] elements_next = new int[3];
        System.out.println("calling next:");
        for (int i = 0; i < 3; ++i) {
            System.out.println("[" + i + "] " + (elements_next[i] = iter.next()));
        }

        int[] elements_fwd = new int[3];
        System.out.println("calling set/next:");
        for (int i = 0; i < 3; ++i) {
            iter.setOffset(i);
            System.out.println("[" + i + "] " + (elements_fwd[i] = iter.next()));
        }

        for (int i = 0; i < 3; ++i) {
            if (elements_next[i] != elements_fwd[i]) {
                fail("mismatch at position " + i +
                      ": " + elements_next[i] +
                      " != " + elements_fwd[i]);
            }
        }
    }

    //------------------------------------------------------------------------
    // Internal utilities
    //
    private void compareArray(Collator c, String[] tests) {
        for (int i = 0; i < tests.length; i += 3) {

            int expect = 0;
            if (tests[i+1].equals("<")) {
                expect = -1;
            } else if (tests[i+1].equals(">")) {
                expect = 1;
            } else if (tests[i+1].equals("=")) {
                expect = 0;
            } else {
                expect = Integer.decode(tests[i+1]).intValue();
            }

            int result = c.compare(tests[i], tests[i+2]);
            if (sign(result) != sign(expect))
            {
                fail( i/3 + ": compare(" + TestUtils.prettify(tests[i])
                                    + " , " + TestUtils.prettify(tests[i+2])
                                    + ") got " + result + "; expected " + expect);
            }
            else
            {
                // Collator.compare worked OK; now try the collation keys
                CollationKey k1 = c.getCollationKey(tests[i]);
                CollationKey k2 = c.getCollationKey(tests[i+2]);

                result = k1.compareTo(k2);
                if (sign(result) != sign(expect)) {
                    fail( i/3 + ": key(" + TestUtils.prettify(tests[i])
                                        + ").compareTo(key(" + TestUtils.prettify(tests[i+2])
                                        + ")) got " + result + "; expected " + expect);

                    fail("  " + TestUtils.prettifyCKey(k1) + " vs. " + TestUtils.prettifyCKey(k2));
                }
            }
        }
    }

    private static final int sign(int i) {
        if (i < 0) return -1;
        if (i > 0) return 1;
        return 0;
    }


    static RuleBasedCollator en_us = (RuleBasedCollator)Collator.getInstance(Locale.US);

    String test1 = "XFILE What subset of all possible test cases has the highest probability of detecting the most errors?";
    String test2 = "Xf ile What subset of all possible test cases has the lowest probability of detecting the least errors?";
    String test3 = "a\u00FCbeck Gr\u00F6\u00DFe L\u00FCbeck";
}
