diff options
Diffstat (limited to 'libjava/classpath/java/util/Collections.java')
-rw-r--r-- | libjava/classpath/java/util/Collections.java | 5493 |
1 files changed, 5493 insertions, 0 deletions
diff --git a/libjava/classpath/java/util/Collections.java b/libjava/classpath/java/util/Collections.java new file mode 100644 index 0000000..ed8a005 --- /dev/null +++ b/libjava/classpath/java/util/Collections.java @@ -0,0 +1,5493 @@ +/* Collections.java -- Utility class with methods to operate on collections + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.util; + +import java.io.Serializable; + +/** + * Utility class consisting of static methods that operate on, or return + * Collections. Contains methods to sort, search, reverse, fill and shuffle + * Collections, methods to facilitate interoperability with legacy APIs that + * are unaware of collections, a method to return a list which consists of + * multiple copies of one element, and methods which "wrap" collections to give + * them extra properties, such as thread-safety and unmodifiability. + * <p> + * + * All methods which take a collection throw a {@link NullPointerException} if + * that collection is null. Algorithms which can change a collection may, but + * are not required, to throw the {@link UnsupportedOperationException} that + * the underlying collection would throw during an attempt at modification. + * For example, + * <code>Collections.singleton("").addAll(Collections.EMPTY_SET)</code> + * does not throw a exception, even though addAll is an unsupported operation + * on a singleton; the reason for this is that addAll did not attempt to + * modify the set. + * + * @author Original author unknown + * @author Eric Blake (ebb9@email.byu.edu) + * @see Collection + * @see Set + * @see List + * @see Map + * @see Arrays + * @since 1.2 + * @status updated to 1.4 + */ +public class Collections +{ + /** + * Constant used to decide cutoff for when a non-RandomAccess list should + * be treated as sequential-access. Basically, quadratic behavior is + * acceptable for small lists when the overhead is so small in the first + * place. I arbitrarily set it to 16, so it may need some tuning. + */ + private static final int LARGE_LIST_SIZE = 16; + + /** + * Determines if a list should be treated as a sequential-access one. + * Rather than the old method of JDK 1.3 of assuming only instanceof + * AbstractSequentialList should be sequential, this uses the new method + * of JDK 1.4 of assuming anything that does NOT implement RandomAccess + * and exceeds a large (unspecified) size should be sequential. + * + * @param l the list to check + * @return <code>true</code> if it should be treated as sequential-access + */ + private static boolean isSequential(List l) + { + return ! (l instanceof RandomAccess) && l.size() > LARGE_LIST_SIZE; + } + + /** + * This class is non-instantiable. + */ + private Collections() + { + } + + /** + * An immutable, serializable, empty Set. + * @see Serializable + */ + public static final Set EMPTY_SET = new EmptySet(); + + /** + * The implementation of {@link #EMPTY_SET}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptySet extends AbstractSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1582296315990362920L; + + /** + * A private constructor adds overhead. + */ + EmptySet() + { + } + + /** + * The size: always 0! + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * Returns an iterator that does not iterate. + * @return A non-iterating iterator. + */ + // This is really cheating! I think it's perfectly valid, though. + public Iterator iterator() + { + return EMPTY_LIST.iterator(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractSet. + /** + * The empty set never contains anything. + * @param o The object to search for. + * @return <code>false</code>. + */ + public boolean contains(Object o) + { + return false; + } + + /** + * This is true only if the given collection is also empty. + * @param c The collection of objects which are to be compared + * against the members of this set. + * @return <code>true</code> if c is empty. + */ + public boolean containsAll(Collection c) + { + return c.isEmpty(); + } + + /** + * Equal only if the other set is empty. + * @param o The object to compare with this set. + * @return <code>true</code> if o is an empty instance of <code>Set</code>. + */ + public boolean equals(Object o) + { + return o instanceof Set && ((Set) o).isEmpty(); + } + + /** + * The hashcode is always 0. + * @return 0. + */ + public int hashCode() + { + return 0; + } + + /** + * Always succeeds with a <code>false</code> result. + * @param o The object to remove. + * @return <code>false</code>. + */ + public boolean remove(Object o) + { + return false; + } + + /** + * Always succeeds with a <code>false</code> result. + * @param c The collection of objects which should + * all be removed from this set. + * @return <code>false</code>. + */ + public boolean removeAll(Collection c) + { + return false; + } + + /** + * Always succeeds with a <code>false</code> result. + * @param c The collection of objects which should + * all be retained within this set. + * @return <code>false</code>. + */ + public boolean retainAll(Collection c) + { + return false; + } + + /** + * The array is always empty. + * @return A new array with a size of 0. + */ + public Object[] toArray() + { + return new Object[0]; + } + + /** + * We don't even need to use reflection! + * @param a An existing array, which can be empty. + * @return The original array with any existing + * initial element set to null. + */ + public Object[] toArray(Object[] a) + { + if (a.length > 0) + a[0] = null; + return a; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptySet + + /** + * An immutable, serializable, empty List, which implements RandomAccess. + * @see Serializable + * @see RandomAccess + */ + public static final List EMPTY_LIST = new EmptyList(); + + /** + * The implementation of {@link #EMPTY_LIST}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptyList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8842843931221139166L; + + /** + * A private constructor adds overhead. + */ + EmptyList() + { + } + + /** + * The size is always 0. + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * No matter the index, it is out of bounds. This + * method never returns, throwing an exception instead. + * + * @param index The index of the element to retrieve. + * @return the object at the specified index. + * @throws IndexOutOfBoundsException as any given index + * is outside the bounds of an empty array. + */ + public Object get(int index) + { + throw new IndexOutOfBoundsException(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * Never contains anything. + * @param o The object to search for. + * @return <code>false</code>. + */ + public boolean contains(Object o) + { + return false; + } + + /** + * This is true only if the given collection is also empty. + * @param c The collection of objects, which should be compared + * against the members of this list. + * @return <code>true</code> if c is also empty. + */ + public boolean containsAll(Collection c) + { + return c.isEmpty(); + } + + /** + * Equal only if the other list is empty. + * @param o The object to compare against this list. + * @return <code>true</code> if o is also an empty instance of + * <code>List</code>. + */ + public boolean equals(Object o) + { + return o instanceof List && ((List) o).isEmpty(); + } + + /** + * The hashcode is always 1. + * @return 1. + */ + public int hashCode() + { + return 1; + } + + /** + * Returns -1. + * @param o The object to search for. + * @return -1. + */ + public int indexOf(Object o) + { + return -1; + } + + /** + * Returns -1. + * @param o The object to search for. + * @return -1. + */ + public int lastIndexOf(Object o) + { + return -1; + } + + /** + * Always succeeds with <code>false</code> result. + * @param o The object to remove. + * @return -1. + */ + public boolean remove(Object o) + { + return false; + } + + /** + * Always succeeds with <code>false</code> result. + * @param c The collection of objects which should + * all be removed from this list. + * @return <code>false</code>. + */ + public boolean removeAll(Collection c) + { + return false; + } + + /** + * Always succeeds with <code>false</code> result. + * @param c The collection of objects which should + * all be retained within this list. + * @return <code>false</code>. + */ + public boolean retainAll(Collection c) + { + return false; + } + + /** + * The array is always empty. + * @return A new array with a size of 0. + */ + public Object[] toArray() + { + return new Object[0]; + } + + /** + * We don't even need to use reflection! + * @param a An existing array, which can be empty. + * @return The original array with any existing + * initial element set to null. + */ + public Object[] toArray(Object[] a) + { + if (a.length > 0) + a[0] = null; + return a; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptyList + + /** + * An immutable, serializable, empty Map. + * @see Serializable + */ + public static final Map EMPTY_MAP = new EmptyMap(); + + /** + * The implementation of {@link #EMPTY_MAP}. This class name is required + * for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class EmptyMap extends AbstractMap + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 6428348081105594320L; + + /** + * A private constructor adds overhead. + */ + EmptyMap() + { + } + + /** + * There are no entries. + * @return The empty set. + */ + public Set entrySet() + { + return EMPTY_SET; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractMap. + /** + * No entries! + * @param key The key to search for. + * @return <code>false</code>. + */ + public boolean containsKey(Object key) + { + return false; + } + + /** + * No entries! + * @param value The value to search for. + * @return <code>false</code>. + */ + public boolean containsValue(Object value) + { + return false; + } + + /** + * Equal to all empty maps. + * @param o The object o to compare against this map. + * @return <code>true</code> if o is also an empty instance of + * <code>Map</code>. + */ + public boolean equals(Object o) + { + return o instanceof Map && ((Map) o).isEmpty(); + } + + /** + * No mappings, so this returns null. + * @param o The key of the object to retrieve. + * @return null. + */ + public Object get(Object o) + { + return null; + } + + /** + * The hashcode is always 0. + * @return 0. + */ + public int hashCode() + { + return 0; + } + + /** + * No entries. + * @return The empty set. + */ + public Set keySet() + { + return EMPTY_SET; + } + + /** + * Remove always succeeds, with null result. + * @param o The key of the mapping to remove. + * @return null, as there is never a mapping for o. + */ + public Object remove(Object o) + { + return null; + } + + /** + * Size is always 0. + * @return 0. + */ + public int size() + { + return 0; + } + + /** + * No entries. Technically, EMPTY_SET, while more specific than a general + * Collection, will work. Besides, that's what the JDK uses! + * @return The empty set. + */ + public Collection values() + { + return EMPTY_SET; + } + + /** + * The string never changes. + * + * @return the string "[]". + */ + public String toString() + { + return "[]"; + } + } // class EmptyMap + + + /** + * Compare two objects with or without a Comparator. If c is null, uses the + * natural ordering. Slightly slower than doing it inline if the JVM isn't + * clever, but worth it for removing a duplicate of the search code. + * Note: This code is also used in Arrays (for sort as well as search). + */ + static final int compare(Object o1, Object o2, Comparator c) + { + return c == null ? ((Comparable) o1).compareTo(o2) : c.compare(o1, o2); + } + + /** + * Perform a binary search of a List for a key, using the natural ordering of + * the elements. The list must be sorted (as by the sort() method) - if it is + * not, the behavior of this method is undefined, and may be an infinite + * loop. Further, the key must be comparable with every item in the list. If + * the list contains the key more than once, any one of them may be found. + * <p> + * + * This algorithm behaves in log(n) time for {@link RandomAccess} lists, + * and uses a linear search with O(n) link traversals and log(n) comparisons + * with {@link AbstractSequentialList} lists. Note: although the + * specification allows for an infinite loop if the list is unsorted, it will + * not happen in this (Classpath) implementation. + * + * @param l the list to search (must be sorted) + * @param key the value to search for + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value + * @throws ClassCastException if key could not be compared with one of the + * elements of l + * @throws NullPointerException if a null element has compareTo called + * @see #sort(List) + */ + public static int binarySearch(List l, Object key) + { + return binarySearch(l, key, null); + } + + /** + * Perform a binary search of a List for a key, using a supplied Comparator. + * The list must be sorted (as by the sort() method with the same Comparator) + * - if it is not, the behavior of this method is undefined, and may be an + * infinite loop. Further, the key must be comparable with every item in the + * list. If the list contains the key more than once, any one of them may be + * found. If the comparator is null, the elements' natural ordering is used. + * <p> + * + * This algorithm behaves in log(n) time for {@link RandomAccess} lists, + * and uses a linear search with O(n) link traversals and log(n) comparisons + * with {@link AbstractSequentialList} lists. Note: although the + * specification allows for an infinite loop if the list is unsorted, it will + * not happen in this (Classpath) implementation. + * + * @param l the list to search (must be sorted) + * @param key the value to search for + * @param c the comparator by which the list is sorted + * @return the index at which the key was found, or -n-1 if it was not + * found, where n is the index of the first value higher than key or + * a.length if there is no such value + * @throws ClassCastException if key could not be compared with one of the + * elements of l + * @throws NullPointerException if a null element is compared with natural + * ordering (only possible when c is null) + * @see #sort(List, Comparator) + */ + public static int binarySearch(List l, Object key, Comparator c) + { + int pos = 0; + int low = 0; + int hi = l.size() - 1; + + // We use a linear search with log(n) comparisons using an iterator + // if the list is sequential-access. + if (isSequential(l)) + { + ListIterator itr = l.listIterator(); + int i = 0; + Object o = itr.next(); // Assumes list is not empty (see isSequential) + boolean forward = true; + while (low <= hi) + { + pos = (low + hi) >> 1; + if (i < pos) + { + if (!forward) + itr.next(); // Changing direction first. + for ( ; i != pos; i++, o = itr.next()); + forward = true; + } + else + { + if (forward) + itr.previous(); // Changing direction first. + for ( ; i != pos; i--, o = itr.previous()); + forward = false; + } + final int d = compare(key, o, c); + if (d == 0) + return pos; + else if (d < 0) + hi = pos - 1; + else + // This gets the insertion point right on the last loop + low = ++pos; + } + } + else + { + while (low <= hi) + { + pos = (low + hi) >> 1; + final int d = compare(key, l.get(pos), c); + if (d == 0) + return pos; + else if (d < 0) + hi = pos - 1; + else + // This gets the insertion point right on the last loop + low = ++pos; + } + } + + // If we failed to find it, we do the same whichever search we did. + return -pos - 1; + } + + /** + * Copy one list to another. If the destination list is longer than the + * source list, the remaining elements are unaffected. This method runs in + * linear time. + * + * @param dest the destination list + * @param source the source list + * @throws IndexOutOfBoundsException if the destination list is shorter + * than the source list (the destination will be unmodified) + * @throws UnsupportedOperationException if dest.listIterator() does not + * support the set operation + */ + public static void copy(List dest, List source) + { + int pos = source.size(); + if (dest.size() < pos) + throw new IndexOutOfBoundsException("Source does not fit in dest"); + + Iterator i1 = source.iterator(); + ListIterator i2 = dest.listIterator(); + + while (--pos >= 0) + { + i2.next(); + i2.set(i1.next()); + } + } + + /** + * Returns an Enumeration over a collection. This allows interoperability + * with legacy APIs that require an Enumeration as input. + * + * @param c the Collection to iterate over + * @return an Enumeration backed by an Iterator over c + */ + public static Enumeration enumeration(Collection c) + { + final Iterator i = c.iterator(); + return new Enumeration() + { + /** + * Returns <code>true</code> if there are more elements to + * be enumerated. + * + * @return The result of <code>hasNext()</code> + * called on the underlying iterator. + */ + public final boolean hasMoreElements() + { + return i.hasNext(); + } + + /** + * Returns the next element to be enumerated. + * + * @return The result of <code>next()</code> + * called on the underlying iterator. + */ + public final Object nextElement() + { + return i.next(); + } + }; + } + + /** + * Replace every element of a list with a given value. This method runs in + * linear time. + * + * @param l the list to fill. + * @param val the object to vill the list with. + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation. + */ + public static void fill(List l, Object val) + { + ListIterator itr = l.listIterator(); + for (int i = l.size() - 1; i >= 0; --i) + { + itr.next(); + itr.set(val); + } + } + + /** + * Returns the starting index where the specified sublist first occurs + * in a larger list, or -1 if there is no matching position. If + * <code>target.size() > source.size()</code>, this returns -1, + * otherwise this implementation uses brute force, checking for + * <code>source.sublist(i, i + target.size()).equals(target)</code> + * for all possible i. + * + * @param source the list to search + * @param target the sublist to search for + * @return the index where found, or -1 + * @since 1.4 + */ + public static int indexOfSubList(List source, List target) + { + int ssize = source.size(); + for (int i = 0, j = target.size(); j <= ssize; i++, j++) + if (source.subList(i, j).equals(target)) + return i; + return -1; + } + + /** + * Returns the starting index where the specified sublist last occurs + * in a larger list, or -1 if there is no matching position. If + * <code>target.size() > source.size()</code>, this returns -1, + * otherwise this implementation uses brute force, checking for + * <code>source.sublist(i, i + target.size()).equals(target)</code> + * for all possible i. + * + * @param source the list to search + * @param target the sublist to search for + * @return the index where found, or -1 + * @since 1.4 + */ + public static int lastIndexOfSubList(List source, List target) + { + int ssize = source.size(); + for (int i = ssize - target.size(), j = ssize; i >= 0; i--, j--) + if (source.subList(i, j).equals(target)) + return i; + return -1; + } + + /** + * Returns an ArrayList holding the elements visited by a given + * Enumeration. This method exists for interoperability between legacy + * APIs and the new Collection API. + * + * @param e the enumeration to put in a list + * @return a list containing the enumeration elements + * @see ArrayList + * @since 1.4 + */ + public static ArrayList list(Enumeration e) + { + ArrayList l = new ArrayList(); + while (e.hasMoreElements()) + l.add(e.nextElement()); + return l; + } + + /** + * Find the maximum element in a Collection, according to the natural + * ordering of the elements. This implementation iterates over the + * Collection, so it works in linear time. + * + * @param c the Collection to find the maximum element of + * @return the maximum element of c + * @exception NoSuchElementException if c is empty + * @exception ClassCastException if elements in c are not mutually comparable + * @exception NullPointerException if null.compareTo is called + */ + public static Object max(Collection c) + { + return max(c, null); + } + + /** + * Find the maximum element in a Collection, according to a specified + * Comparator. This implementation iterates over the Collection, so it + * works in linear time. + * + * @param c the Collection to find the maximum element of + * @param order the Comparator to order the elements by, or null for natural + * ordering + * @return the maximum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when order is null) + */ + public static Object max(Collection c, Comparator order) + { + Iterator itr = c.iterator(); + Object max = itr.next(); // throws NoSuchElementException + int csize = c.size(); + for (int i = 1; i < csize; i++) + { + Object o = itr.next(); + if (compare(max, o, order) < 0) + max = o; + } + return max; + } + + /** + * Find the minimum element in a Collection, according to the natural + * ordering of the elements. This implementation iterates over the + * Collection, so it works in linear time. + * + * @param c the Collection to find the minimum element of + * @return the minimum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null.compareTo is called + */ + public static Object min(Collection c) + { + return min(c, null); + } + + /** + * Find the minimum element in a Collection, according to a specified + * Comparator. This implementation iterates over the Collection, so it + * works in linear time. + * + * @param c the Collection to find the minimum element of + * @param order the Comparator to order the elements by, or null for natural + * ordering + * @return the minimum element of c + * @throws NoSuchElementException if c is empty + * @throws ClassCastException if elements in c are not mutually comparable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when order is null) + */ + public static Object min(Collection c, Comparator order) + { + Iterator itr = c.iterator(); + Object min = itr.next(); // throws NoSuchElementExcception + int csize = c.size(); + for (int i = 1; i < csize; i++) + { + Object o = itr.next(); + if (compare(min, o, order) > 0) + min = o; + } + return min; + } + + /** + * Creates an immutable list consisting of the same object repeated n times. + * The returned object is tiny, consisting of only a single reference to the + * object and a count of the number of elements. It is Serializable, and + * implements RandomAccess. You can use it in tandem with List.addAll for + * fast list construction. + * + * @param n the number of times to repeat the object + * @param o the object to repeat + * @return a List consisting of n copies of o + * @throws IllegalArgumentException if n < 0 + * @see List#addAll(Collection) + * @see Serializable + * @see RandomAccess + */ + public static List nCopies(final int n, final Object o) + { + return new CopiesList(n, o); + } + + /** + * The implementation of {@link #nCopies(int, Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class CopiesList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 2739099268398711800L; + + /** + * The count of elements in this list. + * @serial the list size + */ + private final int n; + + /** + * The repeated list element. + * @serial the list contents + */ + private final Object element; + + /** + * Constructs the list. + * + * @param n the count + * @param o the object + * @throws IllegalArgumentException if n < 0 + */ + CopiesList(int n, Object o) + { + if (n < 0) + throw new IllegalArgumentException(); + this.n = n; + element = o; + } + + /** + * The size is fixed. + * @return The size of the list. + */ + public int size() + { + return n; + } + + /** + * The same element is returned. + * @param index The index of the element to be returned (irrelevant + * as the list contains only copies of <code>element</code>). + * @return The element used by this list. + */ + public Object get(int index) + { + if (index < 0 || index >= n) + throw new IndexOutOfBoundsException(); + return element; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * This list only contains one element. + * @param o The object to search for. + * @return <code>true</code> if o is the element used by this list. + */ + public boolean contains(Object o) + { + return n > 0 && equals(o, element); + } + + /** + * The index is either 0 or -1. + * @param o The object to find the index of. + * @return 0 if <code>o == element</code>, -1 if not. + */ + public int indexOf(Object o) + { + return (n > 0 && equals(o, element)) ? 0 : -1; + } + + /** + * The index is either n-1 or -1. + * @param o The object to find the last index of. + * @return The last index in the list if <code>o == element</code>, + * -1 if not. + */ + public int lastIndexOf(Object o) + { + return equals(o, element) ? n - 1 : -1; + } + + /** + * A subList is just another CopiesList. + * @param from The starting bound of the sublist. + * @param to The ending bound of the sublist. + * @return A list of copies containing <code>from - to</code> + * elements, all of which are equal to the element + * used by this list. + */ + public List subList(int from, int to) + { + if (from < 0 || to > n) + throw new IndexOutOfBoundsException(); + return new CopiesList(to - from, element); + } + + /** + * The array is easy. + * @return An array of size n filled with copies of + * the element used by this list. + */ + public Object[] toArray() + { + Object[] a = new Object[n]; + Arrays.fill(a, element); + return a; + } + + /** + * The string is easy to generate. + * @return A string representation of the list. + */ + public String toString() + { + StringBuffer r = new StringBuffer("{"); + for (int i = n - 1; --i > 0; ) + r.append(element).append(", "); + r.append(element).append("}"); + return r.toString(); + } + } // class CopiesList + + /** + * Replace all instances of one object with another in the specified list. + * The list does not change size. An element e is replaced if + * <code>oldval == null ? e == null : oldval.equals(e)</code>. + * + * @param list the list to iterate over + * @param oldval the element to replace + * @param newval the new value for the element + * @return <code>true</code> if a replacement occurred. + * @throws UnsupportedOperationException if the list iterator does not allow + * for the set operation + * @throws ClassCastException if newval is of a type which cannot be added + * to the list + * @throws IllegalArgumentException if some other aspect of newval stops + * it being added to the list + * @since 1.4 + */ + public static boolean replaceAll(List list, Object oldval, Object newval) + { + ListIterator itr = list.listIterator(); + boolean replace_occured = false; + for (int i = list.size(); --i >= 0; ) + if (AbstractCollection.equals(oldval, itr.next())) + { + itr.set(newval); + replace_occured = true; + } + return replace_occured; + } + + /** + * Reverse a given list. This method works in linear time. + * + * @param l the list to reverse + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void reverse(List l) + { + ListIterator i1 = l.listIterator(); + int pos1 = 1; + int pos2 = l.size(); + ListIterator i2 = l.listIterator(pos2); + while (pos1 < pos2) + { + Object o = i1.next(); + i1.set(i2.previous()); + i2.set(o); + ++pos1; + --pos2; + } + } + + /** + * Get a comparator that implements the reverse of natural ordering. In + * other words, this sorts Comparable objects opposite of how their + * compareTo method would sort. This makes it easy to sort into reverse + * order, by simply passing Collections.reverseOrder() to the sort method. + * The return value of this method is Serializable. + * + * @return a comparator that imposes reverse natural ordering + * @see Comparable + * @see Serializable + */ + public static Comparator reverseOrder() + { + return rcInstance; + } + + /** + * The object for {@link #reverseOrder()}. + */ + private static final ReverseComparator rcInstance = new ReverseComparator(); + + /** + * The implementation of {@link #reverseOrder()}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class ReverseComparator + implements Comparator, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7207038068494060240L; + + /** + * A private constructor adds overhead. + */ + ReverseComparator() + { + } + + /** + * Compare two objects in reverse natural order. + * + * @param a the first object + * @param b the second object + * @return <, ==, or > 0 according to b.compareTo(a) + */ + public int compare(Object a, Object b) + { + return ((Comparable) b).compareTo(a); + } + } + + /** + * Rotate the elements in a list by a specified distance. After calling this + * method, the element now at index <code>i</code> was formerly at index + * <code>(i - distance) mod list.size()</code>. The list size is unchanged. + * <p> + * + * For example, suppose a list contains <code>[t, a, n, k, s]</code>. After + * either <code>Collections.rotate(l, 4)</code> or + * <code>Collections.rotate(l, -1)</code>, the new contents are + * <code>[s, t, a, n, k]</code>. This can be applied to sublists to rotate + * just a portion of the list. For example, to move element <code>a</code> + * forward two positions in the original example, use + * <code>Collections.rotate(l.subList(1, 3+1), -1)</code>, which will + * result in <code>[t, n, k, a, s]</code>. + * <p> + * + * If the list is small or implements {@link RandomAccess}, the + * implementation exchanges the first element to its destination, then the + * displaced element, and so on until a circuit has been completed. The + * process is repeated if needed on the second element, and so forth, until + * all elements have been swapped. For large non-random lists, the + * implementation breaks the list into two sublists at index + * <code>-distance mod size</code>, calls {@link #reverse(List)} on the + * pieces, then reverses the overall list. + * + * @param list the list to rotate + * @param distance the distance to rotate by; unrestricted in value + * @throws UnsupportedOperationException if the list does not support set + * @since 1.4 + */ + public static void rotate(List list, int distance) + { + int size = list.size(); + if (size == 0) + return; + distance %= size; + if (distance == 0) + return; + if (distance < 0) + distance += size; + + if (isSequential(list)) + { + reverse(list); + reverse(list.subList(0, distance)); + reverse(list.subList(distance, size)); + } + else + { + // Determine the least common multiple of distance and size, as there + // are (distance / LCM) loops to cycle through. + int a = size; + int lcm = distance; + int b = a % lcm; + while (b != 0) + { + a = lcm; + lcm = b; + b = a % lcm; + } + + // Now, make the swaps. We must take the remainder every time through + // the inner loop so that we don't overflow i to negative values. + while (--lcm >= 0) + { + Object o = list.get(lcm); + for (int i = lcm + distance; i != lcm; i = (i + distance) % size) + o = list.set(i, o); + list.set(lcm, o); + } + } + } + + /** + * Shuffle a list according to a default source of randomness. The algorithm + * used iterates backwards over the list, swapping each element with an + * element randomly selected from the elements in positions less than or + * equal to it (using r.nextInt(int)). + * <p> + * + * This algorithm would result in a perfectly fair shuffle (that is, each + * element would have an equal chance of ending up in any position) if r were + * a perfect source of randomness. In practice the results are merely very + * close to perfect. + * <p> + * + * This method operates in linear time. To do this on large lists which do + * not implement {@link RandomAccess}, a temporary array is used to acheive + * this speed, since it would be quadratic access otherwise. + * + * @param l the list to shuffle + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void shuffle(List l) + { + if (defaultRandom == null) + { + synchronized (Collections.class) + { + if (defaultRandom == null) + defaultRandom = new Random(); + } + } + shuffle(l, defaultRandom); + } + + /** + * Cache a single Random object for use by shuffle(List). This improves + * performance as well as ensuring that sequential calls to shuffle() will + * not result in the same shuffle order occurring: the resolution of + * System.currentTimeMillis() is not sufficient to guarantee a unique seed. + */ + private static Random defaultRandom = null; + + /** + * Shuffle a list according to a given source of randomness. The algorithm + * used iterates backwards over the list, swapping each element with an + * element randomly selected from the elements in positions less than or + * equal to it (using r.nextInt(int)). + * <p> + * + * This algorithm would result in a perfectly fair shuffle (that is, each + * element would have an equal chance of ending up in any position) if r were + * a perfect source of randomness. In practise (eg if r = new Random()) the + * results are merely very close to perfect. + * <p> + * + * This method operates in linear time. To do this on large lists which do + * not implement {@link RandomAccess}, a temporary array is used to acheive + * this speed, since it would be quadratic access otherwise. + * + * @param l the list to shuffle + * @param r the source of randomness to use for the shuffle + * @throws UnsupportedOperationException if l.listIterator() does not + * support the set operation + */ + public static void shuffle(List l, Random r) + { + int lsize = l.size(); + ListIterator i = l.listIterator(lsize); + boolean sequential = isSequential(l); + Object[] a = null; // stores a copy of the list for the sequential case + + if (sequential) + a = l.toArray(); + + for (int pos = lsize - 1; pos > 0; --pos) + { + // Obtain a random position to swap with. pos + 1 is used so that the + // range of the random number includes the current position. + int swap = r.nextInt(pos + 1); + + // Swap the desired element. + Object o; + if (sequential) + { + o = a[swap]; + a[swap] = i.previous(); + } + else + o = l.set(swap, i.previous()); + + i.set(o); + } + } + + + /** + * Obtain an immutable Set consisting of a single element. The return value + * of this method is Serializable. + * + * @param o the single element + * @return an immutable Set containing only o + * @see Serializable + */ + public static Set singleton(Object o) + { + return new SingletonSet(o); + } + + /** + * The implementation of {@link #singleton(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonSet extends AbstractSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3193687207550431679L; + + + /** + * The single element; package visible for use in nested class. + * @serial the singleton + */ + final Object element; + + /** + * Construct a singleton. + * @param o the element + */ + SingletonSet(Object o) + { + element = o; + } + + /** + * The size: always 1! + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Returns an iterator over the lone element. + */ + public Iterator iterator() + { + return new Iterator() + { + /** + * Flag to indicate whether or not the element has + * been retrieved. + */ + private boolean hasNext = true; + + /** + * Returns <code>true</code> if elements still remain to be + * iterated through. + * + * @return <code>true</code> if the element has not yet been returned. + */ + public boolean hasNext() + { + return hasNext; + } + + /** + * Returns the element. + * + * @return The element used by this singleton. + * @throws NoSuchElementException if the object + * has already been retrieved. + */ + public Object next() + { + if (hasNext) + { + hasNext = false; + return element; + } + else + throw new NoSuchElementException(); + } + + /** + * Removes the element from the singleton. + * As this set is immutable, this will always + * throw an exception. + * + * @throws UnsupportedOperationException as the + * singleton set doesn't support + * <code>remove()</code>. + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + }; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractSet. + /** + * The set only contains one element. + * + * @param o The object to search for. + * @return <code>true</code> if o == the element of the singleton. + */ + public boolean contains(Object o) + { + return equals(o, element); + } + + /** + * This is true if the other collection only contains the element. + * + * @param c A collection to compare against this singleton. + * @return <code>true</code> if c only contains either no elements or + * elements equal to the element in this singleton. + */ + public boolean containsAll(Collection c) + { + Iterator i = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (! equals(i.next(), element)) + return false; + return true; + } + + /** + * The hash is just that of the element. + * + * @return The hashcode of the element. + */ + public int hashCode() + { + return hashCode(element); + } + + /** + * Returning an array is simple. + * + * @return An array containing the element. + */ + public Object[] toArray() + { + return new Object[] {element}; + } + + /** + * Obvious string. + * + * @return The string surrounded by enclosing + * square brackets. + */ + public String toString() + { + return "[" + element + "]"; + } + } // class SingletonSet + + /** + * Obtain an immutable List consisting of a single element. The return value + * of this method is Serializable, and implements RandomAccess. + * + * @param o the single element + * @return an immutable List containing only o + * @see Serializable + * @see RandomAccess + * @since 1.3 + */ + public static List singletonList(Object o) + { + return new SingletonList(o); + } + + /** + * The implementation of {@link #singletonList(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonList extends AbstractList + implements Serializable, RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3093736618740652951L; + + /** + * The single element. + * @serial the singleton + */ + private final Object element; + + /** + * Construct a singleton. + * @param o the element + */ + SingletonList(Object o) + { + element = o; + } + + /** + * The size: always 1! + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Only index 0 is valid. + * @param index The index of the element + * to retrieve. + * @return The singleton's element if the + * index is 0. + * @throws IndexOutOfBoundsException if + * index is not 0. + */ + public Object get(int index) + { + if (index == 0) + return element; + throw new IndexOutOfBoundsException(); + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractList. + /** + * The set only contains one element. + * + * @param o The object to search for. + * @return <code>true</code> if o == the singleton element. + */ + public boolean contains(Object o) + { + return equals(o, element); + } + + /** + * This is true if the other collection only contains the element. + * + * @param c A collection to compare against this singleton. + * @return <code>true</code> if c only contains either no elements or + * elements equal to the element in this singleton. + */ + public boolean containsAll(Collection c) + { + Iterator i = c.iterator(); + int pos = c.size(); + while (--pos >= 0) + if (! equals(i.next(), element)) + return false; + return true; + } + + /** + * Speed up the hashcode computation. + * + * @return The hashcode of the list, based + * on the hashcode of the singleton element. + */ + public int hashCode() + { + return 31 + hashCode(element); + } + + /** + * Either the list has it or not. + * + * @param o The object to find the first index of. + * @return 0 if o is the singleton element, -1 if not. + */ + public int indexOf(Object o) + { + return equals(o, element) ? 0 : -1; + } + + /** + * Either the list has it or not. + * + * @param o The object to find the last index of. + * @return 0 if o is the singleton element, -1 if not. + */ + public int lastIndexOf(Object o) + { + return equals(o, element) ? 0 : -1; + } + + /** + * Sublists are limited in scope. + * + * @param from The starting bound for the sublist. + * @param to The ending bound for the sublist. + * @return Either an empty list if both bounds are + * 0 or 1, or this list if the bounds are 0 and 1. + * @throws IllegalArgumentException if <code>from > to</code> + * @throws IndexOutOfBoundsException if either bound is greater + * than 1. + */ + public List subList(int from, int to) + { + if (from == to && (to == 0 || to == 1)) + return EMPTY_LIST; + if (from == 0 && to == 1) + return this; + if (from > to) + throw new IllegalArgumentException(); + throw new IndexOutOfBoundsException(); + } + + /** + * Returning an array is simple. + * + * @return An array containing the element. + */ + public Object[] toArray() + { + return new Object[] {element}; + } + + /** + * Obvious string. + * + * @return The string surrounded by enclosing + * square brackets. + */ + public String toString() + { + return "[" + element + "]"; + } + } // class SingletonList + + /** + * Obtain an immutable Map consisting of a single key-value pair. + * The return value of this method is Serializable. + * + * @param key the single key + * @param value the single value + * @return an immutable Map containing only the single key-value pair + * @see Serializable + * @since 1.3 + */ + public static Map singletonMap(Object key, Object value) + { + return new SingletonMap(key, value); + } + + /** + * The implementation of {@link #singletonMap(Object)}. This class name + * is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SingletonMap extends AbstractMap + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -6979724477215052911L; + + /** + * The single key. + * @serial the singleton key + */ + private final Object k; + + /** + * The corresponding value. + * @serial the singleton value + */ + private final Object v; + + /** + * Cache the entry set. + */ + private transient Set entries; + + /** + * Construct a singleton. + * @param key the key + * @param value the value + */ + SingletonMap(Object key, Object value) + { + k = key; + v = value; + } + + /** + * There is a single immutable entry. + * + * @return A singleton containing the map entry. + */ + public Set entrySet() + { + if (entries == null) + entries = singleton(new AbstractMap.BasicMapEntry(k, v) + { + /** + * Sets the value of the map entry to the supplied value. + * An exception is always thrown, as the map is immutable. + * + * @param o The new value. + * @return The old value. + * @throws UnsupportedOperationException as setting the value + * is not supported. + */ + public Object setValue(Object o) + { + throw new UnsupportedOperationException(); + } + }); + return entries; + } + + // The remaining methods are optional, but provide a performance + // advantage by not allocating unnecessary iterators in AbstractMap. + /** + * Single entry. + * + * @param key The key to look for. + * @return <code>true</code> if the key is the same as the one used by + * this map. + */ + public boolean containsKey(Object key) + { + return equals(key, k); + } + + /** + * Single entry. + * + * @param value The value to look for. + * @return <code>true</code> if the value is the same as the one used by + * this map. + */ + public boolean containsValue(Object value) + { + return equals(value, v); + } + + /** + * Single entry. + * + * @param key The key of the value to be retrieved. + * @return The singleton value if the key is the same as the + * singleton key, null otherwise. + */ + public Object get(Object key) + { + return equals(key, k) ? v : null; + } + + /** + * Calculate the hashcode directly. + * + * @return The hashcode computed from the singleton key + * and the singleton value. + */ + public int hashCode() + { + return hashCode(k) ^ hashCode(v); + } + + /** + * Return the keyset. + * + * @return A singleton containing the key. + */ + public Set keySet() + { + if (keys == null) + keys = singleton(k); + return keys; + } + + /** + * The size: always 1! + * + * @return 1. + */ + public int size() + { + return 1; + } + + /** + * Return the values. Technically, a singleton, while more specific than + * a general Collection, will work. Besides, that's what the JDK uses! + * + * @return A singleton containing the value. + */ + public Collection values() + { + if (values == null) + values = singleton(v); + return values; + } + + /** + * Obvious string. + * + * @return A string containing the string representations of the key + * and its associated value. + */ + public String toString() + { + return "{" + k + "=" + v + "}"; + } + } // class SingletonMap + + /** + * Sort a list according to the natural ordering of its elements. The list + * must be modifiable, but can be of fixed size. The sort algorithm is + * precisely that used by Arrays.sort(Object[]), which offers guaranteed + * nlog(n) performance. This implementation dumps the list into an array, + * sorts the array, and then iterates over the list setting each element from + * the array. + * + * @param l the List to sort + * @throws ClassCastException if some items are not mutually comparable + * @throws UnsupportedOperationException if the List is not modifiable + * @throws NullPointerException if some element is null + * @see Arrays#sort(Object[]) + */ + public static void sort(List l) + { + sort(l, null); + } + + /** + * Sort a list according to a specified Comparator. The list must be + * modifiable, but can be of fixed size. The sort algorithm is precisely that + * used by Arrays.sort(Object[], Comparator), which offers guaranteed + * nlog(n) performance. This implementation dumps the list into an array, + * sorts the array, and then iterates over the list setting each element from + * the array. + * + * @param l the List to sort + * @param c the Comparator specifying the ordering for the elements, or + * null for natural ordering + * @throws ClassCastException if c will not compare some pair of items + * @throws UnsupportedOperationException if the List is not modifiable + * @throws NullPointerException if null is compared by natural ordering + * (only possible when c is null) + * @see Arrays#sort(Object[], Comparator) + */ + public static void sort(List l, Comparator c) + { + Object[] a = l.toArray(); + Arrays.sort(a, c); + ListIterator i = l.listIterator(); + for (int pos = 0, alen = a.length; pos < alen; pos++) + { + i.next(); + i.set(a[pos]); + } + } + + /** + * Swaps the elements at the specified positions within the list. Equal + * positions have no effect. + * + * @param l the list to work on + * @param i the first index to swap + * @param j the second index + * @throws UnsupportedOperationException if list.set is not supported + * @throws IndexOutOfBoundsException if either i or j is < 0 or >= + * list.size() + * @since 1.4 + */ + public static void swap(List l, int i, int j) + { + l.set(i, l.set(j, l.get(i))); + } + + + /** + * Returns a synchronized (thread-safe) collection wrapper backed by the + * given collection. Notice that element access through the iterators + * is thread-safe, but if the collection can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * Collection c = Collections.synchronizedCollection(new Collection(...)); + * ... + * synchronized (c) + * { + * Iterator i = c.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * Since the collection might be a List or a Set, and those have incompatible + * equals and hashCode requirements, this relies on Object's implementation + * rather than passing those calls on to the wrapped collection. The returned + * Collection implements Serializable, but can only be serialized if + * the collection it wraps is likewise Serializable. + * + * @param c the collection to wrap + * @return a synchronized view of the collection + * @see Serializable + */ + public static Collection synchronizedCollection(Collection c) + { + return new SynchronizedCollection(c); + } + + /** + * The implementation of {@link #synchronizedCollection(Collection)}. This + * class name is required for compatibility with Sun's JDK serializability. + * Package visible, so that collections such as the one for + * Hashtable.values() can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 3053995032091335093L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * The object to synchronize on. When an instance is created via public + * methods, it will be this; but other uses like SynchronizedMap.values() + * must specify another mutex. Package visible for use by subclasses. + * @serial the lock + */ + final Object mutex; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @throws NullPointerException if c is null + */ + SynchronizedCollection(Collection c) + { + this.c = c; + mutex = this; + if (c == null) + throw new NullPointerException(); + } + + /** + * Called only by trusted code to specify the mutex as well as the + * collection. + * @param sync the mutex + * @param c the collection + */ + SynchronizedCollection(Object sync, Collection c) + { + this.c = c; + mutex = sync; + } + + /** + * Adds the object to the underlying collection, first + * obtaining a lock on the mutex. + * + * @param o The object to add. + * @return <code>true</code> if the collection was modified as a result + * of this action. + * @throws UnsupportedOperationException if this collection does not + * support the add operation. + * @throws ClassCastException if o cannot be added to this collection due + * to its type. + * @throws NullPointerException if o is null and this collection doesn't + * support the addition of null values. + * @throws IllegalArgumentException if o cannot be added to this + * collection for some other reason. + */ + public boolean add(Object o) + { + synchronized (mutex) + { + return c.add(o); + } + } + + /** + * Adds the objects in col to the underlying collection, first + * obtaining a lock on the mutex. + * + * @param col The collection to take the new objects from. + * @return <code>true</code> if the collection was modified as a result + * of this action. + * @throws UnsupportedOperationException if this collection does not + * support the addAll operation. + * @throws ClassCastException if some element of col cannot be added to this + * collection due to its type. + * @throws NullPointerException if some element of col is null and this + * collection does not support the addition of null values. + * @throws NullPointerException if col itself is null. + * @throws IllegalArgumentException if some element of col cannot be added + * to this collection for some other reason. + */ + public boolean addAll(Collection col) + { + synchronized (mutex) + { + return c.addAll(col); + } + } + + /** + * Removes all objects from the underlying collection, + * first obtaining a lock on the mutex. + * + * @throws UnsupportedOperationException if this collection does not + * support the clear operation. + */ + public void clear() + { + synchronized (mutex) + { + c.clear(); + } + } + + /** + * Checks for the existence of o within the underlying + * collection, first obtaining a lock on the mutex. + * + * @param o the element to look for. + * @return <code>true</code> if this collection contains at least one + * element e such that <code>o == null ? e == null : o.equals(e)</code>. + * @throws ClassCastException if the type of o is not a valid type for this + * collection. + * @throws NullPointerException if o is null and this collection doesn't + * support null values. + */ + public boolean contains(Object o) + { + synchronized (mutex) + { + return c.contains(o); + } + } + + /** + * Checks for the existence of each object in cl + * within the underlying collection, first obtaining + * a lock on the mutex. + * + * @param c1 the collection to test for. + * @return <code>true</code> if for every element o in c, contains(o) + * would return <code>true</code>. + * @throws ClassCastException if the type of any element in cl is not a valid + * type for this collection. + * @throws NullPointerException if some element of cl is null and this + * collection does not support null values. + * @throws NullPointerException if cl itself is null. + */ + public boolean containsAll(Collection c1) + { + synchronized (mutex) + { + return c.containsAll(c1); + } + } + + /** + * Returns <code>true</code> if there are no objects in the underlying + * collection. A lock on the mutex is obtained before the + * check is performed. + * + * @return <code>true</code> if this collection contains no elements. + */ + public boolean isEmpty() + { + synchronized (mutex) + { + return c.isEmpty(); + } + } + + /** + * Returns a synchronized iterator wrapper around the underlying + * collection's iterator. A lock on the mutex is obtained before + * retrieving the collection's iterator. + * + * @return An iterator over the elements in the underlying collection, + * which returns each element in any order. + */ + public Iterator iterator() + { + synchronized (mutex) + { + return new SynchronizedIterator(mutex, c.iterator()); + } + } + + /** + * Removes the specified object from the underlying collection, + * first obtaining a lock on the mutex. + * + * @param o The object to remove. + * @return <code>true</code> if the collection changed as a result of this call, that is, + * if the collection contained at least one occurrence of o. + * @throws UnsupportedOperationException if this collection does not + * support the remove operation. + * @throws ClassCastException if the type of o is not a valid type + * for this collection. + * @throws NullPointerException if o is null and the collection doesn't + * support null values. + */ + public boolean remove(Object o) + { + synchronized (mutex) + { + return c.remove(o); + } + } + + /** + * Removes all elements, e, of the underlying + * collection for which <code>col.contains(e)</code> + * returns <code>true</code>. A lock on the mutex is obtained + * before the operation proceeds. + * + * @param col The collection of objects to be removed. + * @return <code>true</code> if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + public boolean removeAll(Collection col) + { + synchronized (mutex) + { + return c.removeAll(col); + } + } + + /** + * Retains all elements, e, of the underlying + * collection for which <code>col.contains(e)</code> + * returns <code>true</code>. That is, every element that doesn't + * exist in col is removed. A lock on the mutex is obtained + * before the operation proceeds. + * + * @param col The collection of objects to be removed. + * @return <code>true</code> if this collection was modified as a result of this call. + * @throws UnsupportedOperationException if this collection does not + * support the removeAll operation. + * @throws ClassCastException if the type of any element in c is not a valid + * type for this collection. + * @throws NullPointerException if some element of c is null and this + * collection does not support removing null values. + * @throws NullPointerException if c itself is null. + */ + public boolean retainAll(Collection col) + { + synchronized (mutex) + { + return c.retainAll(col); + } + } + + /** + * Retrieves the size of the underlying collection. + * A lock on the mutex is obtained before the collection + * is accessed. + * + * @return The size of the collection. + */ + public int size() + { + synchronized (mutex) + { + return c.size(); + } + } + + /** + * Returns an array containing each object within the underlying + * collection. A lock is obtained on the mutex before the collection + * is accessed. + * + * @return An array of objects, matching the collection in size. The + * elements occur in any order. + */ + public Object[] toArray() + { + synchronized (mutex) + { + return c.toArray(); + } + } + + /** + * Copies the elements in the underlying collection to the supplied + * array. If <code>a.length < size()</code>, a new array of the + * same run-time type is created, with a size equal to that of + * the collection. If <code>a.length > size()</code>, then the + * elements from 0 to <code>size() - 1</code> contain the elements + * from this collection. The following element is set to null + * to indicate the end of the collection objects. However, this + * only makes a difference if null is not a permitted value within + * the collection. + * Before the copying takes place, a lock is obtained on the mutex. + * + * @param a An array to copy elements to. + * @return An array containing the elements of the underlying collection. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public Object[] toArray(Object[] a) + { + synchronized (mutex) + { + return c.toArray(a); + } + } + + /** + * Returns a string representation of the underlying collection. + * A lock is obtained on the mutex before the string is created. + * + * @return A string representation of the collection. + */ + public String toString() + { + synchronized (mutex) + { + return c.toString(); + } + } + } // class SynchronizedCollection + + /** + * The implementation of the various iterator methods in the + * synchronized classes. These iterators must "sync" on the same object + * as the collection they iterate over. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SynchronizedIterator implements Iterator + { + /** + * The object to synchronize on. Package visible for use by subclass. + */ + final Object mutex; + + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper, with the specified sync. + * @param sync the mutex + * @param i the wrapped iterator + */ + SynchronizedIterator(Object sync, Iterator i) + { + this.i = i; + mutex = sync; + } + + /** + * Retrieves the next object in the underlying collection. + * A lock is obtained on the mutex before the collection is accessed. + * + * @return The next object in the collection. + * @throws NoSuchElementException if there are no more elements + */ + public Object next() + { + synchronized (mutex) + { + return i.next(); + } + } + + /** + * Returns <code>true</code> if objects can still be retrieved from the iterator + * using <code>next()</code>. A lock is obtained on the mutex before + * the collection is accessed. + * + * @return <code>true</code> if at least one element is still to be returned by + * <code>next()</code>. + */ + public boolean hasNext() + { + synchronized (mutex) + { + return i.hasNext(); + } + } + + /** + * Removes the object that was last returned by <code>next()</code> + * from the underlying collection. Only one call to this method is + * allowed per call to the <code>next()</code> method, and it does + * not affect the value that will be returned by <code>next()</code>. + * Thus, if element n was retrieved from the collection by + * <code>next()</code>, it is this element that gets removed. + * Regardless of whether this takes place or not, element n+1 is + * still returned on the subsequent <code>next()</code> call. + * + * @throws IllegalStateException if next has not yet been called or remove + * has already been called since the last call to next. + * @throws UnsupportedOperationException if this Iterator does not support + * the remove operation. + */ + public void remove() + { + synchronized (mutex) + { + i.remove(); + } + } + } // class SynchronizedIterator + + /** + * Returns a synchronized (thread-safe) list wrapper backed by the + * given list. Notice that element access through the iterators + * is thread-safe, but if the list can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * List l = Collections.synchronizedList(new List(...)); + * ... + * synchronized (l) + * { + * Iterator i = l.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + * + * @param l the list to wrap + * @return a synchronized view of the list + * @see Serializable + * @see RandomAccess + */ + public static List synchronizedList(List l) + { + if (l instanceof RandomAccess) + return new SynchronizedRandomAccessList(l); + return new SynchronizedList(l); + } + + /** + * The implementation of {@link #synchronizedList(List)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. Package visible, so that lists such as Vector.subList() + * can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedList extends SynchronizedCollection + implements List + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -7754090372962971524L; + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + SynchronizedList(List l) + { + super(l); + list = l; + } + + /** + * Called only by trusted code to specify the mutex as well as the list. + * @param sync the mutex + * @param l the list + */ + SynchronizedList(Object sync, List l) + { + super(sync, l); + list = l; + } + + /** + * Insert an element into the underlying list at a given position (optional + * operation). This shifts all existing elements from that position to the + * end one index to the right. This version of add has no return, since it is + * assumed to always succeed if there is no exception. Before the + * addition takes place, a lock is obtained on the mutex. + * + * @param index the location to insert the item + * @param o the object to insert + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws IndexOutOfBoundsException if index < 0 || index > size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + public void add(int index, Object o) + { + synchronized (mutex) + { + list.add(index, o); + } + } + + /** + * Add an element to the end of the underlying list (optional operation). + * If the list imposes restraints on what can be inserted, such as no null + * elements, this should be documented. A lock is obtained on the mutex before + * any of the elements are added. + * + * @param o the object to add + * @return <code>true</code>, as defined by Collection for a modified list + * @throws UnsupportedOperationException if this list does not support the + * add operation + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this list doesn't support + * the addition of null values. + */ + public boolean addAll(int index, Collection c) + { + synchronized (mutex) + { + return list.addAll(index, c); + } + } + + /** + * Tests whether the underlying list is equal to the supplied object. + * The object is deemed to be equal if it is also a <code>List</code> + * of equal size and with the same elements (i.e. each element, e1, + * in list, l1, and each element, e2, in l2, must return <code>true</code> for + * <code>e1 == null ? e2 == null : e1.equals(e2)</code>. Before the + * comparison is made, a lock is obtained on the mutex. + * + * @param o The object to test for equality with the underlying list. + * @return <code>true</code> if o is equal to the underlying list under the above + * definition. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return list.equals(o); + } + } + + /** + * Retrieves the object at the specified index. A lock + * is obtained on the mutex before the list is accessed. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + synchronized (mutex) + { + return list.get(index); + } + } + + /** + * Obtains a hashcode for the underlying list, first obtaining + * a lock on the mutex. The calculation of the hashcode is + * detailed in the documentation for the <code>List</code> + * interface. + * + * @return The hashcode of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + synchronized (mutex) + { + return list.hashCode(); + } + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. A lock is obtained on the mutex before the list is + * accessed. + * + * @param o the object to search for + * @return the least integer n such that <code>o == null ? get(n) == null : + * o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + + public int indexOf(Object o) + { + synchronized (mutex) + { + return list.indexOf(o); + } + } + + /** + * Obtain the last index at which a given object is to be found in this + * underlying list. A lock is obtained on the mutex before the list + * is accessed. + * + * @return the greatest integer n such that <code>o == null ? get(n) == null + * : o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for this list. + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + synchronized (mutex) + { + return list.lastIndexOf(o); + } + } + + /** + * Retrieves a synchronized wrapper around the underlying list's + * list iterator. A lock is obtained on the mutex before the + * list iterator is retrieved. + * + * @return A list iterator over the elements in the underlying list. + * The list iterator allows additional list-specific operations + * to be performed, in addition to those supplied by the + * standard iterator. + */ + public ListIterator listIterator() + { + synchronized (mutex) + { + return new SynchronizedListIterator(mutex, list.listIterator()); + } + } + + /** + * Retrieves a synchronized wrapper around the underlying list's + * list iterator. A lock is obtained on the mutex before the + * list iterator is retrieved. The iterator starts at the + * index supplied, leading to the element at that index being + * the first one returned by <code>next()</code>. Calling + * <code>previous()</code> from this initial position returns + * index - 1. + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from + * @return A list iterator over the elements in the underlying list. + * The list iterator allows additional list-specific operations + * to be performed, in addition to those supplied by the + * standard iterator. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + synchronized (mutex) + { + return new SynchronizedListIterator(mutex, list.listIterator(index)); + } + } + + /** + * Remove the element at a given position in the underlying list (optional + * operation). All remaining elements are shifted to the left to fill the gap. + * A lock on the mutex is obtained before the element is removed. + * + * @param index the position within the list of the object to remove + * @return the object that was removed + * @throws UnsupportedOperationException if this list does not support the + * remove operation + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object remove(int index) + { + synchronized (mutex) + { + return list.remove(index); + } + } + + /** + * Replace an element of the underlying list with another object (optional + * operation). A lock is obtained on the mutex before the element is + * replaced. + * + * @param index the position within this list of the element to be replaced + * @param o the object to replace it with + * @return the object that was replaced + * @throws UnsupportedOperationException if this list does not support the + * set operation. + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + * @throws ClassCastException if o cannot be added to this list due to its + * type + * @throws IllegalArgumentException if o cannot be added to this list for + * some other reason + * @throws NullPointerException if o is null and this + * list does not support null values. + */ + public Object set(int index, Object o) + { + synchronized (mutex) + { + return list.set(index, o); + } + } + + /** + * Obtain a List view of a subsection of the underlying list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. A lock is obtained + * on the mutex before the creation of the sublist. The returned list + * is also synchronized, using the same mutex. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + public List subList(int fromIndex, int toIndex) + { + synchronized (mutex) + { + return new SynchronizedList(mutex, list.subList(fromIndex, toIndex)); + } + } + } // class SynchronizedList + + /** + * The implementation of {@link #synchronizedList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedRandomAccessList + extends SynchronizedList implements RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1530674583602358482L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + SynchronizedRandomAccessList(List l) + { + super(l); + } + + /** + * Called only by trusted code to specify the mutex as well as the + * collection. + * @param sync the mutex + * @param l the list + */ + SynchronizedRandomAccessList(Object sync, List l) + { + super(sync, l); + } + + /** + * Obtain a List view of a subsection of the underlying list, from fromIndex + * (inclusive) to toIndex (exclusive). If the two indices are equal, the + * sublist is empty. The returned list should be modifiable if and only + * if this list is modifiable. Changes to the returned list should be + * reflected in this list. If this list is structurally modified in + * any way other than through the returned list, the result of any subsequent + * operations on the returned list is undefined. A lock is obtained + * on the mutex before the creation of the sublist. The returned list + * is also synchronized, using the same mutex. Random accessibility + * is also extended to the new list. + * + * @param fromIndex the index that the returned list should start from + * (inclusive) + * @param toIndex the index that the returned list should go to (exclusive) + * @return a List backed by a subsection of this list + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex + */ + public List subList(int fromIndex, int toIndex) + { + synchronized (mutex) + { + return new SynchronizedRandomAccessList(mutex, + list.subList(fromIndex, + toIndex)); + } + } + } // class SynchronizedRandomAccessList + + /** + * The implementation of {@link SynchronizedList#listIterator()}. This + * iterator must "sync" on the same object as the list it iterates over. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedListIterator + extends SynchronizedIterator implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper, with the specified sync. + * @param sync the mutex + * @param li the wrapped iterator + */ + SynchronizedListIterator(Object sync, ListIterator li) + { + super(sync, li); + this.li = li; + } + + /** + * Insert an element into the underlying list at the current position of + * the iterator (optional operation). The element is inserted in between + * the element that would be returned by <code>previous()</code> and the + * element that would be returned by <code>next()</code>. After the + * insertion, a subsequent call to next is unaffected, but + * a call to previous returns the item that was added. The values returned + * by nextIndex() and previousIndex() are incremented. A lock is obtained + * on the mutex before the addition takes place. + * + * @param o the object to insert into the list + * @throws ClassCastException if the object is of a type which cannot be added + * to this list. + * @throws IllegalArgumentException if some other aspect of the object stops + * it being added to this list. + * @throws UnsupportedOperationException if this ListIterator does not + * support the add operation. + */ + public void add(Object o) + { + synchronized (mutex) + { + li.add(o); + } + } + + /** + * Tests whether there are elements remaining in the underlying list + * in the reverse direction. In other words, <code>previous()</code> + * will not fail with a NoSuchElementException. A lock is obtained + * on the mutex before the check takes place. + * + * @return <code>true</code> if the list continues in the reverse direction + */ + public boolean hasPrevious() + { + synchronized (mutex) + { + return li.hasPrevious(); + } + } + + /** + * Find the index of the element that would be returned by a call to + * <code>next()</code>. If hasNext() returns <code>false</code>, this + * returns the list size. A lock is obtained on the mutex before the + * query takes place. + * + * @return the index of the element that would be returned by next() + */ + public int nextIndex() + { + synchronized (mutex) + { + return li.nextIndex(); + } + } + + /** + * Obtain the previous element from the underlying list. Repeated + * calls to previous may be used to iterate backwards over the entire list, + * or calls to next and previous may be used together to go forwards and + * backwards. Alternating calls to next and previous will return the same + * element. A lock is obtained on the mutex before the object is retrieved. + * + * @return the next element in the list in the reverse direction + * @throws NoSuchElementException if there are no more elements + */ + public Object previous() + { + synchronized (mutex) + { + return li.previous(); + } + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If hasPrevious() returns <code>false</code>, this returns -1. + * A lock is obtained on the mutex before the query takes place. + * + * @return the index of the element that would be returned by previous() + */ + public int previousIndex() + { + synchronized (mutex) + { + return li.previousIndex(); + } + } + + /** + * Replace the element last returned by a call to <code>next()</code> or + * <code>previous()</code> with a given object (optional operation). This + * method may only be called if neither <code>add()</code> nor + * <code>remove()</code> have been called since the last call to + * <code>next()</code> or <code>previous</code>. A lock is obtained + * on the mutex before the list is modified. + * + * @param o the object to replace the element with + * @throws ClassCastException the object is of a type which cannot be added + * to this list + * @throws IllegalArgumentException some other aspect of the object stops + * it being added to this list + * @throws IllegalStateException if neither next or previous have been + * called, or if add or remove has been called since the last call + * to next or previous + * @throws UnsupportedOperationException if this ListIterator does not + * support the set operation + */ + public void set(Object o) + { + synchronized (mutex) + { + li.set(o); + } + } + } // class SynchronizedListIterator + + /** + * Returns a synchronized (thread-safe) map wrapper backed by the given + * map. Notice that element access through the collection views and their + * iterators are thread-safe, but if the map can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * Map m = Collections.synchronizedMap(new Map(...)); + * ... + * Set s = m.keySet(); // safe outside a synchronized block + * synchronized (m) // synch on m, not s + * { + * Iterator i = s.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a synchronized view of the map + * @see Serializable + */ + public static Map synchronizedMap(Map m) + { + return new SynchronizedMap(m); + } + + /** + * The implementation of {@link #synchronizedMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class SynchronizedMap implements Map, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1978198479659022715L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * The object to synchronize on. When an instance is created via public + * methods, it will be this; but other uses like + * SynchronizedSortedMap.subMap() must specify another mutex. Package + * visible for use by subclass. + * @serial the lock + */ + final Object mutex; + + /** + * Cache the entry set. + */ + private transient Set entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @throws NullPointerException if m is null + */ + SynchronizedMap(Map m) + { + this.m = m; + mutex = this; + if (m == null) + throw new NullPointerException(); + } + + /** + * Called only by trusted code to specify the mutex as well as the map. + * @param sync the mutex + * @param m the map + */ + SynchronizedMap(Object sync, Map m) + { + this.m = m; + mutex = sync; + } + + /** + * Clears all the entries from the underlying map. A lock is obtained + * on the mutex before the map is cleared. + * + * @throws UnsupportedOperationException if clear is not supported + */ + public void clear() + { + synchronized (mutex) + { + m.clear(); + } + } + + /** + * Returns <code>true</code> if the underlying map contains a entry for the given key. + * A lock is obtained on the mutex before the map is queried. + * + * @param key the key to search for. + * @return <code>true</code> if the underlying map contains the key. + * @throws ClassCastException if the key is of an inappropriate type. + * @throws NullPointerException if key is <code>null</code> but the map + * does not permit null keys. + */ + public boolean containsKey(Object key) + { + synchronized (mutex) + { + return m.containsKey(key); + } + } + + /** + * Returns <code>true</code> if the underlying map contains at least one entry with the + * given value. In other words, returns <code>true</code> if a value v exists where + * <code>(value == null ? v == null : value.equals(v))</code>. This usually + * requires linear time. A lock is obtained on the mutex before the map + * is queried. + * + * @param value the value to search for + * @return <code>true</code> if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + synchronized (mutex) + { + return m.containsValue(value); + } + } + + // This is one of the ickiest cases of nesting I've ever seen. It just + // means "return a SynchronizedSet, except that the iterator() method + // returns an SynchronizedIterator whose next() method returns a + // synchronized wrapper around its normal return value". + public Set entrySet() + { + // Define this here to spare some nesting. + class SynchronizedMapEntry implements Map.Entry + { + final Map.Entry e; + SynchronizedMapEntry(Object o) + { + e = (Map.Entry) o; + } + + /** + * Returns <code>true</code> if the object, o, implements <code>Map.Entry</code> + * with the same key and value as the underlying entry. A lock is + * obtained on the mutex before the comparison takes place. + * + * @param o The object to compare with this entry. + * @return <code>true</code> if o is equivalent to the underlying map entry. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return e.equals(o); + } + } + + /** + * Returns the key used in the underlying map entry. A lock is obtained + * on the mutex before the key is retrieved. + * + * @return The key of the underlying map entry. + */ + public Object getKey() + { + synchronized (mutex) + { + return e.getKey(); + } + } + + /** + * Returns the value used in the underlying map entry. A lock is obtained + * on the mutex before the value is retrieved. + * + * @return The value of the underlying map entry. + */ + public Object getValue() + { + synchronized (mutex) + { + return e.getValue(); + } + } + + /** + * Computes the hash code for the underlying map entry. + * This computation is described in the documentation for the + * <code>Map</code> interface. A lock is obtained on the mutex + * before the underlying map is accessed. + * + * @return The hash code of the underlying map entry. + * @see Map#hashCode() + */ + public int hashCode() + { + synchronized (mutex) + { + return e.hashCode(); + } + } + + /** + * Replaces the value in the underlying map entry with the specified + * object (optional operation). A lock is obtained on the mutex + * before the map is altered. The map entry, in turn, will alter + * the underlying map object. The operation is undefined if the + * <code>remove()</code> method of the iterator has been called + * beforehand. + * + * @param value the new value to store + * @return the old value + * @throws UnsupportedOperationException if the operation is not supported. + * @throws ClassCastException if the value is of the wrong type. + * @throws IllegalArgumentException if something about the value + * prevents it from existing in this map. + * @throws NullPointerException if the map forbids null values. + */ + public Object setValue(Object value) + { + synchronized (mutex) + { + return e.setValue(value); + } + } + + /** + * Returns a textual representation of the underlying map entry. + * A lock is obtained on the mutex before the entry is accessed. + * + * @return The contents of the map entry in <code>String</code> form. + */ + public String toString() + { + synchronized (mutex) + { + return e.toString(); + } + } + } // class SynchronizedMapEntry + + // Now the actual code. + if (entries == null) + synchronized (mutex) + { + entries = new SynchronizedSet(mutex, m.entrySet()) + { + /** + * Returns an iterator over the set. The iterator has no specific order, + * unless further specified. A lock is obtained on the set's mutex + * before the iterator is created. The created iterator is also + * thread-safe. + * + * @return A synchronized set iterator. + */ + public Iterator iterator() + { + synchronized (super.mutex) + { + return new SynchronizedIterator(super.mutex, c.iterator()) + { + /** + * Retrieves the next map entry from the iterator. + * A lock is obtained on the iterator's mutex before + * the entry is created. The new map entry is enclosed in + * a thread-safe wrapper. + * + * @return A synchronized map entry. + */ + public Object next() + { + synchronized (super.mutex) + { + return new SynchronizedMapEntry(super.next()); + } + } + }; + } + } + }; + } + return entries; + } + + /** + * Returns <code>true</code> if the object, o, is also an instance + * of <code>Map</code> and contains an equivalent + * entry set to that of the underlying map. A lock + * is obtained on the mutex before the objects are + * compared. + * + * @param o The object to compare. + * @return <code>true</code> if o and the underlying map are equivalent. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return m.equals(o); + } + } + + /** + * Returns the value associated with the given key, or null + * if no such mapping exists. An ambiguity exists with maps + * that accept null values as a return value of null could + * be due to a non-existent mapping or simply a null value + * for that key. To resolve this, <code>containsKey</code> + * should be used. A lock is obtained on the mutex before + * the value is retrieved from the underlying map. + * + * @param key The key of the required mapping. + * @return The value associated with the given key, or + * null if no such mapping exists. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + */ + public Object get(Object key) + { + synchronized (mutex) + { + return m.get(key); + } + } + + /** + * Calculates the hash code of the underlying map as the + * sum of the hash codes of all entries. A lock is obtained + * on the mutex before the hash code is computed. + * + * @return The hash code of the underlying map. + */ + public int hashCode() + { + synchronized (mutex) + { + return m.hashCode(); + } + } + + /** + * Returns <code>true</code> if the underlying map contains no entries. + * A lock is obtained on the mutex before the map is examined. + * + * @return <code>true</code> if the map is empty. + */ + public boolean isEmpty() + { + synchronized (mutex) + { + return m.isEmpty(); + } + } + + /** + * Returns a thread-safe set view of the keys in the underlying map. The + * set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. If the set supports removal, these methods remove the + * underlying mapping from the map: <code>Iterator.remove</code>, + * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code>, + * and <code>clear</code>. Element addition, via <code>add</code> or + * <code>addAll</code>, is not supported via this set. A lock is obtained + * on the mutex before the set is created. + * + * @return A synchronized set containing the keys of the underlying map. + */ + public Set keySet() + { + if (keys == null) + synchronized (mutex) + { + keys = new SynchronizedSet(mutex, m.keySet()); + } + return keys; + } + + /** + * Associates the given key to the given value (optional operation). If the + * underlying map already contains the key, its value is replaced. Be aware + * that in a map that permits <code>null</code> values, a null return does not + * always imply that the mapping was created. A lock is obtained on the mutex + * before the modification is made. + * + * @param key the key to map. + * @param value the value to be mapped. + * @return the previous value of the key, or null if there was no mapping + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if the key or value is of the wrong type + * @throws IllegalArgumentException if something about this key or value + * prevents it from existing in this map + * @throws NullPointerException if either the key or the value is null, + * and the map forbids null keys or values + * @see #containsKey(Object) + */ + public Object put(Object key, Object value) + { + synchronized (mutex) + { + return m.put(key, value); + } + } + + /** + * Copies all entries of the given map to the underlying one (optional + * operation). If the map already contains a key, its value is replaced. + * A lock is obtained on the mutex before the operation proceeds. + * + * @param map the mapping to load into this map + * @throws UnsupportedOperationException if the operation is not supported + * @throws ClassCastException if a key or value is of the wrong type + * @throws IllegalArgumentException if something about a key or value + * prevents it from existing in this map + * @throws NullPointerException if the map forbids null keys or values, or + * if <code>m</code> is null. + * @see #put(Object, Object) + */ + public void putAll(Map map) + { + synchronized (mutex) + { + m.putAll(map); + } + } + + /** + * Removes the mapping for the key, o, if present (optional operation). If + * the key is not present, this returns null. Note that maps which permit + * null values may also return null if the key was removed. A prior + * <code>containsKey()</code> check is required to avoid this ambiguity. + * Before the mapping is removed, a lock is obtained on the mutex. + * + * @param o the key to remove + * @return the value the key mapped to, or null if not present + * @throws UnsupportedOperationException if deletion is unsupported + * @throws NullPointerException if the key is null and this map doesn't + * support null keys. + * @throws ClassCastException if the type of the key is not a valid type + * for this map. + */ + public Object remove(Object o) + { + synchronized (mutex) + { + return m.remove(o); + } + } + + /** + * Retrieves the size of the underlying map. A lock + * is obtained on the mutex before access takes place. + * Maps with a size greater than <code>Integer.MAX_VALUE</code> + * return <code>Integer.MAX_VALUE</code> instead. + * + * @return The size of the underlying map. + */ + public int size() + { + synchronized (mutex) + { + return m.size(); + } + } + + /** + * Returns a textual representation of the underlying + * map. A lock is obtained on the mutex before the map + * is accessed. + * + * @return The map in <code>String</code> form. + */ + public String toString() + { + synchronized (mutex) + { + return m.toString(); + } + } + + /** + * Returns a synchronized collection view of the values in the underlying + * map. The collection is backed by the map, so that changes in one show up in + * the other. Modifications made while an iterator is in progress cause + * undefined behavior. If the collection supports removal, these methods + * remove the underlying mapping from the map: <code>Iterator.remove</code>, + * <code>Collection.remove</code>, <code>removeAll</code>, + * <code>retainAll</code>, and <code>clear</code>. Element addition, via + * <code>add</code> or <code>addAll</code>, is not supported via this + * collection. A lock is obtained on the mutex before the collection + * is created. + * + * @return the collection of all values in the underlying map. + */ + public Collection values() + { + if (values == null) + synchronized (mutex) + { + values = new SynchronizedCollection(mutex, m.values()); + } + return values; + } + } // class SynchronizedMap + + /** + * Returns a synchronized (thread-safe) set wrapper backed by the given + * set. Notice that element access through the iterator is thread-safe, but + * if the set can be structurally modified (adding or removing elements) + * then you should synchronize around the iteration to avoid + * non-deterministic behavior:<br> + * <pre> + * Set s = Collections.synchronizedSet(new Set(...)); + * ... + * synchronized (s) + * { + * Iterator i = s.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a synchronized view of the set + * @see Serializable + */ + public static Set synchronizedSet(Set s) + { + return new SynchronizedSet(s); + } + + /** + * The implementation of {@link #synchronizedSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * Package visible, so that sets such as Hashtable.keySet() + * can specify which object to synchronize on. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + static class SynchronizedSet extends SynchronizedCollection + implements Set + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 487447009682186044L; + + /** + * Wrap a given set. + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + SynchronizedSet(Set s) + { + super(s); + } + + /** + * Called only by trusted code to specify the mutex as well as the set. + * @param sync the mutex + * @param s the set + */ + SynchronizedSet(Object sync, Set s) + { + super(sync, s); + } + + /** + * Returns <code>true</code> if the object, o, is a <code>Set</code> + * of the same size as the underlying set, and contains + * each element, e, which occurs in the underlying set. + * A lock is obtained on the mutex before the comparison + * takes place. + * + * @param o The object to compare against. + * @return <code>true</code> if o is an equivalent set. + */ + public boolean equals(Object o) + { + synchronized (mutex) + { + return c.equals(o); + } + } + + /** + * Computes the hash code for the underlying set as the + * sum of the hash code of all elements within the set. + * A lock is obtained on the mutex before the computation + * occurs. + * + * @return The hash code for the underlying set. + */ + public int hashCode() + { + synchronized (mutex) + { + return c.hashCode(); + } + } + } // class SynchronizedSet + + /** + * Returns a synchronized (thread-safe) sorted map wrapper backed by the + * given map. Notice that element access through the collection views, + * subviews, and their iterators are thread-safe, but if the map can be + * structurally modified (adding or removing elements) then you should + * synchronize around the iteration to avoid non-deterministic behavior:<br> + * <pre> + * SortedMap m = Collections.synchronizedSortedMap(new SortedMap(...)); + * ... + * Set s = m.keySet(); // safe outside a synchronized block + * SortedMap m2 = m.headMap(foo); // safe outside a synchronized block + * Set s2 = m2.keySet(); // safe outside a synchronized block + * synchronized (m) // synch on m, not m2, s or s2 + * { + * Iterator i = s.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * i = s2.iterator(); + * while (i.hasNext()) + * bar(i.next()); + * } + * </pre><p> + * + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + * + * @param m the sorted map to wrap + * @return a synchronized view of the sorted map + * @see Serializable + */ + public static SortedMap synchronizedSortedMap(SortedMap m) + { + return new SynchronizedSortedMap(m); + } + + /** + * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedSortedMap extends SynchronizedMap + implements SortedMap + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -8798146769416483793L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * @param sm the map to wrap + * @throws NullPointerException if sm is null + */ + SynchronizedSortedMap(SortedMap sm) + { + super(sm); + this.sm = sm; + } + + /** + * Called only by trusted code to specify the mutex as well as the map. + * @param sync the mutex + * @param sm the map + */ + SynchronizedSortedMap(Object sync, SortedMap sm) + { + super(sync, sm); + this.sm = sm; + } + + /** + * Returns the comparator used in sorting the underlying map, or null if + * it is the keys' natural ordering. A lock is obtained on the mutex + * before the comparator is retrieved. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + synchronized (mutex) + { + return sm.comparator(); + } + } + + /** + * Returns the first, lowest sorted, key from the underlying map. + * A lock is obtained on the mutex before the map is accessed. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public Object firstKey() + { + synchronized (mutex) + { + return sm.firstKey(); + } + } + + /** + * Returns a submap containing the keys from the first + * key (as returned by <code>firstKey()</code>) to + * the key before that specified. The submap supports all + * operations supported by the underlying map and all actions + * taking place on the submap are also reflected in the underlying + * map. A lock is obtained on the mutex prior to submap creation. + * This operation is equivalent to <code>subMap(firstKey(), toKey)</code>. + * The submap retains the thread-safe status of this map. + * + * @param toKey the exclusive upper range of the submap. + * @return a submap from <code>firstKey()</code> to the + * the key preceding toKey. + * @throws ClassCastException if toKey is not comparable to the underlying + * map's contents. + * @throws IllegalArgumentException if toKey is outside the map's range. + * @throws NullPointerException if toKey is null. but the map does not allow + * null keys. + */ + public SortedMap headMap(Object toKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.headMap(toKey)); + } + } + + /** + * Returns the last, highest sorted, key from the underlying map. + * A lock is obtained on the mutex before the map is accessed. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public Object lastKey() + { + synchronized (mutex) + { + return sm.lastKey(); + } + } + + /** + * Returns a submap containing the keys from fromKey to + * the key before toKey. The submap supports all + * operations supported by the underlying map and all actions + * taking place on the submap are also reflected in the underlying + * map. A lock is obtained on the mutex prior to submap creation. + * The submap retains the thread-safe status of this map. + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return a submap from fromKey to the key preceding toKey. + * @throws ClassCastException if fromKey or toKey is not comparable + * to the underlying map's contents. + * @throws IllegalArgumentException if fromKey or toKey is outside the map's + * range. + * @throws NullPointerException if fromKey or toKey is null. but the map does + * not allow null keys. + */ + public SortedMap subMap(Object fromKey, Object toKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.subMap(fromKey, toKey)); + } + } + + /** + * Returns a submap containing all the keys from fromKey onwards. + * The submap supports all operations supported by the underlying + * map and all actions taking place on the submap are also reflected + * in the underlying map. A lock is obtained on the mutex prior to + * submap creation. The submap retains the thread-safe status of + * this map. + * + * @param fromKey the inclusive lower range of the submap. + * @return a submap from fromKey to <code>lastKey()</code>. + * @throws ClassCastException if fromKey is not comparable to the underlying + * map's contents. + * @throws IllegalArgumentException if fromKey is outside the map's range. + * @throws NullPointerException if fromKey is null. but the map does not allow + * null keys. + */ + public SortedMap tailMap(Object fromKey) + { + synchronized (mutex) + { + return new SynchronizedSortedMap(mutex, sm.tailMap(fromKey)); + } + } + } // class SynchronizedSortedMap + + /** + * Returns a synchronized (thread-safe) sorted set wrapper backed by the + * given set. Notice that element access through the iterator and through + * subviews are thread-safe, but if the set can be structurally modified + * (adding or removing elements) then you should synchronize around the + * iteration to avoid non-deterministic behavior:<br> + * <pre> + * SortedSet s = Collections.synchronizedSortedSet(new SortedSet(...)); + * ... + * SortedSet s2 = s.headSet(foo); // safe outside a synchronized block + * synchronized (s) // synch on s, not s2 + * { + * Iterator i = s2.iterator(); + * while (i.hasNext()) + * foo(i.next()); + * } + * </pre><p> + * + * The returned SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + * + * @param s the sorted set to wrap + * @return a synchronized view of the sorted set + * @see Serializable + */ + public static SortedSet synchronizedSortedSet(SortedSet s) + { + return new SynchronizedSortedSet(s); + } + + /** + * The implementation of {@link #synchronizedSortedSet(SortedSet)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class SynchronizedSortedSet extends SynchronizedSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 8695801310862127406L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped set + */ + private final SortedSet ss; + + /** + * Wrap a given set. + * @param ss the set to wrap + * @throws NullPointerException if ss is null + */ + SynchronizedSortedSet(SortedSet ss) + { + super(ss); + this.ss = ss; + } + + /** + * Called only by trusted code to specify the mutex as well as the set. + * @param sync the mutex + * @param l the list + */ + SynchronizedSortedSet(Object sync, SortedSet ss) + { + super(sync, ss); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, or null if + * it is the elements' natural ordering. A lock is obtained on the mutex + * before the comparator is retrieved. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + synchronized (mutex) + { + return ss.comparator(); + } + } + + /** + * Returns the first, lowest sorted, element from the underlying set. + * A lock is obtained on the mutex before the set is accessed. + * + * @return the first element. + * @throws NoSuchElementException if this set is empty. + */ + public Object first() + { + synchronized (mutex) + { + return ss.first(); + } + } + + /** + * Returns a subset containing the element from the first + * element (as returned by <code>first()</code>) to + * the element before that specified. The subset supports all + * operations supported by the underlying set and all actions + * taking place on the subset are also reflected in the underlying + * set. A lock is obtained on the mutex prior to subset creation. + * This operation is equivalent to <code>subSet(first(), toElement)</code>. + * The subset retains the thread-safe status of this set. + * + * @param toElement the exclusive upper range of the subset. + * @return a subset from <code>first()</code> to the + * the element preceding toElement. + * @throws ClassCastException if toElement is not comparable to the underlying + * set's contents. + * @throws IllegalArgumentException if toElement is outside the set's range. + * @throws NullPointerException if toElement is null. but the set does not allow + * null elements. + */ + public SortedSet headSet(Object toElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, ss.headSet(toElement)); + } + } + + /** + * Returns the last, highest sorted, element from the underlying set. + * A lock is obtained on the mutex before the set is accessed. + * + * @return the last element. + * @throws NoSuchElementException if this set is empty. + */ + public Object last() + { + synchronized (mutex) + { + return ss.last(); + } + } + + /** + * Returns a subset containing the elements from fromElement to + * the element before toElement. The subset supports all + * operations supported by the underlying set and all actions + * taking place on the subset are also reflected in the underlying + * set. A lock is obtained on the mutex prior to subset creation. + * The subset retains the thread-safe status of this set. + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return a subset from fromElement to the element preceding toElement. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the underlying set's contents. + * @throws IllegalArgumentException if fromElement or toElement is outside the set's + * range. + * @throws NullPointerException if fromElement or toElement is null. but the set does + * not allow null elements. + */ + public SortedSet subSet(Object fromElement, Object toElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, + ss.subSet(fromElement, toElement)); + } + } + + /** + * Returns a subset containing all the elements from fromElement onwards. + * The subset supports all operations supported by the underlying + * set and all actions taking place on the subset are also reflected + * in the underlying set. A lock is obtained on the mutex prior to + * subset creation. The subset retains the thread-safe status of + * this set. + * + * @param fromElement the inclusive lower range of the subset. + * @return a subset from fromElement to <code>last()</code>. + * @throws ClassCastException if fromElement is not comparable to the underlying + * set's contents. + * @throws IllegalArgumentException if fromElement is outside the set's range. + * @throws NullPointerException if fromElement is null. but the set does not allow + * null elements. + */ + public SortedSet tailSet(Object fromElement) + { + synchronized (mutex) + { + return new SynchronizedSortedSet(mutex, ss.tailSet(fromElement)); + } + } + } // class SynchronizedSortedSet + + + /** + * Returns an unmodifiable view of the given collection. This allows + * "read-only" access, although changes in the backing collection show up + * in this view. Attempts to modify the collection directly or via iterators + * will fail with {@link UnsupportedOperationException}. Although this view + * prevents changes to the structure of the collection and its elements, the values + * referenced by the objects in the collection can still be modified. + * <p> + * + * Since the collection might be a List or a Set, and those have incompatible + * equals and hashCode requirements, this relies on Object's implementation + * rather than passing those calls on to the wrapped collection. The returned + * Collection implements Serializable, but can only be serialized if + * the collection it wraps is likewise Serializable. + * + * @param c the collection to wrap + * @return a read-only view of the collection + * @see Serializable + */ + public static Collection unmodifiableCollection(Collection c) + { + return new UnmodifiableCollection(c); + } + + /** + * The implementation of {@link #unmodifiableCollection(Collection)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableCollection + implements Collection, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 1820017752578914078L; + + /** + * The wrapped collection. Package visible for use by subclasses. + * @serial the real collection + */ + final Collection c; + + /** + * Wrap a given collection. + * @param c the collection to wrap + * @throws NullPointerException if c is null + */ + UnmodifiableCollection(Collection c) + { + this.c = c; + if (c == null) + throw new NullPointerException(); + } + + /** + * Blocks the addition of elements to the underlying collection. + * This method never returns, throwing an exception instead. + * + * @param o the object to add. + * @return <code>true</code> if the collection was modified as a result of this action. + * @throws UnsupportedOperationException as an unmodifiable collection does not + * support the add operation. + */ + public boolean add(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the addition of a collection of elements to the underlying + * collection. This method never returns, throwing an exception instead. + * + * @param c the collection to add. + * @return <code>true</code> if the collection was modified as a result of this action. + * @throws UnsupportedOperationException as an unmodifiable collection does not + * support the <code>addAll</code> operation. + */ + public boolean addAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the clearing of the underlying collection. This method never + * returns, throwing an exception instead. + * + * @throws UnsupportedOperationException as an unmodifiable collection does + * not support the <code>clear()</code> operation. + */ + public void clear() + { + throw new UnsupportedOperationException(); + } + + /** + * Test whether the underlying collection contains a given object as one of its + * elements. + * + * @param o the element to look for. + * @return <code>true</code> if the underlying collection contains at least + * one element e such that + * <code>o == null ? e == null : o.equals(e)</code>. + * @throws ClassCastException if the type of o is not a valid type for the + * underlying collection. + * @throws NullPointerException if o is null and the underlying collection + * doesn't support null values. + */ + public boolean contains(Object o) + { + return c.contains(o); + } + + /** + * Test whether the underlying collection contains every element in a given + * collection. + * + * @param c1 the collection to test for. + * @return <code>true</code> if for every element o in c, contains(o) would + * return <code>true</code>. + * @throws ClassCastException if the type of any element in c is not a valid + * type for the underlying collection. + * @throws NullPointerException if some element of c is null and the underlying + * collection does not support null values. + * @throws NullPointerException if c itself is null. + */ + public boolean containsAll(Collection c1) + { + return c.containsAll(c1); + } + + /** + * Tests whether the underlying collection is empty, that is, + * if size() == 0. + * + * @return <code>true</code> if this collection contains no elements. + */ + public boolean isEmpty() + { + return c.isEmpty(); + } + + /** + * Obtain an Iterator over the underlying collection, which maintains + * its unmodifiable nature. + * + * @return an UnmodifiableIterator over the elements of the underlying + * collection, in any order. + */ + public Iterator iterator() + { + return new UnmodifiableIterator(c.iterator()); + } + + /** + * Blocks the removal of an object from the underlying collection. + * This method never returns, throwing an exception instead. + * + * @param o The object to remove. + * @return <code>true</code> if the object was removed (i.e. the underlying + * collection returned 1 or more instances of o). + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the <code>remove()</code> operation. + */ + public boolean remove(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of a collection of objects from the underlying + * collection. This method never returns, throwing an exception + * instead. + * + * @param c The collection of objects to remove. + * @return <code>true</code> if the collection was modified. + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the <code>removeAll()</code> operation. + */ + public boolean removeAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of all elements from the underlying collection, + * except those in the supplied collection. This method never returns, + * throwing an exception instead. + * + * @param c The collection of objects to retain. + * @return <code>true</code> if the collection was modified. + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the <code>retainAll()</code> operation. + */ + public boolean retainAll(Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Retrieves the number of elements in the underlying collection. + * + * @return the number of elements in the collection. + */ + public int size() + { + return c.size(); + } + + /** + * Copy the current contents of the underlying collection into an array. + * + * @return an array of type Object[] with a length equal to the size of the + * underlying collection and containing the elements currently in + * the underlying collection, in any order. + */ + public Object[] toArray() + { + return c.toArray(); + } + + /** + * Copy the current contents of the underlying collection into an array. If + * the array passed as an argument has length less than the size of the + * underlying collection, an array of the same run-time type as a, with a length + * equal to the size of the underlying collection, is allocated using reflection. + * Otherwise, a itself is used. The elements of the underlying collection are + * copied into it, and if there is space in the array, the following element is + * set to null. The resultant array is returned. + * Note: The fact that the following element is set to null is only useful + * if it is known that this collection does not contain any null elements. + * + * @param a the array to copy this collection into. + * @return an array containing the elements currently in the underlying + * collection, in any order. + * @throws ArrayStoreException if the type of any element of the + * collection is not a subtype of the element type of a. + */ + public Object[] toArray(Object[] a) + { + return c.toArray(a); + } + + /** + * A textual representation of the unmodifiable collection. + * + * @return The unmodifiable collection in the form of a <code>String</code>. + */ + public String toString() + { + return c.toString(); + } + } // class UnmodifiableCollection + + /** + * The implementation of the various iterator methods in the + * unmodifiable classes. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableIterator implements Iterator + { + /** + * The wrapped iterator. + */ + private final Iterator i; + + /** + * Only trusted code creates a wrapper. + * @param i the wrapped iterator + */ + UnmodifiableIterator(Iterator i) + { + this.i = i; + } + + /** + * Obtains the next element in the underlying collection. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public Object next() + { + return i.next(); + } + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by <code>next()</code>. When this method + * returns <code>true</code>, an exception will not be thrown on calling + * <code>next()</code>. + * + * @return <code>true</code> if there is at least one more element in the underlying + * collection. + */ + public boolean hasNext() + { + return i.hasNext(); + } + + /** + * Blocks the removal of elements from the underlying collection by the + * iterator. + * + * @throws UnsupportedOperationException as an unmodifiable collection + * does not support the removal of elements by its iterator. + */ + public void remove() + { + throw new UnsupportedOperationException(); + } + } // class UnmodifiableIterator + + /** + * Returns an unmodifiable view of the given list. This allows + * "read-only" access, although changes in the backing list show up + * in this view. Attempts to modify the list directly, via iterators, or + * via sublists, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the list and + * its elements, the values referenced by the objects in the list can + * still be modified. + * <p> + * + * The returned List implements Serializable, but can only be serialized if + * the list it wraps is likewise Serializable. In addition, if the wrapped + * list implements RandomAccess, this does too. + * + * @param l the list to wrap + * @return a read-only view of the list + * @see Serializable + * @see RandomAccess + */ + public static List unmodifiableList(List l) + { + if (l instanceof RandomAccess) + return new UnmodifiableRandomAccessList(l); + return new UnmodifiableList(l); + } + + /** + * The implementation of {@link #unmodifiableList(List)} for sequential + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableList extends UnmodifiableCollection + implements List + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -283967356065247728L; + + + /** + * The wrapped list; stored both here and in the superclass to avoid + * excessive casting. Package visible for use by subclass. + * @serial the wrapped list + */ + final List list; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + UnmodifiableList(List l) + { + super(l); + list = l; + } + + /** + * Blocks the addition of an element to the underlying + * list at a specific index. This method never returns, + * throwing an exception instead. + * + * @param index The index at which to place the new element. + * @param o the object to add. + * @throws UnsupportedOperationException as an unmodifiable + * list doesn't support the <code>add()</code> operation. + */ + public void add(int index, Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the addition of a collection of elements to the + * underlying list at a specific index. This method never + * returns, throwing an exception instead. + * + * @param index The index at which to place the new element. + * @param c the collections of objects to add. + * @throws UnsupportedOperationException as an unmodifiable + * list doesn't support the <code>addAll()</code> operation. + */ + public boolean addAll(int index, Collection c) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns <code>true</code> if the object, o, is an instance of + * <code>List</code> with the same size and elements + * as the underlying list. + * + * @param o The object to compare. + * @return <code>true</code> if o is equivalent to the underlying list. + */ + public boolean equals(Object o) + { + return list.equals(o); + } + + /** + * Retrieves the element at a given index in the underlying list. + * + * @param index the index of the element to be returned + * @return the element at index index in this list + * @throws IndexOutOfBoundsException if index < 0 || index >= size() + */ + public Object get(int index) + { + return list.get(index); + } + + /** + * Computes the hash code for the underlying list. + * The exact computation is described in the documentation + * of the <code>List</code> interface. + * + * @return The hash code of the underlying list. + * @see List#hashCode() + */ + public int hashCode() + { + return list.hashCode(); + } + + /** + * Obtain the first index at which a given object is to be found in the + * underlying list. + * + * @param o the object to search for + * @return the least integer n such that <code>o == null ? get(n) == null : + * o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int indexOf(Object o) + { + return list.indexOf(o); + } + + /** + * Obtain the last index at which a given object is to be found in the + * underlying list. + * + * @return the greatest integer n such that <code>o == null ? get(n) == null + * : o.equals(get(n))</code>, or -1 if there is no such index. + * @throws ClassCastException if the type of o is not a valid + * type for the underlying list. + * @throws NullPointerException if o is null and the underlying + * list does not support null values. + */ + public int lastIndexOf(Object o) + { + return list.lastIndexOf(o); + } + + /** + * Obtains a list iterator over the underlying list, starting at the beginning + * and maintaining the unmodifiable nature of this list. + * + * @return a <code>UnmodifiableListIterator</code> over the elements of the + * underlying list, in order, starting at the beginning. + */ + public ListIterator listIterator() + { + return new UnmodifiableListIterator(list.listIterator()); + } + + /** + * Obtains a list iterator over the underlying list, starting at the specified + * index and maintaining the unmodifiable nature of this list. An initial call + * to <code>next()</code> will retrieve the element at the specified index, + * and an initial call to <code>previous()</code> will retrieve the element + * at index - 1. + * + * + * @param index the position, between 0 and size() inclusive, to begin the + * iteration from. + * @return a <code>UnmodifiableListIterator</code> over the elements of the + * underlying list, in order, starting at the specified index. + * @throws IndexOutOfBoundsException if index < 0 || index > size() + */ + public ListIterator listIterator(int index) + { + return new UnmodifiableListIterator(list.listIterator(index)); + } + + /** + * Blocks the removal of the element at the specified index. + * This method never returns, throwing an exception instead. + * + * @param index The index of the element to remove. + * @return the removed element. + * @throws UnsupportedOperationException as an unmodifiable + * list does not support the <code>remove()</code> + * operation. + */ + public Object remove(int index) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the replacement of the element at the specified index. + * This method never returns, throwing an exception instead. + * + * @param index The index of the element to replace. + * @param o The new object to place at the specified index. + * @return the replaced element. + * @throws UnsupportedOperationException as an unmodifiable + * list does not support the <code>set()</code> + * operation. + */ + public Object set(int index, Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Obtain a List view of a subsection of the underlying list, from + * fromIndex (inclusive) to toIndex (exclusive). If the two indices + * are equal, the sublist is empty. The returned list will be + * unmodifiable, like this list. Changes to the elements of the + * returned list will be reflected in the underlying list. No structural + * modifications can take place in either list. + * + * @param fromIndex the index that the returned list should start from + * (inclusive). + * @param toIndex the index that the returned list should go to (exclusive). + * @return a List backed by a subsection of the underlying list. + * @throws IndexOutOfBoundsException if fromIndex < 0 + * || toIndex > size() || fromIndex > toIndex. + */ + public List subList(int fromIndex, int toIndex) + { + return unmodifiableList(list.subList(fromIndex, toIndex)); + } + } // class UnmodifiableList + + /** + * The implementation of {@link #unmodifiableList(List)} for random-access + * lists. This class name is required for compatibility with Sun's JDK + * serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableRandomAccessList + extends UnmodifiableList implements RandomAccess + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -2542308836966382001L; + + /** + * Wrap a given list. + * @param l the list to wrap + * @throws NullPointerException if l is null + */ + UnmodifiableRandomAccessList(List l) + { + super(l); + } + } // class UnmodifiableRandomAccessList + + /** + * The implementation of {@link UnmodifiableList#listIterator()}. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableListIterator + extends UnmodifiableIterator implements ListIterator + { + /** + * The wrapped iterator, stored both here and in the superclass to + * avoid excessive casting. + */ + private final ListIterator li; + + /** + * Only trusted code creates a wrapper. + * @param li the wrapped iterator + */ + UnmodifiableListIterator(ListIterator li) + { + super(li); + this.li = li; + } + + /** + * Blocks the addition of an object to the list underlying this iterator. + * This method never returns, throwing an exception instead. + * + * @param o The object to add. + * @throws UnsupportedOperationException as the iterator of an unmodifiable + * list does not support the <code>add()</code> operation. + */ + public void add(Object o) + { + throw new UnsupportedOperationException(); + } + + /** + * Tests whether there are still elements to be retrieved from the + * underlying collection by <code>previous()</code>. When this method + * returns <code>true</code>, an exception will not be thrown on calling + * <code>previous()</code>. + * + * @return <code>true</code> if there is at least one more element prior to the + * current position in the underlying list. + */ + public boolean hasPrevious() + { + return li.hasPrevious(); + } + + /** + * Find the index of the element that would be returned by a call to next. + * If <code>hasNext()</code> returns <code>false</code>, this returns the list size. + * + * @return the index of the element that would be returned by + * <code>next()</code>. + */ + public int nextIndex() + { + return li.nextIndex(); + } + + /** + * Obtains the previous element in the underlying list. + * + * @return the previous element in the list. + * @throws NoSuchElementException if there are no more prior elements. + */ + public Object previous() + { + return li.previous(); + } + + /** + * Find the index of the element that would be returned by a call to + * previous. If <code>hasPrevious()</code> returns <code>false</code>, + * this returns -1. + * + * @return the index of the element that would be returned by + * <code>previous()</code>. + */ + public int previousIndex() + { + return li.previousIndex(); + } + + /** + * Blocks the replacement of an element in the list underlying this + * iterator. This method never returns, throwing an exception instead. + * + * @param o The new object to replace the existing one. + * @throws UnsupportedOperationException as the iterator of an unmodifiable + * list does not support the <code>set()</code> operation. + */ + public void set(Object o) + { + throw new UnsupportedOperationException(); + } + } // class UnmodifiableListIterator + + /** + * Returns an unmodifiable view of the given map. This allows "read-only" + * access, although changes in the backing map show up in this view. + * Attempts to modify the map directly, or via collection views or their + * iterators will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the map and its + * entries, the values referenced by the objects in the map can still be + * modified. + * <p> + * + * The returned Map implements Serializable, but can only be serialized if + * the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a read-only view of the map + * @see Serializable + */ + public static Map unmodifiableMap(Map m) + { + return new UnmodifiableMap(m); + } + + /** + * The implementation of {@link #unmodifiableMap(Map)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableMap implements Map, Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -1034234728574286014L; + + /** + * The wrapped map. + * @serial the real map + */ + private final Map m; + + /** + * Cache the entry set. + */ + private transient Set entries; + + /** + * Cache the key set. + */ + private transient Set keys; + + /** + * Cache the value collection. + */ + private transient Collection values; + + /** + * Wrap a given map. + * @param m the map to wrap + * @throws NullPointerException if m is null + */ + UnmodifiableMap(Map m) + { + this.m = m; + if (m == null) + throw new NullPointerException(); + } + + /** + * Blocks the clearing of entries from the underlying map. + * This method never returns, throwing an exception instead. + * + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>clear()</code> operation. + */ + public void clear() + { + throw new UnsupportedOperationException(); + } + + /** + * Returns <code>true</code> if the underlying map contains a mapping for + * the given key. + * + * @param key the key to search for + * @return <code>true</code> if the map contains the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if key is <code>null</code> but the map + * does not permit null keys + */ + public boolean containsKey(Object key) + { + return m.containsKey(key); + } + + /** + * Returns <code>true</code> if the underlying map contains at least one mapping with + * the given value. In other words, it returns <code>true</code> if a value v exists where + * <code>(value == null ? v == null : value.equals(v))</code>. This usually + * requires linear time. + * + * @param value the value to search for + * @return <code>true</code> if the map contains the value + * @throws ClassCastException if the type of the value is not a valid type + * for this map. + * @throws NullPointerException if the value is null and the map doesn't + * support null values. + */ + public boolean containsValue(Object value) + { + return m.containsValue(value); + } + + /** + * Returns a unmodifiable set view of the entries in the underlying map. + * Each element in the set is a unmodifiable variant of <code>Map.Entry</code>. + * The set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the objects. + * + * @return the unmodifiable set view of all mapping entries. + * @see Map.Entry + */ + public Set entrySet() + { + if (entries == null) + entries = new UnmodifiableEntrySet(m.entrySet()); + return entries; + } + + /** + * The implementation of {@link UnmodifiableMap#entrySet()}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static final class UnmodifiableEntrySet extends UnmodifiableSet + implements Serializable + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = 7854390611657943733L; + + /** + * Wrap a given set. + * @param s the set to wrap + */ + UnmodifiableEntrySet(Set s) + { + super(s); + } + + // The iterator must return unmodifiable map entries. + public Iterator iterator() + { + return new UnmodifiableIterator(c.iterator()) + { + /** + * Obtains the next element from the underlying set of + * map entries. + * + * @return the next element in the collection. + * @throws NoSuchElementException if there are no more elements. + */ + public Object next() + { + final Map.Entry e = (Map.Entry) super.next(); + return new Map.Entry() + { + /** + * Returns <code>true</code> if the object, o, is also a map entry with an + * identical key and value. + * + * @param o the object to compare. + * @return <code>true</code> if o is an equivalent map entry. + */ + public boolean equals(Object o) + { + return e.equals(o); + } + + /** + * Returns the key of this map entry. + * + * @return the key. + */ + public Object getKey() + { + return e.getKey(); + } + + /** + * Returns the value of this map entry. + * + * @return the value. + */ + public Object getValue() + { + return e.getValue(); + } + + /** + * Computes the hash code of this map entry. + * The computation is described in the <code>Map</code> + * interface documentation. + * + * @return the hash code of this entry. + * @see Map#hashCode() + */ + public int hashCode() + { + return e.hashCode(); + } + + /** + * Blocks the alteration of the value of this map entry. + * This method never returns, throwing an exception instead. + * + * @param value The new value. + * @throws UnsupportedOperationException as an unmodifiable + * map entry does not support the <code>setValue()</code> + * operation. + */ + public Object setValue(Object value) + { + throw new UnsupportedOperationException(); + } + + /** + * Returns a textual representation of the map entry. + * + * @return The map entry as a <code>String</code>. + */ + public String toString() + { + return e.toString(); + } + }; + } + }; + } + } // class UnmodifiableEntrySet + + /** + * Returns <code>true</code> if the object, o, is also an instance + * of <code>Map</code> with an equal set of map entries. + * + * @param o The object to compare. + * @return <code>true</code> if o is an equivalent map. + */ + public boolean equals(Object o) + { + return m.equals(o); + } + + /** + * Returns the value associated with the supplied key or + * null if no such mapping exists. An ambiguity can occur + * if null values are accepted by the underlying map. + * In this case, <code>containsKey()</code> can be used + * to separate the two possible cases of a null result. + * + * @param key The key to look up. + * @return the value associated with the key, or null if key not in map. + * @throws ClassCastException if the key is an inappropriate type. + * @throws NullPointerException if this map does not accept null keys. + * @see #containsKey(Object) + */ + public Object get(Object key) + { + return m.get(key); + } + + /** + * Blocks the addition of a new entry to the underlying map. + * This method never returns, throwing an exception instead. + * + * @param key The new key. + * @param value The new value. + * @return the previous value of the key, or null if there was no mapping. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>put()</code> operation. + */ + public Object put(Object key, Object value) + { + throw new UnsupportedOperationException(); + } + + /** + * Computes the hash code for the underlying map, as the sum + * of the hash codes of all entries. + * + * @return The hash code of the underlying map. + * @see Map.Entry#hashCode() + */ + public int hashCode() + { + return m.hashCode(); + } + + /** + * Returns <code>true</code> if the underlying map contains no entries. + * + * @return <code>true</code> if the map is empty. + */ + public boolean isEmpty() + { + return m.isEmpty(); + } + + /** + * Returns a unmodifiable set view of the keys in the underlying map. + * The set is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + * + * @return the set view of all keys. + */ + public Set keySet() + { + if (keys == null) + keys = new UnmodifiableSet(m.keySet()); + return keys; + } + + /** + * Blocks the addition of the entries in the supplied map. + * This method never returns, throwing an exception instead. + * + * @param m The map, the entries of which should be added + * to the underlying map. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>putAll</code> operation. + */ + public void putAll(Map m) + { + throw new UnsupportedOperationException(); + } + + /** + * Blocks the removal of an entry from the map. + * This method never returns, throwing an exception instead. + * + * @param o The key of the entry to remove. + * @return The value the key was associated with, or null + * if no such mapping existed. Null is also returned + * if the removed entry had a null key. + * @throws UnsupportedOperationException as an unmodifiable + * map does not support the <code>remove</code> operation. + */ + public Object remove(Object o) + { + throw new UnsupportedOperationException(); + } + + + /** + * Returns the number of key-value mappings in the underlying map. + * If there are more than Integer.MAX_VALUE mappings, Integer.MAX_VALUE + * is returned. + * + * @return the number of mappings. + */ + public int size() + { + return m.size(); + } + + /** + * Returns a textual representation of the map. + * + * @return The map in the form of a <code>String</code>. + */ + public String toString() + { + return m.toString(); + } + + /** + * Returns a unmodifiable collection view of the values in the underlying map. + * The collection is backed by the map, so that changes in one show up in the other. + * Modifications made while an iterator is in progress cause undefined + * behavior. These modifications are again limited to the values of + * the keys. + * + * @return the collection view of all values. + */ + public Collection values() + { + if (values == null) + values = new UnmodifiableCollection(m.values()); + return values; + } + } // class UnmodifiableMap + + /** + * Returns an unmodifiable view of the given set. This allows + * "read-only" access, although changes in the backing set show up + * in this view. Attempts to modify the set directly or via iterators + * will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the set and its + * entries, the values referenced by the objects in the set can still be + * modified. + * <p> + * + * The returned Set implements Serializable, but can only be serialized if + * the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a read-only view of the set + * @see Serializable + */ + public static Set unmodifiableSet(Set s) + { + return new UnmodifiableSet(s); + } + + /** + * The implementation of {@link #unmodifiableSet(Set)}. This class + * name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSet extends UnmodifiableCollection + implements Set + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -9215047833775013803L; + + /** + * Wrap a given set. + * @param s the set to wrap + * @throws NullPointerException if s is null + */ + UnmodifiableSet(Set s) + { + super(s); + } + + /** + * Returns <code>true</code> if the object, o, is also an instance of + * <code>Set</code> of the same size and with the same entries. + * + * @return <code>true</code> if o is an equivalent set. + */ + public boolean equals(Object o) + { + return c.equals(o); + } + + /** + * Computes the hash code of this set, as the sum of the + * hash codes of all elements within the set. + * + * @return the hash code of the set. + */ + public int hashCode() + { + return c.hashCode(); + } + } // class UnmodifiableSet + + /** + * Returns an unmodifiable view of the given sorted map. This allows + * "read-only" access, although changes in the backing map show up in this + * view. Attempts to modify the map directly, via subviews, via collection + * views, or iterators, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the map and its + * entries, the values referenced by the objects in the map can still be + * modified. + * <p> + * + * The returned SortedMap implements Serializable, but can only be + * serialized if the map it wraps is likewise Serializable. + * + * @param m the map to wrap + * @return a read-only view of the map + * @see Serializable + */ + public static SortedMap unmodifiableSortedMap(SortedMap m) + { + return new UnmodifiableSortedMap(m); + } + + /** + * The implementation of {@link #unmodifiableSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSortedMap extends UnmodifiableMap + implements SortedMap + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -8806743815996713206L; + + /** + * The wrapped map; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped map + */ + private final SortedMap sm; + + /** + * Wrap a given map. + * @param sm the map to wrap + * @throws NullPointerException if sm is null + */ + UnmodifiableSortedMap(SortedMap sm) + { + super(sm); + this.sm = sm; + } + + /** + * Returns the comparator used in sorting the underlying map, + * or null if it is the keys' natural ordering. + * + * @return the sorting comparator. + */ + public Comparator comparator() + { + return sm.comparator(); + } + + /** + * Returns the first (lowest sorted) key in the map. + * + * @return the first key. + * @throws NoSuchElementException if this map is empty. + */ + public Object firstKey() + { + return sm.firstKey(); + } + + /** + * Returns a unmodifiable view of the portion of the map strictly less + * than toKey. The view is backed by the underlying map, so changes in + * one show up in the other. The submap supports all optional operations + * of the original. This operation is equivalent to + * <code>subMap(firstKey(), toKey)</code>. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of toKey. Note that the endpoint, toKey, + * is not included; if you want this value to be included, pass its successor + * object in to toKey. For example, for Integers, you could request + * <code>headMap(new Integer(limit.intValue() + 1))</code>. + * + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if toKey is not comparable to the map contents. + * @throws IllegalArgumentException if this is a subMap, and toKey is out + * of range. + * @throws NullPointerException if toKey is null but the map does not allow + * null keys. + */ + public SortedMap headMap(Object toKey) + { + return new UnmodifiableSortedMap(sm.headMap(toKey)); + } + + /** + * Returns the last (highest sorted) key in the map. + * + * @return the last key. + * @throws NoSuchElementException if this map is empty. + */ + public Object lastKey() + { + return sm.lastKey(); + } + + /** + * Returns a unmodifiable view of the portion of the map greater than or + * equal to fromKey, and strictly less than toKey. The view is backed by + * the underlying map, so changes in one show up in the other. The submap + * supports all optional operations of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey and toKey. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you could request + * <code>subMap(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1))</code> to reverse + * the inclusiveness of both endpoints. + * + * @param fromKey the inclusive lower range of the submap. + * @param toKey the exclusive upper range of the submap. + * @return the submap. + * @throws ClassCastException if fromKey or toKey is not comparable to + * the map contents. + * @throws IllegalArgumentException if this is a subMap, and fromKey or + * toKey is out of range. + * @throws NullPointerException if fromKey or toKey is null but the map + * does not allow null keys. + */ + public SortedMap subMap(Object fromKey, Object toKey) + { + return new UnmodifiableSortedMap(sm.subMap(fromKey, toKey)); + } + + /** + * Returns a unmodifiable view of the portion of the map greater than or + * equal to fromKey. The view is backed by the underlying map, so changes + * in one show up in the other. The submap supports all optional operations + * of the original. + * <p> + * + * The returned map throws an IllegalArgumentException any time a key is + * used which is out of the range of fromKey. Note that the endpoint, fromKey, is + * included; if you do not want this value to be included, pass its successor object in + * to fromKey. For example, for Integers, you could request + * <code>tailMap(new Integer(limit.intValue() + 1))</code>. + * + * @param fromKey the inclusive lower range of the submap + * @return the submap + * @throws ClassCastException if fromKey is not comparable to the map + * contents + * @throws IllegalArgumentException if this is a subMap, and fromKey is out + * of range + * @throws NullPointerException if fromKey is null but the map does not allow + * null keys + */ + public SortedMap tailMap(Object fromKey) + { + return new UnmodifiableSortedMap(sm.tailMap(fromKey)); + } + } // class UnmodifiableSortedMap + + /** + * Returns an unmodifiable view of the given sorted set. This allows + * "read-only" access, although changes in the backing set show up + * in this view. Attempts to modify the set directly, via subsets, or via + * iterators, will fail with {@link UnsupportedOperationException}. + * Although this view prevents changes to the structure of the set and its + * entries, the values referenced by the objects in the set can still be + * modified. + * <p> + * + * The returns SortedSet implements Serializable, but can only be + * serialized if the set it wraps is likewise Serializable. + * + * @param s the set to wrap + * @return a read-only view of the set + * @see Serializable + */ + public static SortedSet unmodifiableSortedSet(SortedSet s) + { + return new UnmodifiableSortedSet(s); + } + + /** + * The implementation of {@link #synchronizedSortedMap(SortedMap)}. This + * class name is required for compatibility with Sun's JDK serializability. + * + * @author Eric Blake (ebb9@email.byu.edu) + */ + private static class UnmodifiableSortedSet extends UnmodifiableSet + implements SortedSet + { + /** + * Compatible with JDK 1.4. + */ + private static final long serialVersionUID = -4929149591599911165L; + + /** + * The wrapped set; stored both here and in the superclass to avoid + * excessive casting. + * @serial the wrapped set + */ + private SortedSet ss; + + /** + * Wrap a given set. + * @param ss the set to wrap + * @throws NullPointerException if ss is null + */ + UnmodifiableSortedSet(SortedSet ss) + { + super(ss); + this.ss = ss; + } + + /** + * Returns the comparator used in sorting the underlying set, + * or null if it is the elements' natural ordering. + * + * @return the sorting comparator + */ + public Comparator comparator() + { + return ss.comparator(); + } + + /** + * Returns the first (lowest sorted) element in the underlying + * set. + * + * @return the first element. + * @throws NoSuchElementException if the set is empty. + */ + public Object first() + { + return ss.first(); + } + + /** + * Returns a unmodifiable view of the portion of the set strictly + * less than toElement. The view is backed by the underlying set, + * so changes in one show up in the other. The subset supports + * all optional operations of the original. This operation + * is equivalent to <code>subSet(first(), toElement)</code>. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of toElement. Note that the endpoint, toElement, + * is not included; if you want this value included, pass its successor object in to + * toElement. For example, for Integers, you could request + * <code>headSet(new Integer(limit.intValue() + 1))</code>. + * + * @param toElement the exclusive upper range of the subset + * @return the subset. + * @throws ClassCastException if toElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and toElement is out + * of range. + * @throws NullPointerException if toElement is null but the set does not + * allow null elements. + */ + public SortedSet headSet(Object toElement) + { + return new UnmodifiableSortedSet(ss.headSet(toElement)); + } + + /** + * Returns the last (highest sorted) element in the underlying + * set. + * + * @return the last element. + * @throws NoSuchElementException if the set is empty. + */ + public Object last() + { + return ss.last(); + } + + /** + * Returns a unmodifiable view of the portion of the set greater than or + * equal to fromElement, and strictly less than toElement. The view is backed by + * the underlying set, so changes in one show up in the other. The subset + * supports all optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement and toElement. Note that the + * lower endpoint is included, but the upper is not; if you want to + * change the inclusion or exclusion of an endpoint, pass its successor + * object in instead. For example, for Integers, you can request + * <code>subSet(new Integer(lowlimit.intValue() + 1), + * new Integer(highlimit.intValue() + 1))</code> to reverse + * the inclusiveness of both endpoints. + * + * @param fromElement the inclusive lower range of the subset. + * @param toElement the exclusive upper range of the subset. + * @return the subset. + * @throws ClassCastException if fromElement or toElement is not comparable + * to the set contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement or + * toElement is out of range. + * @throws NullPointerException if fromElement or toElement is null but the + * set does not allow null elements. + */ + public SortedSet subSet(Object fromElement, Object toElement) + { + return new UnmodifiableSortedSet(ss.subSet(fromElement, toElement)); + } + + /** + * Returns a unmodifiable view of the portion of the set greater than or equal to + * fromElement. The view is backed by the underlying set, so changes in one show up + * in the other. The subset supports all optional operations of the original. + * <p> + * + * The returned set throws an IllegalArgumentException any time an element is + * used which is out of the range of fromElement. Note that the endpoint, + * fromElement, is included; if you do not want this value to be included, pass its + * successor object in to fromElement. For example, for Integers, you could request + * <code>tailSet(new Integer(limit.intValue() + 1))</code>. + * + * @param fromElement the inclusive lower range of the subset + * @return the subset. + * @throws ClassCastException if fromElement is not comparable to the set + * contents. + * @throws IllegalArgumentException if this is a subSet, and fromElement is + * out of range. + * @throws NullPointerException if fromElement is null but the set does not + * allow null elements. + */ + public SortedSet tailSet(Object fromElement) + { + return new UnmodifiableSortedSet(ss.tailSet(fromElement)); + } + } // class UnmodifiableSortedSet +} // class Collections |