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