# HG changeset patch # User mduigou # Date 1371602745 25200 # Node ID f64e4960c78e7207b9de04a609f43b645d8ddc3f # Parent 1f7cbe4829feb151a474c6aa4ff5c918737cc1ec 7129185: Add Collections.{checked|empty|unmodifiable}Navigable{Map|Set} Reviewed-by: dmocek, martin, smarks diff --git a/src/share/classes/java/util/AbstractMap.java b/src/share/classes/java/util/AbstractMap.java --- a/src/share/classes/java/util/AbstractMap.java +++ b/src/share/classes/java/util/AbstractMap.java @@ -543,6 +543,8 @@ /** * Utility method for SimpleEntry and SimpleImmutableEntry. * Test for equality, checking for nulls. + * + * NB: Do not replace with Object.equals until JDK-8015417 is resolved. */ private static boolean eq(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); diff --git a/src/share/classes/java/util/Collections.java b/src/share/classes/java/util/Collections.java --- a/src/share/classes/java/util/Collections.java +++ b/src/share/classes/java/util/Collections.java @@ -27,6 +27,7 @@ import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.IOException; +import java.io.InvalidObjectException; import java.lang.reflect.Array; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -136,7 +137,7 @@ * *

The implementation was adapted from Tim Peters's list sort for Python * ( - * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * TimSort). It uses techniques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. @@ -197,7 +198,7 @@ * *

The implementation was adapted from Tim Peters's list sort for Python * ( - * TimSort). It uses techiques from Peter McIlroy's "Optimistic + * TimSort). It uses techniques from Peter McIlroy's "Optimistic * Sorting and Information Theoretic Complexity", in Proceedings of the * Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474, * January 1993. @@ -1212,6 +1213,94 @@ } /** + * Returns an unmodifiable view of the specified navigable set. This method + * allows modules to provide users with "read-only" access to internal + * navigable sets. Query operations on the returned navigable set "read + * through" to the specified navigable set. Attempts to modify the returned + * navigable set, whether direct, via its iterator, or via its + * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in + * an {@code UnsupportedOperationException}.

+ * + * The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param s the navigable set for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified navigable set + * @since 1.8 + */ + public static NavigableSet unmodifiableNavigableSet(NavigableSet s) { + return new UnmodifiableNavigableSet<>(s); + } + + /** + * Wraps a navigable set and disables all of the mutative operations. + * + * @param type of elements + * @serial include + */ + static class UnmodifiableNavigableSet + extends UnmodifiableSortedSet + implements NavigableSet, Serializable { + + private static final long serialVersionUID = -6027448201786391929L; + + /** + * A singleton empty unmodifiable navigable set used for + * {@link #emptyNavigableSet()}. + * + * @param type of elements, if there were any, and bounds + */ + private static class EmptyNavigableSet extends UnmodifiableNavigableSet + implements Serializable { + private static final long serialVersionUID = -6291252904449939134L; + + public EmptyNavigableSet() { + super(new TreeSet()); + } + + private Object readResolve() { return EMPTY_NAVIGABLE_SET; } + } + + @SuppressWarnings("rawtypes") + private static final NavigableSet EMPTY_NAVIGABLE_SET = + new EmptyNavigableSet<>(); + + /** + * The instance we are protecting. + */ + private final NavigableSet ns; + + UnmodifiableNavigableSet(NavigableSet s) {super(s); ns = s;} + + public E lower(E e) { return ns.lower(e); } + public E floor(E e) { return ns.floor(e); } + public E ceiling(E e) { return ns.ceiling(e); } + public E higher(E e) { return ns.higher(e); } + public E pollFirst() { throw new UnsupportedOperationException(); } + public E pollLast() { throw new UnsupportedOperationException(); } + public NavigableSet descendingSet() + { return new UnmodifiableNavigableSet<>(ns.descendingSet()); } + public Iterator descendingIterator() + { return descendingSet().iterator(); } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return new UnmodifiableNavigableSet<>( + ns.subSet(fromElement, fromInclusive, toElement, toInclusive)); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>( + ns.headSet(toElement, inclusive)); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return new UnmodifiableNavigableSet<>( + ns.tailSet(fromElement, inclusive)); + } + } + + /** * Returns an unmodifiable view of the specified list. This method allows * modules to provide users with "read-only" access to internal * lists. Query operations on the returned list "read through" to the @@ -1238,6 +1327,7 @@ static class UnmodifiableList extends UnmodifiableCollection implements List { private static final long serialVersionUID = -283967356065247728L; + final List list; UnmodifiableList(List list) { @@ -1600,7 +1690,8 @@ private static class UnmodifiableEntry implements Map.Entry { private Map.Entry e; - UnmodifiableEntry(Map.Entry e) {this.e = e;} + UnmodifiableEntry(Map.Entry e) + {this.e = Objects.requireNonNull(e);} public K getKey() {return e.getKey();} public V getValue() {return e.getValue();} @@ -1652,24 +1743,151 @@ private final SortedMap sm; - UnmodifiableSortedMap(SortedMap m) {super(m); sm = m;} - - public Comparator comparator() {return sm.comparator();} - - public SortedMap subMap(K fromKey, K toKey) { - return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); - } - public SortedMap headMap(K toKey) { - return new UnmodifiableSortedMap<>(sm.headMap(toKey)); - } - public SortedMap tailMap(K fromKey) { - return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); - } - - public K firstKey() {return sm.firstKey();} - public K lastKey() {return sm.lastKey();} + UnmodifiableSortedMap(SortedMap m) {super(m); sm = m; } + public Comparator comparator() { return sm.comparator(); } + public SortedMap subMap(K fromKey, K toKey) + { return new UnmodifiableSortedMap<>(sm.subMap(fromKey, toKey)); } + public SortedMap headMap(K toKey) + { return new UnmodifiableSortedMap<>(sm.headMap(toKey)); } + public SortedMap tailMap(K fromKey) + { return new UnmodifiableSortedMap<>(sm.tailMap(fromKey)); } + public K firstKey() { return sm.firstKey(); } + public K lastKey() { return sm.lastKey(); } } + /** + * Returns an unmodifiable view of the specified navigable map. This method + * allows modules to provide users with "read-only" access to internal + * navigable maps. Query operations on the returned navigable map "read + * through" to the specified navigable map. Attempts to modify the returned + * navigable map, whether direct, via its collection views, or via its + * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in + * an {@code UnsupportedOperationException}.

+ * + * The returned navigable map will be serializable if the specified + * navigable map is serializable. + * + * @param m the navigable map for which an unmodifiable view is to be + * returned + * @return an unmodifiable view of the specified navigable map + * @since 1.8 + */ + public static NavigableMap unmodifiableNavigableMap(NavigableMap m) { + return new UnmodifiableNavigableMap<>(m); + } + + /** + * @serial include + */ + static class UnmodifiableNavigableMap + extends UnmodifiableSortedMap + implements NavigableMap, Serializable { + private static final long serialVersionUID = -4858195264774772197L; + + /** + * A class for the {@link EMPTY_NAVIGABLE_MAP} which needs readResolve + * to preserve singleton property. + * + * @param type of keys, if there were any, and of bounds + * @param type of values, if there were any + */ + private static class EmptyNavigableMap extends UnmodifiableNavigableMap + implements Serializable { + + private static final long serialVersionUID = -2239321462712562324L; + + EmptyNavigableMap() { super(new TreeMap()); } + + @Override + public NavigableSet navigableKeySet() + { return emptyNavigableSet(); } + + private Object readResolve() { return EMPTY_NAVIGABLE_MAP; } + } + + /** + * Singleton for {@link emptyNavigableMap()} which is also immutable. + */ + private static final EmptyNavigableMap EMPTY_NAVIGABLE_MAP = + new EmptyNavigableMap<>(); + + /** + * The instance we wrap and protect. + */ + private final NavigableMap nm; + + UnmodifiableNavigableMap(NavigableMap m) + {super(m); nm = m;} + + public K lowerKey(K key) { return nm.lowerKey(key); } + public K floorKey(K key) { return nm.floorKey(key); } + public K ceilingKey(K key) { return nm.ceilingKey(key); } + public K higherKey(K key) { return nm.higherKey(key); } + + public Entry lowerEntry(K key) { + Entry lower = (Entry) nm.lowerEntry(key); + return (null != lower) + ? new UnmodifiableEntrySet.UnmodifiableEntry(lower) + : null; + } + + public Entry floorEntry(K key) { + Entry floor = (Entry) nm.floorEntry(key); + return (null != floor) + ? new UnmodifiableEntrySet.UnmodifiableEntry(floor) + : null; + } + + public Entry ceilingEntry(K key) { + Entry ceiling = (Entry) nm.ceilingEntry(key); + return (null != ceiling) + ? new UnmodifiableEntrySet.UnmodifiableEntry(ceiling) + : null; + } + + + public Entry higherEntry(K key) { + Entry higher = (Entry) nm.higherEntry(key); + return (null != higher) + ? new UnmodifiableEntrySet.UnmodifiableEntry(higher) + : null; + } + + public Entry firstEntry() { + Entry first = (Entry) nm.firstEntry(); + return (null != first) + ? new UnmodifiableEntrySet.UnmodifiableEntry(first) + : null; + } + + public Entry lastEntry() { + Entry last = (Entry) nm.lastEntry(); + return (null != last) + ? new UnmodifiableEntrySet.UnmodifiableEntry(last) + : null; + } + + public Entry pollFirstEntry() + { throw new UnsupportedOperationException(); } + public Entry pollLastEntry() + { throw new UnsupportedOperationException(); } + public NavigableMap descendingMap() + { return unmodifiableNavigableMap(nm.descendingMap()); } + public NavigableSet navigableKeySet() + { return unmodifiableNavigableSet(nm.navigableKeySet()); } + public NavigableSet descendingKeySet() + { return unmodifiableNavigableSet(nm.descendingKeySet()); } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return unmodifiableNavigableMap( + nm.subMap(fromKey, fromInclusive, toKey, toInclusive)); + } + + public NavigableMap headMap(K toKey, boolean inclusive) + { return unmodifiableNavigableMap(nm.headMap(toKey, inclusive)); } + public NavigableMap tailMap(K fromKey, boolean inclusive) + { return unmodifiableNavigableMap(nm.tailMap(fromKey, inclusive)); } + } // Synch Wrappers @@ -1723,14 +1941,13 @@ final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection c) { - if (c==null) - throw new NullPointerException(); - this.c = c; + this.c = Objects.requireNonNull(c); mutex = this; } + SynchronizedCollection(Collection c, Object mutex) { - this.c = c; - this.mutex = mutex; + this.c = Objects.requireNonNull(c); + this.mutex = Objects.requireNonNull(mutex); } public int size() { @@ -1945,6 +2162,120 @@ } /** + * Returns a synchronized (thread-safe) navigable set backed by the + * specified navigable set. In order to guarantee serial access, it is + * critical that all access to the backing navigable set is + * accomplished through the returned navigable set (or its views).

+ * + * It is imperative that the user manually synchronize on the returned + * navigable set when iterating over it or any of its {@code subSet}, + * {@code headSet}, or {@code tailSet} views. + *

