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 HashSet<HashCodeAndEqualsMockWrapper> backingHashSet = new HashSet<HashCodeAndEqualsMockWrapper>(); 35 iterator()36 public Iterator<Object> iterator() { 37 return new Iterator<Object>() { 38 private 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 size()54 public int size() { 55 return backingHashSet.size(); 56 } 57 isEmpty()58 public boolean isEmpty() { 59 return backingHashSet.isEmpty(); 60 } 61 contains(Object mock)62 public boolean contains(Object mock) { 63 return backingHashSet.contains(HashCodeAndEqualsMockWrapper.of(mock)); 64 } 65 add(Object mock)66 public boolean add(Object mock) { 67 return backingHashSet.add(HashCodeAndEqualsMockWrapper.of(mock)); 68 } 69 remove(Object mock)70 public boolean remove(Object mock) { 71 return backingHashSet.remove(HashCodeAndEqualsMockWrapper.of(mock)); 72 } 73 clear()74 public void clear() { 75 backingHashSet.clear(); 76 } 77 clone()78 @Override public Object clone() throws CloneNotSupportedException { 79 throw new CloneNotSupportedException(); 80 } 81 equals(Object o)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 hashCode()90 @Override public int hashCode() { 91 return backingHashSet.hashCode(); 92 } 93 toArray()94 public Object[] toArray() { 95 return unwrapTo(new Object[size()]); 96 } 97 unwrapTo(T[] array)98 private <T> T[] unwrapTo(T[] array) { 99 Iterator<Object> iterator = iterator(); 100 for (int i = 0, objectsLength = array.length; i < objectsLength; i++) { 101 if (iterator.hasNext()) { 102 array[i] = (T) iterator.next(); 103 } 104 } 105 return array; 106 } 107 108 toArray(T[] typedArray)109 public <T> T[] toArray(T[] typedArray) { 110 T[] array = typedArray.length >= size() ? typedArray : 111 (T[]) newInstance(typedArray.getClass().getComponentType(), size()); 112 return unwrapTo(array); 113 } 114 removeAll(Collection<?> mocks)115 public boolean removeAll(Collection<?> mocks) { 116 return backingHashSet.removeAll(asWrappedMocks(mocks)); 117 } 118 containsAll(Collection<?> mocks)119 public boolean containsAll(Collection<?> mocks) { 120 return backingHashSet.containsAll(asWrappedMocks(mocks)); 121 } 122 addAll(Collection<?> mocks)123 public boolean addAll(Collection<?> mocks) { 124 return backingHashSet.addAll(asWrappedMocks(mocks)); 125 } 126 retainAll(Collection<?> mocks)127 public boolean retainAll(Collection<?> mocks) { 128 return backingHashSet.retainAll(asWrappedMocks(mocks)); 129 } 130 asWrappedMocks(Collection<?> mocks)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 toString()141 @Override public String toString() { 142 return backingHashSet.toString(); 143 } 144 of(Object... mocks)145 public static HashCodeAndEqualsSafeSet of(Object... mocks) { 146 return of(Arrays.asList(mocks)); 147 } 148 of(Iterable<Object> objects)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