View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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 }