1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.collections;
  27 
  28 import com.sun.javafx.collections.ListListenerHelper;
  29 import com.sun.javafx.collections.MapListenerHelper;
  30 import com.sun.javafx.collections.SetListenerHelper;
  31 import java.lang.reflect.Array;
  32 import java.util.AbstractList;
  33 import java.util.AbstractMap;
  34 import java.util.AbstractSet;
  35 import java.util.ArrayList;
  36 import java.util.Arrays;
  37 import java.util.Collection;
  38 import java.util.Collections;
  39 import java.util.Comparator;
  40 import java.util.HashMap;
  41 import java.util.HashSet;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.ListIterator;
  45 import java.util.Map;
  46 import java.util.NoSuchElementException;
  47 import java.util.Random;
  48 import java.util.Set;
  49 
  50 import javafx.beans.InvalidationListener;
  51 
  52 import com.sun.javafx.collections.ObservableListWrapper;
  53 import com.sun.javafx.collections.ObservableMapWrapper;
  54 import com.sun.javafx.collections.ObservableSetWrapper;
  55 import com.sun.javafx.collections.MapAdapterChange;
  56 import com.sun.javafx.collections.ObservableFloatArrayImpl;
  57 import com.sun.javafx.collections.ObservableIntegerArrayImpl;
  58 import com.sun.javafx.collections.ObservableSequentialListWrapper;
  59 import com.sun.javafx.collections.SetAdapterChange;
  60 import com.sun.javafx.collections.SortableList;
  61 import com.sun.javafx.collections.SourceAdapterChange;
  62 import java.util.RandomAccess;
  63 import javafx.beans.Observable;
  64 import javafx.collections.ListChangeListener.Change;
  65 import javafx.util.Callback;
  66 
  67 /**
  68  * Utility class that consists of static methods that are 1:1 copies of java.util.Collections methods.
  69  * <br><br>
  70  * The wrapper methods (like synchronizedObservableList or emptyObservableList) has exactly the same
  71  * functionality as the methods in Collections, with exception that they return ObservableList and are
  72  * therefore suitable for methods that require ObservableList on input.
  73  * <br><br>
  74  * The utility methods are here mainly for performance reasons. All methods are optimized in a way that
  75  * they yield only limited number of notifications. On the other hand, java.util.Collections methods
  76  * might call "modification methods" on an ObservableList multiple times, resulting in a number of notifications.
  77  *
  78  * @since JavaFX 2.0
  79  */
  80 public class FXCollections {
  81     /** Not to be instantiated. */
  82     private FXCollections() { }
  83 
  84     /**
  85      * Constructs an ObservableList that is backed by the specified list.
  86      * Mutation operations on the ObservableList instance will be reported
  87      * to observers that have registered on that instance.<br>
  88      * Note that mutation operations made directly to the underlying list are
  89      * <em>not</em> reported to observers of any ObservableList that
  90      * wraps it.
  91      *
  92      * @param list a concrete List that backs this ObservableList
  93      * @return a newly created ObservableList
  94      */
  95     public static <E> ObservableList<E> observableList(List<E> list) {
  96         if (list == null) {
  97             throw new NullPointerException();
  98         }
  99         return list instanceof RandomAccess ? new ObservableListWrapper<E>(list) :
 100                 new ObservableSequentialListWrapper<E>(list);
 101     }
 102 
 103     /**
 104      * Constructs an ObservableList that is backed by the specified list.
 105      * Mutation operations on the ObservableList instance will be reported
 106      * to observers that have registered on that instance.<br>
 107      * Note that mutation operations made directly to the underlying list are
 108      * <em>not</em> reported to observers of any ObservableList that
 109      * wraps it.
 110      * <br>
 111      * This list also reports mutations of the elements in it by using <code>extractor</code>.
 112      * Observable objects returned by extractor (applied to each list element) are listened for changes
 113      * and transformed into "update" change of ListChangeListener.
 114      *
 115      * @param list a concrete List that backs this ObservableList
 116      * @param extractor element to Observable[] convertor
 117      * @since JavaFX 2.1
 118      * @return a newly created ObservableList
 119      */
 120     public static <E> ObservableList<E> observableList(List<E> list, Callback<E, Observable[]> extractor) {
 121         if (list == null || extractor == null) {
 122             throw new NullPointerException();
 123         }
 124         return list instanceof RandomAccess ? new ObservableListWrapper<E>(list, extractor) :
 125             new ObservableSequentialListWrapper<E>(list, extractor);
 126     }
 127 
 128     /**
 129      * Constructs an ObservableMap that is backed by the specified map.
 130      * Mutation operations on the ObservableMap instance will be reported
 131      * to observers that have registered on that instance.<br>
 132      * Note that mutation operations made directly to the underlying map are <em>not</em>
 133      * reported to observers of any ObservableMap that wraps it.
 134      * @param map a Map that backs this ObservableMap
 135      * @return a newly created ObservableMap
 136      */
 137     public static <K, V> ObservableMap<K, V> observableMap(Map<K, V> map) {
 138         if (map == null) {
 139             throw new NullPointerException();
 140         }
 141         return new ObservableMapWrapper<K, V>(map);
 142     }
 143 
 144     /**
 145      * Constructs an ObservableSet that is backed by the specified set.
 146      * Mutation operations on the ObservableSet instance will be reported
 147      * to observers that have registered on that instance.<br>
 148      * Note that mutation operations made directly to the underlying set are <em>not</em>
 149      * reported to observers of any ObservableSet that wraps it.
 150      * @param set a Set that backs this ObservableSet
 151      * @return a newly created ObservableSet
 152      * @since JavaFX 2.1
 153      */
 154     public static <E> ObservableSet<E> observableSet(Set<E> set) {
 155         if (set == null) {
 156             throw new NullPointerException();
 157         }
 158         return new ObservableSetWrapper<E>(set);
 159     }
 160 
 161     /**
 162      * Constructs an ObservableSet backed by a HashSet
 163      * that contains all the specified elements.
 164      * @param elements elements that will be added into returned ObservableSet
 165      * @return a newly created ObservableSet
 166      * @since JavaFX 2.1
 167      */
 168     public static <E> ObservableSet<E> observableSet(E... elements) {
 169         if (elements == null) {
 170             throw new NullPointerException();
 171         }
 172         Set<E> set = new HashSet<E>(elements.length);
 173         Collections.addAll(set, elements);
 174         return new ObservableSetWrapper<E>(set);
 175     }
 176 
 177     /**
 178      * Constructs a read-only interface to the specified ObservableMap. Only
 179      * mutation operations made to the underlying ObservableMap will be reported
 180      * to observers that have registered on the unmodifiable instance. This allows
 181      * clients to track changes in a Map but disallows the ability to modify it.
 182      * @param map an ObservableMap that is to be monitored by this interface
 183      * @return a newly created UnmodifiableObservableMap
 184      */
 185     public static <K, V> ObservableMap<K, V> unmodifiableObservableMap(ObservableMap<K, V> map) {
 186         if (map == null) {
 187             throw new NullPointerException();
 188         }
 189         return new com.sun.javafx.collections.UnmodifiableObservableMap<K, V>(map);
 190     }
 191 
 192     /**
 193      * Creates and returns a typesafe wrapper on top of provided observable map.
 194      * @param map an Observable map to be wrapped
 195      * @param keyType the type of key that {@code map} is permitted to hold
 196      * @param valueType the type of value that {@code map} is permitted to hold
 197      * @return a dynamically typesafe view of the specified map
 198      * @see Collections#checkedMap(java.util.Map, java.lang.Class)
 199      * @since JavaFX 8.0
 200      */
 201     public static <K, V> ObservableMap<K, V> checkedObservableMap(ObservableMap<K, V> map, Class<K> keyType, Class<V> valueType) {
 202         if (map == null || keyType == null || valueType == null) {
 203             throw new NullPointerException();
 204         }
 205         return new CheckedObservableMap<K, V>(map, keyType, valueType);
 206     }
 207 
 208     /**
 209      * Creates and returns a synchronized wrapper on top of provided observable map.
 210      * @param  map the map to be "wrapped" in a synchronized map.
 211      * @return A synchronized version of the observable map
 212      * @see Collections#synchronizedMap(java.util.Map)
 213      * @since JavaFX 8.0
 214      */
 215     public static <K, V> ObservableMap<K, V> synchronizedObservableMap(ObservableMap<K, V> map) {
 216         if (map == null) {
 217             throw new NullPointerException();
 218         }
 219         return new SynchronizedObservableMap<K, V>(map);
 220     }
 221 
 222     private static ObservableMap EMPTY_OBSERVABLE_MAP = new EmptyObservableMap();
 223 
 224     /**
 225      * Creates and empty unmodifiable observable map.
 226      * @return An empty unmodifiable observable map
 227      * @see Collections#emptyMap()
 228      * @since JavaFX 8.0
 229      */
 230     @SuppressWarnings("unchecked")
 231     public static <K, V> ObservableMap<K, V> emptyObservableMap() {
 232         return EMPTY_OBSERVABLE_MAP;
 233     }
 234 
 235     /**
 236      * Creates a new empty observable integer array.
 237      * @return a newly created ObservableIntegerArray
 238      * @since JavaFX 8.0
 239      */
 240     public static ObservableIntegerArray observableIntegerArray() {
 241         return new ObservableIntegerArrayImpl();
 242     }
 243 
 244     /**
 245      * Creates a new observable integer array with {@code values} set to it.
 246      * @param values the values that will be in the new observable integer array
 247      * @return a newly created ObservableIntegerArray
 248      * @since JavaFX 8.0
 249      */
 250     public static ObservableIntegerArray observableIntegerArray(int... values) {
 251         return new ObservableIntegerArrayImpl(values);
 252     }
 253 
 254     /**
 255      * Creates a new observable integer array with copy of elements in given
 256      * {@code array}.
 257      * @param array observable integer array to copy
 258      * @return a newly created ObservableIntegerArray
 259      * @since JavaFX 8.0
 260      */
 261     public static ObservableIntegerArray observableIntegerArray(ObservableIntegerArray array) {
 262         return new ObservableIntegerArrayImpl(array);
 263     }
 264 
 265     /**
 266      * Creates a new empty observable float array.
 267      * @return a newly created ObservableFloatArray
 268      * @since JavaFX 8.0
 269      */
 270     public static ObservableFloatArray observableFloatArray() {
 271         return new ObservableFloatArrayImpl();
 272     }
 273 
 274     /**
 275      * Creates a new observable float array with {@code values} set to it.
 276      * @param values the values that will be in the new observable float array
 277      * @return a newly created ObservableFloatArray
 278      * @since JavaFX 8.0
 279      */
 280     public static ObservableFloatArray observableFloatArray(float... values) {
 281         return new ObservableFloatArrayImpl(values);
 282     }
 283 
 284     /**
 285      * Creates a new observable float array with copy of elements in given
 286      * {@code array}.
 287      * @param array observable float array to copy
 288      * @return a newly created ObservableFloatArray
 289      * @since JavaFX 8.0
 290      */
 291     public static ObservableFloatArray observableFloatArray(ObservableFloatArray array) {
 292         return new ObservableFloatArrayImpl(array);
 293     }
 294 
 295     /**
 296      * Creates a new empty observable list that is backed by an arraylist.
 297      * @see #observableList(java.util.List)
 298      * @return a newly created ObservableList
 299      */
 300     @SuppressWarnings("unchecked")
 301     public static <E> ObservableList<E> observableArrayList() {
 302         return observableList(new ArrayList());
 303     }
 304 
 305     /**
 306      * Creates a new empty observable list backed by an arraylist.
 307      *
 308      * This list reports element updates.
 309      * @param extractor element to Observable[] convertor. Observable objects are listened for changes on the element.
 310      * @see #observableList(java.util.List, javafx.util.Callback)
 311      * @since JavaFX 2.1
 312      * @return a newly created ObservableList
 313      */
 314     public static <E> ObservableList<E> observableArrayList(Callback<E, Observable[]> extractor) {
 315         return observableList(new ArrayList(), extractor);
 316     }
 317 
 318     /**
 319      * Creates a new observable array list with {@code items} added to it.
 320      * @return a newly created observableArrayList
 321      * @param items the items that will be in the new observable ArrayList
 322      * @see #observableArrayList()
 323      */
 324     public static <E> ObservableList<E> observableArrayList(E... items) {
 325         ObservableList<E> list = observableArrayList();
 326         list.addAll(items);
 327         return list;
 328     }
 329 
 330     /**
 331      * Creates a new observable array list and adds a content of collection {@code col}
 332      * to it.
 333      * @param col a collection which content should be added to the observableArrayList
 334      * @return a newly created observableArrayList
 335      */
 336     public static <E> ObservableList<E> observableArrayList(Collection<? extends E> col) {
 337         ObservableList<E> list = observableArrayList();
 338         list.addAll(col);
 339         return list;
 340     }
 341 
 342     /**
 343      * Creates a new empty observable map that is backed by a HashMap.
 344      * @param <K> the type of keys
 345      * @param <V> the type of values
 346      * @return a newly created observable HashMap
 347      */
 348     public static <K,V> ObservableMap<K,V> observableHashMap() {
 349         return observableMap(new HashMap<K, V>());
 350     }
 351 
 352     /**
 353      * Concatenates more observable lists into one. The resulting list
 354      * would be backed by an arraylist.
 355      * @param lists lists to concatenate
 356      * @return new observable array list concatenated from the arguments
 357      */
 358     public static <E> ObservableList<E> concat(ObservableList<E>... lists) {
 359         if (lists.length == 0 ) {
 360             return observableArrayList();
 361         }
 362         if (lists.length == 1) {
 363             return observableArrayList(lists[0]);
 364         }
 365         ArrayList<E> backingList = new ArrayList<E>();
 366         for (ObservableList<E> s : lists) {
 367             backingList.addAll(s);
 368         }
 369 
 370         return observableList(backingList);
 371     }
 372 
 373     /**
 374      * Creates and returns unmodifiable wrapper list on top of provided observable list.
 375      * @param list  an ObservableList that is to be wrapped
 376      * @return an ObserableList wrapper that is unmodifiable
 377      * @see Collections#unmodifiableList(java.util.List)
 378      */
 379     public static<E> ObservableList<E> unmodifiableObservableList(ObservableList<E> list) {
 380         if (list == null) {
 381             throw new NullPointerException();
 382         }
 383         return new UnmodifiableObservableListImpl<E>(list);
 384     }
 385 
 386     /**
 387      * Creates and returns a typesafe wrapper on top of provided observable list.
 388      * @param list  an Observable list to be wrapped
 389      * @param type   the type of element that <tt>list</tt> is permitted to hold
 390      * @return a dynamically typesafe view of the specified list
 391      * @see Collections#checkedList(java.util.List, java.lang.Class)
 392      */
 393     public static<E> ObservableList<E> checkedObservableList(ObservableList<E> list, Class<E> type) {
 394         if (list == null) {
 395             throw new NullPointerException();
 396         }
 397         return new CheckedObservableList<E>(list, type);
 398     }
 399 
 400     /**
 401      * Creates and returns a synchronized wrapper on top of provided observable list.
 402      * @param  list the list to be "wrapped" in a synchronized list.
 403      * @return A synchronized version of the observable list
 404      * @see Collections#synchronizedList(java.util.List)
 405      */
 406     public static<E> ObservableList<E> synchronizedObservableList(ObservableList<E> list) {
 407         if (list == null) {
 408             throw new NullPointerException();
 409         }
 410         return new SynchronizedObservableList<E>(list);
 411     }
 412 
 413     private static ObservableList EMPTY_OBSERVABLE_LIST = new EmptyObservableList();
 414 
 415 
 416     /**
 417      * Creates and empty unmodifiable observable list.
 418      * @return An empty unmodifiable observable list
 419      * @see Collections#emptyList()
 420      */
 421     @SuppressWarnings("unchecked")
 422     public static<E> ObservableList<E> emptyObservableList() {
 423         return EMPTY_OBSERVABLE_LIST;
 424     }
 425 
 426     /**
 427      * Creates an unmodifiable observable list with single element.
 428      * @param e the only elements that will be contained in this singleton observable list
 429      * @return a singleton observable list
 430      * @see Collections#singletonList(java.lang.Object)
 431      */
 432     public static<E> ObservableList<E> singletonObservableList(E e) {
 433         return new SingletonObservableList<E>(e);
 434     }
 435 
 436     /**
 437      * Creates and returns unmodifiable wrapper on top of provided observable set.
 438      * @param set an ObservableSet that is to be wrapped
 439      * @return an ObserableSet wrapper that is unmodifiable
 440      * @see Collections#unmodifiableSet(java.util.Set)
 441      * @since JavaFX 8.0
 442      */
 443     public static<E> ObservableSet<E> unmodifiableObservableSet(ObservableSet<E> set) {
 444         if (set == null) {
 445             throw new NullPointerException();
 446         }
 447         return new UnmodifiableObservableSet<E>(set);
 448     }
 449 
 450     /**
 451      * Creates and returns a typesafe wrapper on top of provided observable set.
 452      * @param set an Observable set to be wrapped
 453      * @param type  the type of element that <tt>set</tt> is permitted to hold
 454      * @return a dynamically typesafe view of the specified set
 455      * @see Collections#checkedSet(java.util.Set, java.lang.Class)
 456      * @since JavaFX 8.0
 457      */
 458     public static<E> ObservableSet<E> checkedObservableSet(ObservableSet<E> set, Class<E> type) {
 459         if (set == null) {
 460             throw new NullPointerException();
 461         }
 462         return new CheckedObservableSet<E>(set, type);
 463     }
 464 
 465     /**
 466      * Creates and returns a synchronized wrapper on top of provided observable set.
 467      * @param  set the set to be "wrapped" in a synchronized set.
 468      * @return A synchronized version of the observable set
 469      * @see Collections#synchronizedSet(java.util.Set)
 470      * @since JavaFX 8.0
 471      */
 472     public static<E> ObservableSet<E> synchronizedObservableSet(ObservableSet<E> set) {
 473         if (set == null) {
 474             throw new NullPointerException();
 475         }
 476         return new SynchronizedObservableSet<E>(set);
 477     }
 478 
 479     private static ObservableSet EMPTY_OBSERVABLE_SET = new EmptyObservableSet();
 480 
 481     /**
 482      * Creates and empty unmodifiable observable set.
 483      * @return An empty unmodifiable observable set
 484      * @see Collections#emptySet()
 485      * @since JavaFX 8.0
 486      */
 487     @SuppressWarnings("unchecked")
 488     public static<E> ObservableSet<E> emptyObservableSet() {
 489         return EMPTY_OBSERVABLE_SET;
 490     }
 491 
 492     /**
 493      * Copies elements from src to dest. Fires only <b>one</b> change notification on dest.
 494      * @param dest the destination observable list
 495      * @param src the source list
 496      * @see Collections#copy(java.util.List, java.util.List)
 497      */
 498     @SuppressWarnings("unchecked")
 499     public static <T> void copy(ObservableList<? super T> dest, List<? extends T> src) {
 500         final int srcSize = src.size();
 501         if (srcSize > dest.size()) {
 502             throw new IndexOutOfBoundsException("Source does not fit in dest");
 503         }
 504         T[] destArray = (T[]) dest.toArray();
 505         System.arraycopy(src.toArray(), 0, destArray, 0, srcSize);
 506         dest.setAll(destArray);
 507     }
 508 
 509     /**
 510      * Fills the provided list with obj. Fires only <b>one</b> change notification on the list.
 511      * @param list the list to fill
 512      * @param obj the object to fill the list with
 513      * @see Collections#fill(java.util.List, java.lang.Object)
 514      */
 515     @SuppressWarnings("unchecked")
 516     public static <T> void fill(ObservableList<? super T> list, T obj) {
 517         T[] newContent = (T[]) new Object[list.size()];
 518         Arrays.fill(newContent, obj);
 519         list.setAll(newContent);
 520     }
 521 
 522     /**
 523      * Replace all oldVal elements in the list with newVal element.
 524      * Fires only <b>one</b> change notification on the list.
 525      * @param list the list which will have it's elements replaced
 526      * @param oldVal the element that is going to be replace
 527      * @param newVal the replacement
 528      * @return true if the list was modified
 529      * @see Collections#replaceAll(java.util.List, java.lang.Object, java.lang.Object)
 530      */
 531     @SuppressWarnings("unchecked")
 532     public static <T> boolean replaceAll(ObservableList<T> list, T oldVal, T newVal) {
 533         T[] newContent = (T[]) list.toArray();
 534         boolean modified = false;
 535         for (int i = 0 ; i < newContent.length; ++i) {
 536             if (newContent[i].equals(oldVal)) {
 537                 newContent[i] = newVal;
 538                 modified = true;
 539             }
 540         }
 541         if (modified) {
 542             list.setAll(newContent);
 543         }
 544         return modified;
 545     }
 546 
 547     /**
 548      * Reverse the order in the list
 549      * Fires only <b>one</b> change notification on the list.
 550      * @param list the list to be reversed
 551      * @see Collections#reverse(java.util.List)
 552      */
 553     @SuppressWarnings("unchecked")
 554     public static void reverse(ObservableList list) {
 555         Object[] newContent = list.toArray();
 556         for (int i = 0; i < newContent.length / 2; ++i) {
 557             Object tmp = newContent[i];
 558             newContent[i] = newContent[newContent.length - i - 1];
 559             newContent[newContent.length -i - 1] = tmp;
 560         }
 561         list.setAll(newContent);
 562     }
 563 
 564     /**
 565      * Rotates the list by distance.
 566      * Fires only <b>one</b> change notification on the list.
 567      * @param list the list to be rotated
 568      * @param distance the distance of rotation
 569      * @see Collections#rotate(java.util.List, int)
 570      */
 571     @SuppressWarnings("unchecked")
 572     public static void rotate(ObservableList list, int distance) {
 573         Object[] newContent = list.toArray();
 574 
 575         int size = list.size();
 576         distance = distance % size;
 577         if (distance < 0)
 578             distance += size;
 579         if (distance == 0)
 580             return;
 581 
 582         for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) {
 583             Object displaced = newContent[cycleStart];
 584             Object tmp;
 585             int i = cycleStart;
 586             do {
 587                 i += distance;
 588                 if (i >= size)
 589                     i -= size;
 590                 tmp = newContent[i];
 591                 newContent[i] = displaced;
 592                 displaced = tmp;
 593                 nMoved ++;
 594             } while(i != cycleStart);
 595         }
 596         list.setAll(newContent);
 597     }
 598 
 599     /**
 600      * Shuffles all elements in the observable list.
 601      * Fires only <b>one</b> change notification on the list.
 602      * @param list the list to shuffle
 603      * @see Collections#shuffle(java.util.List)
 604      */
 605     public static void shuffle(ObservableList<?> list) {
 606         if (r == null) {
 607             r = new Random();
 608         }
 609         shuffle(list, r);
 610     }
 611     private static Random r;
 612 
 613     /**
 614      * Shuffles all elements in the observable list.
 615      * Fires only <b>one</b> change notification on the list.
 616      * @param list the list to be shuffled
 617      * @param rnd the random generator used for shuffling
 618      * @see Collections#shuffle(java.util.List, java.util.Random)
 619      */
 620     @SuppressWarnings("unchecked")
 621     public static void shuffle(ObservableList list, Random rnd) {
 622         Object newContent[] = list.toArray();
 623 
 624         for (int i = list.size(); i > 1; i--) {
 625             swap(newContent, i - 1, rnd.nextInt(i));
 626         }
 627 
 628         list.setAll(newContent);
 629     }
 630 
 631     private static void swap(Object[] arr, int i, int j) {
 632         Object tmp = arr[i];
 633         arr[i] = arr[j];
 634         arr[j] = tmp;
 635     }
 636 
 637     /**
 638      * Sorts the provided observable list.
 639      * Fires only <b>one</b> change notification on the list.
 640      * @see Collections#sort(java.util.List)
 641      */
 642     @SuppressWarnings("unchecked")
 643     public static <T extends Comparable<? super T>> void sort(ObservableList<T> list) {
 644         if (list instanceof SortableList) {
 645             ((SortableList<? extends T>)list).sort();
 646         } else {
 647             List<T> newContent = new ArrayList<T>(list);
 648             Collections.sort(newContent);
 649             list.setAll((Collection<T>)newContent);
 650         }
 651     }
 652 
 653     /**
 654      * Sorts the provided observable list using the c comparator.
 655      * Fires only <b>one</b> change notification on the list.
 656      * @param list the list to sort
 657      * @param c comparator used for sorting. Null if natural ordering is required.
 658      * @see Collections#sort(java.util.List, java.util.Comparator)
 659      */
 660     @SuppressWarnings("unchecked")
 661     public static <T> void sort(ObservableList<T> list, Comparator<? super T> c) {
 662         if (list instanceof SortableList) {
 663             ((SortableList<? extends T>)list).sort(c);
 664         } else {
 665             List<T> newContent = new ArrayList<T>(list);
 666             Collections.sort(newContent, c);
 667             list.setAll((Collection<T>)newContent);
 668         }
 669     }
 670 
 671     private static class EmptyObservableList<E> extends AbstractList<E> implements ObservableList<E> {
 672 
 673         private static final ListIterator iterator = new ListIterator() {
 674 
 675             @Override
 676             public boolean hasNext() {
 677                 return false;
 678             }
 679 
 680             @Override
 681             public Object next() {
 682                 throw new NoSuchElementException();
 683             }
 684 
 685             @Override
 686             public void remove() {
 687                 throw new UnsupportedOperationException();
 688             }
 689 
 690             @Override
 691             public boolean hasPrevious() {
 692                 return false;
 693             }
 694 
 695             @Override
 696             public Object previous() {
 697                 throw new NoSuchElementException();
 698             }
 699 
 700             @Override
 701             public int nextIndex() {
 702                 return 0;
 703             }
 704 
 705             @Override
 706             public int previousIndex() {
 707                 return -1;
 708             }
 709 
 710             @Override
 711             public void set(Object e) {
 712                 throw new UnsupportedOperationException();
 713             }
 714 
 715             @Override
 716             public void add(Object e) {
 717                 throw new UnsupportedOperationException();
 718             }
 719         };
 720 
 721         public EmptyObservableList() {
 722         }
 723 
 724         @Override
 725         public final void addListener(InvalidationListener listener) {
 726         }
 727 
 728         @Override
 729         public final void removeListener(InvalidationListener listener) {
 730         }
 731 
 732 
 733         @Override
 734         public void addListener(ListChangeListener<? super E> o) {
 735         }
 736 
 737         @Override
 738         public void removeListener(ListChangeListener<? super E> o) {
 739         }
 740 
 741         @Override
 742         public int size() {
 743             return 0;
 744         }
 745 
 746         @Override
 747         public boolean contains(Object o) {
 748             return false;
 749         }
 750 
 751         @Override
 752         @SuppressWarnings("unchecked")
 753         public Iterator<E> iterator() {
 754             return iterator;
 755         }
 756 
 757         @Override
 758         public boolean containsAll(Collection<?> c) {
 759             return c.isEmpty();
 760         }
 761 
 762         @Override
 763         public E get(int index) {
 764             throw new IndexOutOfBoundsException();
 765         }
 766 
 767         @Override
 768         public int indexOf(Object o) {
 769             return -1;
 770         }
 771 
 772         @Override
 773         public int lastIndexOf(Object o) {
 774             return -1;
 775         }
 776 
 777         @Override
 778         @SuppressWarnings("unchecked")
 779         public ListIterator<E> listIterator() {
 780             return iterator;
 781         }
 782 
 783         @Override
 784         @SuppressWarnings("unchecked")
 785         public ListIterator<E> listIterator(int index) {
 786             if (index != 0) {
 787                 throw new IndexOutOfBoundsException();
 788             }
 789             return iterator;
 790         }
 791 
 792         @Override
 793         public List<E> subList(int fromIndex, int toIndex) {
 794             if (fromIndex != 0 || toIndex != 0) {
 795                 throw new IndexOutOfBoundsException();
 796             }
 797             return this;
 798         }
 799 
 800         @Override
 801         public boolean addAll(E... elements) {
 802             throw new UnsupportedOperationException();
 803         }
 804 
 805         @Override
 806         public boolean setAll(E... elements) {
 807             throw new UnsupportedOperationException();
 808         }
 809 
 810         @Override
 811         public boolean setAll(Collection<? extends E> col) {
 812             throw new UnsupportedOperationException();
 813         }
 814 
 815         @Override
 816         public boolean removeAll(E... elements) {
 817             throw new UnsupportedOperationException();
 818         }
 819 
 820         @Override
 821         public boolean retainAll(E... elements) {
 822             throw new UnsupportedOperationException();
 823         }
 824 
 825         @Override
 826         public void remove(int from, int to) {
 827             throw new UnsupportedOperationException();
 828         }
 829     }
 830 
 831     private static class SingletonObservableList<E> extends AbstractList<E> implements ObservableList<E> {
 832 
 833         private final E element;
 834 
 835         public SingletonObservableList(E element) {
 836             if (element == null) {
 837                 throw new NullPointerException();
 838             }
 839             this.element = element;
 840         }
 841 
 842         @Override
 843         public boolean addAll(E... elements) {
 844             throw new UnsupportedOperationException();
 845         }
 846 
 847         @Override
 848         public boolean setAll(E... elements) {
 849             throw new UnsupportedOperationException();
 850         }
 851 
 852         @Override
 853         public boolean setAll(Collection<? extends E> col) {
 854             throw new UnsupportedOperationException();
 855         }
 856 
 857         @Override
 858         public boolean removeAll(E... elements) {
 859             throw new UnsupportedOperationException();
 860         }
 861 
 862         @Override
 863         public boolean retainAll(E... elements) {
 864             throw new UnsupportedOperationException();
 865         }
 866 
 867         @Override
 868         public void remove(int from, int to) {
 869             throw new UnsupportedOperationException();
 870         }
 871 
 872         @Override
 873         public void addListener(InvalidationListener listener) {
 874         }
 875 
 876         @Override
 877         public void removeListener(InvalidationListener listener) {
 878         }
 879 
 880         @Override
 881         public void addListener(ListChangeListener<? super E> o) {
 882         }
 883 
 884         @Override
 885         public void removeListener(ListChangeListener<? super E> o) {
 886         }
 887 
 888         @Override
 889         public int size() {
 890             return 1;
 891         }
 892 
 893         @Override
 894         public boolean isEmpty() {
 895             return false;
 896         }
 897 
 898         @Override
 899         public boolean contains(Object o) {
 900             return element.equals(o);
 901         }
 902 
 903         @Override
 904         public E get(int index) {
 905             if (index != 0) {
 906                 throw new IndexOutOfBoundsException();
 907             }
 908             return element;
 909         }
 910 
 911     }
 912 
 913     private static class UnmodifiableObservableListImpl<T> extends ObservableListBase<T> implements ObservableList<T> {
 914 
 915         private final ObservableList<T> backingList;
 916         private final ListChangeListener<T> listener;
 917 
 918         public UnmodifiableObservableListImpl(ObservableList<T> backingList) {
 919             this.backingList = backingList;
 920             listener = c -> {
 921                 fireChange(new SourceAdapterChange<T>(UnmodifiableObservableListImpl.this, c));
 922             };
 923             this.backingList.addListener(new WeakListChangeListener<T>(listener));
 924         }
 925 
 926         @Override
 927         public T get(int index) {
 928             return backingList.get(index);
 929         }
 930 
 931         @Override
 932         public int size() {
 933             return backingList.size();
 934         }
 935 
 936         @Override
 937         public boolean addAll(T... elements) {
 938             throw new UnsupportedOperationException();
 939         }
 940 
 941         @Override
 942         public boolean setAll(T... elements) {
 943             throw new UnsupportedOperationException();
 944         }
 945 
 946         @Override
 947         public boolean setAll(Collection<? extends T> col) {
 948             throw new UnsupportedOperationException();
 949         }
 950 
 951         @Override
 952         public boolean removeAll(T... elements) {
 953             throw new UnsupportedOperationException();
 954         }
 955 
 956         @Override
 957         public boolean retainAll(T... elements) {
 958             throw new UnsupportedOperationException();
 959         }
 960 
 961         @Override
 962         public void remove(int from, int to) {
 963             throw new UnsupportedOperationException();
 964         }
 965 
 966     }
 967 
 968     private static class SynchronizedList<T> implements List<T> {
 969         final Object mutex;
 970         private final List<T> backingList;
 971 
 972         SynchronizedList(List<T> list, Object mutex) {
 973             this.backingList = list;
 974             this.mutex = mutex;
 975         }
 976 
 977         @Override
 978         public int size() {
 979             synchronized(mutex) {
 980                 return backingList.size();
 981             }
 982         }
 983 
 984         @Override
 985         public boolean isEmpty() {
 986             synchronized(mutex) {
 987                 return backingList.isEmpty();
 988             }
 989         }
 990 
 991         @Override
 992         public boolean contains(Object o) {
 993             synchronized(mutex) {
 994                 return backingList.contains(o);
 995             }
 996         }
 997 
 998         @Override
 999         public Iterator<T> iterator() {
1000             return backingList.iterator();
1001         }
1002 
1003         @Override
1004         public Object[] toArray() {
1005             synchronized(mutex)  {
1006                 return backingList.toArray();
1007             }
1008         }
1009 
1010         @Override
1011         public <T> T[] toArray(T[] a) {
1012             synchronized(mutex) {
1013                 return backingList.toArray(a);
1014             }
1015         }
1016 
1017         @Override
1018         public boolean add(T e) {
1019             synchronized(mutex) {
1020                 return backingList.add(e);
1021             }
1022         }
1023 
1024         @Override
1025         public boolean remove(Object o) {
1026             synchronized(mutex) {
1027                 return backingList.remove(o);
1028             }
1029         }
1030 
1031         @Override
1032         public boolean containsAll(Collection<?> c) {
1033             synchronized(mutex) {
1034                 return backingList.containsAll(c);
1035             }
1036         }
1037 
1038         @Override
1039         public boolean addAll(Collection<? extends T> c) {
1040             synchronized(mutex) {
1041                 return backingList.addAll(c);
1042             }
1043         }
1044 
1045         @Override
1046         public boolean addAll(int index, Collection<? extends T> c) {
1047             synchronized(mutex) {
1048                 return backingList.addAll(index, c);
1049 
1050             }
1051         }
1052 
1053         @Override
1054         public boolean removeAll(Collection<?> c) {
1055             synchronized(mutex) {
1056                 return backingList.removeAll(c);
1057             }
1058         }
1059 
1060         @Override
1061         public boolean retainAll(Collection<?> c) {
1062             synchronized(mutex) {
1063                 return backingList.retainAll(c);
1064             }
1065         }
1066 
1067         @Override
1068         public void clear() {
1069             synchronized(mutex) {
1070                 backingList.clear();
1071             }
1072         }
1073 
1074         @Override
1075         public T get(int index) {
1076             synchronized(mutex) {
1077                 return backingList.get(index);
1078             }
1079         }
1080 
1081         @Override
1082         public T set(int index, T element) {
1083             synchronized(mutex) {
1084                 return backingList.set(index, element);
1085             }
1086         }
1087 
1088         @Override
1089         public void add(int index, T element) {
1090             synchronized(mutex) {
1091                 backingList.add(index, element);
1092             }
1093         }
1094 
1095         @Override
1096         public T remove(int index) {
1097             synchronized(mutex) {
1098                 return backingList.remove(index);
1099             }
1100         }
1101 
1102         @Override
1103         public int indexOf(Object o) {
1104             synchronized(mutex) {
1105                 return backingList.indexOf(o);
1106             }
1107         }
1108 
1109         @Override
1110         public int lastIndexOf(Object o) {
1111             synchronized(mutex) {
1112                 return backingList.lastIndexOf(o);
1113             }
1114         }
1115 
1116         @Override
1117         public ListIterator<T> listIterator() {
1118             return backingList.listIterator();
1119         }
1120 
1121         @Override
1122         public ListIterator<T> listIterator(int index) {
1123             synchronized(mutex) {
1124                 return backingList.listIterator(index);
1125             }
1126         }
1127 
1128         @Override
1129         public List<T> subList(int fromIndex, int toIndex) {
1130             synchronized(mutex) {
1131                 return new SynchronizedList<T>(backingList.subList(fromIndex, toIndex),
1132                         mutex);
1133             }
1134         }
1135 
1136         @Override
1137         public String toString() {
1138             synchronized(mutex) {
1139                 return backingList.toString();
1140             }
1141         }
1142 
1143         @Override
1144         public int hashCode() {
1145             synchronized(mutex) {
1146                 return backingList.hashCode();
1147             }
1148         }
1149 
1150         @Override
1151         public boolean equals(Object o) {
1152             synchronized(mutex) {
1153                 return backingList.equals(o);
1154             }
1155         }
1156 
1157     }
1158 
1159     private static class SynchronizedObservableList<T> extends SynchronizedList<T> implements ObservableList<T> {
1160 
1161         private ListListenerHelper helper;
1162 
1163         private final ObservableList<T> backingList;
1164         private final ListChangeListener<T> listener;
1165 
1166         SynchronizedObservableList(ObservableList<T> seq, Object mutex) {
1167             super(seq, mutex);
1168             this.backingList = seq;
1169             listener = c -> {
1170                 ListListenerHelper.fireValueChangedEvent(helper, new SourceAdapterChange<T>(SynchronizedObservableList.this, c));
1171             };
1172             backingList.addListener(new WeakListChangeListener<T>(listener));
1173         }
1174 
1175         SynchronizedObservableList(ObservableList<T> seq) {
1176             this(seq, new Object());
1177         }
1178 
1179         @Override
1180         public boolean addAll(T... elements) {
1181             synchronized(mutex) {
1182                 return backingList.addAll(elements);
1183             }
1184         }
1185 
1186         @Override
1187         public boolean setAll(T... elements) {
1188             synchronized(mutex) {
1189                 return backingList.setAll(elements);
1190             }
1191         }
1192 
1193         @Override
1194         public boolean removeAll(T... elements) {
1195             synchronized(mutex) {
1196                 return backingList.removeAll(elements);
1197             }
1198         }
1199 
1200         @Override
1201         public boolean retainAll(T... elements) {
1202             synchronized(mutex) {
1203                 return backingList.retainAll(elements);
1204             }
1205         }
1206 
1207         @Override
1208         public void remove(int from, int to) {
1209             synchronized(mutex) {
1210                 backingList.remove(from, to);
1211             }
1212         }
1213 
1214         @Override
1215         public boolean setAll(Collection<? extends T> col) {
1216             synchronized(mutex) {
1217                 return backingList.setAll(col);
1218             }
1219         }
1220 
1221         @Override
1222         public final void addListener(InvalidationListener listener) {
1223             synchronized (mutex) {
1224                 helper = ListListenerHelper.addListener(helper, listener);
1225             }
1226         }
1227 
1228         @Override
1229         public final void removeListener(InvalidationListener listener) {
1230             synchronized (mutex) {
1231                 helper = ListListenerHelper.removeListener(helper, listener);
1232             }
1233         }
1234 
1235         @Override
1236         public void addListener(ListChangeListener<? super T> listener) {
1237             synchronized (mutex) {
1238                 helper = ListListenerHelper.addListener(helper, listener);
1239             }
1240         }
1241 
1242         @Override
1243         public void removeListener(ListChangeListener<? super T> listener) {
1244             synchronized (mutex) {
1245                 helper = ListListenerHelper.removeListener(helper, listener);
1246             }
1247         }
1248 
1249 
1250     }
1251 
1252     private static class CheckedObservableList<T> extends ObservableListBase<T> implements ObservableList<T> {
1253 
1254         private final ObservableList<T> list;
1255         private final Class<T> type;
1256         private final ListChangeListener<T> listener;
1257 
1258         CheckedObservableList(ObservableList<T> list, Class<T> type) {
1259             if (list == null || type == null) {
1260                 throw new NullPointerException();
1261             }
1262             this.list = list;
1263             this.type = type;
1264             listener = c -> {
1265                 fireChange(new SourceAdapterChange<T>(CheckedObservableList.this, c));
1266             };
1267             list.addListener(new WeakListChangeListener<T>(listener));
1268         }
1269 
1270         void typeCheck(Object o) {
1271             if (o != null && !type.isInstance(o)) {
1272                 throw new ClassCastException("Attempt to insert "
1273                         + o.getClass() + " element into collection with element type "
1274                         + type);
1275             }
1276         }
1277 
1278         @Override
1279         public int size() {
1280             return list.size();
1281         }
1282 
1283         @Override
1284         public boolean isEmpty() {
1285             return list.isEmpty();
1286         }
1287 
1288         @Override
1289         public boolean contains(Object o) {
1290             return list.contains(o);
1291         }
1292 
1293         @Override
1294         public Object[] toArray() {
1295             return list.toArray();
1296         }
1297 
1298         @Override
1299         public <T> T[] toArray(T[] a) {
1300             return list.toArray(a);
1301         }
1302 
1303         @Override
1304         public String toString() {
1305             return list.toString();
1306         }
1307 
1308         @Override
1309         public boolean remove(Object o) {
1310             return list.remove(o);
1311         }
1312 
1313         @Override
1314         public boolean containsAll(Collection<?> coll) {
1315             return list.containsAll(coll);
1316         }
1317 
1318         @Override
1319         public boolean removeAll(Collection<?> coll) {
1320             return list.removeAll(coll);
1321         }
1322 
1323         @Override
1324         public boolean retainAll(Collection<?> coll) {
1325             return list.retainAll(coll);
1326         }
1327 
1328         @Override
1329         public boolean removeAll(T... elements) {
1330             return list.removeAll(elements);
1331         }
1332 
1333         @Override
1334         public boolean retainAll(T... elements) {
1335             return list.retainAll(elements);
1336         }
1337 
1338         @Override
1339         public void remove(int from, int to) {
1340             list.remove(from, to);
1341         }
1342 
1343         @Override
1344         public void clear() {
1345             list.clear();
1346         }
1347 
1348         @Override
1349         public boolean equals(Object o) {
1350             return o == this || list.equals(o);
1351         }
1352 
1353         @Override
1354         public int hashCode() {
1355             return list.hashCode();
1356         }
1357 
1358         @Override
1359         public T get(int index) {
1360             return list.get(index);
1361         }
1362 
1363         @Override
1364         public T remove(int index) {
1365             return list.remove(index);
1366         }
1367 
1368         @Override
1369         public int indexOf(Object o) {
1370             return list.indexOf(o);
1371         }
1372 
1373         @Override
1374         public int lastIndexOf(Object o) {
1375             return list.lastIndexOf(o);
1376         }
1377 
1378         @Override
1379         public T set(int index, T element) {
1380             typeCheck(element);
1381             return list.set(index, element);
1382         }
1383 
1384         @Override
1385         public void add(int index, T element) {
1386             typeCheck(element);
1387             list.add(index, element);
1388         }
1389 
1390         @Override
1391         @SuppressWarnings("unchecked")
1392         public boolean addAll(int index, Collection<? extends T> c) {
1393             T[] a = null;
1394             try {
1395                 a = c.toArray((T[]) Array.newInstance(type, 0));
1396             } catch (ArrayStoreException e) {
1397                 throw new ClassCastException();
1398             }
1399 
1400             return this.list.addAll(index, Arrays.asList(a));
1401         }
1402 
1403         @Override
1404         @SuppressWarnings("unchecked")
1405         public boolean addAll(Collection<? extends T> coll) {
1406             T[] a = null;
1407             try {
1408                 a = coll.toArray((T[]) Array.newInstance(type, 0));
1409             } catch (ArrayStoreException e) {
1410                 throw new ClassCastException();
1411             }
1412 
1413             return this.list.addAll(Arrays.asList(a));
1414         }
1415 
1416         @Override
1417         public ListIterator<T> listIterator() {
1418             return listIterator(0);
1419         }
1420 
1421         @Override
1422         public ListIterator<T> listIterator(final int index) {
1423             return new ListIterator<T>() {
1424 
1425                 ListIterator<T> i = list.listIterator(index);
1426 
1427                 @Override
1428                 public boolean hasNext() {
1429                     return i.hasNext();
1430                 }
1431 
1432                 @Override
1433                 public T next() {
1434                     return i.next();
1435                 }
1436 
1437                 @Override
1438                 public boolean hasPrevious() {
1439                     return i.hasPrevious();
1440                 }
1441 
1442                 @Override
1443                 public T previous() {
1444                     return i.previous();
1445                 }
1446 
1447                 @Override
1448                 public int nextIndex() {
1449                     return i.nextIndex();
1450                 }
1451 
1452                 @Override
1453                 public int previousIndex() {
1454                     return i.previousIndex();
1455                 }
1456 
1457                 @Override
1458                 public void remove() {
1459                     i.remove();
1460                 }
1461 
1462                 @Override
1463                 public void set(T e) {
1464                     typeCheck(e);
1465                     i.set(e);
1466                 }
1467 
1468                 @Override
1469                 public void add(T e) {
1470                     typeCheck(e);
1471                     i.add(e);
1472                 }
1473             };
1474         }
1475 
1476         @Override
1477         public Iterator<T> iterator() {
1478             return new Iterator<T>() {
1479 
1480                 private final Iterator<T> it = list.iterator();
1481 
1482                 @Override
1483                 public boolean hasNext() {
1484                     return it.hasNext();
1485                 }
1486 
1487                 @Override
1488                 public T next() {
1489                     return it.next();
1490                 }
1491 
1492                 @Override
1493                 public void remove() {
1494                     it.remove();
1495                 }
1496             };
1497         }
1498 
1499         @Override
1500         public boolean add(T e) {
1501             typeCheck(e);
1502             return list.add(e);
1503         }
1504 
1505         @Override
1506         public List<T> subList(int fromIndex, int toIndex) {
1507             return Collections.checkedList(list.subList(fromIndex, toIndex), type);
1508         }
1509 
1510         @Override
1511         @SuppressWarnings("unchecked")
1512         public boolean addAll(T... elements) {
1513             try {
1514                 T[] array = (T[]) Array.newInstance(type, elements.length);
1515                 System.arraycopy(elements, 0, array, 0, elements.length);
1516                 return list.addAll(array);
1517             } catch (ArrayStoreException e) {
1518                 throw new ClassCastException();
1519             }
1520         }
1521 
1522         @Override
1523         @SuppressWarnings("unchecked")
1524         public boolean setAll(T... elements) {
1525             try {
1526                 T[] array = (T[]) Array.newInstance(type, elements.length);
1527                 System.arraycopy(elements, 0, array, 0, elements.length);
1528                 return list.setAll(array);
1529             } catch (ArrayStoreException e) {
1530                 throw new ClassCastException();
1531             }
1532         }
1533 
1534         @Override
1535         @SuppressWarnings("unchecked")
1536         public boolean setAll(Collection<? extends T> col) {
1537             T[] a = null;
1538             try {
1539                 a = col.toArray((T[]) Array.newInstance(type, 0));
1540             } catch (ArrayStoreException e) {
1541                 throw new ClassCastException();
1542             }
1543 
1544             return list.setAll(Arrays.asList(a));
1545         }
1546     }
1547 
1548     private static class EmptyObservableSet<E> extends AbstractSet<E> implements ObservableSet<E> {
1549 
1550         public EmptyObservableSet() {
1551         }
1552 
1553         @Override
1554         public void addListener(InvalidationListener listener) {
1555         }
1556 
1557         @Override
1558         public void removeListener(InvalidationListener listener) {
1559         }
1560 
1561         @Override
1562         public void addListener(SetChangeListener<? super E> listener) {
1563         }
1564 
1565         @Override
1566         public void removeListener(SetChangeListener<? super E> listener) {
1567         }
1568 
1569         @Override
1570         public int size() {
1571             return 0;
1572         }
1573 
1574         @Override
1575         public boolean isEmpty() {
1576             return true;
1577         }
1578 
1579         @Override
1580         public boolean contains(Object obj) {
1581             return false;
1582         }
1583 
1584         @Override
1585         public boolean containsAll(Collection<?> c) {
1586             return c.isEmpty();
1587         }
1588 
1589         @Override
1590         public Object[] toArray() {
1591             return new Object[0];
1592         }
1593 
1594         @Override
1595         public <E> E[] toArray(E[] a) {
1596             if (a.length > 0)
1597                 a[0] = null;
1598             return a;
1599         }
1600 
1601         @Override
1602         public Iterator<E> iterator() {
1603             return new Iterator() {
1604 
1605                 @Override
1606                 public boolean hasNext() {
1607                     return false;
1608                 }
1609 
1610                 @Override
1611                 public Object next() {
1612                     throw new NoSuchElementException();
1613                 }
1614 
1615                 @Override
1616                 public void remove() {
1617                     throw new UnsupportedOperationException();
1618                 }
1619             };
1620         }
1621 
1622     }
1623 
1624     private static class UnmodifiableObservableSet<E> extends AbstractSet<E> implements ObservableSet<E> {
1625 
1626         private final ObservableSet<E> backingSet;
1627         private SetListenerHelper<E> listenerHelper;
1628         private SetChangeListener<E> listener;
1629 
1630         public UnmodifiableObservableSet(ObservableSet<E> backingSet) {
1631             this.backingSet = backingSet;
1632             this.listener = null;
1633         }
1634 
1635         private void initListener() {
1636             if (listener == null) {
1637                 listener = c -> {
1638                     callObservers(new SetAdapterChange<E>(UnmodifiableObservableSet.this, c));
1639                 };
1640                 this.backingSet.addListener(new WeakSetChangeListener<E>(listener));
1641             }
1642         }
1643 
1644         private void callObservers(SetChangeListener.Change<? extends E> change) {
1645             SetListenerHelper.fireValueChangedEvent(listenerHelper, change);
1646         }
1647 
1648         @Override
1649         public Iterator<E> iterator() {
1650             return new Iterator<E>() {
1651                 private final Iterator<? extends E> i = backingSet.iterator();
1652 
1653                 @Override
1654                 public boolean hasNext() {
1655                     return i.hasNext();
1656                 }
1657 
1658                 @Override
1659                 public E next() {
1660                     return i.next();
1661                 }
1662             };
1663         }
1664 
1665         @Override
1666         public int size() {
1667             return backingSet.size();
1668         }
1669 
1670         @Override
1671         public boolean isEmpty() {
1672             return backingSet.isEmpty();
1673         }
1674 
1675         @Override
1676         public boolean contains(Object o) {
1677             return backingSet.contains(o);
1678         }
1679 
1680         @Override
1681         public void addListener(InvalidationListener listener) {
1682             initListener();
1683             listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
1684         }
1685 
1686         @Override
1687         public void removeListener(InvalidationListener listener) {
1688             listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
1689         }
1690 
1691         @Override
1692         public void addListener(SetChangeListener<? super E> listener) {
1693             initListener();
1694             listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
1695         }
1696 
1697         @Override
1698         public void removeListener(SetChangeListener<? super E> listener) {
1699             listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
1700         }
1701 
1702         @Override
1703         public boolean add(E e) {
1704             throw new UnsupportedOperationException();
1705         }
1706 
1707         @Override
1708         public boolean remove(Object o) {
1709             throw new UnsupportedOperationException();
1710         }
1711 
1712         @Override
1713         public boolean addAll(Collection<? extends E> c) {
1714             throw new UnsupportedOperationException();
1715         }
1716 
1717         @Override
1718         public boolean retainAll(Collection<?> c) {
1719             throw new UnsupportedOperationException();
1720         }
1721 
1722         @Override
1723         public boolean removeAll(Collection<?> c) {
1724             throw new UnsupportedOperationException();
1725         }
1726 
1727         @Override
1728         public void clear() {
1729             throw new UnsupportedOperationException();
1730         }
1731     }
1732 
1733     private static class SynchronizedSet<E> implements Set<E> {
1734         final Object mutex;
1735         private final Set<E> backingSet;
1736 
1737         SynchronizedSet(Set<E> set, Object mutex) {
1738             this.backingSet = set;
1739             this.mutex = mutex;
1740         }
1741 
1742         SynchronizedSet(Set<E> set) {
1743             this(set, new Object());
1744         }
1745 
1746         @Override
1747         public int size() {
1748             synchronized(mutex) {
1749                 return backingSet.size();
1750             }
1751         }
1752 
1753         @Override
1754         public boolean isEmpty() {
1755             synchronized(mutex) {
1756                 return backingSet.isEmpty();
1757             }
1758         }
1759 
1760         @Override
1761         public boolean contains(Object o) {
1762             synchronized(mutex) {
1763                 return backingSet.contains(o);
1764             }
1765         }
1766 
1767         @Override
1768         public Iterator<E> iterator() {
1769             return backingSet.iterator();
1770         }
1771 
1772         @Override
1773         public Object[] toArray() {
1774             synchronized(mutex) {
1775                 return backingSet.toArray();
1776             }
1777         }
1778 
1779         @Override
1780         public <E> E[] toArray(E[] a) {
1781             synchronized(mutex) {
1782                 return backingSet.toArray(a);
1783             }
1784         }
1785 
1786         @Override
1787         public boolean add(E e) {
1788             synchronized(mutex) {
1789                 return backingSet.add(e);
1790             }
1791         }
1792 
1793         @Override
1794         public boolean remove(Object o) {
1795             synchronized(mutex) {
1796                 return backingSet.remove(o);
1797             }
1798         }
1799 
1800         @Override
1801         public boolean containsAll(Collection<?> c) {
1802             synchronized(mutex) {
1803                 return backingSet.containsAll(c);
1804             }
1805         }
1806 
1807         @Override
1808         public boolean addAll(Collection<? extends E> c) {
1809             synchronized(mutex) {
1810                 return backingSet.addAll(c);
1811             }
1812         }
1813 
1814         @Override
1815         public boolean retainAll(Collection<?> c) {
1816             synchronized(mutex) {
1817                 return backingSet.retainAll(c);
1818             }
1819         }
1820 
1821         @Override
1822         public boolean removeAll(Collection<?> c) {
1823             synchronized(mutex) {
1824                 return backingSet.removeAll(c);
1825             }
1826         }
1827 
1828         @Override
1829         public void clear() {
1830             synchronized(mutex) {
1831                 backingSet.clear();
1832             }
1833         }
1834 
1835         @Override
1836         public boolean equals(Object o) {
1837             if (o == this) {
1838                 return true;
1839             }
1840             synchronized(mutex) {
1841                 return backingSet.equals(o);
1842             }
1843         }
1844 
1845         @Override
1846         public int hashCode() {
1847             synchronized (mutex) {
1848                 return backingSet.hashCode();
1849             }
1850         }
1851     }
1852 
1853     private static class SynchronizedObservableSet<E> extends SynchronizedSet<E> implements ObservableSet<E> {
1854 
1855         private final ObservableSet<E> backingSet;
1856         private SetListenerHelper listenerHelper;
1857         private final SetChangeListener<E> listener;
1858 
1859         SynchronizedObservableSet(ObservableSet<E> set, Object mutex) {
1860             super(set, mutex);
1861             backingSet = set;
1862             listener = c -> {
1863                 SetListenerHelper.fireValueChangedEvent(listenerHelper, new SetAdapterChange<E>(SynchronizedObservableSet.this, c));
1864             };
1865             backingSet.addListener(new WeakSetChangeListener<E>(listener));
1866         }
1867 
1868         SynchronizedObservableSet(ObservableSet<E> set) {
1869             this(set, new Object());
1870         }
1871 
1872         @Override
1873         public void addListener(InvalidationListener listener) {
1874             synchronized (mutex) {
1875                 listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
1876             }
1877         }
1878 
1879         @Override
1880         public void removeListener(InvalidationListener listener) {
1881             synchronized (mutex) {
1882                 listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
1883             }
1884         }
1885         @Override
1886         public void addListener(SetChangeListener<? super E> listener) {
1887             synchronized (mutex) {
1888                 listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
1889             }
1890         }
1891 
1892         @Override
1893         public void removeListener(SetChangeListener<? super E> listener) {
1894             synchronized (mutex) {
1895                 listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
1896             }
1897         }
1898     }
1899 
1900     private static class CheckedObservableSet<E> extends AbstractSet<E> implements ObservableSet<E> {
1901 
1902         private final ObservableSet<E> backingSet;
1903         private final Class<E> type;
1904         private SetListenerHelper listenerHelper;
1905         private final SetChangeListener<E> listener;
1906 
1907         CheckedObservableSet(ObservableSet<E> set, Class<E> type) {
1908             if (set == null || type == null) {
1909                 throw new NullPointerException();
1910             }
1911             backingSet = set;
1912             this.type = type;
1913             listener = c -> {
1914                 callObservers(new SetAdapterChange<E>(CheckedObservableSet.this, c));
1915             };
1916             backingSet.addListener(new WeakSetChangeListener<E>(listener));
1917         }
1918 
1919         private void callObservers(SetChangeListener.Change<? extends E> c) {
1920             SetListenerHelper.fireValueChangedEvent(listenerHelper, c);
1921         }
1922 
1923         void typeCheck(Object o) {
1924             if (o != null && !type.isInstance(o)) {
1925                 throw new ClassCastException("Attempt to insert "
1926                         + o.getClass() + " element into collection with element type "
1927                         + type);
1928             }
1929         }
1930 
1931         @Override
1932         public void addListener(InvalidationListener listener) {
1933             listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
1934         }
1935 
1936         @Override
1937         public void removeListener(InvalidationListener listener) {
1938             listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
1939         }
1940 
1941         @Override
1942         public void addListener(SetChangeListener<? super E> listener) {
1943             listenerHelper = SetListenerHelper.addListener(listenerHelper, listener);
1944         }
1945 
1946         @Override
1947         public void removeListener(SetChangeListener<? super E> listener) {
1948             listenerHelper = SetListenerHelper.removeListener(listenerHelper, listener);
1949         }
1950 
1951         @Override
1952         public int size() {
1953             return backingSet.size();
1954         }
1955 
1956         @Override
1957         public boolean isEmpty() {
1958             return backingSet.isEmpty();
1959         }
1960 
1961         @Override
1962         public boolean contains(Object o) {
1963             return backingSet.contains(o);
1964         }
1965 
1966         @Override
1967         public Object[] toArray() {
1968             return backingSet.toArray();
1969         }
1970 
1971         @Override
1972         public <T> T[] toArray(T[] a) {
1973             return backingSet.toArray(a);
1974         }
1975 
1976         @Override
1977         public boolean add(E e) {
1978             typeCheck(e);
1979             return backingSet.add(e);
1980         }
1981 
1982         @Override
1983         public boolean remove(Object o) {
1984             return backingSet.remove(o);
1985         }
1986 
1987         @Override
1988         public boolean containsAll(Collection<?> c) {
1989             return backingSet.containsAll(c);
1990         }
1991 
1992         @Override
1993         @SuppressWarnings("unchecked")
1994         public boolean addAll(Collection<? extends E> c) {
1995             E[] a = null;
1996             try {
1997                 a = c.toArray((E[]) Array.newInstance(type, 0));
1998             } catch (ArrayStoreException e) {
1999                 throw new ClassCastException();
2000             }
2001 
2002             return backingSet.addAll(Arrays.asList(a));
2003         }
2004 
2005         @Override
2006         public boolean retainAll(Collection<?> c) {
2007             return backingSet.retainAll(c);
2008         }
2009 
2010         @Override
2011         public boolean removeAll(Collection<?> c) {
2012             return backingSet.removeAll(c);
2013         }
2014 
2015         @Override
2016         public void clear() {
2017             backingSet.clear();
2018         }
2019 
2020         @Override
2021         public boolean equals(Object o) {
2022             return o == this || backingSet.equals(o);
2023         }
2024 
2025         @Override
2026         public int hashCode() {
2027             return backingSet.hashCode();
2028         }
2029 
2030         @Override
2031         public Iterator<E> iterator() {
2032             final Iterator<E> it = backingSet.iterator();
2033 
2034             return new Iterator<E>() {
2035                 @Override
2036                 public boolean hasNext() {
2037                     return it.hasNext();
2038                 }
2039 
2040                 @Override
2041                 public E next() {
2042                     return it.next();
2043                 }
2044 
2045                 @Override
2046                 public void remove() {
2047                     it.remove();
2048                 }
2049             };
2050         }
2051 
2052     }
2053 
2054     private static class EmptyObservableMap<K, V> extends AbstractMap<K, V> implements ObservableMap<K, V> {
2055 
2056         public EmptyObservableMap() {
2057         }
2058 
2059         @Override
2060         public void addListener(InvalidationListener listener) {
2061         }
2062 
2063         @Override
2064         public void removeListener(InvalidationListener listener) {
2065         }
2066 
2067         @Override
2068         public void addListener(MapChangeListener<? super K, ? super V> listener) {
2069         }
2070 
2071         @Override
2072         public void removeListener(MapChangeListener<? super K, ? super V> listener) {
2073         }
2074 
2075         @Override
2076         public int size() {
2077             return 0;
2078         }
2079 
2080         @Override
2081         public boolean isEmpty() {
2082             return true;
2083         }
2084 
2085         @Override
2086         public boolean containsKey(Object key) {
2087             return false;
2088         }
2089 
2090         @Override
2091         public boolean containsValue(Object value) {
2092             return false;
2093         }
2094 
2095         @Override
2096         public V get(Object key) {
2097             return null;
2098         }
2099 
2100         @Override
2101         public Set<K> keySet() {
2102             return emptyObservableSet();
2103         }
2104 
2105         @Override
2106         public Collection<V> values() {
2107             return emptyObservableSet();
2108         }
2109 
2110         @Override
2111         public Set<Map.Entry<K,V>> entrySet() {
2112             return emptyObservableSet();
2113         }
2114 
2115         @Override
2116         public boolean equals(Object o) {
2117             return (o instanceof Map) && ((Map<?,?>)o).isEmpty();
2118         }
2119 
2120         @Override
2121         public int hashCode() {
2122             return 0;
2123         }
2124     }
2125 
2126     private static class CheckedObservableMap<K, V> extends AbstractMap<K, V> implements ObservableMap<K, V> {
2127 
2128         private final ObservableMap<K, V> backingMap;
2129         private final Class<K> keyType;
2130         private final Class<V> valueType;
2131         private MapListenerHelper listenerHelper;
2132         private final MapChangeListener<K, V> listener;
2133 
2134         CheckedObservableMap(ObservableMap<K, V> map, Class<K> keyType, Class<V> valueType) {
2135             backingMap = map;
2136             this.keyType = keyType;
2137             this.valueType = valueType;
2138             listener = c -> {
2139                 callObservers(new MapAdapterChange<K, V>(CheckedObservableMap.this, c));
2140             };
2141             backingMap.addListener(new WeakMapChangeListener<K, V>(listener));
2142         }
2143 
2144         private void callObservers(MapChangeListener.Change<? extends K, ? extends V> c) {
2145             MapListenerHelper.fireValueChangedEvent(listenerHelper, c);
2146         }
2147 
2148         void typeCheck(Object key, Object value) {
2149             if (key != null && !keyType.isInstance(key)) {
2150                 throw new ClassCastException("Attempt to insert "
2151                         + key.getClass() + " key into map with key type "
2152                         + keyType);
2153             }
2154 
2155             if (value != null && !valueType.isInstance(value)) {
2156                 throw new ClassCastException("Attempt to insert "
2157                         + value.getClass() + " value into map with value type "
2158                         + valueType);
2159             }
2160         }
2161 
2162         @Override
2163         public void addListener(InvalidationListener listener) {
2164             listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
2165         }
2166 
2167         @Override
2168         public void removeListener(InvalidationListener listener) {
2169             listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
2170         }
2171 
2172         @Override
2173         public void addListener(MapChangeListener<? super K, ? super V> listener) {
2174             listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
2175         }
2176 
2177         @Override
2178         public void removeListener(MapChangeListener<? super K, ? super V> listener) {
2179             listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
2180         }
2181 
2182         @Override
2183         public int size() {
2184             return backingMap.size();
2185         }
2186 
2187         @Override
2188         public boolean isEmpty() {
2189             return backingMap.isEmpty();
2190         }
2191 
2192         @Override
2193         public boolean containsKey(Object key) {
2194             return backingMap.containsKey(key);
2195         }
2196 
2197         @Override
2198         public boolean containsValue(Object value) {
2199             return backingMap.containsValue(value);
2200         }
2201 
2202         @Override
2203         public V get(Object key) {
2204             return backingMap.get(key);
2205         }
2206 
2207         @Override
2208         public V put(K key, V value) {
2209             typeCheck(key, value);
2210             return backingMap.put(key, value);
2211         }
2212 
2213         @Override
2214         public V remove(Object key) {
2215             return backingMap.remove(key);
2216         }
2217 
2218         @Override
2219         @SuppressWarnings("unchecked")
2220         public void putAll(Map t) {
2221             // Satisfy the following goals:
2222             // - good diagnostics in case of type mismatch
2223             // - all-or-nothing semantics
2224             // - protection from malicious t
2225             // - correct behavior if t is a concurrent map
2226             Object[] entries = t.entrySet().toArray();
2227             List<Map.Entry<K,V>> checked =
2228                 new ArrayList<Map.Entry<K,V>>(entries.length);
2229             for (Object o : entries) {
2230                 Map.Entry<?,?> e = (Map.Entry<?,?>) o;
2231                 Object k = e.getKey();
2232                 Object v = e.getValue();
2233                 typeCheck(k, v);
2234                 checked.add(
2235                     new AbstractMap.SimpleImmutableEntry<K,V>((K) k, (V) v));
2236             }
2237             for (Map.Entry<K,V> e : checked)
2238                 backingMap.put(e.getKey(), e.getValue());
2239         }
2240 
2241         @Override
2242         public void clear() {
2243             backingMap.clear();
2244         }
2245 
2246         @Override
2247         public Set<K> keySet() {
2248             return backingMap.keySet();
2249         }
2250 
2251         @Override
2252         public Collection<V> values() {
2253             return backingMap.values();
2254         }
2255 
2256         private transient Set<Map.Entry<K,V>> entrySet = null;
2257 
2258         @Override
2259         public Set entrySet() {
2260             if (entrySet==null)
2261                 entrySet = new CheckedEntrySet<K,V>(backingMap.entrySet(), valueType);
2262             return entrySet;
2263         }
2264 
2265         @Override
2266         public boolean equals(Object o) {
2267             return o == this || backingMap.equals(o);
2268         }
2269 
2270         @Override
2271         public int hashCode() {
2272             return backingMap.hashCode();
2273         }
2274 
2275         static class CheckedEntrySet<K,V> implements Set<Map.Entry<K,V>> {
2276             private final Set<Map.Entry<K,V>> s;
2277             private final Class<V> valueType;
2278 
2279             CheckedEntrySet(Set<Map.Entry<K, V>> s, Class<V> valueType) {
2280                 this.s = s;
2281                 this.valueType = valueType;
2282             }
2283 
2284             @Override
2285             public int size() {
2286                 return s.size();
2287             }
2288 
2289             @Override
2290             public boolean isEmpty() {
2291                 return s.isEmpty();
2292             }
2293 
2294             @Override
2295             public String toString() {
2296                 return s.toString();
2297             }
2298 
2299             @Override
2300             public int hashCode() {
2301                 return s.hashCode();
2302             }
2303 
2304             @Override
2305             public void clear() {
2306                 s.clear();
2307             }
2308 
2309             @Override
2310             public boolean add(Map.Entry<K, V> e) {
2311                 throw new UnsupportedOperationException();
2312             }
2313 
2314             @Override
2315             public boolean addAll(Collection<? extends Map.Entry<K, V>> coll) {
2316                 throw new UnsupportedOperationException();
2317             }
2318 
2319             @Override
2320             public Iterator<Map.Entry<K,V>> iterator() {
2321                 final Iterator<Map.Entry<K, V>> i = s.iterator();
2322                 final Class<V> valueType = this.valueType;
2323 
2324                 return new Iterator<Map.Entry<K,V>>() {
2325                     @Override
2326                     public boolean hasNext() {
2327                         return i.hasNext();
2328                     }
2329 
2330                     @Override
2331                     public void remove() {
2332                         i.remove();
2333                     }
2334 
2335                     @Override
2336                     public Map.Entry<K,V> next() {
2337                         return checkedEntry(i.next(), valueType);
2338                     }
2339                 };
2340             }
2341 
2342             @Override
2343             @SuppressWarnings("unchecked")
2344             public Object[] toArray() {
2345                 Object[] source = s.toArray();
2346 
2347                 /*
2348                  * Ensure that we don't get an ArrayStoreException even if
2349                  * s.toArray returns an array of something other than Object
2350                  */
2351                 Object[] dest = (CheckedEntry.class.isInstance(
2352                     source.getClass().getComponentType()) ? source :
2353                                  new Object[source.length]);
2354 
2355                 for (int i = 0; i < source.length; i++)
2356                     dest[i] = checkedEntry((Map.Entry<K,V>)source[i],
2357                                            valueType);
2358                 return dest;
2359             }
2360 
2361             @Override
2362             @SuppressWarnings("unchecked")
2363             public <T> T[] toArray(T[] a) {
2364                 // We don't pass a to s.toArray, to avoid window of
2365                 // vulnerability wherein an unscrupulous multithreaded client
2366                 // could get his hands on raw (unwrapped) Entries from s.
2367                 T[] arr = s.toArray(a.length==0 ? a : Arrays.copyOf(a, 0));
2368 
2369                 for (int i=0; i<arr.length; i++)
2370                     arr[i] = (T) checkedEntry((Map.Entry<K,V>)arr[i],
2371                                               valueType);
2372                 if (arr.length > a.length)
2373                     return arr;
2374 
2375                 System.arraycopy(arr, 0, a, 0, arr.length);
2376                 if (a.length > arr.length)
2377                     a[arr.length] = null;
2378                 return a;
2379             }
2380 
2381             /**
2382              * This method is overridden to protect the backing set against
2383              * an object with a nefarious equals function that senses
2384              * that the equality-candidate is Map.Entry and calls its
2385              * setValue method.
2386              */
2387             @Override
2388             public boolean contains(Object o) {
2389                 if (!(o instanceof Map.Entry))
2390                     return false;
2391                 Map.Entry<?,?> e = (Map.Entry<?,?>) o;
2392                 return s.contains(
2393                     (e instanceof CheckedEntry) ? e : checkedEntry(e, valueType));
2394             }
2395 
2396             /**
2397              * The bulk collection methods are overridden to protect
2398              * against an unscrupulous collection whose contains(Object o)
2399              * method senses when o is a Map.Entry, and calls o.setValue.
2400              */
2401             @Override
2402             public boolean containsAll(Collection<?> c) {
2403                 for (Object o : c)
2404                     if (!contains(o)) // Invokes safe contains() above
2405                         return false;
2406                 return true;
2407             }
2408 
2409             @Override
2410             public boolean remove(Object o) {
2411                 if (!(o instanceof Map.Entry))
2412                     return false;
2413                 return s.remove(new AbstractMap.SimpleImmutableEntry
2414                                 <Object, Object>((Map.Entry<?,?>)o));
2415             }
2416 
2417             @Override
2418             public boolean removeAll(Collection<?> c) {
2419                 return batchRemove(c, false);
2420             }
2421 
2422             @Override
2423             public boolean retainAll(Collection<?> c) {
2424                 return batchRemove(c, true);
2425             }
2426 
2427             private boolean batchRemove(Collection<?> c, boolean complement) {
2428                 boolean modified = false;
2429                 Iterator<Map.Entry<K,V>> it = iterator();
2430                 while (it.hasNext()) {
2431                     if (c.contains(it.next()) != complement) {
2432                         it.remove();
2433                         modified = true;
2434                     }
2435                 }
2436                 return modified;
2437             }
2438 
2439             @Override
2440             public boolean equals(Object o) {
2441                 if (o == this)
2442                     return true;
2443                 if (!(o instanceof Set))
2444                     return false;
2445                 Set<?> that = (Set<?>) o;
2446                 return that.size() == s.size()
2447                     && containsAll(that); // Invokes safe containsAll() above
2448             }
2449 
2450             static <K,V,T> CheckedEntry<K,V,T> checkedEntry(Map.Entry<K,V> e,
2451                                                             Class<T> valueType) {
2452                 return new CheckedEntry<K,V,T>(e, valueType);
2453             }
2454 
2455             /**
2456              * This "wrapper class" serves two purposes: it prevents
2457              * the client from modifying the backing Map, by short-circuiting
2458              * the setValue method, and it protects the backing Map against
2459              * an ill-behaved Map.Entry that attempts to modify another
2460              * Map.Entry when asked to perform an equality check.
2461              */
2462             private static class CheckedEntry<K,V,T> implements Map.Entry<K,V> {
2463                 private final Map.Entry<K, V> e;
2464                 private final Class<T> valueType;
2465 
2466                 CheckedEntry(Map.Entry<K, V> e, Class<T> valueType) {
2467                     this.e = e;
2468                     this.valueType = valueType;
2469                 }
2470 
2471                 @Override
2472                 public K getKey() {
2473                     return e.getKey();
2474                 }
2475 
2476                 @Override
2477                 public V getValue() {
2478                     return e.getValue();
2479                 }
2480 
2481                 @Override
2482                 public int hashCode() {
2483                     return e.hashCode();
2484                 }
2485 
2486                 @Override
2487                 public String toString() {
2488                     return e.toString();
2489                 }
2490 
2491                 @Override
2492                 public V setValue(V value) {
2493                     if (value != null && !valueType.isInstance(value))
2494                         throw new ClassCastException(badValueMsg(value));
2495                     return e.setValue(value);
2496                 }
2497 
2498                 private String badValueMsg(Object value) {
2499                     return "Attempt to insert " + value.getClass() +
2500                         " value into map with value type " + valueType;
2501                 }
2502 
2503                 @Override
2504                 public boolean equals(Object o) {
2505                     if (o == this)
2506                         return true;
2507                     if (!(o instanceof Map.Entry))
2508                         return false;
2509                     return e.equals(new AbstractMap.SimpleImmutableEntry
2510                                     <Object, Object>((Map.Entry<?,?>)o));
2511                 }
2512             }
2513         }
2514 
2515     }
2516 
2517     private static class SynchronizedMap<K, V> implements Map<K, V> {
2518         final Object mutex;
2519         private final Map<K, V> backingMap;
2520 
2521         SynchronizedMap(Map<K, V> map, Object mutex) {
2522             backingMap = map;
2523             this.mutex = mutex;
2524         }
2525 
2526         SynchronizedMap(Map<K, V> map) {
2527             this(map, new Object());
2528         }
2529 
2530         @Override
2531         public int size() {
2532             synchronized (mutex) {
2533                 return backingMap.size();
2534             }
2535         }
2536 
2537         @Override
2538         public boolean isEmpty() {
2539             synchronized (mutex) {
2540                 return backingMap.isEmpty();
2541             }
2542         }
2543 
2544         @Override
2545         public boolean containsKey(Object key) {
2546             synchronized (mutex) {
2547                 return backingMap.containsKey(key);
2548             }
2549         }
2550 
2551         @Override
2552         public boolean containsValue(Object value) {
2553             synchronized (mutex) {
2554                 return backingMap.containsValue(value);
2555             }
2556         }
2557 
2558         @Override
2559         public V get(Object key) {
2560             synchronized (mutex) {
2561                 return backingMap.get(key);
2562             }
2563         }
2564 
2565         @Override
2566         public V put(K key, V value) {
2567             synchronized (mutex) {
2568                 return backingMap.put(key, value);
2569             }
2570         }
2571 
2572         @Override
2573         public V remove(Object key) {
2574             synchronized (mutex) {
2575                 return backingMap.remove(key);
2576             }
2577         }
2578 
2579         @Override
2580         public void putAll(Map<? extends K, ? extends V> m) {
2581             synchronized (mutex) {
2582                 backingMap.putAll(m);
2583             }
2584         }
2585 
2586         @Override
2587         public void clear() {
2588             synchronized (mutex) {
2589                 backingMap.clear();
2590             }
2591         }
2592 
2593         private transient Set<K> keySet = null;
2594         private transient Set<Map.Entry<K,V>> entrySet = null;
2595         private transient Collection<V> values = null;
2596 
2597         @Override
2598         public Set<K> keySet() {
2599             synchronized(mutex) {
2600                 if (keySet==null)
2601                     keySet = new SynchronizedSet<K>(backingMap.keySet(), mutex);
2602                 return keySet;
2603             }
2604         }
2605 
2606         @Override
2607         public Collection<V> values() {
2608             synchronized(mutex) {
2609                 if (values==null)
2610                     values = new SynchronizedCollection<V>(backingMap.values(), mutex);
2611                 return values;
2612             }
2613         }
2614 
2615         @Override
2616         public Set<Entry<K, V>> entrySet() {
2617             synchronized(mutex) {
2618                 if (entrySet==null)
2619                     entrySet = new SynchronizedSet<Map.Entry<K,V>>(backingMap.entrySet(), mutex);
2620                 return entrySet;
2621             }
2622         }
2623 
2624         @Override
2625         public boolean equals(Object o) {
2626             if (o == this) {
2627                 return true;
2628             }
2629             synchronized(mutex) {
2630                 return backingMap.equals(o);
2631             }
2632         }
2633 
2634         @Override
2635         public int hashCode() {
2636             synchronized(mutex) {
2637                 return backingMap.hashCode();
2638             }
2639         }
2640 
2641     }
2642 
2643     private static class SynchronizedCollection<E> implements Collection<E> {
2644 
2645         private final Collection<E> backingCollection;
2646         final Object mutex;
2647 
2648         SynchronizedCollection(Collection<E> c, Object mutex) {
2649             backingCollection = c;
2650             this.mutex = mutex;
2651         }
2652 
2653         SynchronizedCollection(Collection<E> c) {
2654             this(c, new Object());
2655         }
2656 
2657         @Override
2658         public int size() {
2659             synchronized (mutex) {
2660                 return backingCollection.size();
2661             }
2662         }
2663 
2664         @Override
2665         public boolean isEmpty() {
2666             synchronized (mutex) {
2667                 return backingCollection.isEmpty();
2668             }
2669         }
2670 
2671         @Override
2672         public boolean contains(Object o) {
2673             synchronized (mutex) {
2674                 return backingCollection.contains(o);
2675             }
2676         }
2677 
2678         @Override
2679         public Iterator<E> iterator() {
2680             return backingCollection.iterator();
2681         }
2682 
2683         @Override
2684         public Object[] toArray() {
2685             synchronized (mutex) {
2686                 return backingCollection.toArray();
2687             }
2688         }
2689 
2690         @Override
2691         public <T> T[] toArray(T[] a) {
2692             synchronized (mutex) {
2693                 return backingCollection.toArray(a);
2694             }
2695         }
2696 
2697         @Override
2698         public boolean add(E e) {
2699             synchronized (mutex) {
2700                 return backingCollection.add(e);
2701             }
2702         }
2703 
2704         @Override
2705         public boolean remove(Object o) {
2706             synchronized (mutex) {
2707                 return backingCollection.remove(o);
2708             }
2709         }
2710 
2711         @Override
2712         public boolean containsAll(Collection<?> c) {
2713             synchronized (mutex) {
2714                 return backingCollection.containsAll(c);
2715             }
2716         }
2717 
2718         @Override
2719         public boolean addAll(Collection<? extends E> c) {
2720             synchronized (mutex) {
2721                 return backingCollection.addAll(c);
2722             }
2723         }
2724 
2725         @Override
2726         public boolean removeAll(Collection<?> c) {
2727             synchronized (mutex) {
2728                 return backingCollection.removeAll(c);
2729             }
2730         }
2731 
2732         @Override
2733         public boolean retainAll(Collection<?> c) {
2734             synchronized (mutex) {
2735                 return backingCollection.retainAll(c);
2736             }
2737         }
2738 
2739         @Override
2740         public void clear() {
2741             synchronized (mutex) {
2742                 backingCollection.clear();
2743             }
2744         }
2745     }
2746 
2747     private static class SynchronizedObservableMap<K, V> extends SynchronizedMap<K, V> implements ObservableMap<K, V> {
2748 
2749         private final ObservableMap<K, V> backingMap;
2750         private MapListenerHelper listenerHelper;
2751         private final MapChangeListener<K, V> listener;
2752 
2753         SynchronizedObservableMap(ObservableMap<K, V> map, Object mutex) {
2754             super(map, mutex);
2755             backingMap = map;
2756             listener = c -> {
2757                 MapListenerHelper.fireValueChangedEvent(listenerHelper, new MapAdapterChange<K, V>(SynchronizedObservableMap.this, c));
2758             };
2759             backingMap.addListener(new WeakMapChangeListener<K, V>(listener));
2760         }
2761 
2762         SynchronizedObservableMap(ObservableMap<K, V> map) {
2763             this(map, new Object());
2764         }
2765 
2766         @Override
2767         public void addListener(InvalidationListener listener) {
2768             synchronized (mutex) {
2769                 listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
2770             }
2771         }
2772 
2773         @Override
2774         public void removeListener(InvalidationListener listener) {
2775             synchronized (mutex) {
2776                 listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
2777             }
2778         }
2779 
2780         @Override
2781         public void addListener(MapChangeListener<? super K, ? super V> listener) {
2782             synchronized (mutex) {
2783                 listenerHelper = MapListenerHelper.addListener(listenerHelper, listener);
2784             }
2785         }
2786 
2787         @Override
2788         public void removeListener(MapChangeListener<? super K, ? super V> listener) {
2789             synchronized (mutex) {
2790                 listenerHelper = MapListenerHelper.removeListener(listenerHelper, listener);
2791             }
2792         }
2793 
2794     }
2795 
2796 }