+     *  NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet());
+     *      ...
+     *  synchronized (s) {
+     *      Iterator i = s.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * or: + *
+     *  NavigableSet s = Collections.synchronizedNavigableSet(new TreeSet());
+     *  NavigableSet s2 = s.headSet(foo, true);
+     *      ...
+     *  synchronized (s) {  // Note: s, not s2!!!
+     *      Iterator i = s2.iterator(); // Must be in the synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + * @param s the navigable set to be "wrapped" in a synchronized navigable + * set + * @return a synchronized view of the specified navigable set + * @since 1.8 + */ + public static NavigableSet synchronizedNavigableSet(NavigableSet s) { + return new SynchronizedNavigableSet<>(s); + } + + /** + * @serial include + */ + static class SynchronizedNavigableSet + extends SynchronizedSortedSet + implements NavigableSet + { + private static final long serialVersionUID = -5505529816273629798L; + + private final NavigableSet ns; + + SynchronizedNavigableSet(NavigableSet s) { + super(s); + ns = s; + } + + SynchronizedNavigableSet(NavigableSet s, Object mutex) { + super(s, mutex); + ns = s; + } + public E lower(E e) { synchronized (mutex) {return ns.lower(e);} } + public E floor(E e) { synchronized (mutex) {return ns.floor(e);} } + public E ceiling(E e) { synchronized (mutex) {return ns.ceiling(e);} } + public E higher(E e) { synchronized (mutex) {return ns.higher(e);} } + public E pollFirst() { synchronized (mutex) {return ns.pollFirst();} } + public E pollLast() { synchronized (mutex) {return ns.pollLast();} } + + public NavigableSet descendingSet() { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.descendingSet(), mutex); + } + } + + public Iterator descendingIterator() + { synchronized (mutex) { return descendingSet().iterator(); } } + + public NavigableSet subSet(E fromElement, E toElement) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.subSet(fromElement, true, toElement, false), mutex); + } + } + public NavigableSet headSet(E toElement) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.headSet(toElement, false), mutex); + } + } + public NavigableSet tailSet(E fromElement) { + synchronized (mutex) { + return new SynchronizedNavigableSet(ns.tailSet(fromElement, true), mutex); + } + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), mutex); + } + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.headSet(toElement, inclusive), mutex); + } + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableSet<>(ns.tailSet(fromElement, inclusive)); + } + } + } + + /** * Returns a synchronized (thread-safe) list backed by the specified * list. In order to guarantee serial access, it is critical that * all access to the backing list is accomplished @@ -2153,9 +2484,7 @@ final Object mutex; // Object on which to synchronize SynchronizedMap(Map m) { - if (m==null) - throw new NullPointerException(); - this.m = m; + this.m = Objects.requireNonNull(m); mutex = this; } @@ -2334,7 +2663,6 @@ return new SynchronizedSortedMap<>(m); } - /** * @serial include */ @@ -2384,6 +2712,164 @@ } } + /** + * Returns a synchronized (thread-safe) navigable map backed by the + * specified navigable map. In order to guarantee serial access, it is + * critical that all access to the backing navigable map is + * accomplished through the returned navigable map (or its views).

+ * + * It is imperative that the user manually synchronize on the returned + * navigable map when iterating over any of its collection views, or the + * collections views of any of its {@code subMap}, {@code headMap} or + * {@code tailMap} views. + *

+     *  NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
+     *      ...
+     *  Set s = m.keySet();  // Needn't be in synchronized block
+     *      ...
+     *  synchronized (m) {  // Synchronizing on m, not s!
+     *      Iterator i = s.iterator(); // Must be in synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * or: + *
+     *  NavigableMap m = Collections.synchronizedNavigableMap(new TreeMap());
+     *  NavigableMap m2 = m.subMap(foo, true, bar, false);
+     *      ...
+     *  Set s2 = m2.keySet();  // Needn't be in synchronized block
+     *      ...
+     *  synchronized (m) {  // Synchronizing on m, not m2 or s2!
+     *      Iterator i = s.iterator(); // Must be in synchronized block
+     *      while (i.hasNext())
+     *          foo(i.next());
+     *  }
+     * 
+ * Failure to follow this advice may result in non-deterministic behavior. + * + *

The returned navigable map will be serializable if the specified + * navigable map is serializable. + * + * @param m the navigable map to be "wrapped" in a synchronized navigable + * map + * @return a synchronized view of the specified navigable map. + * @since 1.8 + */ + public static NavigableMap synchronizedNavigableMap(NavigableMap m) { + return new SynchronizedNavigableMap<>(m); + } + + /** + * A synchronized NavigableMap. + * + * @serial include + */ + static class SynchronizedNavigableMap + extends SynchronizedSortedMap + implements NavigableMap + { + private static final long serialVersionUID = 699392247599746807L; + + private final NavigableMap nm; + + SynchronizedNavigableMap(NavigableMap m) { + super(m); + nm = m; + } + SynchronizedNavigableMap(NavigableMap m, Object mutex) { + super(m, mutex); + nm = m; + } + + public Entry lowerEntry(K key) + { synchronized (mutex) { return nm.lowerEntry(key); } } + public K lowerKey(K key) + { synchronized (mutex) { return nm.lowerKey(key); } } + public Entry floorEntry(K key) + { synchronized (mutex) { return nm.floorEntry(key); } } + public K floorKey(K key) + { synchronized (mutex) { return nm.floorKey(key); } } + public Entry ceilingEntry(K key) + { synchronized (mutex) { return nm.ceilingEntry(key); } } + public K ceilingKey(K key) + { synchronized (mutex) { return nm.ceilingKey(key); } } + public Entry higherEntry(K key) + { synchronized (mutex) { return nm.higherEntry(key); } } + public K higherKey(K key) + { synchronized (mutex) { return nm.higherKey(key); } } + public Entry firstEntry() + { synchronized (mutex) { return nm.firstEntry(); } } + public Entry lastEntry() + { synchronized (mutex) { return nm.lastEntry(); } } + public Entry pollFirstEntry() + { synchronized (mutex) { return nm.pollFirstEntry(); } } + public Entry pollLastEntry() + { synchronized (mutex) { return nm.pollLastEntry(); } } + + public NavigableMap descendingMap() { + synchronized (mutex) { + return + new SynchronizedNavigableMap(nm.descendingMap(), mutex); + } + } + + public NavigableSet keySet() { + return navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.navigableKeySet(), mutex); + } + } + + public NavigableSet descendingKeySet() { + synchronized (mutex) { + return new SynchronizedNavigableSet(nm.descendingKeySet(), mutex); + } + } + + + public SortedMap subMap(K fromKey, K toKey) { + synchronized (mutex) { + return new SynchronizedNavigableMap<>( + nm.subMap(fromKey, true, toKey, false), mutex); + } + } + public SortedMap headMap(K toKey) { + synchronized (mutex) { + return new SynchronizedNavigableMap<>(nm.headMap(toKey, false), mutex); + } + } + public SortedMap tailMap(K fromKey) { + synchronized (mutex) { + return new SynchronizedNavigableMap<>(nm.tailMap(fromKey, true),mutex); + } + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + synchronized (mutex) { + return new SynchronizedNavigableMap( + nm.subMap(fromKey, fromInclusive, toKey, toInclusive), mutex); + } + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableMap( + nm.headMap(toKey, inclusive), mutex); + } + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + synchronized (mutex) { + return new SynchronizedNavigableMap( + nm.tailMap(fromKey, inclusive), mutex); + } + } + } + // Dynamically typesafe collection wrappers /** @@ -2415,12 +2901,12 @@ * program to wrap the collection with a dynamically typesafe view. * For example, this declaration: *

 {@code
-     *     Collection c = new HashSet();
+     *     Collection c = new HashSet<>();
      * }
* may be replaced temporarily by this one: *
 {@code
      *     Collection c = Collections.checkedCollection(
-     *         new HashSet(), String.class);
+     *         new HashSet<>(), String.class);
      * }
