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.util.List;
11 import java.util.Map;
12
13 import mockit.internal.expectations.argumentMatching.AlwaysTrueMatcher;
14 import mockit.internal.expectations.argumentMatching.ArgumentMatcher;
15 import mockit.internal.expectations.argumentMatching.ArgumentMismatch;
16 import mockit.internal.expectations.argumentMatching.EqualityMatcher;
17 import mockit.internal.expectations.argumentMatching.HamcrestAdapter;
18 import mockit.internal.expectations.argumentMatching.ReflectiveMatcher;
19
20 import org.checkerframework.checker.index.qual.NonNegative;
21
22 abstract class ArgumentValuesAndMatchers {
23 @NonNull
24 final InvocationArguments signature;
25 @NonNull
26 Object[] values;
27 @Nullable
28 List<ArgumentMatcher<?>> matchers;
29
30 ArgumentValuesAndMatchers(@NonNull InvocationArguments signature, @NonNull Object[] values) {
31 this.signature = signature;
32 this.values = values;
33 }
34
35 final void setValuesWithNoMatchers(@NonNull Object[] argsToVerify) {
36 setValuesAndMatchers(argsToVerify, null);
37 }
38
39 @NonNull
40 final Object[] prepareForVerification(@NonNull Object[] argsToVerify,
41 @Nullable List<ArgumentMatcher<?>> matchersToUse) {
42 Object[] replayArgs = values;
43 setValuesAndMatchers(argsToVerify, matchersToUse);
44 return replayArgs;
45 }
46
47 final void setValuesAndMatchers(@NonNull Object[] argsToVerify, @Nullable List<ArgumentMatcher<?>> matchersToUse) {
48 values = argsToVerify;
49 matchers = matchersToUse;
50 }
51
52 @Nullable
53 final ArgumentMatcher<?> getArgumentMatcher(@NonNegative int parameterIndex) {
54 if (matchers == null) {
55 return null;
56 }
57
58 ArgumentMatcher<?> matcher = parameterIndex < matchers.size() ? matchers.get(parameterIndex) : null;
59
60 if (matcher == null && parameterIndex < values.length && values[parameterIndex] == null) {
61 matcher = AlwaysTrueMatcher.ANY_VALUE;
62 }
63
64 return matcher;
65 }
66
67 abstract boolean isMatch(@NonNull Object[] replayArgs, @NonNull Map<Object, Object> instanceMap);
68
69 static boolean areEqual(@NonNull Object[] expectedValues, @NonNull Object[] actualValues, @NonNegative int count,
70 @NonNull Map<Object, Object> instanceMap) {
71 for (int i = 0; i < count; i++) {
72 if (isNotEqual(expectedValues[i], actualValues[i], instanceMap)) {
73 return false;
74 }
75 }
76
77 return true;
78 }
79
80 private static boolean isNotEqual(@Nullable Object expected, @Nullable Object actual,
81 @NonNull Map<Object, Object> instanceMap) {
82 return actual == null == (expected != null) || actual != null && actual != expected
83 && expected != instanceMap.get(actual) && !EqualityMatcher.areEqualWhenNonNull(actual, expected);
84 }
85
86 abstract boolean hasEquivalentMatchers(@NonNull ArgumentValuesAndMatchers other);
87
88 private static boolean equivalentMatches(@NonNull ArgumentMatcher<?> matcher1, @Nullable Object arg1,
89 @NonNull ArgumentMatcher<?> matcher2, @Nullable Object arg2) {
90 boolean matcher1MatchesArg2 = matcher1.matches(arg2);
91 boolean matcher2MatchesArg1 = matcher2.matches(arg1);
92
93 if (arg1 != null && arg2 != null && matcher1MatchesArg2 && matcher2MatchesArg1) {
94 return true;
95 }
96
97 if (arg1 == arg2 && matcher1MatchesArg2 == matcher2MatchesArg1) {
98 ArgumentMismatch desc1 = new ArgumentMismatch();
99 matcher1.writeMismatchPhrase(desc1);
100 ArgumentMismatch desc2 = new ArgumentMismatch();
101 matcher2.writeMismatchPhrase(desc2);
102 return desc1.toString().equals(desc2.toString());
103 }
104
105 return false;
106 }
107
108 @SuppressWarnings("unchecked")
109 final <M1 extends ArgumentMatcher<M1>, M2 extends ArgumentMatcher<M2>> int indexOfFirstValueAfterEquivalentMatchers(
110 @NonNull ArgumentValuesAndMatchers other) {
111 List<ArgumentMatcher<?>> otherMatchers = other.matchers;
112
113 if (hasDifferentAmountOfMatchers(otherMatchers)) {
114 return -1;
115 }
116
117
118 int m = matchers.size();
119 int i;
120
121 for (i = 0; i < m; i++) {
122 M1 matcher1 = (M1) matchers.get(i);
123 M2 matcher2 = (M2) otherMatchers.get(i);
124
125 if (matcher1 == null || matcher2 == null) {
126 if (!EqualityMatcher.areEqual(values[i], other.values[i])) {
127 return -1;
128 }
129 } else if (matcher1 != matcher2 && (matcher1.getClass() != matcher2.getClass()
130 || !matcher1.same((M1) matcher2) && areNonEquivalentMatches(other, matcher1, matcher2, i))) {
131 return -1;
132 }
133 }
134
135 return i;
136 }
137
138 private boolean hasDifferentAmountOfMatchers(@Nullable List<ArgumentMatcher<?>> otherMatchers) {
139 return otherMatchers == null || matchers == null || otherMatchers.size() != matchers.size();
140 }
141
142 private boolean areNonEquivalentMatches(@NonNull ArgumentValuesAndMatchers other,
143 @NonNull ArgumentMatcher<?> matcher1, @NonNull ArgumentMatcher<?> matcher2, @NonNegative int matcherIndex) {
144 Class<?> matcherClass = matcher1.getClass();
145 return matcherClass == ReflectiveMatcher.class || matcherClass == HamcrestAdapter.class
146 || !equivalentMatches(matcher1, values[matcherIndex], matcher2, other.values[matcherIndex]);
147 }
148
149 @NonNull
150 final String toString(@NonNull List<String> parameterTypes) {
151 ArgumentMismatch desc = new ArgumentMismatch();
152 int parameterCount = values.length;
153
154 if (parameterCount > 0) {
155 if (matchers == null) {
156 desc.appendFormatted(values);
157 } else {
158 String sep = "";
159
160 for (int i = 0; i < parameterCount; i++) {
161 ArgumentMatcher<?> matcher = getArgumentMatcher(i);
162 String parameterType = parameterTypes.get(i);
163 desc.append(sep).appendFormatted(parameterType, values[i], matcher);
164 sep = ", ";
165 }
166 }
167
168 desc.append(')');
169 }
170
171 return desc.toString();
172 }
173 }