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