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