Home | History | Annotate | Download | only in collections
      1 /*
      2  * Copyright (c) 2007 Mockito contributors
      3  * This program is made available under the terms of the MIT License.
      4  */
      5 package org.mockito.internal.util.collections;
      6 
      7 import org.mockito.internal.util.Checks;
      8 
      9 import java.util.Arrays;
     10 import java.util.Collection;
     11 import java.util.HashSet;
     12 import java.util.Iterator;
     13 import java.util.Set;
     14 
     15 import static java.lang.reflect.Array.*;
     16 
     17 /**
     18  * hashCode and equals safe hash based set.
     19  *
     20  * <p>
     21  *     Useful for holding mocks that have un-stubbable hashCode or equals method,
     22  *     meaning that in this scenario the real code is always called and will most probably
     23  *     cause an {@link NullPointerException}.
     24  * </p>
     25  * <p>
     26  *     This collection wraps the mock in an augmented type {@link HashCodeAndEqualsMockWrapper}
     27  *     that have his own implementation.
     28  * </p>
     29  *
     30  * @see HashCodeAndEqualsMockWrapper
     31  */
     32 public class HashCodeAndEqualsSafeSet implements Set<Object> {
     33 
     34     private final HashSet<HashCodeAndEqualsMockWrapper> backingHashSet = new HashSet<HashCodeAndEqualsMockWrapper>();
     35 
     36     public Iterator<Object> iterator() {
     37         return new Iterator<Object>() {
     38             private final Iterator<HashCodeAndEqualsMockWrapper> iterator = backingHashSet.iterator();
     39 
     40             public boolean hasNext() {
     41                 return iterator.hasNext();
     42             }
     43 
     44             public Object next() {
     45                 return iterator.next().get();
     46             }
     47 
     48             public void remove() {
     49                 iterator.remove();
     50             }
     51         };
     52     }
     53 
     54     public int size() {
     55         return backingHashSet.size();
     56     }
     57 
     58     public boolean isEmpty() {
     59         return backingHashSet.isEmpty();
     60     }
     61 
     62     public boolean contains(Object mock) {
     63         return backingHashSet.contains(HashCodeAndEqualsMockWrapper.of(mock));
     64     }
     65 
     66     public boolean add(Object mock) {
     67         return backingHashSet.add(HashCodeAndEqualsMockWrapper.of(mock));
     68     }
     69 
     70     public boolean remove(Object mock) {
     71         return backingHashSet.remove(HashCodeAndEqualsMockWrapper.of(mock));
     72     }
     73 
     74     public void clear() {
     75         backingHashSet.clear();
     76     }
     77 
     78     @Override public Object clone() throws CloneNotSupportedException {
     79         throw new CloneNotSupportedException();
     80     }
     81 
     82     @Override public boolean equals(Object o) {
     83         if (!(o instanceof HashCodeAndEqualsSafeSet)) {
     84             return false;
     85         }
     86         HashCodeAndEqualsSafeSet that = (HashCodeAndEqualsSafeSet) o;
     87         return backingHashSet.equals(that.backingHashSet);
     88     }
     89 
     90     @Override public int hashCode() {
     91         return backingHashSet.hashCode();
     92     }
     93 
     94     public Object[] toArray() {
     95         return unwrapTo(new Object[size()]);
     96     }
     97 
     98     @SuppressWarnings("unchecked")
     99     private <T> T[] unwrapTo(T[] array) {
    100         Iterator<Object> iterator = iterator();
    101         for (int i = 0, objectsLength = array.length; i < objectsLength; i++) {
    102             if (iterator.hasNext()) {
    103                 array[i] = (T) iterator.next();
    104             }
    105         }
    106         return array;
    107     }
    108 
    109     @SuppressWarnings("unchecked")
    110     public <T> T[] toArray(T[] typedArray) {
    111         T[] array = typedArray.length >= size() ? typedArray : (T[]) newInstance(typedArray.getClass().getComponentType(), size());
    112         return unwrapTo(array);
    113     }
    114 
    115     public boolean removeAll(Collection<?> mocks) {
    116         return backingHashSet.removeAll(asWrappedMocks(mocks));
    117     }
    118 
    119     public boolean containsAll(Collection<?> mocks) {
    120         return backingHashSet.containsAll(asWrappedMocks(mocks));
    121     }
    122 
    123     public boolean addAll(Collection<?> mocks) {
    124         return backingHashSet.addAll(asWrappedMocks(mocks));
    125     }
    126 
    127     public boolean retainAll(Collection<?> mocks) {
    128         return backingHashSet.retainAll(asWrappedMocks(mocks));
    129     }
    130 
    131     private HashSet<HashCodeAndEqualsMockWrapper> asWrappedMocks(Collection<?> mocks) {
    132         Checks.checkNotNull(mocks, "Passed collection should notify() be null");
    133         HashSet<HashCodeAndEqualsMockWrapper> hashSet = new HashSet<HashCodeAndEqualsMockWrapper>();
    134         for (Object mock : mocks) {
    135             assert ! (mock instanceof HashCodeAndEqualsMockWrapper) : "WRONG";
    136             hashSet.add(HashCodeAndEqualsMockWrapper.of(mock));
    137         }
    138         return hashSet;
    139     }
    140 
    141     @Override public String toString() {
    142         return backingHashSet.toString();
    143     }
    144 
    145     public static HashCodeAndEqualsSafeSet of(Object... mocks) {
    146         return of(Arrays.asList(mocks));
    147     }
    148 
    149     public static HashCodeAndEqualsSafeSet of(Iterable<Object> objects) {
    150         HashCodeAndEqualsSafeSet hashCodeAndEqualsSafeSet = new HashCodeAndEqualsSafeSet();
    151         if (objects != null) {
    152             for (Object mock : objects) {
    153                 hashCodeAndEqualsSafeSet.add(mock);
    154             }
    155         }
    156         return hashCodeAndEqualsSafeSet;
    157     }
    158 }
    159