* Running the program again will cause it to fail at the point where * an incorrectly typed element is inserted into the collection, clearly @@ -2706,6 +3192,7 @@ implements SortedSet, Serializable { private static final long serialVersionUID = 1599911165492914959L; + private final SortedSet ss; CheckedSortedSet(SortedSet s, Class type) { @@ -2728,6 +3215,87 @@ } } +/** + * Returns a dynamically typesafe view of the specified navigable set. + * Any attempt to insert an element of the wrong type will result in an + * immediate {@link ClassCastException}. Assuming a navigable set + * contains no incorrectly typed elements prior to the time a + * dynamically typesafe view is generated, and that all subsequent + * access to the navigable set takes place through the view, it is + * guaranteed that the navigable set cannot contain an incorrectly + * typed element. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned navigable set will be serializable if the specified + * navigable set is serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned navigable set permits insertion of null elements + * whenever the backing sorted set does. + * + * @param s the navigable set for which a dynamically typesafe view is to be + * returned + * @param type the type of element that {@code s} is permitted to hold + * @return a dynamically typesafe view of the specified navigable set + * @since 1.8 + */ + public static NavigableSet checkedNavigableSet(NavigableSet s, + Class type) { + return new CheckedNavigableSet<>(s, type); + } + + /** + * @serial include + */ + static class CheckedNavigableSet extends CheckedSortedSet + implements NavigableSet, Serializable + { + private static final long serialVersionUID = -5429120189805438922L; + + private final NavigableSet ns; + + CheckedNavigableSet(NavigableSet s, Class type) { + super(s, type); + ns = s; + } + + public E lower(E e) { return ns.lower(e); } + public E floor(E e) { return ns.floor(e); } + public E ceiling(E e) { return ns.ceiling(e); } + public E higher(E e) { return ns.higher(e); } + public E pollFirst() { return ns.pollFirst(); } + public E pollLast() {return ns.pollLast(); } + public NavigableSet descendingSet() + { return checkedNavigableSet(ns.descendingSet(), type); } + public Iterator descendingIterator() + {return checkedNavigableSet(ns.descendingSet(), type).iterator(); } + + public NavigableSet subSet(E fromElement, E toElement) { + return checkedNavigableSet(ns.subSet(fromElement, true, toElement, false), type); + } + public NavigableSet headSet(E toElement) { + return checkedNavigableSet(ns.headSet(toElement, false), type); + } + public NavigableSet tailSet(E fromElement) { + return checkedNavigableSet(ns.tailSet(fromElement, true), type); + } + + public NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { + return checkedNavigableSet(ns.subSet(fromElement, fromInclusive, toElement, toInclusive), type); + } + + public NavigableSet headSet(E toElement, boolean inclusive) { + return checkedNavigableSet(ns.headSet(toElement, inclusive), type); + } + + public NavigableSet tailSet(E fromElement, boolean inclusive) { + return checkedNavigableSet(ns.tailSet(fromElement, inclusive), type); + } + } + /** * Returns a dynamically typesafe view of the specified list. * Any attempt to insert an element of the wrong type will result in @@ -2940,11 +3508,9 @@ } CheckedMap(Map m, Class keyType, Class valueType) { - if (m == null || keyType == null || valueType == null) - throw new NullPointerException(); - this.m = m; - this.keyType = keyType; - this.valueType = valueType; + this.m = Objects.requireNonNull(m); + this.keyType = Objects.requireNonNull(keyType); + this.valueType = Objects.requireNonNull(valueType); } public int size() { return m.size(); } @@ -3221,8 +3787,8 @@ private final Class valueType; CheckedEntry(Map.Entry e, Class valueType) { - this.e = e; - this.valueType = valueType; + this.e = Objects.requireNonNull(e); + this.valueType = Objects.requireNonNull(valueType); } public K getKey() { return e.getKey(); } @@ -3325,6 +3891,177 @@ } } + /** + * Returns a dynamically typesafe view of the specified navigable map. + * Any attempt to insert a mapping whose key or value have the wrong + * type will result in an immediate {@link ClassCastException}. + * Similarly, any attempt to modify the value currently associated with + * a key will result in an immediate {@link ClassCastException}, + * whether the modification is attempted directly through the map + * itself, or through a {@link Map.Entry} instance obtained from the + * map's {@link Map#entrySet() entry set} view. + * + *

Assuming a map contains no incorrectly typed keys or values + * prior to the time a dynamically typesafe view is generated, and + * that all subsequent access to the map takes place through the view + * (or one of its collection views), it is guaranteed that the + * map cannot contain an incorrectly typed key or value. + * + *

A discussion of the use of dynamically typesafe views may be + * found in the documentation for the {@link #checkedCollection + * checkedCollection} method. + * + *

The returned map will be serializable if the specified map is + * serializable. + * + *

Since {@code null} is considered to be a value of any reference + * type, the returned map permits insertion of null keys or values + * whenever the backing map does. + * + * @param type of map keys + * @param type of map values + * @param m the map for which a dynamically typesafe view is to be + * returned + * @param keyType the type of key that {@code m} is permitted to hold + * @param valueType the type of value that {@code m} is permitted to hold + * @return a dynamically typesafe view of the specified map + * @since 1.8 + */ + public static NavigableMap checkedNavigableMap(NavigableMap m, + Class keyType, + Class valueType) { + return new CheckedNavigableMap<>(m, keyType, valueType); + } + + /** + * @serial include + */ + static class CheckedNavigableMap extends CheckedSortedMap + implements NavigableMap, Serializable + { + private static final long serialVersionUID = -4852462692372534096L; + + private final NavigableMap nm; + + CheckedNavigableMap(NavigableMap m, + Class keyType, Class valueType) { + super(m, keyType, valueType); + nm = m; + } + + public Comparator comparator() { return nm.comparator(); } + public K firstKey() { return nm.firstKey(); } + public K lastKey() { return nm.lastKey(); } + + public Entry lowerEntry(K key) { + Entry lower = nm.lowerEntry(key); + return (null != lower) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(lower, valueType) + : null; + } + + public K lowerKey(K key) { return nm.lowerKey(key); } + + public Entry floorEntry(K key) { + Entry floor = nm.floorEntry(key); + return (null != floor) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(floor, valueType) + : null; + } + + public K floorKey(K key) { return nm.floorKey(key); } + + public Entry ceilingEntry(K key) { + Entry ceiling = nm.ceilingEntry(key); + return (null != ceiling) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(ceiling, valueType) + : null; + } + + public K ceilingKey(K key) { return nm.ceilingKey(key); } + + public Entry higherEntry(K key) { + Entry higher = nm.higherEntry(key); + return (null != higher) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(higher, valueType) + : null; + } + + public K higherKey(K key) { return nm.higherKey(key); } + + public Entry firstEntry() { + Entry first = nm.firstEntry(); + return (null != first) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(first, valueType) + : null; + } + + public Entry lastEntry() { + Entry last = nm.lastEntry(); + return (null != last) + ? new CheckedMap.CheckedEntrySet.CheckedEntry(last, valueType) + : null; + } + + public Entry pollFirstEntry() { + Entry entry = nm.pollFirstEntry(); + return (null == entry) + ? null + : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); + } + + public Entry pollLastEntry() { + Entry entry = nm.pollLastEntry(); + return (null == entry) + ? null + : new CheckedMap.CheckedEntrySet.CheckedEntry(entry, valueType); + } + + public NavigableMap descendingMap() { + return checkedNavigableMap(nm.descendingMap(), keyType, valueType); + } + + public NavigableSet keySet() { + return navigableKeySet(); + } + + public NavigableSet navigableKeySet() { + return checkedNavigableSet(nm.navigableKeySet(), keyType); + } + + public NavigableSet descendingKeySet() { + return checkedNavigableSet(nm.descendingKeySet(), keyType); + } + + @Override + public NavigableMap subMap(K fromKey, K toKey) { + return checkedNavigableMap(nm.subMap(fromKey, true, toKey, false), + keyType, valueType); + } + + @Override + public NavigableMap headMap(K toKey) { + return checkedNavigableMap(nm.headMap(toKey, false), keyType, valueType); + } + + @Override + public NavigableMap tailMap(K fromKey) { + return checkedNavigableMap(nm.tailMap(fromKey, true), keyType, valueType); + } + + public NavigableMap subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) { + return checkedNavigableMap(nm.subMap(fromKey, fromInclusive, toKey, toInclusive), keyType, valueType); + } + + public NavigableMap headMap(K toKey, boolean inclusive) { + return checkedNavigableMap(nm.headMap(toKey, inclusive), keyType, valueType); + } + + public NavigableMap tailMap(K fromKey, boolean inclusive) { + return checkedNavigableMap(nm.tailMap(fromKey, inclusive), keyType, valueType); + } + } + // Empty collections /** @@ -3346,6 +4083,7 @@ *

Implementations of this method are permitted, but not * required, to return the same object from multiple invocations. * + * @param type of elements, if there were any, in the iterator * @return an empty iterator * @since 1.7 */ @@ -3396,6 +4134,7 @@ *

