1
2
3
4
5 package mockit.internal.expectations.invocation;
6
7 import edu.umd.cs.findbugs.annotations.NonNull;
8 import edu.umd.cs.findbugs.annotations.Nullable;
9
10 import java.lang.reflect.Array;
11 import java.util.Map;
12
13 import mockit.internal.expectations.argumentMatching.ArgumentMatcher;
14 import mockit.internal.expectations.argumentMatching.CaptureMatcher;
15 import mockit.internal.expectations.argumentMatching.EqualityMatcher;
16 import mockit.internal.expectations.argumentMatching.LenientEqualityMatcher;
17
18 import org.checkerframework.checker.index.qual.NonNegative;
19
20 final class ArgumentValuesAndMatchersWithVarargs extends ArgumentValuesAndMatchers {
21 ArgumentValuesAndMatchersWithVarargs(@NonNull InvocationArguments signature, @NonNull Object[] values) {
22 super(signature, values);
23 }
24
25 @Override
26 boolean isMatch(@NonNull Object[] replayArgs, @NonNull Map<Object, Object> instanceMap) {
27 if (matchers == null) {
28 return areEqual(replayArgs, instanceMap);
29 }
30
31 VarargsComparison varargsComparison = new VarargsComparison(replayArgs);
32 int totalArgCount = varargsComparison.getTotalArgumentCountWhenDifferent();
33 int regularArgCount = varargsComparison.regularArgCount;
34
35 if (totalArgCount < 0) {
36 return false;
37 }
38
39 for (int i = 0; i < totalArgCount; i++) {
40 Object actual = varargsComparison.getOtherArgument(i);
41 ArgumentMatcher<?> expected = getArgumentMatcher(i);
42
43 if (expected == null) {
44 Object arg = varargsComparison.getThisArgument(i);
45 if (arg == null) {
46 continue;
47 }
48 expected = new LenientEqualityMatcher(arg, instanceMap);
49 } else if (i == regularArgCount && expected instanceof CaptureMatcher<?>) {
50 actual = varargsComparison.getOtherVarArgs();
51 i = totalArgCount;
52 }
53
54 if (!expected.matches(actual)) {
55 return false;
56 }
57 }
58
59 return true;
60 }
61
62 private boolean areEqual(@NonNull Object[] replayArgs, @NonNull Map<Object, Object> instanceMap) {
63 int argCount = replayArgs.length;
64
65 if (!areEqual(values, replayArgs, argCount - 1, instanceMap)) {
66 return false;
67 }
68
69 VarargsComparison varargsComparison = new VarargsComparison(replayArgs);
70 Object[] expectedValues = varargsComparison.getThisVarArgs();
71 Object[] actualValues = varargsComparison.getOtherVarArgs();
72
73 return varargsComparison.sameVarargArrayLength()
74 && areEqual(expectedValues, actualValues, expectedValues.length, instanceMap);
75 }
76
77 @Override
78 boolean hasEquivalentMatchers(@NonNull ArgumentValuesAndMatchers other) {
79 @SuppressWarnings("unchecked")
80 int i = indexOfFirstValueAfterEquivalentMatchers(other);
81
82 if (i < 0) {
83 return false;
84 }
85
86 VarargsComparison varargsComparison = new VarargsComparison(other.values);
87 int n = varargsComparison.getTotalArgumentCountWhenDifferent();
88
89 if (n < 0) {
90 return false;
91 }
92
93 while (i < n) {
94 Object thisArg = varargsComparison.getThisArgument(i);
95 Object otherArg = varargsComparison.getOtherArgument(i);
96
97 if (!EqualityMatcher.areEqual(thisArg, otherArg)) {
98 return false;
99 }
100
101 i++;
102 }
103
104 return true;
105 }
106
107 private static final Object[] NULL_VARARGS = {};
108
109 private final class VarargsComparison {
110 @NonNull
111 private final Object[] otherValues;
112 @Nullable
113 private final Object[] thisVarArgs;
114 @Nullable
115 private final Object[] otherVarArgs;
116 final int regularArgCount;
117
118 VarargsComparison(@NonNull Object[] otherValues) {
119 this.otherValues = otherValues;
120 thisVarArgs = getVarArgs(values);
121 otherVarArgs = getVarArgs(otherValues);
122 regularArgCount = values.length - 1;
123 }
124
125 @NonNull
126 Object[] getThisVarArgs() {
127 return thisVarArgs == null ? NULL_VARARGS : thisVarArgs;
128 }
129
130 @NonNull
131 Object[] getOtherVarArgs() {
132 return otherVarArgs == null ? NULL_VARARGS : otherVarArgs;
133 }
134
135 @Nullable
136 private Object[] getVarArgs(@NonNull Object[] args) {
137 Object lastArg = args[args.length - 1];
138
139 if (lastArg == null) {
140 return null;
141 }
142
143 if (lastArg instanceof Object[]) {
144 return (Object[]) lastArg;
145 }
146
147 int varArgsLength = Array.getLength(lastArg);
148 Object[] results = new Object[varArgsLength];
149
150 for (int i = 0; i < varArgsLength; i++) {
151 results[i] = Array.get(lastArg, i);
152 }
153
154 return results;
155 }
156
157 int getTotalArgumentCountWhenDifferent() {
158 if (thisVarArgs == null) {
159 return regularArgCount + 1;
160 }
161
162 if (!sameVarargArrayLength()) {
163 return -1;
164 }
165
166 return regularArgCount + thisVarArgs.length;
167 }
168
169 boolean sameVarargArrayLength() {
170 return getThisVarArgs().length == getOtherVarArgs().length;
171 }
172
173 @Nullable
174 Object getThisArgument(@NonNegative int parameter) {
175 if (parameter < regularArgCount) {
176 return values[parameter];
177 }
178 int p = parameter - regularArgCount;
179 if (thisVarArgs == null || p >= thisVarArgs.length) {
180 return null;
181 }
182 return thisVarArgs[p];
183 }
184
185 @Nullable
186 Object getOtherArgument(@NonNegative int parameter) {
187 if (parameter < regularArgCount) {
188 return otherValues[parameter];
189 }
190 int p = parameter - regularArgCount;
191 if (otherVarArgs == null || p >= otherVarArgs.length) {
192 return null;
193 }
194 return otherVarArgs[p];
195 }
196 }
197 }