View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.util;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.ObjectOutputStream;
24  import java.io.ObjectStreamException;
25  import java.io.Serializable;
26  import java.lang.reflect.Array;
27  import java.util.AbstractQueue;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.ConcurrentModificationException;
32  import java.util.EnumSet;
33  import java.util.HashSet;
34  import java.util.Iterator;
35  import java.util.LinkedList;
36  import java.util.List;
37  import java.util.ListIterator;
38  import java.util.Map;
39  import java.util.NoSuchElementException;
40  import java.util.Queue;
41  import java.util.RandomAccess;
42  import java.util.Set;
43  import java.util.concurrent.atomic.AtomicReference;
44  
45  import org.apache.myfaces.trinidad.component.CompositeIterator;
46  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
47  
48  /**
49   * This class contains Collection utilities.
50   */
51  public final class CollectionUtils
52  {
53    private CollectionUtils()
54    {
55      // no-op
56    }
57    
58    /**
59     * Returns an ArrayList containing all of the elements of the
60     * Iterator
61     * @param iterator Iterator to copy the contexts of
62     * @return an ArrayList containing a copy of the iterator contents
63     */
64    public static <T> ArrayList<T> arrayList(Iterator<T> iterator)
65    {
66      ArrayList<T> arrayList = new ArrayList<T>();
67      
68      while (iterator.hasNext())
69        arrayList.add(iterator.next());
70      
71      return arrayList;
72    }
73  
74    /**
75     * Returns an array containing all of the elements of the
76     * Iterator
77     * @param iterator Iterator to copy the contexts of
78     * @return an array containing a copy of the iterator contents
79     */
80    public static <T> T[] toArray(Iterator<? extends T> iterator, Class<T> type)
81    {
82      if (iterator.hasNext())
83      {
84        Collection arrayList = arrayList(iterator);
85        T[] outArray = (T[])Array.newInstance(type, arrayList.size());
86      
87        return (T[])arrayList.toArray(outArray);
88      }
89      else
90      {
91        // optimize empty iterator case
92        return (T[])Array.newInstance(type, 0);
93      }
94    }
95  
96    /**
97     * Returns an empty, unmodifiable, Serializable Queue.
98     * @return an empty, unmodifiable, Serializable Queue.
99     */
100   public static <T> Queue<T> emptyQueue()
101   {
102     return (Queue<T>)_EMPTY_QUEUE;
103   }
104 
105   /**
106    * Returns an empty, unmodifiable, Iterator.
107    * @return an empty, unmodifiable, Iterator.
108    */
109   public static <T> Iterator<T> emptyIterator()
110   {
111     return (Iterator<T>)_EMPTY_ITERATOR;
112   }
113 
114   /**
115    * Returns an empty, unmodifiable, ListIterator.
116    * @return an empty, unmodifiable, ListIterator.
117    */
118   public static <T> ListIterator<T> emptyListIterator()
119   {
120     return (ListIterator<T>)_EMPTY_LIST_ITERATOR;
121   }
122   
123   /**
124    * Return an iterator of the values in <code>map</code> with entries in <code>allowedLeys</code>
125    * @param <K> Map key type
126    * @param <V> Map value type
127    * @param map Map of keys and values
128    * @param allowedKeys Collection of keys to return values for if present in map
129    * @return Iterator of values for which the map contains a key from allowedKeys
130    */
131   public static <K,V> Iterator<V> subsetValueIterator(Map<? extends K, ? extends V> map,
132                                                       Collection<? extends K> allowedKeys)
133   {
134     if (map.isEmpty() || allowedKeys.isEmpty())
135     {
136       return emptyIterator();
137     }
138     else
139     {
140       return new SubsetValueIterator<K,V>(map, allowedKeys.iterator());
141     }
142   }
143 
144   /**
145    * Return an iterator of the values in <code>map</code> with entries in <code>allowedLeys</code>
146    * The values returned in the iterator will be in the same order as their corresponding
147    * keys in allowedKeys.
148    * @param <K> Map key type
149    * @param <V> Map value type
150    * @param map Map of keys and values
151    * @param allowedKeys Iterator of keys to return values for if present in map
152    * @return Iterator of values for which the map contains a key from allowedKeys
153    */
154   public static <K,V> Iterator<V> subsetValueIterator(Map<? extends K, ? extends V> map,
155                                                       Iterator<? extends K> allowedKeys)
156   {
157     if (map.isEmpty() || !allowedKeys.hasNext())
158     {
159       return emptyIterator();
160     }
161     else
162     {
163       return new SubsetValueIterator<K,V>(map, allowedKeys);
164     }
165   }
166   
167   /**
168    * Return an iterator of the values in <code>map</code> with entries in <code>allowedLeys</code>
169    */
170   private static class SubsetValueIterator<K,V> implements Iterator<V>
171   {
172     public SubsetValueIterator(Map<? extends K, ? extends V> sourceMap,
173                                Iterator<? extends K> allowedkeys)
174     {
175       if ((sourceMap == null) || (allowedkeys == null))
176         throw new NullPointerException();
177       
178       _sourceMap = sourceMap;
179       _allowedkeys = allowedkeys;
180     }
181     
182     @Override
183     public boolean hasNext()
184     {      
185       return (_getKey() != null);
186     }
187     
188     @Override
189     public V next()
190     {
191       K key = _getKey();
192       
193       if (key == null)
194         throw new NoSuchElementException();
195       
196       // next call to _getKey() will nove to the next key
197       _calcNext = true;
198       
199       return _sourceMap.get(key);
200     }
201     
202     @Override
203     public void remove()
204     {
205       // make sure that we have called next() before remove()
206       if (_nextValidKey == null)
207         throw new IllegalStateException();
208       
209       try
210       {
211         _sourceMap.remove(_nextValidKey);
212       }
213       finally
214       {
215         _nextValidKey = null;
216         _calcNext = true;
217       } 
218     }
219 
220     private K _getKey()
221     {
222       if ((_nextValidKey == null) || _calcNext)
223       {
224         _nextValidKey = _getNextContainedKey();
225         _calcNext = false;
226       }
227       
228       return _nextValidKey;
229     }
230         
231     private K _getNextContainedKey()
232     {
233       while (_allowedkeys.hasNext())
234       {
235         K nextKey = _allowedkeys.next();
236         
237         if (_sourceMap.containsKey(nextKey))
238         {
239           return nextKey;
240         }
241       }
242       
243       return null;
244     }
245         
246     private final Map<? extends K, ? extends V> _sourceMap;
247     private final Iterator<? extends K> _allowedkeys;
248     private K _nextValidKey;
249     private boolean _calcNext = true;
250   }
251   
252   /**
253    * Returns a minimal Set containing the elements passed in.  There is no guarantee that the
254    * returned set is mutable or immutable.
255    * @param <T>
256    * @param a The elements to add to the Set
257    * @return A set containing the elements passed in.
258    * @see #asUnmodifiableSet
259    */
260   public static <T> Set<T> asSet(T... a)
261   {
262     return _asSet(a, false);
263   }
264 
265   /**
266    * Returns an unmodifiable Set containing the elements passed in.
267    * @param <T>
268    * @param a The elements to add to the Set
269    * @return A set containing the elements passed in.
270    * @see #asSet
271    */
272   public static <T> Set<T> asUnmodifiableSet(T... a)
273   {
274     return _asSet(a, true);
275   }
276 
277   /**
278    * Returns an unmodifiable versions of the Set of Enums.  If the contents of the set are known
279    * to be unmodifiable by the caller in any way, the set itself will be retured, otherwise an
280    * unmodifiable copy of the Set will be returned.
281    * @param s Set to get the tamper-proof version of
282    * @return An unmodifiable tamper-proof version of the set
283    */
284   public static <E extends Enum<E>> Set<E> unmodifiableCopyOfEnumSet(Set<E> s)
285   {
286     Class<? extends Set> copyClass = s.getClass();
287 
288     if ((_EMPTY_SET == copyClass) || (_SINGLETON_SET == copyClass))
289     {
290       // these classes are already unmodifiable, so just return
291       return s;
292     }
293     else
294     {
295       return Collections.unmodifiableSet(EnumSet.copyOf(s));
296     }
297   }
298 
299   private static <T> Set<T> _asSet(T[] a, boolean makeImmutable)
300   {
301     int count = (a != null) ? a.length : 0;
302 
303     Set<T> outSet;
304     
305     if (count == 0)
306     {
307       outSet = Collections.emptySet();
308     }
309     else
310     {
311       if (count == 1)
312       {
313         outSet = Collections.singleton(a[0]);
314       }
315       else
316       {
317         // make the initial size big enough that we don't have to rehash to fit the array, for
318         // the .75 load factor we pass in
319         int initialSize = (int)Math.ceil(count / .75d);
320         
321         outSet = new HashSet<T>(initialSize, .75f);
322         
323         for (int i = 0; i < count ; i++)
324         {
325           outSet.add(a[i]);
326         } 
327         
328         if (makeImmutable)
329         {
330           outSet = Collections.unmodifiableSet(outSet);
331         }
332       }
333     }
334     
335     return outSet;
336   }
337 
338   /**
339    * Given two disjoint sets, returns a live composition of the two Sets that maintains
340    * the disjoint invariant.  If both Sets are Serializable, the returned
341    * implementation will be Serializable as well.  The returned Set implementation is
342    * not thread safe.
343    * @param primarySet The Set that modifications will be applied to
344    * @param secondarySet The other Set.  Modifications will be applied in response to changes
345    * to the primary set to ensure that the disjoint invariant is maintained
346    * @return The composition of the two disjoint Sets
347    * @throws NullPointerException of primarySet or secondarySet are <code>null</code>
348    * @see #overlappingCompositeSet
349    */
350   public static <T> Set<T> compositeSet(Set<T> primarySet, Set<T> secondarySet)
351   {
352     if ((primarySet instanceof Serializable) && (secondarySet instanceof Serializable))
353       return new SerializableFixedCompositeSet<T>(primarySet, secondarySet);
354     else
355       return new FixedCompositeSet<T>(primarySet, secondarySet);
356   }
357 
358   /**
359    * Given two possibly overlapping sets, returns a live composition of the two Sets.
360    * If both Sets are Serializable, the returned
361    * implementation will be Serializable as well.  The lack of the disjoint invariant makes
362    * operations such as calculating the size of the Set expensive.  If the disjoint invariant
363    * can be guaranteed, <code>compositeSet</code> should be used instead.
364    * The returned Set implementation is
365    * not thread safe.
366    * @param primarySet The Set that modifications will be applied to
367    * @param secondarySet The other Set.  If a removal is performed on the primarySet, it will
368    * also be applied to the secondarySet to ensure that the element is logically removed from the
369    * Set.
370    * @return The composition of the two possibly overallping Sets
371    * @throws NullPointerException of primarySet or secondarySet are <code>null</code>
372    * @see #compositeSet
373    */
374   public static <T> Set<T> overlappingCompositeSet(Set<T> primarySet, Set<T> secondarySet)
375   {
376     if ((primarySet instanceof Serializable) && (secondarySet instanceof Serializable))
377       return new SerializableLenientFixedCompositeSet<T>(primarySet, secondarySet);
378     else
379       return new LenientFixedCompositeSet<T>(primarySet, secondarySet);
380   }
381 
382   /**
383    * Returns a Collection based on the passed in Collection <code>c</code>,
384    * guaranteed to be Serializable. If <code>c</code> is Serializable,
385    * <code>c</code> will be returned, otherwise, <code>c</code> will be
386    * wrapped in a Collection that implements Serializable and upon
387    * Serialization the contents of <code>c</code> will be copied into
388    * the result.
389    * <p>
390    * The results is very similar to creating a new ArrayList with the
391    * contents of <code>c</code>, but no wrapper is created unless necessary
392    * and the actual creation of the Serializable copy is deferred until
393    * Serialization occurs.
394    * @param c The Collection to get a Serializable version of
395    * @return A Serializable version of Collection <code>c</code>
396    * @see #getSerializableList
397    */
398   public static <T> Collection<T> getSerializableCollection(Collection<T> c)
399   {
400     if (c instanceof Serializable)
401       return c;
402     else
403       return new SerializableCollection<T>(c);
404   }
405 
406   /**
407    * Returns a List based on the passed in List <code>l</code>,
408    * guaranteed to be Serializable. List <code>l</code> will be
409    * wrapped in a List that implements Serializable and upon
410    * Serialization the contents of <code>l</code> will be copied into
411    * the result.
412    * <p>
413    * If <code>l</code> implements RandomAccess, any returned List will also
414    * implement RandomAccess.
415    * <p>
416    * The results is very similar to creating a new ArrayList with the
417    * contents of <code>l</code>, but no wrapper is created unless necessary
418    * and the actual creation of the Serializable copy is deferred until
419    * Serialization occurs.
420    * <p>
421    * Code that calls List.subList() and needs the result to be Serializable should always
422    * use <code>newSerializableList</code> rather than <code>getSerializableList</code> because
423    * the <code>java.util.Collections</code> implementations of <code>checkedList</code>,
424    * <code>unmodifiableList</code>, and <code>synchronizedList</code> all lie and always implement
425    * Serializable, regardless of the serializability of their backing List.
426    * @param l The List to get a Serializable version of
427    * @return A Serializable version of List <code>l</code>
428    * @see #getSerializableList
429    * @see #getSerializableCollection
430    */
431   public static <T> List<T> newSerializableList(List<T> l)
432   {
433     if (l instanceof RandomAccess)
434     {
435       return new SerializableRandomAccessList<T>(l);
436     }
437     else
438     {
439       return new SerializableList<T>(l);
440     }
441   }
442 
443   /**
444    * Returns a List based on the passed in List <code>l</code>,
445    * guaranteed to be Serializable. If <code>l</code> is Serializable,
446    * <code>l</code> will be returned, otherwise, <code>l</code> will be
447    * wrapped in a List that implements Serializable and upon
448    * Serialization the contents of <code>l</code> will be copied into
449    * the result.
450    * <p>
451    * If <code>l</code> implements RandomAccess, any returned List will also
452    * implement RandomAccess.
453    * <p>
454    * The results is very similar to creating a new ArrayList with the
455    * contents of <code>l</code>, but no wrapper is created unless necessary
456    * and the actual creation of the Serializable copy is deferred until
457    * Serialization occurs.
458    * <p>
459    * Code that calls List.subList() and needs the result to be Serializable should always
460    * use <code>newSerializableList</code> rather than <code>getSerializableList</code> because
461    * the <code>java.util.Collections</code> implementations of <code>checkedList</code>,
462    * <code>unmodifiableList</code>, and <code>synchronizedList</code> all lie and always implement
463    * Serializable, regardless of the serializability of their backing List.
464    * @param l The List to get a Serializable version of
465    * @return A Serializable version of List <code>l</code>
466    * @see #newSerializableList
467    * @see #getSerializableCollection
468    */
469   public static <T> List<T> getSerializableList(List<T> l)
470   {
471     // because we can't trust the implementations of the checked, unmodifiable, and synchronized
472     // versions, always create a Serializable wrapper if we see one of these classes
473     if ((l instanceof Serializable) &&
474          !_CHECKED_LIST.isInstance(l) &&
475          !_UNMODIFIABLE_LIST.isInstance(l) &&
476          !_SYNCHRONIZED_LIST.isInstance(l))
477       return l;
478     else
479     {
480       return newSerializableList(l);
481     }
482   }
483 
484   /**
485    * Interface for trapping mutations to a Map.
486    * @param <K> the type of the keys of the Map that MapMutationHooks are associated with
487    * @param <V> the type of the values of the Map that MapMutationHooks are associated with
488    * @see #newMutationHookedMap
489    */
490   public interface MapMutationHooks<K, V>
491   {
492     /**
493      * Called when the associated Map of the MapMutationHooks is written to
494      * @param map   Map the write occurred on
495      * @param key   key of entry that has changed
496      * @param value value of entry that has changed
497      */
498     public void writeNotify(Map<K,V> map, K key, V value);
499 
500     /**
501      * Called when an entry is removed from the associated Map of the MapMutationHooks
502      * @param map   Map the removal occurred on
503      * @param key   key of entry that has been removed
504      */
505     public void removeNotify(Map<K,V> map, Object key);
506     
507     /**
508      * Called when all entries are removed from the Map associated with the MapMutationHooks
509      * @param map   Map the clear occurred on
510      */
511     public void clearNotify(Map<K,V> map);
512   }
513 
514   /**
515    * Creates a new Map that informs the MapMutationHooks of any direct mutations.  Mutations to
516    * the underlying Map will not be caught.
517    * If the base map is Serializable, the returned Map will be Serializable
518    * @param <K> type of the keys of the Map
519    * @param <V> type of the values of the Map
520    * @param map Underlying map to trap mutations of
521    * @param hooks MapMutationHooks to inform of mutations to the returned Map
522    * @return a new Map that traps the mutations to the underlying Map
523    * @throws NullPointerException if map or hooks are null
524    */
525   public static <K,V> Map<K, V> newMutationHookedMap(Map<K, V> map, MapMutationHooks<K, V> hooks)
526   {
527     if (map == null)
528       throw new NullPointerException();
529     
530     if (hooks == null)
531       throw new NullPointerException();
532     
533     if (map instanceof Serializable)
534       return new SerializableExternalAccessHookMap<K, V>(map, hooks);
535     else
536       return new ExternalAccessHookMap<K, V>(map, hooks);
537   }
538 
539   /**
540    * Creates a Map that dynamically verifies that all keys and values added to it will
541    * succeed Serialization.  The validations checks not only that the keys and values added
542    * to the Map implement Serializable, but that these instances will actually succeed
543    * Serialization.
544    * <p>
545    * This checking can be defeated by either modifying the backing map directly or by modifying
546    * an object added to the checked Map after adding it.
547    * </p>
548    * @param map Map to wrap for Serialization validation
549    * @return Map where all modifications are checked to ensure that they will succeeed if
550    * serialized
551    */
552   public static <K,V> Map<K, V> getCheckedSerializationMap(Map<K, V> map)
553   {
554     return getCheckedSerializationMap(map, true);
555   }
556 
557   /**
558    * Creates a Map that dynamically verifies that all keys and values added to it will
559    * succeed Serialization.  The validations checks not only that the keys and values added
560    * to the Map implement Serializable, but that these instances will actually succeed
561    * Serialization.
562    * <p>
563    * This checking can be defeated by either modifying the backing map directly or by modifying
564    * an object added to the checked Map after adding it.
565    * </p>
566    * @param map Map to wrap for Serialization validation
567    * @param requireSerializable if <code>true</code>, require that all values in the map implement
568    *                            Serializable.
569    * @return Map where  modifications are checked to ensure that they will succeeed if
570    * serialized
571    */
572   public static <K,V> Map<K, V> getCheckedSerializationMap(
573     Map<K, V> map,
574     boolean   requireSerializable)
575   {
576     if (map instanceof CheckedSerializationMap)
577       return map;
578     else
579       return new CheckedSerializationMap<K,V>(map, requireSerializable);
580   }
581 
582   /**
583    * Given two Collections, return the size of their union
584    * @param firstCollection The first collection.  <code>null</code> is allowed.
585    * @param secondCollection The second collection.  <code>null</code> is allowed.
586    * @return
587    */
588   public static <E> int getUnionSize(
589     Collection<? extends E> firstCollection,
590     Collection<? extends E> secondCollection)
591   {    
592     int firstSize = (firstCollection != null)
593                         ? firstCollection.size()
594                         : 0;
595     
596     int secondSize = (secondCollection != null)
597                         ? secondCollection.size()
598                         : 0;
599     
600     if (firstSize == 0)
601       return secondSize;
602     
603     if (secondSize == 0)
604       return firstSize;
605     
606     // determine the size of the union by iterating over the smaller collection
607     int size;
608     Collection<? extends E> iteratingCollection;
609     Collection<? extends E> baseCollection;
610     
611     if (firstSize >= secondSize)
612     {
613       baseCollection      = firstCollection;
614       iteratingCollection = secondCollection;
615       size                = firstSize;
616     }
617     else
618     {
619       baseCollection      = secondCollection;
620       iteratingCollection = firstCollection;
621       size                = secondSize;
622     }
623     
624     for (E currValue : iteratingCollection)
625     {
626       if (!baseCollection.contains(currValue))
627       {
628         size++;
629       }
630     }
631     
632     return size; 
633   }
634   
635   
636   protected static <T> T[] copyOf(T[] original, int newLength)
637   {
638     return (T[]) copyOf(original, newLength, original.getClass());
639   }
640 
641   protected static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType)
642   {
643     T[] copy = ((Object)newType == (Object)Object[].class)
644         ? (T[]) new Object[newLength]
645         : (T[]) Array.newInstance(newType.getComponentType(), newLength);
646     System.arraycopy(original, 0, copy, 0,
647                      Math.min(original.length, newLength));
648     return copy;
649   }
650 
651   protected abstract static class DelegatingCollection<E> implements Collection<E>
652   {
653     protected abstract Collection<E> getDelegate();
654 
655     public int size()
656     {
657       return getDelegate().size();
658     }
659 
660     public boolean isEmpty()
661     {
662       return getDelegate().isEmpty();
663     }
664 
665     public boolean contains(Object o)
666     {
667       return getDelegate().contains(o);
668     }
669 
670     public Iterator<E> iterator()
671     {
672       return getDelegate().iterator();
673     }
674 
675     public Object[] toArray()
676     {
677       return getDelegate().toArray();
678     }
679 
680     public <T> T[] toArray(T[] a)
681     {
682       return getDelegate().toArray(a);
683     }
684 
685     public boolean add(E e)
686     {
687       return getDelegate().add(e);
688     }
689 
690     public boolean remove(Object o)
691     {
692       return getDelegate().remove(0);
693     }
694 
695     public boolean containsAll(Collection<?> c)
696     {
697       return getDelegate().containsAll(c);
698     }
699 
700     public boolean addAll(Collection<? extends E> c)
701     {
702       return getDelegate().addAll(c);
703     }
704 
705     public boolean removeAll(Collection<?> c)
706     {
707       return getDelegate().removeAll(c);
708     }
709 
710     public boolean retainAll(Collection<?> c)
711     {
712       return getDelegate().retainAll(c);
713     }
714 
715     public void clear()
716     {
717       getDelegate().clear();
718     }
719     
720     /**
721      * All Collections
722      * @param o
723      * @return
724      */
725     public boolean equals(Object o)
726     {
727       return (o == this) || getDelegate().equals(o);
728     }
729 
730     public int hashCode()
731     {
732       return getDelegate().hashCode();
733     }
734 
735     public String toString()
736     {
737       return getDelegate().toString();
738     }
739   }
740 
741   /**
742    * Note: Requires contents to be disjoint!
743    * @param <E>
744    */
745   protected abstract static class CompositeCollection<E> implements Collection<E>
746   {
747     protected abstract Collection<E> getPrimaryDelegate();
748     protected abstract Collection<E> getSecondaryDelegate();
749 
750     public int size()
751     {
752       return getPrimaryDelegate().size() + getSecondaryDelegate().size();
753     }
754 
755     public boolean isEmpty()
756     {
757       return getPrimaryDelegate().isEmpty() && getSecondaryDelegate().isEmpty();
758     }
759 
760     public boolean contains(Object o)
761     {
762       return getPrimaryDelegate().contains(o) || getSecondaryDelegate().contains(o);
763     }
764 
765     public Iterator<E> iterator()
766     {
767       return new CompositeIterator<E>(getPrimaryDelegate().iterator(),
768                                       getSecondaryDelegate().iterator());
769     }
770 
771     public Object[] toArray()
772     {
773       int size = size();
774       
775       Object[] out = new Object[size];
776       
777       int i = 0;
778       for (Object currObject : this)
779       {
780         out[i] = currObject;
781         
782         i++;
783         
784         if (i == size)
785           break;
786       }
787       
788       return out;
789     }
790 
791     public <T> T[] toArray(T[] outArray)
792     {
793       int collectionSize = size();
794       int arraySize = outArray.length;
795       
796       // size isn't big enough, so need a new array
797       if (collectionSize > arraySize)
798       {
799         outArray = (T[])Array.newInstance(outArray.getClass().getComponentType(),
800                                           collectionSize);
801       }
802 
803       Iterator<E> iterator = this.iterator();
804       
805       for (int i = 0; i < collectionSize; i++)
806       {
807         if (!iterator.hasNext())
808           break;
809         
810         outArray[i] = (T)iterator.next();
811       }
812       
813       return outArray;
814     }
815 
816     public boolean add(E e)
817     {
818       boolean modified = getPrimaryDelegate().add(e);
819       
820       if (modified)
821       {
822         // maintain disjointness.  If the secondary delegate already contained this element
823         // then we didn't really change
824         modified = !getSecondaryDelegate().remove(e);
825       }
826       
827       return modified;
828     }
829 
830     public boolean remove(Object o)
831     {
832       boolean removed = getPrimaryDelegate().remove(0);
833       
834       if (!removed)
835         removed = getSecondaryDelegate().remove(0);
836       
837       return removed;
838     }
839 
840     public boolean containsAll(Collection<?> c)
841     {
842       // find all of the items in both the collection and the primary delegate
843       
844       Set<Object> intersection = new HashSet<Object>(getPrimaryDelegate());
845       intersection.retainAll(c);
846             
847       if (intersection.size() == c.size())
848       {
849         // the primary delegate contained all of the items, so we're done
850         return true;
851       }
852       else
853       {
854         // compute the set of items we still haven't match in order to check against the
855         // secondary delegate
856         Set<Object> remainder = new HashSet<Object>(c);
857         remainder.removeAll(intersection);
858         
859         return getSecondaryDelegate().containsAll(remainder);
860       }
861     }
862 
863     public boolean addAll(Collection<? extends E> c)
864     {
865       // determine the result ahead of time
866       boolean changed = !containsAll(c);
867       
868       // make sure that the collections maintain disjointness
869       getSecondaryDelegate().removeAll(c);
870       getPrimaryDelegate().addAll(c);
871       
872       return changed;
873     }
874 
875     public boolean removeAll(Collection<?> c)
876     {
877       return getPrimaryDelegate().removeAll(c) || getSecondaryDelegate().removeAll(c);
878     }
879 
880     public boolean retainAll(Collection<?> c)
881     {
882       return getPrimaryDelegate().retainAll(c) || getSecondaryDelegate().retainAll(c);
883     }
884 
885     public void clear()
886     {
887       getPrimaryDelegate().clear();
888       getSecondaryDelegate().clear();
889     }
890     
891     @Override
892     public String toString()
893     {
894       return super.toString() + 
895              "[primary:" + 
896              getPrimaryDelegate() +
897              ", secondary:" +
898              getSecondaryDelegate() +
899              "]";
900     }
901   }
902   
903   /**
904    * Iterator that guarantees that removals are also performed on the non-disjoint Collection
905    * @param <E>
906    */
907   private static class RemovingIterator<E> implements Iterator<E>
908   {    
909     public RemovingIterator(Iterator<E> baseIterator, Collection<E> disjointCollection)
910     {
911       _baseIterator = baseIterator;
912       _disjointCollection = disjointCollection;
913     }
914 
915     public boolean hasNext()
916     {
917       return _baseIterator.hasNext();
918     }
919     
920     public E next()
921     {
922       _last = _baseIterator.next();
923       
924       return _last;
925     }
926  
927     public void remove()
928     {
929       _baseIterator.remove();
930       
931       // ensure that the removed element is also removed from the disjoint collection
932       // so that removing the element from the primary collection doesn't accidentally
933       // expose it in the secondary collection
934       _disjointCollection.remove(_last);
935       _last = null;
936     }
937     
938     private final Iterator<E> _baseIterator;
939     private final Collection<E> _disjointCollection;
940     private E _last;
941   }
942   
943   
944   /**
945    * Iterator returning only the elements in the disjoint Collection that aren't in the
946    * checked Collection
947    */
948   private static class DisjointIterator<E> implements Iterator<E>
949   {    
950     public DisjointIterator(Collection<E> checkedCollection, Collection<E> disjointCollection)
951     {
952       _checkedCollection = checkedCollection;
953       _disjointIterator = disjointCollection.iterator();
954     }
955 
956     public boolean hasNext()
957     {
958       if (_nextHolder == null)
959       {
960         do
961         {
962           if (_disjointIterator.hasNext())
963           {
964             E next = _disjointIterator.next();
965             
966             if (!_checkedCollection.contains(next))
967             {
968               // found it
969               _nextHolder = new AtomicReference<E>(next);
970               break;
971             }
972           }
973           else
974           {
975             return false;
976           }
977         }
978         while (true);
979       }
980       
981       return true;
982     }
983 
984     public E next()
985     {
986       // check if we have another value and if we do, populate _nextHolder
987       if (hasNext())
988       {
989         E value = _nextHolder.get();
990         
991         // clear so we know that we need to recalculate next time
992         _nextHolder = null;
993         return value;
994       }
995       else
996       {
997         throw new NoSuchElementException();
998       }
999     }
1000 
1001     public void remove()
1002     {
1003       // make sure that have have called next() before removing.  In the case where
1004       // next() has never been called, the _disjointIterator should blow up on its own.
1005       // one problem we have is that this code won't work correctly if the call order
1006       // is next(), hasNext(), remove(), since hasNext() calls next() as a side-effect.
1007       // In this case we will throw an IllegalStateException(), which is probably
1008       // preferable to removing the wrong element, which is what would happen if we
1009       // didn't have the (_nextHolder == null) check.
1010       if (_nextHolder == null)
1011         _disjointIterator.remove();
1012       else
1013         throw new IllegalStateException();
1014     }
1015 
1016     private final Collection<E> _checkedCollection;
1017     private final Iterator<E> _disjointIterator;
1018     private AtomicReference<E> _nextHolder;
1019   }
1020   
1021   /**
1022    * Note: Requires contents to be disjoint!
1023    * @param <E>
1024    */
1025   protected abstract static class CompositeSet<E> extends CompositeCollection<E> implements Set<E>
1026   {
1027     @Override
1028     protected abstract Set<E> getPrimaryDelegate();
1029     
1030     @Override
1031     protected abstract Set<E> getSecondaryDelegate();
1032 
1033     /**
1034      * Implement Set-defined equals behavior 
1035      */
1036     @Override
1037     public boolean equals(Object o)
1038     {
1039       if (o == this)
1040         return true;
1041       else if (!(o instanceof Set))
1042         return false;
1043       else
1044       {
1045         Collection other = (Collection) o;
1046 
1047         if (other.size() != size())
1048         {
1049           return false;
1050         }
1051         else
1052         {
1053           // since the sizes are the same, if we contain all of the other collection's
1054           // elements, we are identical
1055           try
1056           {
1057             return containsAll(other);
1058           }
1059           catch(NullPointerException npe)
1060           {
1061             // optional NullPointerException that containsAll is allowed to throw
1062             return false;
1063           }
1064           catch(ClassCastException npe)
1065           {
1066             // optional ClassCastException that containsAll is allowed to throw
1067             return false;
1068           }
1069         }
1070       }
1071     }
1072 
1073     /**
1074      * Implement Set-defined equals behavior 
1075      */
1076     @Override
1077     public int hashCode()
1078     {
1079       // Set defines hashCode() as additive based on the contents
1080       return getPrimaryDelegate().hashCode() + getSecondaryDelegate().hashCode();
1081     }
1082   }
1083   
1084   /**
1085    * Concrete Composite Set that takes the two sets to compose
1086    */
1087   private static class FixedCompositeSet<E> extends CompositeSet<E>
1088   {
1089     FixedCompositeSet(Set<E> primarySet, Set<E> secondarySet)
1090     {
1091       if (primarySet == null)
1092         throw new NullPointerException();
1093 
1094       if (secondarySet == null)
1095         throw new NullPointerException();
1096       
1097       assert Collections.disjoint(primarySet, secondarySet) : "Composed Sets not disjoint";
1098       
1099       _primarySet   = primarySet;
1100       _secondarySet = secondarySet;
1101     }
1102 
1103     @Override
1104     protected Set<E> getPrimaryDelegate()
1105     {
1106       return _primarySet;
1107     }
1108     
1109     @Override
1110     protected Set<E> getSecondaryDelegate()
1111     {
1112       return _secondarySet;
1113     }
1114     
1115     private final Set<E> _primarySet;
1116     private final Set<E> _secondarySet;
1117   }
1118 
1119   /**
1120    * Serializable version of FixedCompositeSet
1121    * @param <E>
1122    */
1123   private static final class SerializableFixedCompositeSet<E> extends FixedCompositeSet<E>
1124                                                               implements Serializable
1125   {
1126     SerializableFixedCompositeSet(Set<E> primarySet, Set<E> secondarySet)
1127     {
1128       super(primarySet, secondarySet);
1129     }
1130 
1131     private static final long serialVersionUID = 0L;
1132   }
1133 
1134   /**
1135    * Live composite set where both sets are allowed to be disjoint.
1136    * @param <E>
1137    */
1138   protected abstract static class LenientCompositeSet<E> extends CompositeSet<E>
1139   {
1140     @Override
1141     public int size()
1142     {
1143       return CollectionUtils.getUnionSize(getPrimaryDelegate(), getSecondaryDelegate());
1144     }
1145 
1146     @Override
1147     public Iterator<E> iterator()
1148     {
1149       // create a CompositeIterator of the primary and secondary Sets such that all of the
1150       // elements of the bigger Set are returned directly and the smaller Collection returns
1151       // only the elements not present in the larger Collection
1152       Set<E> primaryDelegate = getPrimaryDelegate();
1153       Set<E> secondaryDelegate = getSecondaryDelegate();
1154       
1155       if (primaryDelegate.size() >= secondaryDelegate.size())
1156       {
1157         return new CompositeIterator<E>(
1158                         new RemovingIterator<E>(primaryDelegate.iterator(), secondaryDelegate),
1159                         new DisjointIterator<E>(primaryDelegate, secondaryDelegate));
1160       }
1161       else
1162       {
1163         return new CompositeIterator<E>(
1164                         new RemovingIterator<E>(secondaryDelegate.iterator(), primaryDelegate),
1165                         new DisjointIterator<E>(secondaryDelegate, primaryDelegate));
1166       }
1167     }
1168 
1169     @Override
1170     public boolean add(E e)
1171     {
1172       boolean modified = getPrimaryDelegate().add(e);
1173       
1174       if (modified)
1175       {
1176         // If the secondary delegate already contained this element
1177         // then we didn't really change.  Since we don't need to maintain the disjoint
1178         // property, we don't have to remove the item from the secondaryDelegate.
1179         modified = !getSecondaryDelegate().contains(e);
1180       }
1181       
1182       return modified;
1183     }
1184 
1185     @Override
1186     public boolean remove(Object o)
1187     {
1188       // override to remove from both Sets to ensure that removing from the first
1189       // doesn't cause the same value to no longer be eclipsed in the second
1190       boolean removed = getPrimaryDelegate().remove(0);
1191       removed |= getSecondaryDelegate().remove(0);
1192       
1193       return removed;
1194     }
1195 
1196     @Override
1197     public boolean addAll(Collection<? extends E> c)
1198     {
1199       // determine the result ahead of time
1200       boolean changed = !containsAll(c);
1201       
1202       // We don't need to remove the items from the secondaryDelegate because we don't
1203       // need to maintain disjointness
1204       getPrimaryDelegate().addAll(c);
1205       
1206       return changed;
1207     }
1208 
1209     /**
1210      * Implement Set-defined equals behavior 
1211      */
1212     @Override
1213     public int hashCode()
1214     {
1215       // Set defines hashCode() as additive based on the contents
1216       
1217       // create a CompositeIterator of the primary and secondary Sets such that all of the
1218       // elements of the bigger Set are returned directly and the smaller Collection returns
1219       // only the elements not present in the larger Collection
1220       Set<E> primaryDelegate = getPrimaryDelegate();
1221       Set<E> secondaryDelegate = getSecondaryDelegate();
1222       
1223       int hashCode;
1224       Iterator<E> disjointElements;
1225       
1226       if (primaryDelegate.size() >= secondaryDelegate.size())
1227       {
1228         hashCode = primaryDelegate.hashCode();
1229         disjointElements = new DisjointIterator<E>(primaryDelegate, secondaryDelegate);
1230       }
1231       else
1232       {
1233         hashCode = secondaryDelegate.hashCode();
1234         disjointElements = new DisjointIterator<E>(secondaryDelegate, primaryDelegate);
1235       }
1236       
1237       while (disjointElements.hasNext())
1238       {
1239         E currElement = disjointElements.next();
1240         
1241         if (currElement != null)
1242           hashCode += currElement.hashCode();
1243       }
1244       
1245       return hashCode;
1246     }
1247   }
1248   
1249   /**
1250    * Concrete Composite Set that takes the two sets to compose
1251    */
1252   private static class LenientFixedCompositeSet<E> extends LenientCompositeSet<E>
1253   {
1254     LenientFixedCompositeSet(Set<E> primarySet, Set<E> secondarySet)
1255     {
1256       if (primarySet == null)
1257         throw new NullPointerException();
1258 
1259       if (secondarySet == null)
1260         throw new NullPointerException();
1261             
1262       _primarySet   = primarySet;
1263       _secondarySet = secondarySet;
1264     }
1265 
1266     @Override
1267     protected Set<E> getPrimaryDelegate()
1268     {
1269       return _primarySet;
1270     }
1271     
1272     @Override
1273     protected Set<E> getSecondaryDelegate()
1274     {
1275       return _secondarySet;
1276     }
1277     
1278     private final Set<E> _primarySet;
1279     private final Set<E> _secondarySet;
1280   }
1281 
1282   /**
1283    * Serializable version of LenientFixedCompositeSet
1284    * @param <E>
1285    */
1286   private static final class SerializableLenientFixedCompositeSet<E> extends
1287      LenientFixedCompositeSet<E> implements Serializable
1288   {
1289     SerializableLenientFixedCompositeSet(Set<E> primarySet, Set<E> secondarySet)
1290     {
1291       super(primarySet, secondarySet);
1292     }
1293 
1294     private static final long serialVersionUID = 0L;
1295   }
1296 
1297 
1298   private static class SerializableCollection<E> extends DelegatingCollection<E>
1299                                                  implements Serializable
1300   {
1301     SerializableCollection(Collection<E> delegate)
1302     {
1303       // we don't check that the delegate is Serializable because of the Collections
1304       // classes that lie about Serializability
1305       if (delegate == null)
1306         throw new NullPointerException();
1307            
1308       _delegate = delegate;
1309     }
1310 
1311     protected Collection<E> getDelegate()
1312     {
1313       return _delegate;
1314     }
1315     
1316     protected Object writeReplace() throws ObjectStreamException
1317     {
1318       // copy delegate into a Serializable ArrayList on Serialization
1319       return new ArrayList(_delegate);
1320     }
1321 
1322     private static final long serialVersionUID = 0L;
1323 
1324     private final transient Collection<E> _delegate;
1325   }
1326 
1327 
1328   private static class SerializableList<E> extends SerializableCollection<E> implements List<E>
1329   {
1330     SerializableList(List<E> delegate)
1331     {
1332       super(delegate);
1333       _delegate = delegate;
1334     }
1335     
1336     public void add(int index, E element)
1337     {
1338       _delegate.add(index, element);
1339     }
1340 
1341     public E remove(int index)
1342     {
1343       return _delegate.remove(index);
1344     }
1345 
1346     public boolean addAll(int index, Collection<? extends E> c)
1347     {
1348       return _delegate.addAll(index, c);
1349     }
1350 
1351     public E get(int index)
1352     {
1353       return _delegate.get(index);
1354     }
1355 
1356     public E set(int index, E element)
1357     {
1358       return _delegate.set(index, element);
1359     }
1360 
1361     public int indexOf(Object o)
1362     {
1363       return _delegate.indexOf(o);
1364     }
1365 
1366     public int lastIndexOf(Object o)
1367     {
1368       return _delegate.lastIndexOf(o);
1369     }
1370 
1371     public ListIterator<E> listIterator()
1372     {
1373       return _delegate.listIterator();
1374     }
1375 
1376     public ListIterator<E> listIterator(int index)
1377     {
1378       return _delegate.listIterator(index);
1379     }
1380 
1381     public List<E> subList(int fromIndex, int toIndex)
1382     {
1383       return CollectionUtils.getSerializableList(_delegate.subList(fromIndex, toIndex));
1384     }
1385  
1386     private static final long serialVersionUID = 0L;
1387     
1388     private final transient List<E> _delegate;
1389   }
1390 
1391   private static class SerializableRandomAccessList<E> extends SerializableList<E> implements RandomAccess
1392   {
1393     SerializableRandomAccessList(List<E> delegate)
1394     {
1395       super(delegate);
1396     }
1397 
1398     private static final long serialVersionUID = 0L;
1399   }
1400 
1401   protected static abstract class DelegatingMap<K,V> implements Map<K,V>
1402   {
1403     protected abstract Map<K,V> getDelegate();
1404 
1405     public int size()
1406     {
1407       return getDelegate().size();
1408     }
1409 
1410     public boolean isEmpty()
1411     {
1412       return getDelegate().isEmpty();
1413     }
1414 
1415     public boolean containsKey(Object key)
1416     {
1417       return getDelegate().containsKey(key);
1418     }
1419 
1420     public boolean containsValue(Object value)
1421     {
1422       return getDelegate().containsValue(value);
1423     }
1424 
1425     public V get(Object key)
1426     {
1427       return getDelegate().get(key);
1428     }
1429 
1430     // Modification Operations
1431 
1432     public V put(K key, V value)
1433     {
1434       return getDelegate().put(key, value);
1435     }
1436 
1437     public V remove(Object key)
1438     {
1439       return getDelegate().remove(key);
1440     }
1441 
1442     // Bulk Operations
1443 
1444     public void putAll(Map<? extends K, ? extends V> m)
1445     {
1446       getDelegate().putAll(m);
1447     }
1448 
1449     public void clear()
1450     {
1451       getDelegate().clear();
1452     }
1453 
1454     // Views
1455     
1456     public Set<K> keySet()
1457     {
1458       return getDelegate().keySet();
1459     }
1460 
1461     public Collection<V> values()
1462     {
1463       return getDelegate().values();
1464     }
1465 
1466     public Set<Map.Entry<K, V>> entrySet()
1467     {
1468       return getDelegate().entrySet();      
1469     }
1470 
1471     // Comparison and hashing
1472 
1473     public boolean equals(Object o)
1474     {
1475       return getDelegate().equals(o);
1476     }
1477 
1478     public int hashCode()
1479     {
1480       return getDelegate().hashCode();
1481     }
1482   }
1483   
1484   protected static abstract class DelegatingEntry<K,V> implements Map.Entry<K,V>
1485   {
1486     protected abstract Map.Entry<K,V> getDelegate();
1487                                 
1488     public K getKey()
1489     {
1490       return getDelegate().getKey();
1491     }
1492 
1493     public V getValue()
1494     {
1495       return getDelegate().getValue();
1496     }
1497 
1498     public V setValue(V value)
1499     {
1500       return getDelegate().setValue(value);
1501     }
1502 
1503     public boolean equals(Object o)
1504     {
1505       return getDelegate().equals(o);
1506     }
1507 
1508     public int hashCode()
1509     {
1510       return getDelegate().hashCode();
1511     }
1512   }
1513   
1514   protected static abstract class AccessHookMap<K,V> extends DelegatingMap<K, V>
1515   {     
1516     protected abstract void writeNotify(K key, V value);
1517 
1518     protected abstract void removeNotify(Object key);
1519 
1520     protected abstract void clearNotify();
1521 
1522     @Override
1523     public V put(K key, V value)
1524     {
1525       writeNotify(key, value);
1526       
1527       return super.put(key, value);
1528     }
1529 
1530     @Override
1531     public V remove(Object key)
1532     {
1533       removeNotify(key);
1534       
1535       return super.remove(key);
1536     }
1537 
1538     @Override
1539     public void putAll(Map<? extends K, ? extends V> m)
1540     {      
1541       for (Map.Entry<? extends K, ? extends V> entry : m.entrySet())
1542       {        
1543         K key   = entry.getKey();
1544         V value = entry.getValue();
1545         
1546         writeNotify(key, value);
1547         super.put(key, value);
1548       }
1549     }
1550     
1551     @Override
1552     public void clear()
1553     {
1554       clearNotify();
1555       super.clear();
1556     }
1557  
1558     public Set<Map.Entry<K, V>> entrySet()
1559     {
1560       return new MutationHookedEntrySet<K, V>(this);      
1561     }
1562 
1563 
1564     // Entry Set returns CheckedSerializationEntry Objects
1565     private static class MutationHookedEntrySet<K, V> extends DelegatingCollection<Entry<K,V>>
1566                                          implements Set<Entry<K, V>>
1567     {
1568       private MutationHookedEntrySet(AccessHookMap<K, V> accessHookMap)
1569       {
1570         if (accessHookMap == null)
1571           throw new NullPointerException();
1572         
1573         _accessHookMap = accessHookMap;
1574         _delegate = accessHookMap.getDelegate().entrySet();
1575       }
1576 
1577       protected Set<Entry<K, V>> getDelegate()
1578       {
1579         return _delegate;
1580       }
1581 
1582       public Iterator<Entry<K,V>> iterator()
1583       {
1584         return new MutationHookedEntrySetIterator<K, V>(super.iterator(), _accessHookMap);
1585       }
1586       
1587       public Object[] toArray()
1588       {
1589         Object[] delegateEntries = super.toArray();
1590 
1591         int entryCount = delegateEntries.length;
1592         
1593         // make sure that the array allows generic Entry objects.  If so, use the source array
1594         // as the destination array, otherwise create a new destination array for our entries
1595         Object[] entries = 
1596                        (delegateEntries.getClass().getComponentType().isAssignableFrom(Entry.class))
1597                          ? delegateEntries
1598                          : new Entry[entryCount];
1599                         
1600         for (int i = 0; i < entryCount; i++)
1601           entries[i] = new MutationHookedEntry((Entry<K,V>)delegateEntries[i], _accessHookMap);
1602         
1603         return entries;
1604       }
1605 
1606       public <T> T[] toArray(T[] a)
1607       {
1608         int inputSize = a.length;
1609         
1610         // compute the output type array so that the delegate creates an array of the correct
1611         // type.  We always truncate the input array's size to 0 so that the delegate doesn't
1612         // attempt to copy any of its contents into the output array
1613         T[] outTypeArray = (inputSize == 0)
1614                              ? a
1615                              : CollectionUtils.copyOf(a, 0);
1616         
1617         Object[] delegateEntries = super.toArray(outTypeArray);
1618         
1619         // now proxy the contents
1620         int entryCount = delegateEntries.length;
1621         
1622         for (int i = 0; i < entryCount; i++)
1623           delegateEntries[i] = new MutationHookedEntry<K, V>((Entry<K,V>)delegateEntries[i],
1624                                                              _accessHookMap);
1625         
1626         // now figure out whether we have to copy the entries into the passed in array or not
1627         if (entryCount > inputSize)
1628           return (T[])delegateEntries;
1629         
1630         // they fit so we need to copy the values into the input array
1631         System.arraycopy(delegateEntries, 0, a, 0, entryCount);
1632        
1633         // see if we have room for the wacky null terminator
1634         if (inputSize > entryCount)
1635           a[entryCount] = null;
1636         
1637         return a;
1638       }
1639 
1640       // Iterator for MutationHookedEntrySet that returns MutationHookedEntry
1641       private static final class MutationHookedEntrySetIterator<K, V> implements Iterator<Entry<K,V>>
1642       {
1643         private MutationHookedEntrySetIterator(Iterator<Entry<K, V>> delegate,
1644                                                AccessHookMap<K, V>   accessHookMap)
1645         {
1646           _delegate      = delegate;
1647           _accessHookMap = accessHookMap;
1648         }
1649 
1650         public boolean hasNext()
1651         {
1652           return _delegate.hasNext();
1653         }
1654 
1655         public Map.Entry<K,V> next()
1656         {
1657           Map.Entry<K,V> nextEntry = _delegate.next();
1658           
1659           // update the current key
1660           _currKey = nextEntry.getKey();
1661           
1662           // return wrapped entry
1663           return new MutationHookedEntry<K,V>(nextEntry, _accessHookMap);
1664         }
1665 
1666         public void remove()
1667         {
1668           if (_currKey == _NO_KEY)
1669             throw new IllegalStateException();
1670           
1671           // notify listener of removal
1672           _accessHookMap.removeNotify(_currKey);
1673           
1674           // let the delegate remove the entry
1675           _delegate.remove();
1676           
1677           // no more entry to remove until next call to next()
1678           _currKey = _NO_KEY;
1679         }
1680  
1681         private static final Object _NO_KEY = new Object();
1682        
1683         // either _NO_KEY or the current key.  We use volatile to ensure safe publication for
1684         // thread use
1685         private volatile Object _currKey = _NO_KEY;
1686         
1687         private final Iterator<Entry<K, V>> _delegate;
1688         private final AccessHookMap<K, V> _accessHookMap;
1689       }
1690 
1691       // Entry implementation that hooks calls to setValue
1692       private static class MutationHookedEntry<K, V> extends DelegatingEntry<K, V>
1693       {
1694         private MutationHookedEntry(Entry<K, V> delegate, AccessHookMap<K, V> accessHookMap)
1695         {
1696           if (delegate == null)
1697             throw new NullPointerException();
1698           
1699           _delegate = delegate;
1700           _accessHookMap = accessHookMap;
1701         }
1702         
1703         protected Entry<K, V> getDelegate()
1704         {
1705           return _delegate;
1706         }
1707         
1708         public V setValue(V value)
1709         {
1710           _accessHookMap.writeNotify(getKey(), value);
1711           return super.setValue(value);
1712         }
1713       
1714         private final Entry<K, V> _delegate;
1715         private final AccessHookMap<K, V> _accessHookMap;
1716      }
1717 
1718       private final AccessHookMap<K, V> _accessHookMap;
1719       private final Set<Entry<K, V>> _delegate;
1720     }
1721   }
1722 
1723   protected static class ExternalAccessHookMap<K,V> extends AccessHookMap<K, V>
1724   {
1725     protected ExternalAccessHookMap(Map<K, V> delegate, MapMutationHooks<K, V> mutationHooks)
1726     {
1727       if (delegate == null)
1728         throw new NullPointerException("delegate is null");
1729       
1730       if (mutationHooks == null)
1731         throw new NullPointerException("accessHooks is null");
1732       
1733       _delegate = delegate;
1734       _mutationHooks = mutationHooks;
1735     }
1736     
1737     protected final Map<K, V> getDelegate()
1738     {
1739       return _delegate;
1740     }
1741     
1742     protected final void writeNotify(K key, V value)
1743     {
1744       _mutationHooks.writeNotify(this, key, value);      
1745     }
1746   
1747     protected final void removeNotify(Object key)
1748     {
1749       _mutationHooks.removeNotify(this, key);      
1750     }
1751 
1752     protected final void clearNotify()
1753     {
1754       _mutationHooks.clearNotify(this);      
1755     }
1756 
1757     private static final long serialVersionUID = 1L;
1758 
1759     private final Map<K, V> _delegate;
1760     private final MapMutationHooks<K, V> _mutationHooks;
1761   }
1762 
1763   private static final class SerializableExternalAccessHookMap<K, V> 
1764                                                      extends ExternalAccessHookMap<K, V>
1765                                                      implements Serializable
1766   {
1767     private SerializableExternalAccessHookMap(
1768       Map<K, V> delegate,
1769       MapMutationHooks<K, V> mutationHooks)
1770     {
1771       super(delegate, mutationHooks); 
1772 
1773       if (!(delegate instanceof Serializable))
1774         throw new IllegalArgumentException("Delegate must be Serializable");
1775   
1776       if (!(mutationHooks instanceof Serializable))
1777         throw new IllegalArgumentException("mutation hooka must be Serializable");
1778     }
1779     
1780     private static final long serialVersionUID = 1L;
1781   }
1782 
1783 
1784   // Map that validates that the keys and values added to the map are Serializable
1785   private final static class CheckedSerializationMap<K, V> extends AccessHookMap<K,V>
1786                                                            implements Serializable
1787   {
1788     /**
1789      * @param requireSerializable if <code>true</code>, require that all values in the map implement
1790      *                            Serializable.
1791      * @param delegate we do not check whether the delegate itself is Serializable. We just check its contents.
1792      */
1793     public CheckedSerializationMap(
1794       Map<K, V> delegate,
1795       boolean   requireSerializable)
1796     {
1797       if (delegate == null)
1798         throw new NullPointerException();
1799 
1800       _delegate = delegate;
1801       _requireSerializable = requireSerializable;
1802     }
1803 
1804     protected Map<K, V> getDelegate()
1805     {
1806       return _delegate;
1807     }
1808 
1809     protected void writeNotify(K key, V value)
1810     {
1811       // don't bother checking common case of String
1812       if (!(key instanceof String))
1813       {
1814         if (key instanceof Serializable)
1815         {
1816           // verify that the contents of the key are in fact Serializable
1817           try
1818           {
1819             new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(key);
1820           }
1821           catch (IOException e)
1822           {          
1823             throw new IllegalArgumentException(_LOG.getMessage("FAILED_SERIALIZATION_PROPERTY_KEY",
1824                                                        new Object[]{key, this}),
1825                                                        e);
1826           }
1827         }
1828         else
1829         {
1830           if (_requireSerializable)
1831           {
1832             throw new ClassCastException(_LOG.getMessage("UNSERIALIZABLE_PROPERTY_KEY",
1833                                                          new Object[]{key, this}));
1834           }
1835         }
1836       }
1837       
1838       if (value instanceof Serializable)
1839       {
1840         // verify that the contents of the value are in fact Serializable
1841         try
1842         {
1843           new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(value);
1844         }
1845         catch (IOException e)
1846         {          
1847           throw new IllegalArgumentException(_LOG.getMessage("FAILED_SERIALIZATION_PROPERTY_VALUE",
1848                                                      new Object[]{value, key, this}),
1849                                                      e);
1850         }
1851       }
1852       else if (value != null)
1853       {
1854         if (_requireSerializable)
1855         {
1856           throw new ClassCastException(_LOG.getMessage("UNSERIALIZABLE_PROPERTY_VALUE",
1857                                                        new Object[]{value, key, this}));
1858         }
1859       }
1860     }
1861 
1862     protected void removeNotify(Object key)
1863     {
1864       // do nothing
1865     }
1866     
1867     protected void clearNotify()
1868     {
1869       // do nothing
1870     }
1871 
1872     @Override
1873     public void putAll(Map<? extends K, ? extends V> m)
1874     {
1875       
1876       Object[] keys = m.keySet().toArray();
1877       Object[] values = m.values().toArray();
1878       
1879       int keyCount = keys.length;
1880       
1881       // in case an entry was added or removed between to tow toArray calls above
1882       if (keyCount != values.length)
1883         throw new ConcurrentModificationException();
1884       
1885       // atomically check for serializability before adding
1886       for (int k = 0; k < keyCount; k++)
1887       {
1888         writeNotify((K)keys[k], (V)values[k]);        
1889       }
1890 
1891       // add the contents we checked rather that calling super.putAll(m), in case
1892       // the map changed after we checked its contents above
1893       Map<K, V> delegate = getDelegate();
1894       
1895       for (int k = 0; k < keyCount; k++)
1896       {
1897         delegate.put((K)keys[k], (V)values[k]);
1898       }
1899     }
1900 
1901     private static final long serialVersionUID = 1L;
1902 
1903     private final Map<K, V> _delegate;
1904     private final boolean   _requireSerializable;
1905   }
1906 
1907   private static class EmptyIterator implements Iterator
1908   {
1909     public boolean hasNext()
1910     {
1911       return false;
1912     }
1913 
1914     public Object next()
1915     {
1916       throw new NoSuchElementException();
1917     }
1918 
1919     public void remove()
1920     {
1921       throw new UnsupportedOperationException();
1922     }
1923   }
1924   
1925   private static final class EmptyListIterator extends EmptyIterator implements ListIterator
1926   {
1927     public boolean hasPrevious()
1928     {
1929       return false;
1930     }
1931 
1932     public Object previous()
1933     {
1934       throw new NoSuchElementException();
1935     }
1936 
1937     public int nextIndex()
1938     {
1939       return 0;
1940     }
1941 
1942     public int previousIndex()
1943     {
1944       return -1;
1945     }
1946 
1947     public void set(Object e)
1948     {
1949       throw new UnsupportedOperationException();
1950     }
1951 
1952     public void add(Object e)
1953     {
1954       throw new UnsupportedOperationException();
1955     }
1956   }
1957 
1958   private static final class EmptyQueue extends AbstractQueue implements Serializable
1959   {
1960     public Iterator iterator()
1961     {
1962       return _EMPTY_ITERATOR;
1963     }
1964 
1965     public int size()
1966     {
1967       return 0;
1968     }
1969 
1970     @Override
1971     public boolean isEmpty()
1972     {
1973       return true;
1974     }
1975     
1976     @Override
1977     public boolean contains(Object o)
1978     {
1979       return false;
1980     }
1981 
1982     public boolean offer(Object e)
1983     {
1984       throw new UnsupportedOperationException();
1985     }
1986 
1987     public Object poll()
1988     {
1989       return null;
1990     }
1991 
1992     public Object peek()
1993     {
1994       return null;
1995     }
1996     
1997     private Object readResolve()
1998     {
1999       return _EMPTY_QUEUE;
2000     }
2001 
2002     private static final long serialVersionUID = 0L;
2003   }
2004 
2005   //
2006   // Build up references to implementation classes used by Collections to implement the following
2007   // features.  This way we can detect when these classes are used and work around problems.
2008   //
2009   private static final Class<? extends List> _CHECKED_LIST;
2010   private static final Class<? extends List> _UNMODIFIABLE_LIST;
2011   private static final Class<? extends List> _SYNCHRONIZED_LIST;
2012   private static final Class<? extends Set> _EMPTY_SET = Collections.emptySet().getClass();
2013   private static final Class<? extends Set> _SINGLETON_SET = Collections.singleton(null).getClass();
2014   private static final Queue _EMPTY_QUEUE = new EmptyQueue();
2015   private static final Iterator _EMPTY_ITERATOR = new EmptyIterator();
2016   private static final Iterator _EMPTY_LIST_ITERATOR = new EmptyListIterator();
2017    
2018   
2019   static
2020   {
2021     // use a LinkedList as it doesn't implement RandomAccess, so that we don't accidentally get
2022     // the RandomAccess subclasses
2023     LinkedList<Object> dummyList = new LinkedList<Object>();
2024     
2025     _CHECKED_LIST      = Collections.checkedList(dummyList, Object.class).getClass();
2026     _UNMODIFIABLE_LIST = Collections.unmodifiableList(dummyList).getClass();
2027     _SYNCHRONIZED_LIST = Collections.synchronizedList(dummyList).getClass();
2028   }
2029 
2030   private static final TrinidadLogger _LOG = 
2031                                         TrinidadLogger.createTrinidadLogger(CollectionUtils.class);
2032 
2033 }