Implementations of this method are permitted, but not * required, to return the same object from multiple invocations. * + * @param type of elements, if there were any, in the iterator * @return an empty list iterator * @since 1.7 */ @@ -3460,17 +4199,19 @@ public static final Set EMPTY_SET = new EmptySet<>(); /** - * Returns the empty set (immutable). This set is serializable. + * Returns an empty set (immutable). This set is serializable. * Unlike the like-named field, this method is parameterized. * *

This example illustrates the type-safe way to obtain an empty set: *

      *     Set<String> s = Collections.emptySet();
      * 
- * Implementation note: Implementations of this method need not - * create a separate Set object for each call. Using this - * method is likely to have comparable cost to using the like-named - * field. (Unlike this method, the field does not provide type safety.) + * @implNote Implementations of this method need not create a separate + * {@code Set} object for each call. Using this method is likely to have + * comparable cost to using the like-named field. (Unlike this method, the + * field does not provide type safety.) + * + * @return the empty set * * @see #EMPTY_SET * @since 1.5 @@ -3525,121 +4266,45 @@ } /** - * Returns the empty sorted set (immutable). This set is serializable. - * - *

This example illustrates the type-safe way to obtain an empty sorted - * set: - *

-     *     SortedSet<String> s = Collections.emptySortedSet();
-     * 
- * Implementation note: Implementations of this method need not - * create a separate SortedSet object for each call. - * + * Returns an empty sorted set (immutable). This set is serializable. + * + *

This example illustrates the type-safe way to obtain an empty + * sorted set: + *

 {@code
+     *     SortedSet s = Collections.emptySortedSet();
+     * }
+ * + * @implNote Implementations of this method need not create a separate + * {@code SortedSet} object for each call. + * + * @param type of elements, if there were any, in the set + * @return the empty sorted set * @since 1.8 */ @SuppressWarnings("unchecked") - public static final SortedSet emptySortedSet() { - return (SortedSet) new EmptySortedSet<>(); + public static SortedSet emptySortedSet() { + return (SortedSet) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET; } /** - * @serial include + * Returns an empty navigable set (immutable). This set is serializable. + * + *

This example illustrates the type-safe way to obtain an empty + * navigable set: + *

 {@code
+     *     NavigableSet s = Collections.emptyNavigableSet();
+     * }
+ * + * @implNote Implementations of this method need not + * create a separate {@code NavigableSet} object for each call. + * + * @param type of elements, if there were any, in the set + * @return the empty navigable set + * @since 1.8 */ - private static class EmptySortedSet - extends AbstractSet - implements SortedSet, Serializable - { - private static final long serialVersionUID = 6316515401502265487L; - public Iterator iterator() { return emptyIterator(); } - public int size() {return 0;} - public boolean isEmpty() {return true;} - public boolean contains(Object obj) {return false;} - public boolean containsAll(Collection c) { return c.isEmpty(); } - public Object[] toArray() { return new Object[0]; } - - public E[] toArray(E[] a) { - if (a.length > 0) - a[0] = null; - return a; - } - - // Preserves singleton property - private Object readResolve() { - return new EmptySortedSet<>(); - } - - @Override - public Comparator comparator() { - return null; - } - - @Override - @SuppressWarnings("unchecked") - public SortedSet subSet(Object fromElement, Object toElement) { - Objects.requireNonNull(fromElement); - Objects.requireNonNull(toElement); - - if (!(fromElement instanceof Comparable) || - !(toElement instanceof Comparable)) - { - throw new ClassCastException(); - } - - if ((((Comparable)fromElement).compareTo(toElement) >= 0) || - (((Comparable)toElement).compareTo(fromElement) < 0)) - { - throw new IllegalArgumentException(); - } - - return emptySortedSet(); - } - - @Override - public SortedSet headSet(Object toElement) { - Objects.requireNonNull(toElement); - - if (!(toElement instanceof Comparable)) { - throw new ClassCastException(); - } - - return emptySortedSet(); - } - - @Override - public SortedSet tailSet(Object fromElement) { - Objects.requireNonNull(fromElement); - - if (!(fromElement instanceof Comparable)) { - throw new ClassCastException(); - } - - return emptySortedSet(); - } - - @Override - public E first() { - throw new NoSuchElementException(); - } - - @Override - public E last() { - throw new NoSuchElementException(); - } - - // Override default methods in Collection - @Override - public void forEach(Consumer action) { - Objects.requireNonNull(action); - } - - @Override - public boolean removeIf(Predicate filter) { - Objects.requireNonNull(filter); - return false; - } - - @Override - public Spliterator spliterator() { return Spliterators.emptySpliterator(); } + @SuppressWarnings("unchecked") + public static NavigableSet emptyNavigableSet() { + return (NavigableSet) UnmodifiableNavigableSet.EMPTY_NAVIGABLE_SET; } /** @@ -3651,7 +4316,7 @@ public static final List EMPTY_LIST = new EmptyList<>(); /** - * Returns the empty list (immutable). This list is serializable. + * Returns an empty list (immutable). This list is serializable. * *

This example illustrates the type-safe way to obtain an empty list: *

@@ -3662,6 +4327,9 @@
      * method is likely to have comparable cost to using the like-named
      * field.  (Unlike this method, the field does not provide type safety.)
      *
+     * @param  type of elements, if there were any, in the list
+     * @return an empty immutable list
+     *
      * @see #EMPTY_LIST
      * @since 1.5
      */
@@ -3748,17 +4416,18 @@
     public static final Map EMPTY_MAP = new EmptyMap<>();
 
     /**
-     * Returns the empty map (immutable).  This map is serializable.
-     *
-     * 

This example illustrates the type-safe way to obtain an empty set: + * Returns an empty map (immutable). This map is serializable. + * + *

This example illustrates the type-safe way to obtain an empty map: *

      *     Map<String, Date> s = Collections.emptyMap();
      * 
- * Implementation note: Implementations of this method need not - * create a separate Map object for each call. Using this - * method is likely to have comparable cost to using the like-named - * field. (Unlike this method, the field does not provide type safety.) - * + * @implNote Implementations of this method need not create a separate + * {@code Map} object for each call. Using this method is likely to have + * comparable cost to using the like-named field. (Unlike this method, the + * field does not provide type safety.) + * + * @return an empty map * @see #EMPTY_MAP * @since 1.5 */ @@ -3768,6 +4437,44 @@ } /** + * Returns an empty sorted map (immutable). This map is serializable. + * + *

This example illustrates the type-safe way to obtain an empty map: + *

 {@code
+     *     SortedMap s = Collections.emptySortedMap();
+     * }
+ * + * @implNote Implementations of this method need not create a separate + * {@code SortedMap} object for each call. + * + * @return an empty sorted map + * @since 1.8 + */ + @SuppressWarnings("unchecked") + public static final SortedMap emptySortedMap() { + return (SortedMap) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** + * Returns an empty navigable map (immutable). This map is serializable. + * + *

This example illustrates the type-safe way to obtain an empty map: + *

 {@code
+     *     NavigableMap s = Collections.emptyNavigableMap();
+     * }
+ * + * @implNote Implementations of this method need not create a separate + * {@code NavigableMap} object for each call. + * + * @return an empty navigable map + * @since 1.8 + */ + @SuppressWarnings("unchecked") + public static final NavigableMap emptyNavigableMap() { + return (NavigableMap) UnmodifiableNavigableMap.EMPTY_NAVIGABLE_MAP; + } + + /** * @serial include */ private static class EmptyMap @@ -4071,15 +4778,11 @@ v = value; } - public int size() {return 1;} - - public boolean isEmpty() {return false;} - - public boolean containsKey(Object key) {return eq(key, k);} - - public boolean containsValue(Object value) {return eq(value, v);} - - public V get(Object key) {return (eq(key, k) ? v : null);} + public int size() {return 1;} + public boolean isEmpty() {return false;} + public boolean containsKey(Object key) {return eq(key, k);} + public boolean containsValue(Object value) {return eq(value, v);} + public V get(Object key) {return (eq(key, k) ? v : null);} private transient Set keySet = null; private transient Set> entrySet = null; @@ -4416,6 +5119,8 @@ /** * Returns true if the specified arguments are equal, or both null. + * + * NB: Do not replace with Object.equals until JDK-8015417 is resolved. */ static boolean eq(Object o1, Object o2) { return o1==null ? o2==null : o1.equals(o2); diff --git a/src/share/classes/java/util/NavigableSet.java b/src/share/classes/java/util/NavigableSet.java --- a/src/share/classes/java/util/NavigableSet.java +++ b/src/share/classes/java/util/NavigableSet.java @@ -303,7 +303,7 @@ * @throws ClassCastException {@inheritDoc} * @throws NullPointerException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} -na */ + */ SortedSet headSet(E toElement); /** diff --git a/test/java/util/Collection/MOAT.java b/test/java/util/Collection/MOAT.java --- a/test/java/util/Collection/MOAT.java +++ b/test/java/util/Collection/MOAT.java @@ -71,6 +71,14 @@ testCollection(new LinkedList()); testCollection(new LinkedList().subList(0,0)); testCollection(new TreeSet()); + testCollection(Collections.checkedList(new ArrayList(), Integer.class)); + testCollection(Collections.synchronizedList(new ArrayList())); + testCollection(Collections.checkedSet(new HashSet(), Integer.class)); + testCollection(Collections.checkedSortedSet(new TreeSet(), Integer.class)); + testCollection(Collections.checkedNavigableSet(new TreeSet(), Integer.class)); + testCollection(Collections.synchronizedSet(new HashSet())); + testCollection(Collections.synchronizedSortedSet(new TreeSet())); + testCollection(Collections.synchronizedNavigableSet(new TreeSet())); testCollection(new CopyOnWriteArrayList()); testCollection(new CopyOnWriteArrayList().subList(0,0)); @@ -98,6 +106,12 @@ testMap(new Hashtable()); testMap(new ConcurrentHashMap(10, 0.5f)); testMap(new ConcurrentSkipListMap()); + testMap(Collections.checkedMap(new HashMap(), Integer.class, Integer.class)); + testMap(Collections.checkedSortedMap(new TreeMap(), Integer.class, Integer.class)); + testMap(Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class)); + testMap(Collections.synchronizedMap(new HashMap())); + testMap(Collections.synchronizedSortedMap(new TreeMap())); + testMap(Collections.synchronizedNavigableMap(new TreeMap())); // Empty collections final List emptyArray = Arrays.asList(new Integer[]{}); @@ -117,19 +131,29 @@ testCollection(emptySet); testEmptySet(emptySet); testEmptySet(EMPTY_SET); + testEmptySet(Collections.emptySet()); + testEmptySet(Collections.emptySortedSet()); + testEmptySet(Collections.emptyNavigableSet()); testImmutableSet(emptySet); List emptyList = emptyList(); testCollection(emptyList); testEmptyList(emptyList); testEmptyList(EMPTY_LIST); + testEmptyList(Collections.emptyList()); testImmutableList(emptyList); Map emptyMap = emptyMap(); testMap(emptyMap); testEmptyMap(emptyMap); testEmptyMap(EMPTY_MAP); + testEmptyMap(Collections.emptyMap()); + testEmptyMap(Collections.emptySortedMap()); + testEmptyMap(Collections.emptyNavigableMap()); testImmutableMap(emptyMap); + testImmutableMap(Collections.emptyMap()); + testImmutableMap(Collections.emptySortedMap()); + testImmutableMap(Collections.emptyNavigableMap()); // Singleton collections Set singletonSet = singleton(1); diff --git a/test/java/util/Collections/CheckedIdentityMap.java b/test/java/util/Collections/CheckedIdentityMap.java --- a/test/java/util/Collections/CheckedIdentityMap.java +++ b/test/java/util/Collections/CheckedIdentityMap.java @@ -24,59 +24,42 @@ /* * @test * @bug 6585904 + * @run testng CheckedIdentityMap * @summary Checked collections with underlying maps with identity comparisons */ import java.util.*; import static java.util.Collections.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; + +import org.testng.annotations.Test; + public class CheckedIdentityMap { - void test(String[] args) throws Throwable { + + @Test + public void testHashCode() { Map m1 = checkedMap( new IdentityHashMap(), Integer.class, Integer.class); Map m2 = checkedMap( new IdentityHashMap(), Integer.class, Integer.class); + // NB: these are unique instances. Compare vs. Integer.valueOf(1) m1.put(new Integer(1), new Integer(1)); m2.put(new Integer(1), new Integer(1)); Map.Entry e1 = m1.entrySet().iterator().next(); Map.Entry e2 = m2.entrySet().iterator().next(); - check(! e1.equals(e2)); - check(e1.hashCode() == hashCode(e1)); - check(e2.hashCode() == hashCode(e2)); + + assertNotEquals(e1, e2); + assertEquals(e1.hashCode(), hashCode(e1)); + assertEquals(e2.hashCode(), hashCode(e2)); } - int hashCode(Map.Entry e) { + static int hashCode(Map.Entry e) { return (System.identityHashCode(e.getKey()) ^ System.identityHashCode(e.getValue())); } - - //--------------------- Infrastructure --------------------------- - volatile int passed = 0, failed = 0; - void pass() {passed++;} - void fail() {failed++; Thread.dumpStack();} - void fail(String msg) {System.err.println(msg); fail();} - void unexpected(Throwable t) {failed++; t.printStackTrace();} - void check(boolean cond) {if (cond) pass(); else fail();} - void equal(Object x, Object y) { - if (x == null ? y == null : x.equals(y)) pass(); - else fail(x + " not equal to " + y);} - public static void main(String[] args) throws Throwable { - new CheckedIdentityMap().instanceMain(args);} - void instanceMain(String[] args) throws Throwable { - try {test(args);} catch (Throwable t) {unexpected(t);} - System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); - if (failed > 0) throw new AssertionError("Some tests failed");} - abstract class F {abstract void f() throws Throwable;} - void THROWS(Class k, F... fs) { - for (F f : fs) - try {f.f(); fail("Expected " + k.getName() + " not thrown");} - catch (Throwable t) { - if (k.isAssignableFrom(t.getClass())) pass(); - else unexpected(t);}} - Thread checkedThread(final Runnable r) { - return new Thread() {public void run() { - try {r.run();} catch (Throwable t) {unexpected(t);}}};} } diff --git a/test/java/util/Collections/CheckedMapBash.java b/test/java/util/Collections/CheckedMapBash.java --- a/test/java/util/Collections/CheckedMapBash.java +++ b/test/java/util/Collections/CheckedMapBash.java @@ -23,76 +23,83 @@ /* * @test - * @bug 4904067 5023830 + * @bug 4904067 5023830 7129185 * @summary Unit test for Collections.checkedMap * @author Josh Bloch + * @run testng CheckedMapBash */ import java.util.*; +import java.util.function.Supplier; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertTrue; public class CheckedMapBash { - static Random rnd = new Random(); - static Object nil = new Integer(0); + static final Random rnd = new Random(); + static final Object nil = new Integer(0); + static final int numItr = 100; + static final int mapSize = 100; - public static void main(String[] args) { - int numItr = 100; - int mapSize = 100; + @Test(dataProvider = "Bash.Supplier>") + public static void testCheckedMap(String description, Supplier> supplier) { + Map m = supplier.get(); + Object head = nil; - // Linked List test - for (int i=0; i> supplier) { + Map m = supplier.get(); for (int i=0; i bashNavigableMapProvider() { + ArrayList iters = new ArrayList<>(makeCheckedMaps()); + iters.ensureCapacity(numItr * iters.size()); + for(int each=1; each < numItr; each++) { + iters.addAll( makeCheckedMaps()); + } + return iters.iterator(); } - static void fail(String s) { - throw new RuntimeException(s); + @DataProvider(name = "Supplier>", parallel = true) + public static Iterator navigableMapProvider() { + return makeCheckedMaps().iterator(); + } + + public static Collection makeCheckedMaps() { + return Arrays.asList( + new Object[]{"Collections.checkedMap(HashMap)", + (Supplier) () -> {return Collections.checkedMap(new HashMap(), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedMap(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedMap(new TreeMap(Collections.reverseOrder()), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedMap(TreeSet).descendingSet()", + (Supplier) () -> {return Collections.checkedMap(new TreeMap().descendingMap(), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedNavigableMap(TreeSet)", + (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedNavigableMap(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap(Collections.reverseOrder()), Integer.class, Integer.class);}}, + new Object[]{"Collections.checkedNavigableMap().descendingSet()", + (Supplier) () -> {return Collections.checkedNavigableMap(new TreeMap().descendingMap(), Integer.class, Integer.class);}} + ); } } diff --git a/test/java/util/Collections/CheckedSetBash.java b/test/java/util/Collections/CheckedSetBash.java --- a/test/java/util/Collections/CheckedSetBash.java +++ b/test/java/util/Collections/CheckedSetBash.java @@ -23,82 +23,93 @@ /* * @test - * @bug 4904067 + * @bug 4904067 7129185 * @summary Unit test for Collections.checkedSet * @author Josh Bloch + * @run testng CheckedSetBash */ import java.util.*; +import java.util.function.Supplier; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertTrue; public class CheckedSetBash { - static Random rnd = new Random(); + static final int numItr = 100; + static final int setSize = 100; + static final Random rnd = new Random(); - public static void main(String[] args) { - int numItr = 100; - int setSize = 100; + @Test(dataProvider = "Supplier>") + public static void testCheckedSet(String description, Supplier> supplier) { - for (int i=0; i s1 = supplier.get(); + assertTrue(s1.isEmpty()); - Set s2 = newSet(); - AddRandoms(s2, setSize); + AddRandoms(s1, setSize); - Set intersection = clone(s1); - intersection.retainAll(s2); - Set diff1 = clone(s1); diff1.removeAll(s2); - Set diff2 = clone(s2); diff2.removeAll(s1); - Set union = clone(s1); union.addAll(s2); + Set s2 = supplier.get(); - if (diff1.removeAll(diff2)) - fail("Set algebra identity 2 failed"); - if (diff1.removeAll(intersection)) - fail("Set algebra identity 3 failed"); - if (diff2.removeAll(diff1)) - fail("Set algebra identity 4 failed"); - if (diff2.removeAll(intersection)) - fail("Set algebra identity 5 failed"); - if (intersection.removeAll(diff1)) - fail("Set algebra identity 6 failed"); - if (intersection.removeAll(diff1)) - fail("Set algebra identity 7 failed"); + assertTrue(s2.isEmpty()); - intersection.addAll(diff1); intersection.addAll(diff2); - if (!intersection.equals(union)) - fail("Set algebra identity 1 failed"); + AddRandoms(s2, setSize); - if (new HashSet(union).hashCode() != union.hashCode()) - fail("Incorrect hashCode computation."); + Set intersection = clone(s1, supplier); + intersection.retainAll(s2); + Set diff1 = clone(s1, supplier); diff1.removeAll(s2); + Set diff2 = clone(s2, supplier); diff2.removeAll(s1); + Set union = clone(s1, supplier); union.addAll(s2); - Iterator e = union.iterator(); - while (e.hasNext()) - if (!intersection.remove(e.next())) - fail("Couldn't remove element from copy."); - if (!intersection.isEmpty()) - fail("Copy nonempty after deleting all elements."); + if (diff1.removeAll(diff2)) + fail("Set algebra identity 2 failed"); + if (diff1.removeAll(intersection)) + fail("Set algebra identity 3 failed"); + if (diff2.removeAll(diff1)) + fail("Set algebra identity 4 failed"); + if (diff2.removeAll(intersection)) + fail("Set algebra identity 5 failed"); + if (intersection.removeAll(diff1)) + fail("Set algebra identity 6 failed"); + if (intersection.removeAll(diff1)) + fail("Set algebra identity 7 failed"); - e = union.iterator(); - while (e.hasNext()) { - Object o = e.next(); - if (!union.contains(o)) - fail("Set doesn't contain one of its elements."); - e.remove(); - if (union.contains(o)) - fail("Set contains element after deletion."); - } - if (!union.isEmpty()) - fail("Set nonempty after deleting all elements."); + intersection.addAll(diff1); intersection.addAll(diff2); + if (!intersection.equals(union)) + fail("Set algebra identity 1 failed"); - s1.clear(); - if (!s1.isEmpty()) - fail("Set nonempty after clear."); + if (new HashSet(union).hashCode() != union.hashCode()) + fail("Incorrect hashCode computation."); + + Iterator e = union.iterator(); + while (e.hasNext()) + if (!intersection.remove(e.next())) + fail("Couldn't remove element from copy."); + if (!intersection.isEmpty()) + fail("Copy nonempty after deleting all elements."); + + e = union.iterator(); + while (e.hasNext()) { + Object o = e.next(); + if (!union.contains(o)) + fail("Set doesn't contain one of its elements."); + e.remove(); + if (union.contains(o)) + fail("Set contains element after deletion."); } + if (!union.isEmpty()) + fail("Set nonempty after deleting all elements."); + + s1.clear(); + if (!s1.isEmpty()) + fail("Set nonempty after clear."); } // Done inefficiently so as to exercise toArray - static Set clone(Set s) { - Set clone = newSet(); - List arrayList = Arrays.asList(s.toArray()); + static Set clone(Set s, Supplier> supplier) { + Set clone = supplier.get(); + List arrayList = Arrays.asList((T[]) s.toArray()); clone.addAll(arrayList); if (!s.equals(clone)) fail("Set not equal to copy."); @@ -109,13 +120,6 @@ return clone; } - static Set newSet() { - Set s = Collections.checkedSet(new HashSet(), Integer.class); - if (!s.isEmpty()) - fail("New instance non empty."); - return s; - } - static void AddRandoms(Set s, int n) { for (int i=0; i navigableSetsProvider() { + ArrayList iters = new ArrayList<>(makeCheckedSets()); + iters.ensureCapacity(numItr * iters.size()); + for(int each=1; each < numItr; each++) { + iters.addAll( makeCheckedSets()); + } + return iters.iterator(); + } + public static Collection makeCheckedSets() { + return Arrays.asList( + new Object[]{"Collections.checkedSet(HashSet)", + (Supplier) () -> {return Collections.checkedSet(new HashSet(), Integer.class);}}, + new Object[]{"Collections.checkedSet(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedSet(new TreeSet(Collections.reverseOrder()), Integer.class);}}, + new Object[]{"Collections.checkedSet(TreeSet).descendingSet()", + (Supplier) () -> {return Collections.checkedSet(new TreeSet().descendingSet(), Integer.class);}}, + new Object[]{"Collections.checkedNavigableSet(TreeSet)", + (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet(), Integer.class);}}, + new Object[]{"Collections.checkedNavigableSet(TreeSet(reverseOrder)", + (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet(Collections.reverseOrder()), Integer.class);}}, + new Object[]{"Collections.checkedNavigableSet().descendingSet()", + (Supplier) () -> {return Collections.checkedNavigableSet(new TreeSet().descendingSet(), Integer.class);}} + ); } } diff --git a/test/java/util/Collections/EmptyCollectionSerialization.java b/test/java/util/Collections/EmptyCollectionSerialization.java --- a/test/java/util/Collections/EmptyCollectionSerialization.java +++ b/test/java/util/Collections/EmptyCollectionSerialization.java @@ -23,13 +23,20 @@ /* * @test - * @bug 4684279 + * @bug 4684279 7129185 * @summary Empty utility collections should be singletons * @author Josh Bloch + * @run testng EmptyCollectionSerialization */ import java.util.*; +import java.util.function.Supplier; import java.io.*; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.fail; +import static org.testng.Assert.assertSame; public class EmptyCollectionSerialization { private static Object patheticDeepCopy(Object o) throws Exception { @@ -45,16 +52,48 @@ return ois.readObject(); } - private static boolean isSingleton(Object o) throws Exception { - return patheticDeepCopy(o) == o; + @Test(dataProvider="SerializableSingletons") + public static void serializableSingletons(String description, Supplier o) { + try { + Object singleton = o.get(); + assertSame(o.get(), singleton, description + ": broken Supplier not returning singleton"); + Object copy = patheticDeepCopy(singleton); + assertSame( copy, singleton, description + ": " + + copy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(copy)) + + " is not the singleton " + + singleton.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(singleton))); + } catch(Exception all) { + fail(description + ": Unexpected Exception", all); + } } - public static void main(String[] args) throws Exception { - if (!isSingleton(Collections.EMPTY_SET)) - throw new Exception("EMPTY_SET"); - if (!isSingleton(Collections.EMPTY_LIST)) - throw new Exception("EMPTY_LIST"); - if (!isSingleton(Collections.EMPTY_MAP)) - throw new Exception("EMPTY_MAP"); + @DataProvider(name = "SerializableSingletons", parallel = true) + public static Iterator navigableMapProvider() { + return makeSingletons().iterator(); + } + + public static Collection makeSingletons() { + return Arrays.asList( + new Object[]{"Collections.EMPTY_LIST", + (Supplier) () -> {return Collections.EMPTY_LIST;}}, + new Object[]{"Collections.EMPTY_MAP", + (Supplier) () -> {return Collections.EMPTY_MAP;}}, + new Object[]{"Collections.EMPTY_SET", + (Supplier) () -> {return Collections.EMPTY_SET;}}, + new Object[]{"Collections.singletonMap()", + (Supplier) () -> {return Collections.emptyList();}}, + new Object[]{"Collections.emptyMap()", + (Supplier) () -> {return Collections.emptyMap();}}, + new Object[]{"Collections.emptySet()", + (Supplier) () -> {return Collections.emptySet();}}, + new Object[]{"Collections.emptySortedSet()", + (Supplier) () -> {return Collections.emptySortedSet();}}, + new Object[]{"Collections.emptySortedMap()", + (Supplier) () -> {return Collections.emptySortedMap();}}, + new Object[]{"Collections.emptyNavigableSet()", + (Supplier) () -> {return Collections.emptyNavigableSet();}}, + new Object[]{"Collections.emptyNavigableMap()", + (Supplier) () -> {return Collections.emptyNavigableMap();}} + ); } } diff --git a/test/java/util/Collections/EmptySortedSet.java b/test/java/util/Collections/EmptyNavigableMap.java rename from test/java/util/Collections/EmptySortedSet.java rename to test/java/util/Collections/EmptyNavigableMap.java --- a/test/java/util/Collections/EmptySortedSet.java +++ b/test/java/util/Collections/EmptyNavigableMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, 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 @@ -23,329 +23,351 @@ /* * @test - * @bug 4533691 - * @summary Unit test for Collections.emptySortedSet + * @bug 4533691 7129185 + * @summary Unit test for Collections.emptyNavigableMap + * @run testng EmptyNavigableMap */ - -import java.lang.reflect.Method; import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.NavigableMap; +import java.util.SortedMap; +import java.util.TreeMap; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; -public class EmptySortedSet { - static int status = 0; - private static final String FAILED = " failed. "; - private static final String PERIOD = "."; - private final String thisClassName = this.getClass().getName(); +import static org.testng.Assert.fail; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertSame; - public static void main(String[] args) throws Exception { - new EmptySortedSet(); +public class EmptyNavigableMap { + + public static void assertInstance(T actual, Class expected) { + assertInstance(expected.isInstance(actual), null); } - public EmptySortedSet() throws Exception { - run(); + public static void assertInstance(T actual, Class expected, String message) { + assertTrue(expected.isInstance(actual), ((null != message) ? message : "") + + " " + (actual == null ? "" : actual.getClass().getSimpleName()) + " != " + expected.getSimpleName() + ". "); } - /** - * Returns {@code true} if the {@link Object} passed in is an empty - * {@link SortedSet}. - * - * @param obj the object to test - * @return {@code true} if the {@link Object} is an empty {@link SortedSet} - * otherwise {@code false}. - */ - private boolean isEmptySortedSet(Object obj) { - boolean isEmptySortedSet = false; + public static void assertEmptyNavigableMap(Object obj) { + assertInstance(obj, NavigableMap.class); + assertTrue(((NavigableMap)obj).isEmpty() && (((NavigableMap)obj).size() == 0)); + } - // We determine if the object is an empty sorted set by testing if it's - // an instance of SortedSet, and if so, if it's empty. Currently the - // testing doesn't include checks of the other methods. - if (obj instanceof SortedSet) { - SortedSet ss = (SortedSet) obj; + public static void assertEmptyNavigableMap(Object obj, String message) { + assertInstance(obj, NavigableMap.class, message); + assertTrue(((NavigableMap)obj).isEmpty() && (((NavigableMap)obj).size() == 0), + ((null != message) ? message : "") + " Not empty. "); + } - if ((ss.isEmpty()) && (ss.size() == 0)) { - isEmptySortedSet = true; - } + public interface Thrower { + + public void run() throws T; + } + + public static void assertThrows(Thrower thrower, Class throwable) { + assertThrows(thrower, throwable, null); + } + + public static void assertThrows(Thrower thrower, Class throwable, String message) { + Throwable result; + try { + thrower.run(); + fail(((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); + return; + } catch (Throwable caught) { + result = caught; } - return isEmptySortedSet; + assertInstance(result, throwable, ((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); } - private void run() throws Exception { - Method[] methods = this.getClass().getDeclaredMethods(); + public static final boolean isDescending(SortedMap set) { + if (null == set.comparator()) { + // natural order + return false; + } - for (int i = 0; i < methods.length; i++) { - Method method = methods[i]; - String methodName = method.getName(); + if (Collections.reverseOrder() == set.comparator()) { + // reverse natural order. + return true; + } - if (methodName.startsWith("test")) { - try { - Object obj = method.invoke(this, new Object[0]); - } catch(Exception e) { - throw new Exception(this.getClass().getName() + "." + - methodName + " test failed, test exception " - + "follows\n" + e.getCause()); - } - } + if (set.comparator().equals(Collections.reverseOrder(Collections.reverseOrder(set.comparator())))) { + // it's a Collections.reverseOrder(Comparator). + return true; } - } - private void throwException(String methodName, String reason) - throws Exception - { - StringBuilder sb = new StringBuilder(thisClassName); - sb.append(PERIOD); - sb.append(methodName); - sb.append(FAILED); - sb.append(reason); - throw new Exception(sb.toString()); - } - - /** - * - */ - private void test00() throws Exception { - //throwException("test00", "This test has not been implemented yet."); + throw new IllegalStateException("can't determine ordering for " + set); } /** * Tests that the comparator is {@code null}. */ - private void testComparatorIsNull() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Comparator comparator = sortedSet.comparator(); + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testComparatorIsNull(String description, NavigableMap navigableMap) { + Comparator comparator = navigableMap.comparator(); - if (comparator != null) { - throwException("testComparatorIsNull", "Comparator is not null."); - } + assertTrue(comparator == null || comparator == Collections.reverseOrder(), description + ": Comparator (" + comparator + ") is not null."); + } + + /** + * Tests that contains requires Comparable + */ + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testContainsRequiresComparable(String description, NavigableMap navigableMap) { + assertThrows(() -> { + navigableMap.containsKey(new Object()); + }, + ClassCastException.class, + description + ": Compareable should be required"); } /** * Tests that the contains method returns {@code false}. */ - private void testContains() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - if (sortedSet.contains(new Object())) { - throwException("testContains", "Should not contain any elements."); - } + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testContains(String description, NavigableMap navigableMap) { + assertFalse(navigableMap.containsKey(new Integer(1)), + description + ": Should not contain any elements."); + assertFalse(navigableMap.containsValue(new Integer(1)), + description + ": Should not contain any elements."); } /** * Tests that the containsAll method returns {@code false}. */ - private void testContainsAll() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - TreeSet treeSet = new TreeSet(); - treeSet.add("1"); - treeSet.add("2"); - treeSet.add("3"); + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testContainsAll(String description, NavigableMap navigableMap) { + TreeMap treeMap = new TreeMap(); + treeMap.put("1", 1); + treeMap.put("2", 2); + treeMap.put("3", 3); - if (sortedSet.containsAll(treeSet)) { - throwException("testContainsAll", - "Should not contain any elements."); - } + assertFalse(navigableMap.equals(treeMap), "Should not contain any elements."); } /** * Tests that the iterator is empty. */ - private void testEmptyIterator() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Iterator emptyIterator = sortedSet.iterator(); - - if ((emptyIterator != null) && (emptyIterator.hasNext())) { - throwException("testEmptyIterator", "The iterator is not empty."); - } + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testEmptyIterator(String description, NavigableMap navigableMap) { + assertFalse(navigableMap.keySet().iterator().hasNext(), "The iterator is not empty."); + assertFalse(navigableMap.values().iterator().hasNext(), "The iterator is not empty."); + assertFalse(navigableMap.entrySet().iterator().hasNext(), "The iterator is not empty."); } /** * Tests that the set is empty. */ - private void testIsEmpty() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - if ((sortedSet != null) && (!sortedSet.isEmpty())) { - throwException("testSizeIsZero", "The set is not empty."); - } + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testIsEmpty(String description, NavigableMap navigableMap) { + assertTrue(navigableMap.isEmpty(), "The set is not empty."); } /** - * Tests that the first() method throws NoSuchElementException + * Tests the headMap() method. */ - private void testFirst() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testHeadMap(String description, NavigableMap navigableMap) { + assertThrows( + () -> { NavigableMap ss = navigableMap.headMap(null, false); }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - sortedSet.first(); - throwException("testFirst", - "NoSuchElemenException was not thrown."); - } catch(NoSuchElementException nsee) { - // Do nothing - } - } + assertThrows( + () -> { NavigableMap ss = navigableMap.headMap(new Object(), true); }, + ClassCastException.class, + description + ": Must throw ClassCastException for non-Comparable element"); - /** - * Tests the headSet() method. - */ - private void testHeadSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss; + NavigableMap ss = navigableMap.headMap("1", false); - try { - ss = sortedSet.headSet(null); - throwException("testHeadSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } - - try { - ss = sortedSet.headSet(new Object()); - throwException("testHeadSet", - "Must throw ClassCastException for non-Comparable element"); - } catch(ClassCastException cce) { - // Do nothing. - } - - ss = sortedSet.headSet("1"); - - if ((ss == null) || !isEmptySortedSet(ss)) { - throwException("testHeadSet", - "Returned value is null or not an EmptySortedSet."); - } - } - - /** - * Tests that the last() method throws NoSuchElementException - */ - private void testLast() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - try { - sortedSet.last(); - throwException("testLast", - "NoSuchElemenException was not thrown."); - } catch(NoSuchElementException nsee) { - // Do nothing - } + assertEmptyNavigableMap(ss, description + ": Returned value is not empty navigable set."); } /** * Tests that the size is 0. */ - private void testSizeIsZero() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - int size = sortedSet.size(); - - if (size > 0) { - throwException("testSizeIsZero", - "The size of the set is greater then 0."); - } + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testSizeIsZero(String description, NavigableMap navigableMap) { + assertTrue(0 == navigableMap.size(), "The size of the set is not 0."); } /** - * Tests the subSet() method. + * Tests the subMap() method. */ - private void testSubSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss = sortedSet.headSet("1"); + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testSubMap(String description, NavigableMap navigableMap) { + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(null, BigInteger.TEN); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.subSet(null, BigInteger.TEN); - ss = sortedSet.subSet(BigInteger.ZERO, null); - ss = sortedSet.subSet(null, null); - throwException("testSubSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(BigInteger.ZERO, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - Object obj1 = new Object(); - Object obj2 = new Object(); - ss = sortedSet.subSet(obj1, BigInteger.TEN); - ss = sortedSet.subSet(BigInteger.ZERO, obj2); - ss = sortedSet.subSet(obj1, obj2); - throwException("testSubSet", - "Must throw ClassCastException for parameter which is " - + "not Comparable."); - } catch(ClassCastException cce) { - // Do nothing. - } + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(null, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.ZERO); - ss = sortedSet.subSet(BigInteger.TEN, BigInteger.ZERO); - throwException("testSubSet", - "Must throw IllegalArgumentException when fromElement is " - + "not less then then toElement."); - } catch(IllegalArgumentException iae) { - // Do nothing. - } + Object obj1 = new Object(); + Object obj2 = new Object(); - ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.TEN); + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(obj1, BigInteger.TEN); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); - if (!isEmptySortedSet(ss)) { - throw new Exception("Returned value is not empty sorted set."); - } + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(BigInteger.ZERO, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + assertThrows( + () -> { + SortedMap ss = navigableMap.subMap(obj1, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + // minimal range + navigableMap.subMap(BigInteger.ZERO, false, BigInteger.ZERO, false); + navigableMap.subMap(BigInteger.ZERO, false, BigInteger.ZERO, true); + navigableMap.subMap(BigInteger.ZERO, true, BigInteger.ZERO, false); + navigableMap.subMap(BigInteger.ZERO, true, BigInteger.ZERO, true); + + Object first = isDescending(navigableMap) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + assertThrows( + () -> { + navigableMap.subMap(last, true, first, false); + }, + IllegalArgumentException.class, description + + ": Must throw IllegalArgumentException when fromElement is not less then then toElement."); + + navigableMap.subMap(first, true, last, false); + } + + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testSubMapRanges(String description, NavigableMap navigableMap) { + Object first = isDescending(navigableMap) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + NavigableMap subMap = navigableMap.subMap(first, true, last, true); + + // same subset + subMap.subMap(first, true, last, true); + + // slightly smaller + NavigableMap ns = subMap.subMap(first, false, last, false); + // slight exapansion + assertThrows(() -> { + ns.subMap(first, true, last, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subMap.subMap(first, false, BigInteger.ONE, false); + } + + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testheadMapRanges(String description, NavigableMap navigableMap) { + NavigableMap subMap = navigableMap.headMap(BigInteger.ONE, true); + + // same subset + subMap.headMap(BigInteger.ONE, true); + + // slightly smaller + NavigableMap ns = subMap.headMap(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.headMap(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subMap.headMap(isDescending(subMap) ? BigInteger.TEN : BigInteger.ZERO, true); + } + + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testTailMapRanges(String description, NavigableMap navigableMap) { + NavigableMap subMap = navigableMap.tailMap(BigInteger.ONE, true); + + // same subset + subMap.tailMap(BigInteger.ONE, true); + + // slightly smaller + NavigableMap ns = subMap.tailMap(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.tailMap(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subMap.tailMap(isDescending(subMap) ? BigInteger.ZERO : BigInteger.TEN, false); } /** - * Tests the tailSet() method. + * Tests the tailMap() method. */ - private void testTailSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss; + @Test(dataProvider = "NavigableMap", dataProviderClass = EmptyNavigableMap.class) + public void testTailMap(String description, NavigableMap navigableMap) { + assertThrows(() -> { + navigableMap.tailMap(null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.tailSet(null); - throwException("testTailSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } + assertThrows(() -> { + navigableMap.tailMap(new Object()); + }, ClassCastException.class); - try { - SortedSet ss2 = sortedSet.tailSet(new Object()); - throwException("testTailSet", - "Must throw ClassCastException for non-Comparable element"); - } catch(ClassCastException cce) { - // Do nothing. - } + NavigableMap ss = navigableMap.tailMap("1", true); - ss = sortedSet.tailSet("1"); - - if ((ss == null) || !isEmptySortedSet(ss)) { - throwException("testTailSet", - "Returned value is null or not an EmptySortedSet."); - } + assertEmptyNavigableMap(ss, description + ": Returned value is not empty navigable set."); } - /** - * Tests that the array has a size of 0. - */ - private void testToArray() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Object[] emptySortedSetArray = sortedSet.toArray(); + @DataProvider(name = "NavigableMap", parallel = true) + public static Iterator navigableMapsProvider() { + return makeNavigableMaps().iterator(); + } - if ((emptySortedSetArray == null) || (emptySortedSetArray.length > 0)) { - throwException("testToArray", - "Returned null array or array with length > 0."); - } - - String[] strings = new String[2]; - strings[0] = "1"; - strings[1] = "2"; - emptySortedSetArray = sortedSet.toArray(strings); - - if ((emptySortedSetArray == null) || (emptySortedSetArray[0] != null)) { - throwException("testToArray", - "Returned null array or array with length > 0."); - } + public static Collection makeNavigableMaps() { + return Arrays.asList( + new Object[]{"UnmodifiableNavigableMap(TreeMap)", Collections.unmodifiableNavigableMap(new TreeMap())}, + new Object[]{"UnmodifiableNavigableMap(TreeMap.descendingMap()", Collections.unmodifiableNavigableMap(new TreeMap().descendingMap())}, + new Object[]{"UnmodifiableNavigableMap(TreeMap.descendingMap().descendingMap()", Collections.unmodifiableNavigableMap(new TreeMap().descendingMap().descendingMap())}, + new Object[]{"emptyNavigableMap()", Collections.emptyNavigableMap()}, + new Object[]{"emptyNavigableMap().descendingMap()", Collections.emptyNavigableMap().descendingMap()}, + new Object[]{"emptyNavigableMap().descendingMap().descendingMap()", Collections.emptyNavigableMap().descendingMap().descendingMap()} + ); } } diff --git a/test/java/util/Collections/EmptySortedSet.java b/test/java/util/Collections/EmptyNavigableSet.java copy from test/java/util/Collections/EmptySortedSet.java copy to test/java/util/Collections/EmptyNavigableSet.java --- a/test/java/util/Collections/EmptySortedSet.java +++ b/test/java/util/Collections/EmptyNavigableSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, 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 @@ -23,329 +23,388 @@ /* * @test - * @bug 4533691 - * @summary Unit test for Collections.emptySortedSet + * @bug 4533691 7129185 + * @summary Unit test for Collections.emptyNavigableSet + * @run testng EmptyNavigableSet */ - -import java.lang.reflect.Method; import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.NavigableSet; import java.util.SortedSet; import java.util.TreeSet; +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; -public class EmptySortedSet { - static int status = 0; - private static final String FAILED = " failed. "; - private static final String PERIOD = "."; - private final String thisClassName = this.getClass().getName(); +import static org.testng.Assert.fail; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertSame; - public static void main(String[] args) throws Exception { - new EmptySortedSet(); +public class EmptyNavigableSet { + + public static void assertInstance(T actual, Class expected) { + assertInstance(expected.isInstance(actual), null); } - public EmptySortedSet() throws Exception { - run(); + public static void assertInstance(T actual, Class expected, String message) { + assertTrue(expected.isInstance(actual), ((null != message) ? message : "") + + " " + (actual == null ? "" : actual.getClass().getSimpleName()) + " != " + expected.getSimpleName() + ". "); } - /** - * Returns {@code true} if the {@link Object} passed in is an empty - * {@link SortedSet}. - * - * @param obj the object to test - * @return {@code true} if the {@link Object} is an empty {@link SortedSet} - * otherwise {@code false}. - */ - private boolean isEmptySortedSet(Object obj) { - boolean isEmptySortedSet = false; + public static void assertEmptyNavigableSet(Object obj) { + assertInstance(obj, NavigableSet.class); + assertTrue(((NavigableSet)obj).isEmpty() && (((NavigableSet)obj).size() == 0)); + } - // We determine if the object is an empty sorted set by testing if it's - // an instance of SortedSet, and if so, if it's empty. Currently the - // testing doesn't include checks of the other methods. - if (obj instanceof SortedSet) { - SortedSet ss = (SortedSet) obj; + public static void assertEmptyNavigableSet(Object obj, String message) { + assertInstance(obj, NavigableSet.class, message); + assertTrue(((NavigableSet)obj).isEmpty() && (((NavigableSet)obj).size() == 0), + ((null != message) ? message : "") + " Not empty. "); + } - if ((ss.isEmpty()) && (ss.size() == 0)) { - isEmptySortedSet = true; - } + public interface Thrower { + + public void run() throws T; + } + + public static void assertThrows(Thrower thrower, Class throwable) { + assertThrows(thrower, throwable, null); + } + + public static void assertThrows(Thrower thrower, Class throwable, String message) { + Throwable result; + try { + thrower.run(); + fail(((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); + return; + } catch (Throwable caught) { + result = caught; } - return isEmptySortedSet; + assertInstance(result, throwable, ((null != message) ? message : "") + "Failed to throw " + throwable.getCanonicalName() + ". "); } - private void run() throws Exception { - Method[] methods = this.getClass().getDeclaredMethods(); + public static final boolean isDescending(SortedSet set) { + if (null == set.comparator()) { + // natural order + return false; + } - for (int i = 0; i < methods.length; i++) { - Method method = methods[i]; - String methodName = method.getName(); + if (Collections.reverseOrder() == set.comparator()) { + // reverse natural order. + return true; + } - if (methodName.startsWith("test")) { - try { - Object obj = method.invoke(this, new Object[0]); - } catch(Exception e) { - throw new Exception(this.getClass().getName() + "." + - methodName + " test failed, test exception " - + "follows\n" + e.getCause()); - } - } + if (set.comparator().equals(Collections.reverseOrder(Collections.reverseOrder(set.comparator())))) { + // it's a Collections.reverseOrder(Comparator). + return true; } - } - private void throwException(String methodName, String reason) - throws Exception - { - StringBuilder sb = new StringBuilder(thisClassName); - sb.append(PERIOD); - sb.append(methodName); - sb.append(FAILED); - sb.append(reason); - throw new Exception(sb.toString()); - } - - /** - * - */ - private void test00() throws Exception { - //throwException("test00", "This test has not been implemented yet."); + throw new IllegalStateException("can't determine ordering for " + set); } /** * Tests that the comparator is {@code null}. */ - private void testComparatorIsNull() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Comparator comparator = sortedSet.comparator(); + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testComparatorIsNull(String description, NavigableSet navigableSet) { + Comparator comparator = navigableSet.comparator(); - if (comparator != null) { - throwException("testComparatorIsNull", "Comparator is not null."); - } + assertTrue(comparator == null || comparator == Collections.reverseOrder(), description + ": Comparator (" + comparator + ") is not null."); + } + + /** + * Tests that contains requires Comparable + */ + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testContainsRequiresComparable(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.contains(new Object()); + }, + ClassCastException.class, + description + ": Compareable should be required"); } /** * Tests that the contains method returns {@code false}. */ - private void testContains() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - if (sortedSet.contains(new Object())) { - throwException("testContains", "Should not contain any elements."); - } + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testContains(String description, NavigableSet navigableSet) { + assertFalse(navigableSet.contains(new Integer(1)), + description + ": Should not contain any elements."); } /** * Tests that the containsAll method returns {@code false}. */ - private void testContainsAll() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testContainsAll(String description, NavigableSet navigableSet) { TreeSet treeSet = new TreeSet(); treeSet.add("1"); treeSet.add("2"); treeSet.add("3"); - if (sortedSet.containsAll(treeSet)) { - throwException("testContainsAll", - "Should not contain any elements."); - } + assertFalse(navigableSet.containsAll(treeSet), "Should not contain any elements."); } /** * Tests that the iterator is empty. */ - private void testEmptyIterator() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Iterator emptyIterator = sortedSet.iterator(); + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testEmptyIterator(String description, NavigableSet navigableSet) { + Iterator emptyIterator = navigableSet.iterator(); - if ((emptyIterator != null) && (emptyIterator.hasNext())) { - throwException("testEmptyIterator", "The iterator is not empty."); - } + assertFalse((emptyIterator != null) && (emptyIterator.hasNext()), + "The iterator is not empty."); } /** * Tests that the set is empty. */ - private void testIsEmpty() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - if ((sortedSet != null) && (!sortedSet.isEmpty())) { - throwException("testSizeIsZero", "The set is not empty."); - } + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testIsEmpty(String description, NavigableSet navigableSet) { + assertTrue(navigableSet.isEmpty(), "The set is not empty."); } /** * Tests that the first() method throws NoSuchElementException */ - private void testFirst() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - try { - sortedSet.first(); - throwException("testFirst", - "NoSuchElemenException was not thrown."); - } catch(NoSuchElementException nsee) { - // Do nothing - } + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testFirst(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.first(); + }, NoSuchElementException.class, description); } /** * Tests the headSet() method. */ - private void testHeadSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss; + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testHeadSet(String description, NavigableSet navigableSet) { + assertThrows( + () -> { NavigableSet ns = navigableSet.headSet(null, false); }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.headSet(null); - throwException("testHeadSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } + assertThrows( + () -> { NavigableSet ns = navigableSet.headSet(new Object(), true); }, + ClassCastException.class, + description + ": Must throw ClassCastException for non-Comparable element"); - try { - ss = sortedSet.headSet(new Object()); - throwException("testHeadSet", - "Must throw ClassCastException for non-Comparable element"); - } catch(ClassCastException cce) { - // Do nothing. - } + NavigableSet ns = navigableSet.headSet("1", false); - ss = sortedSet.headSet("1"); - - if ((ss == null) || !isEmptySortedSet(ss)) { - throwException("testHeadSet", - "Returned value is null or not an EmptySortedSet."); - } + assertEmptyNavigableSet(ns, description + ": Returned value is not empty navigable set."); } /** * Tests that the last() method throws NoSuchElementException */ - private void testLast() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - - try { - sortedSet.last(); - throwException("testLast", - "NoSuchElemenException was not thrown."); - } catch(NoSuchElementException nsee) { - // Do nothing - } + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testLast(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.last(); + }, NoSuchElementException.class, description); } /** * Tests that the size is 0. */ - private void testSizeIsZero() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - int size = sortedSet.size(); - - if (size > 0) { - throwException("testSizeIsZero", - "The size of the set is greater then 0."); - } + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testSizeIsZero(String description, NavigableSet navigableSet) { + assertTrue(0 == navigableSet.size(), "The size of the set is not 0."); } /** * Tests the subSet() method. */ - private void testSubSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss = sortedSet.headSet("1"); + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testSubSet(String description, NavigableSet navigableSet) { + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(null, BigInteger.TEN); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.subSet(null, BigInteger.TEN); - ss = sortedSet.subSet(BigInteger.ZERO, null); - ss = sortedSet.subSet(null, null); - throwException("testSubSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(BigInteger.ZERO, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - Object obj1 = new Object(); - Object obj2 = new Object(); - ss = sortedSet.subSet(obj1, BigInteger.TEN); - ss = sortedSet.subSet(BigInteger.ZERO, obj2); - ss = sortedSet.subSet(obj1, obj2); - throwException("testSubSet", - "Must throw ClassCastException for parameter which is " - + "not Comparable."); - } catch(ClassCastException cce) { - // Do nothing. - } + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(null, null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.ZERO); - ss = sortedSet.subSet(BigInteger.TEN, BigInteger.ZERO); - throwException("testSubSet", - "Must throw IllegalArgumentException when fromElement is " - + "not less then then toElement."); - } catch(IllegalArgumentException iae) { - // Do nothing. - } + Object obj1 = new Object(); + Object obj2 = new Object(); - ss = sortedSet.subSet(BigInteger.ZERO, BigInteger.TEN); + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(obj1, BigInteger.TEN); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); - if (!isEmptySortedSet(ss)) { - throw new Exception("Returned value is not empty sorted set."); - } + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(BigInteger.ZERO, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + assertThrows( + () -> { + SortedSet ss = navigableSet.subSet(obj1, obj2); + }, + ClassCastException.class, description + + ": Must throw ClassCastException for parameter which is not Comparable."); + + // minimal range + navigableSet.subSet(BigInteger.ZERO, false, BigInteger.ZERO, false); + navigableSet.subSet(BigInteger.ZERO, false, BigInteger.ZERO, true); + navigableSet.subSet(BigInteger.ZERO, true, BigInteger.ZERO, false); + navigableSet.subSet(BigInteger.ZERO, true, BigInteger.ZERO, true); + + Object first = isDescending(navigableSet) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + assertThrows( + () -> { + navigableSet.subSet(last, true, first, false); + }, + IllegalArgumentException.class, description + + ": Must throw IllegalArgumentException when fromElement is not less then then toElement."); + + navigableSet.subSet(first, true, last, false); + } + + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testSubSetRanges(String description, NavigableSet navigableSet) { + Object first = isDescending(navigableSet) ? BigInteger.TEN : BigInteger.ZERO; + Object last = (BigInteger.ZERO == first) ? BigInteger.TEN : BigInteger.ZERO; + + NavigableSet subSet = navigableSet.subSet(first, true, last, true); + + // same subset + subSet.subSet(first, true, last, true); + + // slightly smaller + NavigableSet ns = subSet.subSet(first, false, last, false); + // slight exapansion + assertThrows(() -> { + ns.subSet(first, true, last, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subSet.subSet(first, false, BigInteger.ONE, false); + } + + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testheadSetRanges(String description, NavigableSet navigableSet) { + NavigableSet subSet = navigableSet.headSet(BigInteger.ONE, true); + + // same subset + subSet.headSet(BigInteger.ONE, true); + + // slightly smaller + NavigableSet ns = subSet.headSet(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.headSet(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subSet.headSet(isDescending(subSet) ? BigInteger.TEN : BigInteger.ZERO, true); + } + + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testTailSetRanges(String description, NavigableSet navigableSet) { + NavigableSet subSet = navigableSet.tailSet(BigInteger.ONE, true); + + // same subset + subSet.tailSet(BigInteger.ONE, true); + + // slightly smaller + NavigableSet ns = subSet.tailSet(BigInteger.ONE, false); + + // slight exapansion + assertThrows(() -> { + ns.tailSet(BigInteger.ONE, true); + }, + IllegalArgumentException.class, + description + ": Expansion should not be allowed"); + + // much smaller + subSet.tailSet(isDescending(subSet) ? BigInteger.ZERO : BigInteger.TEN, false); } /** * Tests the tailSet() method. */ - private void testTailSet() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - SortedSet ss; + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testTailSet(String description, NavigableSet navigableSet) { + assertThrows(() -> { + navigableSet.tailSet(null); + }, + NullPointerException.class, + description + ": Must throw NullPointerException for null element"); - try { - ss = sortedSet.tailSet(null); - throwException("testTailSet", - "Must throw NullPointerException for null element"); - } catch(NullPointerException npe) { - // Do nothing - } + assertThrows(() -> { + navigableSet.tailSet(new Object()); + }, ClassCastException.class); - try { - SortedSet ss2 = sortedSet.tailSet(new Object()); - throwException("testTailSet", - "Must throw ClassCastException for non-Comparable element"); - } catch(ClassCastException cce) { - // Do nothing. - } + NavigableSet ss = navigableSet.tailSet("1", true); - ss = sortedSet.tailSet("1"); - - if ((ss == null) || !isEmptySortedSet(ss)) { - throwException("testTailSet", - "Returned value is null or not an EmptySortedSet."); - } + assertEmptyNavigableSet(ss, description + ": Returned value is not empty navigable set."); } /** * Tests that the array has a size of 0. */ - private void testToArray() throws Exception { - SortedSet sortedSet = Collections.emptySortedSet(); - Object[] emptySortedSetArray = sortedSet.toArray(); + @Test(dataProvider = "NavigableSet", dataProviderClass = EmptyNavigableSet.class) + public void testToArray(String description, NavigableSet navigableSet) { + Object[] emptyNavigableSetArray = navigableSet.toArray(); - if ((emptySortedSetArray == null) || (emptySortedSetArray.length > 0)) { - throwException("testToArray", - "Returned null array or array with length > 0."); - } + assertTrue(emptyNavigableSetArray.length == 0, "Returned non-empty Array."); - String[] strings = new String[2]; - strings[0] = "1"; - strings[1] = "2"; - emptySortedSetArray = sortedSet.toArray(strings); + emptyNavigableSetArray = new Object[20]; - if ((emptySortedSetArray == null) || (emptySortedSetArray[0] != null)) { - throwException("testToArray", - "Returned null array or array with length > 0."); - } + Object[] result = navigableSet.toArray(emptyNavigableSetArray); + + assertSame(emptyNavigableSetArray, result); + + assertTrue(result[0] == null); + } + + @DataProvider(name = "NavigableSet", parallel = true) + public static Iterator navigableSetsProvider() { + return makeNavigableSets().iterator(); + } + + public static Collection makeNavigableSets() { + return Arrays.asList( + new Object[]{"UnmodifiableNavigableSet(TreeSet)", Collections.unmodifiableNavigableSet(new TreeSet())}, + new Object[]{"UnmodifiableNavigableSet(TreeSet.descendingSet()", Collections.unmodifiableNavigableSet(new TreeSet().descendingSet())}, + new Object[]{"UnmodifiableNavigableSet(TreeSet.descendingSet().descendingSet()", Collections.unmodifiableNavigableSet(new TreeSet().descendingSet().descendingSet())}, + new Object[]{"emptyNavigableSet()", Collections.emptyNavigableSet()}, + new Object[]{"emptyNavigableSet().descendingSet()", Collections.emptyNavigableSet().descendingSet()}, + new Object[]{"emptyNavigableSet().descendingSet().descendingSet()", Collections.emptyNavigableSet().descendingSet().descendingSet()} + ); } } diff --git a/test/java/util/NavigableMap/LockStep.java b/test/java/util/NavigableMap/LockStep.java --- a/test/java/util/NavigableMap/LockStep.java +++ b/test/java/util/NavigableMap/LockStep.java @@ -55,11 +55,19 @@ lockSteps(new TreeMap(), new ConcurrentSkipListMap()); + lockSteps(new TreeMap(), + Collections.checkedNavigableMap(new TreeMap(), Integer.class, Integer.class)); + lockSteps(new TreeMap(), + Collections.synchronizedNavigableMap(new TreeMap())); lockSteps(new TreeMap(reverseOrder()), new ConcurrentSkipListMap(reverseOrder())); lockSteps(new TreeSet(), new ConcurrentSkipListSet()); + lockSteps(new TreeSet(), + Collections.checkedNavigableSet(new TreeSet(), Integer.class)); + lockSteps(new TreeSet(), + Collections.synchronizedNavigableSet(new TreeSet())); lockSteps(new TreeSet(reverseOrder()), new ConcurrentSkipListSet(reverseOrder())); } @@ -181,7 +189,15 @@ testEmptyCollection(m.values()); } - static final Random rnd = new Random(); + static final Random rnd; + + static { + // sufficiently random for this test + long seed = System.nanoTime(); + System.out.println(LockStep.class.getCanonicalName() + ": Trial random seed: " + seed ); + + rnd = new Random(seed); + } static void equalNext(final Iterator it, Object expected) { if (maybe(2)) @@ -208,8 +224,15 @@ check(s.descendingSet().descendingSet().comparator() == null); equal(s.isEmpty(), s.size() == 0); equal2(s, s.descendingSet()); - if (maybe(4) && s instanceof Serializable) - equal2(s, serialClone(s)); + if (maybe(4) && s instanceof Serializable) { + try { + equal2(s, serialClone(s)); + } catch(RuntimeException uhoh) { + if(!(uhoh.getCause() instanceof NotSerializableException)) { + throw uhoh; + } + } + } Comparator cmp = comparator(s); if (s.isEmpty()) { THROWS(NoSuchElementException.class, @@ -276,6 +299,15 @@ check(! it2.hasNext()); } + static void equalSetsLeaf(final Set s1, final Set s2) { + equal2(s1, s2); + equal( s1.size(), s2.size()); + equal( s1.isEmpty(), s2.isEmpty()); + equal( s1.hashCode(), s2.hashCode()); + equal( s1.toString(), s2.toString()); + equal( s1.containsAll(s2), s2.containsAll(s1)); + } + static void equalNavigableSetsLeaf(final NavigableSet s1, final NavigableSet s2) { equal2(s1, s2); @@ -448,8 +480,7 @@ static void equalNavigableMaps(NavigableMap m1, NavigableMap m2) { equalNavigableMapsLeaf(m1, m2); - equalNavigableSetsLeaf((NavigableSet) m1.keySet(), - (NavigableSet) m2.keySet()); + equalSetsLeaf(m1.keySet(), m2.keySet()); equalNavigableSets(m1.navigableKeySet(), m2.navigableKeySet()); equalNavigableSets(m1.descendingKeySet(), @@ -836,5 +867,13 @@ @SuppressWarnings("unchecked") static T serialClone(T obj) { try { return (T) readObject(serializedForm(obj)); } - catch (Exception e) { throw new RuntimeException(e); }} + catch (Throwable e) { + if (e instanceof Error) { + throw (Error) e; + } else if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } else { + throw new RuntimeException(e); + } + }} }