From b92caf121d76be1b47236daab509ba9f8322a9d2 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Wed, 25 Jan 2012 16:11:58 +0100 Subject: [PATCH] Added test cases + fixed some hard to find bugs. --- .../twelvemonkeys/util/CollectionUtil.java | 171 +++---- .../util/CollectionUtilTest.java | 483 ++++++++++++++++++ 2 files changed, 564 insertions(+), 90 deletions(-) create mode 100644 common/common-lang/src/test/java/com/twelvemonkeys/util/CollectionUtilTest.java diff --git a/common/common-lang/src/main/java/com/twelvemonkeys/util/CollectionUtil.java b/common/common-lang/src/main/java/com/twelvemonkeys/util/CollectionUtil.java index 0b15be9f..63e205ba 100755 --- a/common/common-lang/src/main/java/com/twelvemonkeys/util/CollectionUtil.java +++ b/common/common-lang/src/main/java/com/twelvemonkeys/util/CollectionUtil.java @@ -28,17 +28,21 @@ package com.twelvemonkeys.util; +import com.twelvemonkeys.lang.Validate; + import java.lang.reflect.Array; import java.util.*; +import static com.twelvemonkeys.lang.Validate.isTrue; +import static com.twelvemonkeys.lang.Validate.notNull; + /** * A utility class with some useful collection-related functions. * * @author Harald Kuhr * @author Eirik Torske * @author last modified by $Author: haku $ - * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/CollectionUtil.java#3 $ - * @todo move makeList and makeSet to StringUtil? + * @version $Id: com/twelvemonkeys/util/CollectionUtil.java#3 $ * @see Collections * @see Arrays */ @@ -51,8 +55,6 @@ public final class CollectionUtil { */ @SuppressWarnings({"UnusedDeclaration", "UnusedAssignment", "unchecked"}) public static void main(String[] pArgs) { - test(); - int howMany = 1000; if (pArgs.length > 0) { @@ -257,7 +259,7 @@ public final class CollectionUtil { * If the sub array is same length as the original * ({@code pStart == 0}), the original array will be returned. * - * @param pArray the origianl array + * @param pArray the original array * @param pStart the start index of the original array * @return a subset of the original array, or the original array itself, * if {@code pStart} is 0. @@ -270,16 +272,33 @@ public final class CollectionUtil { return subArray(pArray, pStart, -1); } + /** + * Creates an array containing a subset of the original array. + * If the sub array is same length as the original + * ({@code pStart == 0}), the original array will be returned. + * + * @param pArray the original array + * @param pStart the start index of the original array + * @return a subset of the original array, or the original array itself, + * if {@code pStart} is 0. + * + * @throws IllegalArgumentException if {@code pArray} is {@code null} + * @throws ArrayIndexOutOfBoundsException if {@code pStart} < 0 + */ + public static T[] subArray(T[] pArray, int pStart) { + return subArray(pArray, pStart, -1); + } + /** * Creates an array containing a subset of the original array. * If the {@code pLength} parameter is negative, it will be ignored. * If there are not {@code pLength} elements in the original array - * after {@code pStart}, the {@code pLength} paramter will be + * after {@code pStart}, the {@code pLength} parameter will be * ignored. * If the sub array is same length as the original, the original array will * be returned. * - * @param pArray the origianl array + * @param pArray the original array * @param pStart the start index of the original array * @param pLength the length of the new array * @return a subset of the original array, or the original array itself, @@ -292,9 +311,7 @@ public final class CollectionUtil { */ @SuppressWarnings({"SuspiciousSystemArraycopy"}) public static Object subArray(Object pArray, int pStart, int pLength) { - if (pArray == null) { - throw new IllegalArgumentException("array == null"); - } + Validate.notNull(pArray, "array"); // Get component type Class type; @@ -321,7 +338,7 @@ public final class CollectionUtil { Object result; if (newLength < originalLength) { - // Create subarray & copy into + // Create sub array & copy into result = Array.newInstance(type, newLength); System.arraycopy(pArray, pStart, result, 0, newLength); } @@ -335,7 +352,33 @@ public final class CollectionUtil { return result; } + /** + * Creates an array containing a subset of the original array. + * If the {@code pLength} parameter is negative, it will be ignored. + * If there are not {@code pLength} elements in the original array + * after {@code pStart}, the {@code pLength} parameter will be + * ignored. + * If the sub array is same length as the original, the original array will + * be returned. + * + * @param pArray the original array + * @param pStart the start index of the original array + * @param pLength the length of the new array + * @return a subset of the original array, or the original array itself, + * if {@code pStart} is 0 and {@code pLength} is either + * negative, or greater or equal to {@code pArray.length}. + * + * @throws IllegalArgumentException if {@code pArray} is {@code null} + * @throws ArrayIndexOutOfBoundsException if {@code pStart} < 0 + */ + @SuppressWarnings("unchecked") + public static T[] subArray(T[] pArray, int pStart, int pLength) { + return (T[]) subArray((Object) pArray, pStart, pLength); + } + public static Iterator iterator(final Enumeration pEnum) { + notNull(pEnum, "enumeration"); + return new Iterator() { public boolean hasNext() { return pEnum.hasMoreElements(); @@ -361,8 +404,8 @@ public final class CollectionUtil { * the given collection. * @throws ClassCastException class of the specified element prevents it * from being added to this collection. - * @throws NullPointerException if the specified element is null and this - * collection does not support null elements. + * @throws NullPointerException if the specified element is {@code null} and this + * collection does not support {@code null} elements. * @throws IllegalArgumentException some aspect of this element prevents * it from being added to this collection. */ @@ -372,7 +415,7 @@ public final class CollectionUtil { } } - // Is there a usecase where Arrays.asList(pArray).iterator() can't ne used? + // Is there a use case where Arrays.asList(pArray).iterator() can't ne used? /** * Creates a thin {@link Iterator} wrapper around an array. * @@ -383,7 +426,7 @@ public final class CollectionUtil { * {@code pLength > pArray.length - pStart} */ public static ListIterator iterator(final E[] pArray) { - return iterator(pArray, 0, pArray.length); + return iterator(pArray, 0, notNull(pArray).length); } /** @@ -408,7 +451,7 @@ public final class CollectionUtil { * @return a new {@code Map} of same type as {@code pSource} * @throws IllegalArgumentException if {@code pSource == null}, * or if a new map can't be instantiated, - * or if source map contains duplaicates. + * or if source map contains duplicates. * * @see #invert(java.util.Map, java.util.Map, DuplicateHandler) */ @@ -424,7 +467,7 @@ public final class CollectionUtil { * @param pResult the map used to contain the result, may be {@code null}, * in that case a new {@code Map} of same type as {@code pSource} is created. * The result map should be empty, otherwise duplicate values will need to be resolved. - * @param pHandler duplicate handler, may be {@code null} if source map don't contain dupliate values + * @param pHandler duplicate handler, may be {@code null} if source map don't contain duplicate values * @return {@code pResult}, or a new {@code Map} if {@code pResult == null} * @throws IllegalArgumentException if {@code pSource == null}, * or if result map is {@code null} and a new map can't be instantiated, @@ -476,20 +519,20 @@ public final class CollectionUtil { return result; } - public static Comparator reverseOrder(Comparator pOriginal) { + public static Comparator reverseOrder(final Comparator pOriginal) { return new ReverseComparator(pOriginal); } private static class ReverseComparator implements Comparator { - private Comparator mComparator; + private final Comparator comparator; - public ReverseComparator(Comparator pComparator) { - mComparator = pComparator; + public ReverseComparator(final Comparator pComparator) { + comparator = notNull(pComparator); } public int compare(T pLeft, T pRight) { - int result = mComparator.compare(pLeft, pRight); + int result = comparator.compare(pLeft, pRight); // We can't simply return -result, as -Integer.MIN_VALUE == Integer.MIN_VALUE. return -(result | (result >>> 1)); @@ -516,73 +559,21 @@ public final class CollectionUtil { return (T) pCollection; } - @SuppressWarnings({"UnusedDeclaration"}) - static void test() { - List list = Collections.singletonList("foo"); - @SuppressWarnings({"unchecked"}) - Set set = new HashSet(list); - - List strs0 = CollectionUtil.generify(list, String.class); - List objs0 = CollectionUtil.generify(list, String.class); -// List strs01 = CollectionUtil.generify(list, Object.class); // Not okay - try { - List strs1 = CollectionUtil.generify(set, String.class); // Not ok, runtime CCE unless set is null - } - catch (RuntimeException e) { - e.printStackTrace(); - } - - try { - ArrayList strs01 = CollectionUtil.generify(list, String.class); // Not ok, runtime CCE unless list is null - } - catch (RuntimeException e) { - e.printStackTrace(); - } - - Set setstr1 = CollectionUtil.generify(set, String.class); - Set setobj1 = CollectionUtil.generify(set, String.class); - try { - Set setobj44 = CollectionUtil.generify(list, String.class); // Not ok, runtime CCE unless list is null - } - catch (RuntimeException e) { - e.printStackTrace(); - } - - List strs2 = CollectionUtil., String>generify2(list); - List objs2 = CollectionUtil., String>generify2(list); -// List morestrs = CollectionUtil., String>generify2(list); // Not ok - try { - List strs3 = CollectionUtil., String>generify2(set); // Not ok, runtime CCE unless set is null - } - catch (RuntimeException e) { - e.printStackTrace(); - } - } - private static class ArrayIterator implements ListIterator { - private int mIndex; - private final int mStart; - private final int mLength; - private final E[] mArray; + private int next; + private final int start; + private final int length; + private final E[] array; - public ArrayIterator(E[] pArray, int pStart, int pLength) { - if (pArray == null) { - throw new IllegalArgumentException("array == null"); - } - if (pStart < 0) { - throw new IllegalArgumentException("start < 0"); - } - if (pLength > pArray.length - pStart) { - throw new IllegalArgumentException("length > array.length - start"); - } - mArray = pArray; - mStart = pStart; - mLength = pLength; - mIndex = mStart; + public ArrayIterator(final E[] pArray, final int pStart, final int pLength) { + array = notNull(pArray, "array"); + start = isTrue(pStart >= 0, pStart, "start < 0: %d"); + length = isTrue(pLength <= pArray.length - pStart, pLength, "length > array.length - start: %d"); + next = start; } public boolean hasNext() { - return mIndex < mLength + mStart; + return next < length + start; } public E next() { @@ -591,7 +582,7 @@ public final class CollectionUtil { } try { - return mArray[mIndex++]; + return array[next++]; } catch (ArrayIndexOutOfBoundsException e) { NoSuchElementException nse = new NoSuchElementException(e.getMessage()); @@ -609,11 +600,11 @@ public final class CollectionUtil { } public boolean hasPrevious() { - return mIndex > mStart; + return next > start; } public int nextIndex() { - return mIndex + 1; + return next - start; } public E previous() { @@ -622,7 +613,7 @@ public final class CollectionUtil { } try { - return mArray[mIndex--]; + return array[--next]; } catch (ArrayIndexOutOfBoundsException e) { NoSuchElementException nse = new NoSuchElementException(e.getMessage()); @@ -632,11 +623,11 @@ public final class CollectionUtil { } public int previousIndex() { - return mIndex - 1; + return nextIndex() - 1; } public void set(E pElement) { - mArray[mIndex] = pElement; + array[next - 1] = pElement; } } } \ No newline at end of file diff --git a/common/common-lang/src/test/java/com/twelvemonkeys/util/CollectionUtilTest.java b/common/common-lang/src/test/java/com/twelvemonkeys/util/CollectionUtilTest.java new file mode 100644 index 00000000..948cdbc2 --- /dev/null +++ b/common/common-lang/src/test/java/com/twelvemonkeys/util/CollectionUtilTest.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2012, Harald Kuhr + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name "TwelveMonkeys" nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.twelvemonkeys.util; + +import org.junit.Ignore; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +/** + * CollectionUtilTest + * + * @author Harald Kuhr + * @author last modified by $Author: haraldk$ + * @version $Id: CollectionUtilTest.java,v 1.0 24.01.12 17:39 haraldk Exp$ + */ +public class CollectionUtilTest { + + private static final Object[] stringObjects = new Object[] {"foo", "bar", "baz"}; + private static final Object[] integerObjects = new Object[] {1, 2, 3}; + + @Test + public void testMergeArraysObject() { + Object[] merged = (Object[]) CollectionUtil.mergeArrays(stringObjects, integerObjects); + assertArrayEquals(new Object[] {"foo", "bar", "baz", 1, 2, 3}, merged); + } + + @Test + public void testMergeArraysObjectOffset() { + Object[] merged = (Object[]) CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, 1); + assertArrayEquals(new Object[] {"bar", "baz", 3}, merged); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectBadOffset() { + CollectionUtil.mergeArrays(stringObjects, 4, 2, integerObjects, 2, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectBadSecondOffset() { + CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 4, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectBadLength() { + CollectionUtil.mergeArrays(stringObjects, 1, 4, integerObjects, 2, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectBadSecondLength() { + CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectNegativeOffset() { + CollectionUtil.mergeArrays(stringObjects, -1, 2, integerObjects, 2, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectNegativeSecondOffset() { + CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, -1, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectNegativeLength() { + CollectionUtil.mergeArrays(stringObjects, 1, -1, integerObjects, 2, 1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testMergeArraysObjectNegativeSecondLength() { + CollectionUtil.mergeArrays(stringObjects, 1, 2, integerObjects, 2, -1); + } + + @Test + public void testMergeArraysObjectAssignable() { + Integer[] integers = {1, 2, 3}; // Integer assignable to Object + + Object[] merged = (Object[]) CollectionUtil.mergeArrays(stringObjects, integers); + assertArrayEquals(new Object[] {"foo", "bar", "baz", 1, 2, 3}, merged); + } + + @Test(expected = ArrayStoreException.class) + public void testMergeArraysObjectIllegalType() { + String[] strings = {"foo", "bar", "baz"}; + Integer[] integers = {1, 2, 3}; // Integer not assignable to String + + CollectionUtil.mergeArrays(strings, integers); + } + + @Test(expected = ArrayStoreException.class) + public void testMergeArraysNativeIllegalType() { + char[] chars = {'a', 'b', 'c'}; + int[] integers = {1, 2, 3}; // Integer not assignable to String + + CollectionUtil.mergeArrays(chars, integers); + + } + + @Test + public void testMergeArraysNative() { + char[] chars = {'a', 'b', 'c'}; + char[] more = {'x', 'y', 'z'}; + + char[] merged = (char[]) CollectionUtil.mergeArrays(chars, more); + assertArrayEquals(new char[] {'a', 'b', 'c', 'x', 'y', 'z'}, merged); + } + + @Test + public void testSubArrayObject() { + String[] strings = CollectionUtil.subArray(new String[] {"foo", "bar", "baz", "xyzzy"}, 1, 2); + assertArrayEquals(new String[] {"bar", "baz"}, strings); + } + + @Test + public void testSubArrayNative() { + int[] numbers = (int[]) CollectionUtil.subArray(new int[] {1, 2, 3, 4, 5}, 1, 3); + assertArrayEquals(new int[] {2, 3, 4}, numbers); + } + + @Test(expected = IllegalArgumentException.class) + public void testEnumIteratorNull() { + CollectionUtil.iterator((Enumeration) null); + } + + @Test + public void testEnumIterator() { + @SuppressWarnings("unchecked") + Iterator iterator = CollectionUtil.iterator((Enumeration) new StringTokenizer("foo, bar, baz", ", ")); + + int count = 0; + for (Object stringObject : stringObjects) { + assertTrue(iterator.hasNext()); + assertEquals(stringObject, iterator.next()); + + try { + iterator.remove(); + fail("Enumeration has no remove method, iterator.remove() must throw exception"); + } + catch (UnsupportedOperationException expected) { + } + + count++; + } + + assertEquals(3, count); + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail("Iterator has more elements than enumeration"); + } + catch (NoSuchElementException expected) { + } + } + + @Test(expected = IllegalArgumentException.class) + public void testArrayIteratorNull() { + CollectionUtil.iterator((Object[]) null); + } + + @Test + public void testArrayIterator() { + Iterator iterator = CollectionUtil.iterator(new String[] {"foo", "bar", "baz"}); + + int count = 0; + for (Object stringObject : stringObjects) { + assertTrue(iterator.hasNext()); + assertEquals(stringObject, iterator.next()); + + try { + iterator.remove(); + fail("Array have fixed length, iterator.remove() must throw exception"); + } + catch (UnsupportedOperationException expected) { + } + + count++; + } + + assertEquals(3, count); + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail("Iterator has more elements than array"); + } + catch (NoSuchElementException expected) { + } + } + + @Test + public void testArrayListIterator() { + assertCorrectListIterator(CollectionUtil.iterator(new String[] {"foo", "bar", "baz"}), stringObjects); + } + + @Test + public void testArrayListIteratorRange() { + assertCorrectListIterator(CollectionUtil.iterator(new String[] {"foo", "bar", "baz", "boo"}, 1, 2), new String[] {"bar", "baz"}); + } + + @Test + public void testArrayListIteratorSanityCheckArraysAsList() { + assertCorrectListIterator(Arrays.asList(new String[] {"foo", "bar", "baz"}).listIterator(), stringObjects); + } + + @Test + public void testArrayListIteratorSanityCheckArraysAsListRange() { + // NOTE: sublist(fromInc, toExcl) vs iterator(start, length) + assertCorrectListIterator(Arrays.asList(new String[] {"foo", "bar", "baz", "boo"}).subList(1, 3).listIterator(0), new String[] {"bar", "baz"}, false, true); + } + + @Test + public void testArrayListIteratorSanityCheckArraysList() { + assertCorrectListIterator(new ArrayList(Arrays.asList(new String[] {"foo", "bar", "baz"})).listIterator(), stringObjects, true, true); + } + + @Test + public void testArrayListIteratorSanityCheckArraysListRange() { + // NOTE: sublist(fromInc, toExcl) vs iterator(start, length) + assertCorrectListIterator(new ArrayList(Arrays.asList(new String[] {"foo", "bar", "baz", "boo"})).subList(1, 3).listIterator(0), new String[] {"bar", "baz"}, true, true); + } + + private void assertCorrectListIterator(ListIterator iterator, final Object[] elements) { + assertCorrectListIterator(iterator, elements, false, false); + } + + // NOTE: The test is can only test list iterators with a starting index == 0 + private void assertCorrectListIterator(ListIterator iterator, final Object[] elements, boolean skipRemove, boolean skipAdd) { + // Index is now "before 0" + assertEquals(-1, iterator.previousIndex()); + assertEquals(0, iterator.nextIndex()); + + int count = 0; + for (Object element : elements) { + assertTrue("No next element for element '" + element + "' at index: " + count, iterator.hasNext()); + assertEquals(count > 0, iterator.hasPrevious()); + assertEquals(count, iterator.nextIndex()); + assertEquals(count - 1, iterator.previousIndex()); + assertEquals(element, iterator.next()); + + count++; + + if (!skipRemove) { + try { + iterator.remove(); + fail("Array has fixed length, iterator.remove() must throw exception"); + } + catch (UnsupportedOperationException expected) { + } + + // Verify cursor not moving + assertEquals(count, iterator.nextIndex()); + assertEquals(count - 1, iterator.previousIndex()); + } + + // NOTE: AbstractlList.ListItr.add() moves cursor forward, even if backing list's add() throws Exception + // (coll) AbstractList.ListItr.add might corrupt iterator state if enclosing add throws + // See: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6533203, fixed in Java 7 + if (!skipAdd && !("java.util.AbstractList$ListItr".equals(iterator.getClass().getName()) /* && isJava7OrLater() */)) { + try { + iterator.add("xyzzy"); + fail("Array has fixed length, iterator.add() must throw exception"); + } + catch (UnsupportedOperationException expected) { + } + + // Verify cursor not moving + assertEquals(count, iterator.nextIndex()); + assertEquals(count - 1, iterator.previousIndex()); + } + + // Set is supported + iterator.set(String.valueOf(count)); + } + + assertEquals(elements.length, count); + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail("Iterator has more elements than array"); + } + catch (NoSuchElementException expected) { + } + + // Index should now be "before last" + assertEquals(elements.length - 1, iterator.previousIndex()); + assertEquals(elements.length, iterator.nextIndex()); + + for (int i = count; i > 0; i--) { + assertTrue("No previous element for element '" + elements[i - 1] + "' at index: " + (i - 1), iterator.hasPrevious()); + assertEquals(i < elements.length, iterator.hasNext()); + assertEquals(i - 1, iterator.previousIndex()); + assertEquals(i, iterator.nextIndex()); + + assertEquals(String.valueOf(i), iterator.previous()); + } + + // Index should now be back "before 0" + assertEquals(-1, iterator.previousIndex()); + assertEquals(0, iterator.nextIndex()); + assertFalse(iterator.hasPrevious()); + + try { + iterator.previous(); + fail("Iterator has more elements than array"); + } + catch (NoSuchElementException expected) { + } + } + + @Test(expected = IllegalArgumentException.class) + public void testArrayIteratorRangeNull() { + CollectionUtil.iterator(null, 0, 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testArrayIteratorRangeBadStart() { + CollectionUtil.iterator(stringObjects, stringObjects.length + 1, 2); + } + @Test(expected = IllegalArgumentException.class) + public void testArrayIteratorRangeBadLength() { + CollectionUtil.iterator(stringObjects, 1, stringObjects.length); + } + + @Test + public void testArrayIteratorRange() { + Iterator iterator = CollectionUtil.iterator(new String[] {"foo", "bar", "baz", "xyzzy"}, 1, 2); + + for (int i = 1; i < 3; i++) { + assertTrue(iterator.hasNext()); + assertEquals(stringObjects[i], iterator.next()); + + try { + iterator.remove(); + fail("Array has no remove method, iterator.remove() must throw exception"); + } + catch (UnsupportedOperationException expected) { + } + } + + assertFalse(iterator.hasNext()); + + try { + iterator.next(); + fail("Iterator has more elements than array range"); + } + catch (NoSuchElementException expected) { + } + } + + @Test(expected = IllegalArgumentException.class) + public void testReverseOrderNull() { + CollectionUtil.reverseOrder(null); + } + + @Test + public void testReverseOrder() { + Comparator naturalOrder = new NaturalOrder(); + Comparator reverse = CollectionUtil.reverseOrder(naturalOrder); + + assertNotNull(reverse); + + assertEquals(0, naturalOrder.compare("foo", "foo")); + assertEquals(0, reverse.compare("foo", "foo")); + + assertTrue(naturalOrder.compare("bar", "baz") < 0); + assertTrue(reverse.compare("bar", "baz") > 0); + + assertTrue(naturalOrder.compare("baz", "bar") > 0); + assertTrue(reverse.compare("baz", "bar") < 0); + } + + @Test + public void testReverseOrderRandomIntegers() { + Comparator naturalOrder = new NaturalOrder(); + Comparator reverse = CollectionUtil.reverseOrder(naturalOrder); + + Random random = new Random(243249878l); // Stable "random" sequence + + for (int i = 0; i < 65536; i++) { + // Verified to be ~ 50/50 lt/gt + int integer = random.nextInt(); + int integerToo = random.nextInt(); + + assertEquals(0, reverse.compare(integer, integer)); + assertEquals(0, reverse.compare(integerToo, integerToo)); + + int natural = naturalOrder.compare(integer, integerToo); + + if (natural == 0) { + // Actually never hits, but eq case is tested above + assertEquals(0, reverse.compare(integer, integerToo)); + } + else if (natural < 0) { + assertTrue(reverse.compare(integer, integerToo) > 0); + } + else { + assertTrue(reverse.compare(integer, integerToo) < 0); + } + } + } + + @Ignore("For development only") + @Test + @SuppressWarnings({"UnusedDeclaration"}) + public void testGenerify() { + List list = Collections.singletonList("foo"); + @SuppressWarnings({"unchecked"}) + Set set = new HashSet(list); + + List strs0 = CollectionUtil.generify(list, String.class); + List objs0 = CollectionUtil.generify(list, String.class); +// List strs01 = CollectionUtil.generify(list, Object.class); // Not okay + try { + List strs1 = CollectionUtil.generify(set, String.class); // Not ok, runtime CCE unless set is null + } + catch (RuntimeException e) { + e.printStackTrace(); + } + + try { + ArrayList strs01 = CollectionUtil.generify(list, String.class); // Not ok, runtime CCE unless list is null + } + catch (RuntimeException e) { + e.printStackTrace(); + } + + Set setstr1 = CollectionUtil.generify(set, String.class); + Set setobj1 = CollectionUtil.generify(set, String.class); + try { + Set setobj44 = CollectionUtil.generify(list, String.class); // Not ok, runtime CCE unless list is null + } + catch (RuntimeException e) { + e.printStackTrace(); + } + + List strs2 = CollectionUtil., String>generify2(list); + List objs2 = CollectionUtil., String>generify2(list); +// List morestrs = CollectionUtil., String>generify2(list); // Not ok + try { + List strs3 = CollectionUtil., String>generify2(set); // Not ok, runtime CCE unless set is null + } + catch (RuntimeException e) { + e.printStackTrace(); + } + } + + private static class NaturalOrder> implements Comparator { + public int compare(T left, T right) { + return left.compareTo(right); + } + } +}