javafx.collections.FXCollections Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.collections;
import com.sun.javafx.collections.ListListenerHelper;
import com.sun.javafx.collections.MapListenerHelper;
import com.sun.javafx.collections.SetListenerHelper;
import java.lang.reflect.Array;
import java.util.AbstractList;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Set;
import javafx.beans.InvalidationListener;
import com.sun.javafx.collections.ObservableListWrapper;
import com.sun.javafx.collections.ObservableMapWrapper;
import com.sun.javafx.collections.ObservableSetWrapper;
import com.sun.javafx.collections.MapAdapterChange;
import com.sun.javafx.collections.ObservableFloatArrayImpl;
import com.sun.javafx.collections.ObservableIntegerArrayImpl;
import com.sun.javafx.collections.ObservableSequentialListWrapper;
import com.sun.javafx.collections.SetAdapterChange;
import com.sun.javafx.collections.SortableList;
import com.sun.javafx.collections.SourceAdapterChange;
import java.util.RandomAccess;
import javafx.beans.Observable;
import javafx.util.Callback;
/**
* Utility class that consists of static methods that are 1:1 copies of java.util.Collections methods.
*
* The wrapper methods (like synchronizedObservableList or emptyObservableList) has exactly the same
* functionality as the methods in Collections, with exception that they return ObservableList and are
* therefore suitable for methods that require ObservableList on input.
*
* The utility methods are here mainly for performance reasons. All methods are optimized in a way that
* they yield only limited number of notifications. On the other hand, java.util.Collections methods
* might call "modification methods" on an ObservableList multiple times, resulting in a number of notifications.
*
* @since JavaFX 2.0
*/
public class FXCollections {
/** Not to be instantiated. */
private FXCollections() { }
/**
* Constructs an ObservableList that is backed by the specified list.
* Mutation operations on the ObservableList instance will be reported
* to observers that have registered on that instance.
* Note that mutation operations made directly to the underlying list are
* not reported to observers of any ObservableList that
* wraps it.
*
* @param The type of List to be wrapped
* @param list a concrete List that backs this ObservableList
* @return a newly created ObservableList
*/
public static ObservableList observableList(List list) {
if (list == null) {
throw new NullPointerException();
}
return list instanceof RandomAccess ? new ObservableListWrapper<>(list) :
new ObservableSequentialListWrapper<>(list);
}
/**
* Constructs an {@code ObservableList} that is backed by the specified list and listens to changes in observables of its items.
* Mutation operations made directly to the underlying list are
* not reported to observers of any {@code ObservableList} that
* wraps it.
*
* The {@code extractor} returns observables (usually properties) of the objects in the created list. These observables are
* listened for changes, and the user is notified of these through an
* {@linkplain ListChangeListener.Change#wasUpdated() update} change of an attached {@code ListChangeListener}. These changes
* are unrelated to the changes made to the observable list itself using methods such as {@code add} and {@code remove}.
*
* For example, a list of {@code Shape}s can listen to changes in the shapes' {@code fill} property.
*
* @param The type of {@code List} to be wrapped
* @param list a concrete {@code List} that backs this {@code ObservableList}
* @param extractor element to {@code Observable[]} converter
* @return a newly created {@code ObservableList}
* @see #observableArrayList(javafx.util.Callback)
* @since JavaFX 2.1
*/
public static ObservableList observableList(List list, Callback extractor) {
if (list == null || extractor == null) {
throw new NullPointerException();
}
return list instanceof RandomAccess ? new ObservableListWrapper<>(list, extractor) :
new ObservableSequentialListWrapper<>(list, extractor);
}
/**
* Constructs an ObservableMap that is backed by the specified map.
* Mutation operations on the ObservableMap instance will be reported
* to observers that have registered on that instance.
* Note that mutation operations made directly to the underlying map are not
* reported to observers of any ObservableMap that wraps it.
* @param the type of the wrapped key
* @param the type of the wrapped value
* @param map a Map that backs this ObservableMap
* @return a newly created ObservableMap
*/
public static ObservableMap observableMap(Map map) {
if (map == null) {
throw new NullPointerException();
}
return new ObservableMapWrapper<>(map);
}
/**
* Constructs an ObservableSet that is backed by the specified set.
* Mutation operations on the ObservableSet instance will be reported
* to observers that have registered on that instance.
* Note that mutation operations made directly to the underlying set are not
* reported to observers of any ObservableSet that wraps it.
* @param The type of List to be wrapped
* @param set a Set that backs this ObservableSet
* @return a newly created ObservableSet
* @since JavaFX 2.1
*/
public static ObservableSet observableSet(Set set) {
if (set == null) {
throw new NullPointerException();
}
return new ObservableSetWrapper<>(set);
}
/**
* Constructs an ObservableSet backed by a HashSet
* that contains all the specified elements.
* @param The type of List to be wrapped
* @param elements elements that will be added into returned ObservableSet
* @return a newly created ObservableSet
* @since JavaFX 2.1
*/
public static ObservableSet observableSet(E... elements) {
if (elements == null) {
throw new NullPointerException();
}
Set set = new HashSet<>(elements.length);
Collections.addAll(set, elements);
return new ObservableSetWrapper<>(set);
}
/**
* Constructs a read-only interface to the specified ObservableMap. Only
* mutation operations made to the underlying ObservableMap will be reported
* to observers that have registered on the unmodifiable instance. This allows
* clients to track changes in a Map but disallows the ability to modify it.
* @param the type of the wrapped key
* @param the type of the wrapped value
* @param map an ObservableMap that is to be monitored by this interface
* @return a newly created UnmodifiableObservableMap
*/
public static ObservableMap unmodifiableObservableMap(ObservableMap map) {
if (map == null) {
throw new NullPointerException();
}
return new com.sun.javafx.collections.UnmodifiableObservableMap<>(map);
}
/**
* Creates and returns a typesafe wrapper on top of provided observable map.
* @param the type of the wrapped key
* @param the type of the wrapped value
* @param map an Observable map to be wrapped
* @param keyType the type of key that {@code map} is permitted to hold
* @param valueType the type of value that {@code map} is permitted to hold
* @return a dynamically typesafe view of the specified map
* @see Collections#checkedMap(java.util.Map, java.lang.Class, java.lang.Class)
* @since JavaFX 8.0
*/
public static ObservableMap checkedObservableMap(ObservableMap map, Class keyType, Class valueType) {
if (map == null || keyType == null || valueType == null) {
throw new NullPointerException();
}
return new CheckedObservableMap<>(map, keyType, valueType);
}
/**
* Creates and returns a synchronized wrapper on top of provided observable map.
* @param the type of the wrapped key
* @param the type of the wrapped value
* @param map the map to be "wrapped" in a synchronized map.
* @return A synchronized version of the observable map
* @see Collections#synchronizedMap(java.util.Map)
* @since JavaFX 8.0
*/
public static ObservableMap synchronizedObservableMap(ObservableMap map) {
if (map == null) {
throw new NullPointerException();
}
return new SynchronizedObservableMap<>(map);
}
private static final ObservableMap, ?> EMPTY_OBSERVABLE_MAP = new EmptyObservableMap<>();
/**
* Creates an empty unmodifiable observable map.
* @param the type of the wrapped key
* @param the type of the wrapped value
* @return An empty unmodifiable observable map
* @see Collections#emptyMap()
* @since JavaFX 8.0
*/
@SuppressWarnings("unchecked")
public static ObservableMap emptyObservableMap() {
return (ObservableMap) EMPTY_OBSERVABLE_MAP;
}
/**
* Creates a new empty observable integer array.
* @return a newly created ObservableIntegerArray
* @since JavaFX 8.0
*/
public static ObservableIntegerArray observableIntegerArray() {
return new ObservableIntegerArrayImpl();
}
/**
* Creates a new observable integer array with {@code values} set to it.
* @param values the values that will be in the new observable integer array
* @return a newly created ObservableIntegerArray
* @since JavaFX 8.0
*/
public static ObservableIntegerArray observableIntegerArray(int... values) {
return new ObservableIntegerArrayImpl(values);
}
/**
* Creates a new observable integer array with copy of elements in given
* {@code array}.
* @param array observable integer array to copy
* @return a newly created ObservableIntegerArray
* @since JavaFX 8.0
*/
public static ObservableIntegerArray observableIntegerArray(ObservableIntegerArray array) {
return new ObservableIntegerArrayImpl(array);
}
/**
* Creates a new empty observable float array.
* @return a newly created ObservableFloatArray
* @since JavaFX 8.0
*/
public static ObservableFloatArray observableFloatArray() {
return new ObservableFloatArrayImpl();
}
/**
* Creates a new observable float array with {@code values} set to it.
* @param values the values that will be in the new observable float array
* @return a newly created ObservableFloatArray
* @since JavaFX 8.0
*/
public static ObservableFloatArray observableFloatArray(float... values) {
return new ObservableFloatArrayImpl(values);
}
/**
* Creates a new observable float array with copy of elements in given
* {@code array}.
* @param array observable float array to copy
* @return a newly created ObservableFloatArray
* @since JavaFX 8.0
*/
public static ObservableFloatArray observableFloatArray(ObservableFloatArray array) {
return new ObservableFloatArrayImpl(array);
}
/**
* Creates a new empty observable list that is backed by an array list.
* @see #observableList(java.util.List)
* @param The type of List to be wrapped
* @return a newly created ObservableList
*/
public static ObservableList observableArrayList() {
return observableList(new ArrayList<>());
}
/**
* Creates a new empty {@code ObservableList} that is backed by an array list and listens to changes in observables of its items.
*
* The {@code extractor} returns observables (usually properties) of the objects in the created list. These observables are
* listened for changes and the user is notified of these through an
* {@linkplain ListChangeListener.Change#wasUpdated() update} change of an attached {@code ListChangeListener}. These changes
* are unrelated to the changes made to the observable list itself using methods such as {@code add} and {@code remove}.
*
* For example, a list of {@code Shape}s can listen to changes in the shapes' {@code fill} property.
*
* @param The type of {@code List} to be wrapped
* @param extractor element to {@code Observable[]} converter
* @return a newly created {@code ObservableList}
* @see #observableList(java.util.List, javafx.util.Callback)
* @since JavaFX 2.1
*/
public static ObservableList observableArrayList(Callback extractor) {
return observableList(new ArrayList<>(), extractor);
}
/**
* Creates a new observable array list with {@code items} added to it.
* @param The type of List to be wrapped
* @param items the items that will be in the new observable ArrayList
* @return a newly created observableArrayList
* @see #observableArrayList()
*/
public static ObservableList observableArrayList(E... items) {
return observableList(new ArrayList<>(Arrays.asList(items)));
}
/**
* Creates a new observable array list and adds a content of collection {@code col}
* to it.
* @param The type of List to be wrapped
* @param col a collection which content should be added to the observableArrayList
* @return a newly created observableArrayList
*/
public static ObservableList observableArrayList(Collection extends E> col) {
return observableList(new ArrayList<>(col));
}
/**
* Creates a new empty observable map that is backed by a HashMap.
* @param the type of the wrapped key
* @param the type of the wrapped value
* @return a newly created observable HashMap
*/
public static ObservableMap observableHashMap() {
return observableMap(new HashMap());
}
/**
* Concatenates more observable lists into one. The resulting list
* would be backed by an array list.
* @param The type of List to be wrapped
* @param lists lists to concatenate
* @return new observable array list concatenated from the arguments
*/
public static ObservableList concat(ObservableList... lists) {
if (lists.length == 0 ) {
return observableArrayList();
}
if (lists.length == 1) {
return observableArrayList(lists[0]);
}
ArrayList backingList = new ArrayList<>();
for (ObservableList s : lists) {
backingList.addAll(s);
}
return observableList(backingList);
}
/**
* Creates and returns unmodifiable wrapper list on top of provided observable list.
* @param list an ObservableList that is to be wrapped
* @param The type of List to be wrapped
* @return an ObserableList wrapper that is unmodifiable
* @see Collections#unmodifiableList(java.util.List)
*/
public static ObservableList unmodifiableObservableList(ObservableList list) {
if (list == null) {
throw new NullPointerException();
}
return new UnmodifiableObservableListImpl<>(list);
}
/**
* Creates and returns a typesafe wrapper on top of provided observable list.
* @param The type of List to be wrapped
* @param list an Observable list to be wrapped
* @param type the type of element that {@code list} is permitted to hold
* @return a dynamically typesafe view of the specified list
* @see Collections#checkedList(java.util.List, java.lang.Class)
*/
public static ObservableList checkedObservableList(ObservableList list, Class type) {
if (list == null) {
throw new NullPointerException();
}
return new CheckedObservableList<>(list, type);
}
/**
* Creates and returns a synchronized wrapper on top of provided observable list.
* @param The type of List to be wrapped
* @param list the list to be "wrapped" in a synchronized list.
* @return A synchronized version of the observable list
* @see Collections#synchronizedList(java.util.List)
*/
public static ObservableList synchronizedObservableList(ObservableList list) {
if (list == null) {
throw new NullPointerException();
}
return new SynchronizedObservableList<>(list);
}
private static final ObservableList> EMPTY_OBSERVABLE_LIST = new EmptyObservableList<>();
/**
* Creates an empty unmodifiable observable list.
* @param The type of List to be wrapped
* @return An empty unmodifiable observable list
* @see Collections#emptyList()
*/
@SuppressWarnings("unchecked")
public static ObservableList emptyObservableList() {
return (ObservableList) EMPTY_OBSERVABLE_LIST;
}
/**
* Creates an unmodifiable observable list with single element.
* @param The type of List to be wrapped
* @param e the only elements that will be contained in this singleton observable list
* @return a singleton observable list
* @see Collections#singletonList(java.lang.Object)
*/
public static ObservableList singletonObservableList(E e) {
return new SingletonObservableList<>(e);
}
/**
* Creates and returns unmodifiable wrapper on top of provided observable set.
* @param The type of List to be wrapped
* @param set an ObservableSet that is to be wrapped
* @return an ObserableSet wrapper that is unmodifiable
* @see Collections#unmodifiableSet(java.util.Set)
* @since JavaFX 8.0
*/
public static ObservableSet unmodifiableObservableSet(ObservableSet set) {
if (set == null) {
throw new NullPointerException();
}
return new UnmodifiableObservableSet<>(set);
}
/**
* Creates and returns a typesafe wrapper on top of provided observable set.
* @param The type of List to be wrapped
* @param set an Observable set to be wrapped
* @param type the type of element that {@code set} is permitted to hold
* @return a dynamically typesafe view of the specified set
* @see Collections#checkedSet(java.util.Set, java.lang.Class)
* @since JavaFX 8.0
*/
public static ObservableSet checkedObservableSet(ObservableSet set, Class type) {
if (set == null) {
throw new NullPointerException();
}
return new CheckedObservableSet<>(set, type);
}
/**
* Creates and returns a synchronized wrapper on top of provided observable set.
* @param The type of List to be wrapped
* @param set the set to be "wrapped" in a synchronized set.
* @return A synchronized version of the observable set
* @see Collections#synchronizedSet(java.util.Set)
* @since JavaFX 8.0
*/
public static ObservableSet synchronizedObservableSet(ObservableSet set) {
if (set == null) {
throw new NullPointerException();
}
return new SynchronizedObservableSet<>(set);
}
private static final ObservableSet> EMPTY_OBSERVABLE_SET = new EmptyObservableSet<>();
/**
* Creates an empty unmodifiable observable set.
* @param The type of List to be wrapped
* @return An empty unmodifiable observable set
* @see Collections#emptySet()
* @since JavaFX 8.0
*/
@SuppressWarnings("unchecked")
public static ObservableSet emptyObservableSet() {
return (ObservableSet) EMPTY_OBSERVABLE_SET;
}
/**
* Copies elements from src to dest. Fires only one change notification on dest.
* @param The type of List to be wrapped
* @param dest the destination observable list
* @param src the source list
* @see Collections#copy(java.util.List, java.util.List)
*/
@SuppressWarnings("unchecked")
public static void copy(ObservableList super T> dest, List extends T> src) {
final int srcSize = src.size();
if (srcSize > dest.size()) {
throw new IndexOutOfBoundsException("Source does not fit in dest");
}
T[] destArray = (T[]) dest.toArray();
System.arraycopy(src.toArray(), 0, destArray, 0, srcSize);
dest.setAll(destArray);
}
/**
* Fills the provided list with obj. Fires only one change notification on the list.
* @param The type of List to be wrapped
* @param list the list to fill
* @param obj the object to fill the list with
* @see Collections#fill(java.util.List, java.lang.Object)
*/
@SuppressWarnings("unchecked")
public static void fill(ObservableList super T> list, T obj) {
T[] newContent = (T[]) new Object[list.size()];
Arrays.fill(newContent, obj);
list.setAll(newContent);
}
/**
* Replace all oldVal elements in the list with newVal element.
* Fires only one change notification on the list.
* @param The type of List to be wrapped
* @param list the list which will have it's elements replaced
* @param oldVal the element that is going to be replace
* @param newVal the replacement
* @return true if the list was modified
* @see Collections#replaceAll(java.util.List, java.lang.Object, java.lang.Object)
*/
@SuppressWarnings("unchecked")
public static boolean replaceAll(ObservableList list, T oldVal, T newVal) {
T[] newContent = (T[]) list.toArray();
boolean modified = false;
for (int i = 0 ; i < newContent.length; ++i) {
if (newContent[i].equals(oldVal)) {
newContent[i] = newVal;
modified = true;
}
}
if (modified) {
list.setAll(newContent);
}
return modified;
}
/**
* Reverses the order in the list.
* Fires only one change notification on the list.
* @param list the list to be reversed
* @see Collections#reverse(java.util.List)
*/
@SuppressWarnings("unchecked")
public static void reverse(ObservableList list) {
Object[] newContent = list.toArray();
for (int i = 0; i < newContent.length / 2; ++i) {
Object tmp = newContent[i];
newContent[i] = newContent[newContent.length - i - 1];
newContent[newContent.length -i - 1] = tmp;
}
list.setAll(newContent);
}
/**
* Rotates the list by distance.
* Fires only one change notification on the list.
* @param list the list to be rotated
* @param distance the distance of rotation
* @see Collections#rotate(java.util.List, int)
*/
@SuppressWarnings("unchecked")
public static void rotate(ObservableList list, int distance) {
Object[] newContent = list.toArray();
int size = list.size();
distance = distance % size;
if (distance < 0)
distance += size;
if (distance == 0)
return;
for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) {
Object displaced = newContent[cycleStart];
Object tmp;
int i = cycleStart;
do {
i += distance;
if (i >= size)
i -= size;
tmp = newContent[i];
newContent[i] = displaced;
displaced = tmp;
nMoved ++;
} while(i != cycleStart);
}
list.setAll(newContent);
}
/**
* Shuffles all elements in the observable list.
* Fires only one change notification on the list.
* @param list the list to shuffle
* @see Collections#shuffle(java.util.List)
*/
public static void shuffle(ObservableList> list) {
if (r == null) {
r = new Random();
}
shuffle(list, r);
}
private static Random r;
/**
* Shuffles all elements in the observable list.
* Fires only one change notification on the list.
* @param list the list to be shuffled
* @param rnd the random generator used for shuffling
* @see Collections#shuffle(java.util.List, java.util.Random)
*/
@SuppressWarnings("unchecked")
public static void shuffle(ObservableList list, Random rnd) {
Object newContent[] = list.toArray();
for (int i = list.size(); i > 1; i--) {
swap(newContent, i - 1, rnd.nextInt(i));
}
list.setAll(newContent);
}
private static void swap(Object[] arr, int i, int j) {
Object tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/**
* Sorts the provided observable list as specified in
* {@link java.util.Collections#sort(List) Collections.sort(List)}.
* Fires only one change notification on the list.
*
* @param the type of elements in this list
* @param list the list to be sorted
*/
public static > void sort(ObservableList list) {
sort(list, Comparator.naturalOrder());
}
/**
* Sorts the provided observable list as specified in
* {@link java.util.Collections#sort(List, Comparator) Collections.sort(List, Comparator)}.
* Fires only one change notification on the list.
*
* @param the type of elements in this list
* @param list the list to sort
* @param comparator the comparator to determine the order of the list. A {@code null} value indicates that the
* elements' natural ordering should be used.
*/
public static void sort(ObservableList list, Comparator super T> comparator) {
if (list instanceof SortableList) {
list.sort(comparator);
} else {
List newContent = new ArrayList<>(list);
newContent.sort(comparator);
list.setAll(newContent);
}
}
private static class EmptyObservableList extends AbstractList implements ObservableList {
private final ListIterator iterator = new ListIterator<>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public E next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasPrevious() {
return false;
}
@Override
public E previous() {
throw new NoSuchElementException();
}
@Override
public int nextIndex() {
return 0;
}
@Override
public int previousIndex() {
return -1;
}
@Override
public void set(Object e) {
throw new UnsupportedOperationException();
}
@Override
public void add(Object e) {
throw new UnsupportedOperationException();
}
};
public EmptyObservableList() {
}
@Override
public final void addListener(InvalidationListener listener) {
}
@Override
public final void removeListener(InvalidationListener listener) {
}
@Override
public void addListener(ListChangeListener super E> o) {
}
@Override
public void removeListener(ListChangeListener super E> o) {
}
@Override
public int size() {
return 0;
}
@Override
public boolean contains(Object o) {
return false;
}
@Override
public Iterator iterator() {
return iterator;
}
@Override
public boolean containsAll(Collection> c) {
return c.isEmpty();
}
@Override
public E get(int index) {
throw new IndexOutOfBoundsException();
}
@Override
public int indexOf(Object o) {
return -1;
}
@Override
public int lastIndexOf(Object o) {
return -1;
}
@Override
public ListIterator listIterator() {
return iterator;
}
@Override
public ListIterator listIterator(int index) {
if (index != 0) {
throw new IndexOutOfBoundsException();
}
return iterator;
}
@Override
public List subList(int fromIndex, int toIndex) {
if (fromIndex != 0 || toIndex != 0) {
throw new IndexOutOfBoundsException();
}
return this;
}
@Override
public boolean addAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean setAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean setAll(Collection extends E> col) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public void remove(int from, int to) {
throw new UnsupportedOperationException();
}
}
private static class SingletonObservableList extends AbstractList implements ObservableList {
private final E element;
public SingletonObservableList(E element) {
if (element == null) {
throw new NullPointerException();
}
this.element = element;
}
@Override
public boolean addAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean setAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean setAll(Collection extends E> col) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(E... elements) {
throw new UnsupportedOperationException();
}
@Override
public void remove(int from, int to) {
throw new UnsupportedOperationException();
}
@Override
public void addListener(InvalidationListener listener) {
}
@Override
public void removeListener(InvalidationListener listener) {
}
@Override
public void addListener(ListChangeListener super E> o) {
}
@Override
public void removeListener(ListChangeListener super E> o) {
}
@Override
public int size() {
return 1;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(Object o) {
return element.equals(o);
}
@Override
public E get(int index) {
if (index != 0) {
throw new IndexOutOfBoundsException();
}
return element;
}
}
private static class UnmodifiableObservableListImpl extends ObservableListBase {
private final ObservableList backingList;
private final ListChangeListener listener;
public UnmodifiableObservableListImpl(ObservableList backingList) {
this.backingList = backingList;
listener = c -> {
fireChange(new SourceAdapterChange<>(UnmodifiableObservableListImpl.this, c));
};
this.backingList.addListener(new WeakListChangeListener<>(listener));
}
@Override
public T get(int index) {
return backingList.get(index);
}
@Override
public int size() {
return backingList.size();
}
@Override
public boolean addAll(T... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean setAll(T... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean setAll(Collection extends T> col) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(T... elements) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(T... elements) {
throw new UnsupportedOperationException();
}
@Override
public void remove(int from, int to) {
throw new UnsupportedOperationException();
}
}
private static class SynchronizedList implements List {
final Object mutex;
private final List backingList;
SynchronizedList(List list, Object mutex) {
this.backingList = list;
this.mutex = mutex;
}
SynchronizedList(List list) {
this.backingList = list;
this.mutex = this;
}
@Override
public int size() {
synchronized(mutex) {
return backingList.size();
}
}
@Override
public boolean isEmpty() {
synchronized(mutex) {
return backingList.isEmpty();
}
}
@Override
public boolean contains(Object o) {
synchronized(mutex) {
return backingList.contains(o);
}
}
@Override
public Iterator iterator() {
return backingList.iterator();
}
@Override
public Object[] toArray() {
synchronized(mutex) {
return backingList.toArray();
}
}
@Override
public X[] toArray(X[] a) {
synchronized(mutex) {
return backingList.toArray(a);
}
}
@Override
public boolean add(T e) {
synchronized(mutex) {
return backingList.add(e);
}
}
@Override
public boolean remove(Object o) {
synchronized(mutex) {
return backingList.remove(o);
}
}
@Override
public boolean containsAll(Collection> c) {
synchronized(mutex) {
return backingList.containsAll(c);
}
}
@Override
public boolean addAll(Collection extends T> c) {
synchronized(mutex) {
return backingList.addAll(c);
}
}
@Override
public boolean addAll(int index, Collection extends T> c) {
synchronized(mutex) {
return backingList.addAll(index, c);
}
}
@Override
public boolean removeAll(Collection> c) {
synchronized(mutex) {
return backingList.removeAll(c);
}
}
@Override
public boolean retainAll(Collection> c) {
synchronized(mutex) {
return backingList.retainAll(c);
}
}
@Override
public void clear() {
synchronized(mutex) {
backingList.clear();
}
}
@Override
public T get(int index) {
synchronized(mutex) {
return backingList.get(index);
}
}
@Override
public T set(int index, T element) {
synchronized(mutex) {
return backingList.set(index, element);
}
}
@Override
public void add(int index, T element) {
synchronized(mutex) {
backingList.add(index, element);
}
}
@Override
public T remove(int index) {
synchronized(mutex) {
return backingList.remove(index);
}
}
@Override
public int indexOf(Object o) {
synchronized(mutex) {
return backingList.indexOf(o);
}
}
@Override
public int lastIndexOf(Object o) {
synchronized(mutex) {
return backingList.lastIndexOf(o);
}
}
@Override
public ListIterator listIterator() {
return backingList.listIterator();
}
@Override
public ListIterator listIterator(int index) {
synchronized(mutex) {
return backingList.listIterator(index);
}
}
@Override
public List subList(int fromIndex, int toIndex) {
synchronized(mutex) {
return new SynchronizedList<>(backingList.subList(fromIndex, toIndex),
mutex);
}
}
@Override
public String toString() {
synchronized(mutex) {
return backingList.toString();
}
}
@Override
public int hashCode() {
synchronized(mutex) {
return backingList.hashCode();
}
}
@Override
public boolean equals(Object o) {
synchronized(mutex) {
return backingList.equals(o);
}
}
}
private static class SynchronizedObservableList extends SynchronizedList implements ObservableList {
private ListListenerHelper helper;
private final ObservableList backingList;
private final ListChangeListener listener;
SynchronizedObservableList(ObservableList seq) {
super(seq);
this.backingList = seq;
listener = c -> {
ListListenerHelper.fireValueChangedEvent(helper, new SourceAdapterChange<>(SynchronizedObservableList.this, c));
};
backingList.addListener(new WeakListChangeListener<>(listener));
}
@Override
public boolean addAll(T... elements) {
synchronized(mutex) {
return backingList.addAll(elements);
}
}
@Override
public boolean setAll(T... elements) {
synchronized(mutex) {
return backingList.setAll(elements);
}
}
@Override
public boolean removeAll(T... elements) {
synchronized(mutex) {
return backingList.removeAll(elements);
}
}
@Override
public boolean retainAll(T... elements) {
synchronized(mutex) {
return backingList.retainAll(elements);
}
}
@Override
public void remove(int from, int to) {
synchronized(mutex) {
backingList.remove(from, to);
}
}
@Override
public boolean setAll(Collection extends T> col) {
synchronized(mutex) {
return backingList.setAll(col);
}
}
@Override
public final void addListener(InvalidationListener listener) {
synchronized (mutex) {
helper = ListListenerHelper.addListener(helper, listener);
}
}
@Override
public final void removeListener(InvalidationListener listener) {
synchronized (mutex) {
helper = ListListenerHelper.removeListener(helper, listener);
}
}
@Override
public void addListener(ListChangeListener super T> listener) {
synchronized (mutex) {
helper = ListListenerHelper.addListener(helper, listener);
}
}
@Override
public void removeListener(ListChangeListener super T> listener) {
synchronized (mutex) {
helper = ListListenerHelper.removeListener(helper, listener);
}
}
}
private static class CheckedObservableList extends ObservableListBase {
private final ObservableList list;
private final Class type;
private final ListChangeListener listener;
CheckedObservableList(ObservableList list, Class type) {
if (list == null || type == null) {
throw new NullPointerException();
}
this.list = list;
this.type = type;
listener = c -> {
fireChange(new SourceAdapterChange<>(CheckedObservableList.this, c));
};
list.addListener(new WeakListChangeListener<>(listener));
}
void typeCheck(Object o) {
if (o != null && !type.isInstance(o)) {
throw new ClassCastException("Attempt to insert "
+ o.getClass() + " element into collection with element type "
+ type);
}
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public X[] toArray(X[] a) {
return list.toArray(a);
}
@Override
public String toString() {
return list.toString();
}
@Override
public boolean remove(Object o) {
return list.remove(o);
}
@Override
public boolean containsAll(Collection> coll) {
return list.containsAll(coll);
}
@Override
public boolean removeAll(Collection> coll) {
return list.removeAll(coll);
}
@Override
public boolean retainAll(Collection> coll) {
return list.retainAll(coll);
}
@Override
public boolean removeAll(T... elements) {
return list.removeAll(elements);
}
@Override
public boolean retainAll(T... elements) {
return list.retainAll(elements);
}
@Override
public void remove(int from, int to) {
list.remove(from, to);
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean equals(Object o) {
return o == this || list.equals(o);
}
@Override
public int hashCode() {
return list.hashCode();
}
@Override
public T get(int index) {
return list.get(index);
}
@Override
public T remove(int index) {
return list.remove(index);
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
@Override
public T set(int index, T element) {
typeCheck(element);
return list.set(index, element);
}
@Override
public void add(int index, T element) {
typeCheck(element);
list.add(index, element);
}
@Override
@SuppressWarnings("unchecked")
public boolean addAll(int index, Collection extends T> c) {
T[] a = null;
try {
a = c.toArray((T[]) Array.newInstance(type, 0));
} catch (ArrayStoreException e) {
throw new ClassCastException();
}
return this.list.addAll(index, Arrays.asList(a));
}
@Override
@SuppressWarnings("unchecked")
public boolean addAll(Collection extends T> coll) {
T[] a = null;
try {
a = coll.toArray((T[]) Array.newInstance(type, 0));
} catch (ArrayStoreException e) {
throw new ClassCastException();
}
return this.list.addAll(Arrays.asList(a));
}
@Override
public ListIterator listIterator() {
return listIterator(0);
}
@Override
public ListIterator listIterator(final int index) {
return new ListIterator<>() {
ListIterator i = list.listIterator(index);
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public T next() {
return i.next();
}
@Override
public boolean hasPrevious() {
return i.hasPrevious();
}
@Override
public T previous() {
return i.previous();
}
@Override
public int nextIndex() {
return i.nextIndex();
}
@Override
public int previousIndex() {
return i.previousIndex();
}
@Override
public void remove() {
i.remove();
}
@Override
public void set(T e) {
typeCheck(e);
i.set(e);
}
@Override
public void add(T e) {
typeCheck(e);
i.add(e);
}
};
}
@Override
public Iterator iterator() {
return new Iterator<>() {
private final Iterator it = list.iterator();
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public T next() {
return it.next();
}
@Override
public void remove() {
it.remove();
}
};
}
@Override
public boolean add(T e) {
typeCheck(e);
return list.add(e);
}
@Override
public List subList(int fromIndex, int toIndex) {
return Collections.checkedList(list.subList(fromIndex, toIndex), type);
}
@Override
@SuppressWarnings("unchecked")
public boolean addAll(T... elements) {
try {
T[] array = (T[]) Array.newInstance(type, elements.length);
System.arraycopy(elements, 0, array, 0, elements.length);
return list.addAll(array);
} catch (ArrayStoreException e) {
throw new ClassCastException();
}
}
@Override
@SuppressWarnings("unchecked")
public boolean setAll(T... elements) {
try {
T[] array = (T[]) Array.newInstance(type, elements.length);
System.arraycopy(elements, 0, array, 0, elements.length);
return list.setAll(array);
} catch (ArrayStoreException e) {
throw new ClassCastException();
}
}
@Override
@SuppressWarnings("unchecked")
public boolean setAll(Collection extends T> col) {
T[] a = null;
try {
a = col.toArray((T[]) Array.newInstance(type, 0));
} catch (ArrayStoreException e) {
throw new ClassCastException();
}
return list.setAll(Arrays.asList(a));
}
}
private static class EmptyObservableSet extends AbstractSet implements ObservableSet {
private final Iterator iterator = new Iterator<>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public E next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
public EmptyObservableSet() {
}
@Override
public void addListener(InvalidationListener listener) {
}
@Override
public void removeListener(InvalidationListener listener) {
}
@Override
public void addListener(SetChangeListener super E> listener) {
}
@Override
public void removeListener(SetChangeListener super E> listener) {
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean contains(Object obj) {
return false;
}
@Override
public boolean containsAll(Collection> c) {
return c.isEmpty();
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public X[] toArray(X[] a) {
if (a.length > 0)
a[0] = null;
return a;
}
@Override
public Iterator iterator() {
return iterator;
}
}
private static class UnmodifiableObservableSet extends AbstractSet implements ObservableSet {
private final ObservableSet backingSet;
private SetListenerHelper listenerHelper;
private SetChangeListener listener;
public UnmodifiableObservableSet(ObservableSet backingSet) {
this.backingSet = backingSet;
this.listener = null;
}
private void initListener() {
if (listener == null) {
listener = c -> {
callObservers(new SetAdapterChange<>(UnmodifiableObservableSet.this, c));
};
this.backingSet.addListener(new WeakSetChangeListener<>(listener));
}
}
private void callObservers(SetChangeListener.Change extends E> change) {
SetListenerHelper.fireValueChangedEvent(listenerHelper, change);
}
@Override
public Iterator iterator() {
return new Iterator<>() {
private final Iterator extends E> i = backingSet.iterator();
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public E next() {
return i.next();
}
};
}
@Override
public int size() {
return backingSet.size();
}
@Override
public boolean isEmpty() {
return backingSet.isEmpty();
}
@Override
public boolean contains(Object o) {
return backingSet.contains(o);
}
@Override
public void addListener(InvalidationListener listener) {
initListener();
listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(InvalidationListener listener) {
listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public void addListener(SetChangeListener super E> listener) {
initListener();
listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(SetChangeListener super E> listener) {
listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public boolean add(E e) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
}
private static class SynchronizedSet implements Set {
final Object mutex;
private final Set backingSet;
SynchronizedSet(Set set, Object mutex) {
this.backingSet = set;
this.mutex = mutex;
}
SynchronizedSet(Set set) {
this.backingSet = set;
this.mutex = this;
}
@Override
public int size() {
synchronized(mutex) {
return backingSet.size();
}
}
@Override
public boolean isEmpty() {
synchronized(mutex) {
return backingSet.isEmpty();
}
}
@Override
public boolean contains(Object o) {
synchronized(mutex) {
return backingSet.contains(o);
}
}
@Override
public Iterator iterator() {
return backingSet.iterator();
}
@Override
public Object[] toArray() {
synchronized(mutex) {
return backingSet.toArray();
}
}
@Override
public X[] toArray(X[] a) {
synchronized(mutex) {
return backingSet.toArray(a);
}
}
@Override
public boolean add(E e) {
synchronized(mutex) {
return backingSet.add(e);
}
}
@Override
public boolean remove(Object o) {
synchronized(mutex) {
return backingSet.remove(o);
}
}
@Override
public boolean containsAll(Collection> c) {
synchronized(mutex) {
return backingSet.containsAll(c);
}
}
@Override
public boolean addAll(Collection extends E> c) {
synchronized(mutex) {
return backingSet.addAll(c);
}
}
@Override
public boolean retainAll(Collection> c) {
synchronized(mutex) {
return backingSet.retainAll(c);
}
}
@Override
public boolean removeAll(Collection> c) {
synchronized(mutex) {
return backingSet.removeAll(c);
}
}
@Override
public void clear() {
synchronized(mutex) {
backingSet.clear();
}
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
synchronized(mutex) {
return backingSet.equals(o);
}
}
@Override
public int hashCode() {
synchronized (mutex) {
return backingSet.hashCode();
}
}
}
private static class SynchronizedObservableSet extends SynchronizedSet implements ObservableSet {
private final ObservableSet backingSet;
private SetListenerHelper listenerHelper;
private final SetChangeListener listener;
SynchronizedObservableSet(ObservableSet set) {
super(set);
backingSet = set;
listener = c -> {
SetListenerHelper.fireValueChangedEvent(listenerHelper, new SetAdapterChange<>(SynchronizedObservableSet.this, c));
};
backingSet.addListener(new WeakSetChangeListener<>(listener));
}
@Override
public void addListener(InvalidationListener listener) {
synchronized (mutex) {
listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
}
}
@Override
public void removeListener(InvalidationListener listener) {
synchronized (mutex) {
listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
}
}
@Override
public void addListener(SetChangeListener super E> listener) {
synchronized (mutex) {
listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
}
}
@Override
public void removeListener(SetChangeListener super E> listener) {
synchronized (mutex) {
listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
}
}
}
private static class CheckedObservableSet extends AbstractSet implements ObservableSet {
private final ObservableSet backingSet;
private final Class type;
private SetListenerHelper listenerHelper;
private final SetChangeListener listener;
CheckedObservableSet(ObservableSet set, Class type) {
if (set == null || type == null) {
throw new NullPointerException();
}
backingSet = set;
this.type = type;
listener = c -> {
callObservers(new SetAdapterChange<>(CheckedObservableSet.this, c));
};
backingSet.addListener(new WeakSetChangeListener<>(listener));
}
private void callObservers(SetChangeListener.Change extends E> c) {
SetListenerHelper.fireValueChangedEvent(listenerHelper, c);
}
void typeCheck(Object o) {
if (o != null && !type.isInstance(o)) {
throw new ClassCastException("Attempt to insert "
+ o.getClass() + " element into collection with element type "
+ type);
}
}
@Override
public void addListener(InvalidationListener listener) {
listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(InvalidationListener listener) {
listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public void addListener(SetChangeListener super E> listener) {
listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(SetChangeListener super E> listener) {
listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public int size() {
return backingSet.size();
}
@Override
public boolean isEmpty() {
return backingSet.isEmpty();
}
@Override
public boolean contains(Object o) {
return backingSet.contains(o);
}
@Override
public Object[] toArray() {
return backingSet.toArray();
}
@Override
public T[] toArray(T[] a) {
return backingSet.toArray(a);
}
@Override
public boolean add(E e) {
typeCheck(e);
return backingSet.add(e);
}
@Override
public boolean remove(Object o) {
return backingSet.remove(o);
}
@Override
public boolean containsAll(Collection> c) {
return backingSet.containsAll(c);
}
@Override
@SuppressWarnings("unchecked")
public boolean addAll(Collection extends E> c) {
E[] a = null;
try {
a = c.toArray((E[]) Array.newInstance(type, 0));
} catch (ArrayStoreException e) {
throw new ClassCastException();
}
return backingSet.addAll(Arrays.asList(a));
}
@Override
public boolean retainAll(Collection> c) {
return backingSet.retainAll(c);
}
@Override
public boolean removeAll(Collection> c) {
return backingSet.removeAll(c);
}
@Override
public void clear() {
backingSet.clear();
}
@Override
public boolean equals(Object o) {
return o == this || backingSet.equals(o);
}
@Override
public int hashCode() {
return backingSet.hashCode();
}
@Override
public Iterator iterator() {
final Iterator it = backingSet.iterator();
return new Iterator<>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public E next() {
return it.next();
}
@Override
public void remove() {
it.remove();
}
};
}
}
private static class EmptyObservableMap extends AbstractMap implements ObservableMap {
public EmptyObservableMap() {
}
@Override
public void addListener(InvalidationListener listener) {
}
@Override
public void removeListener(InvalidationListener listener) {
}
@Override
public void addListener(MapChangeListener super K, ? super V> listener) {
}
@Override
public void removeListener(MapChangeListener super K, ? super V> listener) {
}
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public boolean containsKey(Object key) {
return false;
}
@Override
public boolean containsValue(Object value) {
return false;
}
@Override
public V get(Object key) {
return null;
}
@Override
public Set keySet() {
return emptyObservableSet();
}
@Override
public Collection values() {
return emptyObservableSet();
}
@Override
public Set> entrySet() {
return emptyObservableSet();
}
@Override
public boolean equals(Object o) {
return (o instanceof Map) && ((Map,?>)o).isEmpty();
}
@Override
public int hashCode() {
return 0;
}
}
private static class CheckedObservableMap extends AbstractMap implements ObservableMap {
private final ObservableMap backingMap;
private final Class keyType;
private final Class valueType;
private MapListenerHelper listenerHelper;
private final MapChangeListener listener;
CheckedObservableMap(ObservableMap map, Class keyType, Class valueType) {
backingMap = map;
this.keyType = keyType;
this.valueType = valueType;
listener = c -> {
callObservers(new MapAdapterChange<>(CheckedObservableMap.this, c));
};
backingMap.addListener(new WeakMapChangeListener<>(listener));
}
private void callObservers(MapChangeListener.Change extends K, ? extends V> c) {
MapListenerHelper.fireValueChangedEvent(listenerHelper, c);
}
void typeCheck(Object key, Object value) {
if (key != null && !keyType.isInstance(key)) {
throw new ClassCastException("Attempt to insert "
+ key.getClass() + " key into map with key type "
+ keyType);
}
if (value != null && !valueType.isInstance(value)) {
throw new ClassCastException("Attempt to insert "
+ value.getClass() + " value into map with value type "
+ valueType);
}
}
@Override
public void addListener(InvalidationListener listener) {
listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(InvalidationListener listener) {
listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public void addListener(MapChangeListener super K, ? super V> listener) {
listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
}
@Override
public void removeListener(MapChangeListener super K, ? super V> listener) {
listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
}
@Override
public int size() {
return backingMap.size();
}
@Override
public boolean isEmpty() {
return backingMap.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return backingMap.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return backingMap.containsValue(value);
}
@Override
public V get(Object key) {
return backingMap.get(key);
}
@Override
public V put(K key, V value) {
typeCheck(key, value);
return backingMap.put(key, value);
}
@Override
public V remove(Object key) {
return backingMap.remove(key);
}
@Override
@SuppressWarnings("unchecked")
public void putAll(Map extends K, ? extends V> t) {
// Satisfy the following goals:
// - good diagnostics in case of type mismatch
// - all-or-nothing semantics
// - protection from malicious t
// - correct behavior if t is a concurrent map
Object[] entries = t.entrySet().toArray();
List> checked =
new ArrayList<>(entries.length);
for (Object o : entries) {
Map.Entry,?> e = (Map.Entry,?>) o;
Object k = e.getKey();
Object v = e.getValue();
typeCheck(k, v);
checked.add(
new AbstractMap.SimpleImmutableEntry<>((K) k, (V) v));
}
for (Map.Entry e : checked)
backingMap.put(e.getKey(), e.getValue());
}
@Override
public void clear() {
backingMap.clear();
}
@Override
public Set keySet() {
return backingMap.keySet();
}
@Override
public Collection values() {
return backingMap.values();
}
private transient Set> entrySet = null;
@Override
public Set> entrySet() {
if (entrySet==null)
entrySet = new CheckedEntrySet<>(backingMap.entrySet(), valueType);
return entrySet;
}
@Override
public boolean equals(Object o) {
return o == this || backingMap.equals(o);
}
@Override
public int hashCode() {
return backingMap.hashCode();
}
static class CheckedEntrySet implements Set> {
private final Set> s;
private final Class valueType;
CheckedEntrySet(Set> s, Class valueType) {
this.s = s;
this.valueType = valueType;
}
@Override
public int size() {
return s.size();
}
@Override
public boolean isEmpty() {
return s.isEmpty();
}
@Override
public String toString() {
return s.toString();
}
@Override
public int hashCode() {
return s.hashCode();
}
@Override
public void clear() {
s.clear();
}
@Override
public boolean add(Map.Entry e) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection extends Map.Entry> coll) {
throw new UnsupportedOperationException();
}
@Override
public Iterator> iterator() {
final Iterator> i = s.iterator();
final Class valueType = this.valueType;
return new Iterator<>() {
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public void remove() {
i.remove();
}
@Override
public Map.Entry next() {
return checkedEntry(i.next(), valueType);
}
};
}
@Override
@SuppressWarnings("unchecked")
public Object[] toArray() {
Object[] source = s.toArray();
/*
* Ensure that we don't get an ArrayStoreException even if
* s.toArray returns an array of something other than Object
*/
Object[] dest = (CheckedEntry.class.isInstance(
source.getClass().getComponentType()) ? source :
new Object[source.length]);
for (int i = 0; i < source.length; i++)
dest[i] = checkedEntry((Map.Entry)source[i],
valueType);
return dest;
}
@Override
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
// We don't pass a to s.toArray, to avoid window of
// vulnerability wherein an unscrupulous multithreaded client
// could get his hands on raw (unwrapped) Entries from s.
T[] arr = s.toArray(a.length==0 ? a : Arrays.copyOf(a, 0));
for (int i=0; i)arr[i],
valueType);
if (arr.length > a.length)
return arr;
System.arraycopy(arr, 0, a, 0, arr.length);
if (a.length > arr.length)
a[arr.length] = null;
return a;
}
/**
* This method is overridden to protect the backing set against
* an object with a nefarious equals function that senses
* that the equality-candidate is Map.Entry and calls its
* setValue method.
*/
@Override
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> e = (Map.Entry,?>) o;
return s.contains(
(e instanceof CheckedEntry) ? e : checkedEntry(e, valueType));
}
/**
* The bulk collection methods are overridden to protect
* against an unscrupulous collection whose contains(Object o)
* method senses when o is a Map.Entry, and calls o.setValue.
*/
@Override
public boolean containsAll(Collection> c) {
for (Object o : c)
if (!contains(o)) // Invokes safe contains() above
return false;
return true;
}
@Override
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
return s.remove(new AbstractMap.SimpleImmutableEntry