View Javadoc

1   // Copyright (c) 2003, Chad Woolley, All rights reserved.
2   
3   package org.virtualmock.call;
4   
5   import org.virtualmock.VM;
6   import org.virtualmock.matcher.ArgMatcher;
7   
8   
9   /***
10   * Represents a method invocation of a recorded mock call.  This is a subclass
11   * of Call which only contains information which is specific to recorded
12   * calls.
13   *
14   * @author Chad Woolley
15   * @version $Revision: 1.32 $
16   */
17  public class RecordedCall extends Call {
18      private ArgMatcher[] argMatchers = null;
19  
20      /*** Flag to indicate if this call can have unlimited invocations. */
21      private boolean unlimitedInvocations = false;
22  
23      /*** The number of times this recorded call has been invoked. */
24      private int invocationCount = 0;
25  
26      /***
27       * Creates a new RecordedCall object.
28       *
29       * @param signature the unique identifier for the call
30       * @param argValues an array representing the values of the arguments
31       *        passed to (or expected by) the call.
32       */
33      public RecordedCall(Signature signature, Object[] argValues) {
34          super(signature, argValues);
35      }
36  
37      /***
38       * Creates a new RecordedCall object.
39       *
40       * @param signature the unique identifier for the call
41       */
42      public RecordedCall(Signature signature) {
43          super(signature);
44      }
45  
46      /***
47       * Creates a new RecordedCall object which has a return value.
48       *
49       * @param signature the method signature for the call
50       * @param argValues an array representing the values of the arguments
51       *        passed to (or expected by) the call.
52       * @param returnValue the value the call will return
53       */
54      public RecordedCall(Signature signature, Object[] argValues,
55          Object returnValue) {
56          super(signature, argValues, returnValue);
57      }
58  
59      /***
60       * Creates a new RecordedCall object which throws an exception.
61       *
62       * @param signature the method signature for the call
63       * @param argValues an array representing the values of the arguments
64       *        passed to (or expected by) the call.
65       * @param throwable the exception the call will throw
66       */
67      public RecordedCall(Signature signature, Object[] argValues,
68          Throwable throwable) {
69          super(signature, argValues, throwable);
70      }
71  
72      /***
73       * Sets the ArgMatchers that are defined for this call.
74       *
75       * @param argMatchers the array of ArgMatchers to use for this call
76       *
77       * @throws IllegalArgumentException if the number of ArgTypes does not
78       *         match the number of ArgMatchers
79       */
80      public void setArgMatchers(ArgMatcher[] argMatchers) {
81          if (getArgTypes().length != argMatchers.length) {
82              throw new IllegalArgumentException(
83                  "Number of argMatchers specified ('" + argMatchers.length
84                  + "') does not match the number of arguments in this call ('"
85                  + getSignature() + "')");
86          }
87  
88          this.argMatchers = argMatchers;
89      }
90  
91      /***
92       * Gets the ArgMatchers that are defined for this call (if any).
93       *
94       * @return the array of ArgMatchers that are defined for this call, or null
95       *         if none have been defined.
96       */
97      public ArgMatcher[] getArgMatchers() {
98          return argMatchers;
99      }
100 
101     /***
102      * Indicates if this call is invocable.  A call is invocable if has not yet
103      * been invoked, or if unlimited invocation is set.
104      *
105      * @return true if this call is invocable
106      */
107     public boolean isInvocable() {
108         if (hasUnlimitedInvocations() || !isInvoked()) {
109             return true;
110         }
111 
112         return false;
113     }
114 
115     /***
116      * Returns the number of times incrementInvocationCount() has been called
117      * on this Call.
118      *
119      * @return the number of times incrementInvocationCount() has been called
120      *         on this Call.
121      */
122     public int getInvocationCount() {
123         return invocationCount;
124     }
125 
126     /***
127      * Returns true if invocation count is greater than zero.
128      *
129      * @return true if invocation count is greater than zero.
130      */
131     public boolean isInvoked() {
132         if (invocationCount > 0) {
133             return true;
134         }
135 
136         return false;
137     }
138 
139     /***
140      * Set that this call can be invoked an unlimited number of times.
141      *
142      * @param unlimitedInvocations boolean flag.
143      */
144     public void setUnlimitedInvocations(boolean unlimitedInvocations) {
145         this.unlimitedInvocations = unlimitedInvocations;
146     }
147 
148     /***
149      * <p>
150      * Performs matching of all arguments on this Call to the specified Call,
151      * based on the ArgMatchers that are defined for this call. For
152      * ArgMatchers where the order is relevant (such  as GreaterThanMatcher),
153      * it is important to know that the  Call is matched <strong>TO</strong>
154      * the Call.  In other words, Call GreaterThan Call is checked to see if
155      * the match is true.
156      * </p>
157      * 
158      * <p>
159      * Note that all matching is done based on position.  This means that that
160      * all arguments and matchers must be in the same corresponding order on
161      * this Call, the Call which was passed, and the ArgMatchers for this
162      * Call.
163      * </p>
164      *
165      * @param call The Call which will be matched to this Call
166      *
167      * @return true if the this Call matches the Call, or false if it does not
168      *
169      * @throws IllegalArgumentException if the signatures of the calls do not
170      *         match
171      *
172      * @todo need to implement support for configurable default ArgMatcher,
173      *       instead of hardcoding default to EqualsMatcher
174      */
175     public boolean hasMatchingArgs(Call call) {
176         if (!getSignature().equals(call.getSignature())) {
177             throw new IllegalArgumentException(
178                 "Cannot perform argument matching, because the signature of this Call ('"
179                 + getSignature()
180                 + "') does not match the signature of the Call which was passed ('"
181                 + call.getSignature() + "')");
182         }
183 
184         // iterate through all corresponding arguments defined for 
185         // this Call and the passed call, 
186         // and call the corresponding ArgMatchers matches() method.
187         Object[] recordedArgValues = getArgValues();
188         Object[] invokedArgValues = call.getArgValues();
189 
190         for (int i = 0; i < recordedArgValues.length; i++) {
191             ArgMatcher matcher = null;
192 
193             if (getArgMatchers() != null) {
194                 // if ArgMatchers are defined, use them 
195                 matcher = getArgMatchers()[i];
196             } else {
197                 // if no ArgMatchers are defined, default to EqualsMatcher
198                 matcher = VM.EQUALS_MATCHER;
199             }
200 
201             boolean argMatches =
202                 matcher.matches(recordedArgValues[i], invokedArgValues[i]);
203 
204             if (!argMatches) {
205                 return false;
206             }
207         }
208 
209         return true;
210     }
211 
212     /***
213      * Return true if this call has unlimited invocations set.
214      *
215      * @return true if this call has unlimited invocations set.
216      */
217     public boolean hasUnlimitedInvocations() {
218         return unlimitedInvocations;
219     }
220 
221     /***
222      * Increments the internal invocation count for this call by one.
223      */
224     public void incrementInvocationCount() {
225         invocationCount++;
226     }
227 
228     /***
229      * Returns true if hasMatchingSignature() and hasMatchingArgs() both return
230      * true.
231      *
232      * @param call Call to match against
233      *
234      * @return true if hasMatchingSignature() and hasMatchingArgs() both return
235      *         true.
236      */
237     public boolean matches(Call call) {
238         if (!hasMatchingSignature(call)) {
239             return false;
240         }
241 
242         if (!hasMatchingArgs(call)) {
243             return false;
244         }
245 
246         return true;
247     }
248 }