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