/* * util.mocks.MockedCall * * Copyright 2006 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package util.mocks; /** * Represents a call on a mock object. A <code>MockedCall</code> is composed * by: * <ul> * <li>A {@link MethodSignature} to encapsultate the method declaration * information.</li> * <li>The actual arguments of the method call. There will be none of those if * the method declares no parameters.</li> * <li>The actual return value of the method call. There will be none if the * method has no return value.</li> * </ul> * <p> * The method signature has to be defined with exactly the same class objects as * the original return type and parameter types. However, this class requires * the actual arguments to be objects, so if you have a primitive type you must * convert it into the corresponding wrapping object before invoking * {@link #setArgs(Object[])} (i.e., <code>int</code> into * <code>Integer</code>, and so on). The same applies to the return value, * which is set by calling {@link #setResult(Object)}. * </p> * * @author Jean-Marie Burel      <a * href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br> * Andrea Falconi      <a * href="mailto:a.falconi@dundee.ac.uk"> a.falconi@dundee.ac.uk</a> * @since OME2.2 */ public class MockedCall { /** This method's signature. */ private MethodSignature signature; /** * The actual arguments, if any, of this method call. The array is indexed * in the same order as the parameters were declared. Zero-length for * no-params methods. */ private Object[] args; /** * The result of this method call. Set to <code>null</code> if the return * type is <code>void</code>. */ private Object result; /** * Any exception this method call should throw. Set to <code>null</code> * if there's no exception. */ private Throwable exception; /** * Creates an object to represent the method call defined by the specified * method signature, arguments and return value. If there is any primitive * type, you have to wrap that value with the corresponding wrapper object * and, in this case, you may not pass <code>null</code>. * * @param ms * This method's signature. * @param args * The actual arguments of this method call. The array must be * indexed in the same order as the parameters were declared. You * mustn't use this constructor if the method signature declares * no parameters. * @param retVal * The return value of this method call. You mustn't use this * constructor if the method signature declares a * <code>void</code> return type. */ public MockedCall(MethodSignature ms, Object[] args, Object retVal) { // checkSignature(ms, hasParams, isVoid) checkSignature(ms, true, false); signature = ms; this.args = new Object[signature.numberOfParameters()]; setArgs(args); setResult(retVal); } /** * Creates an object to represent the method call defined by the specified * method signature and arguments. Use this constructor if the method * declares a <code>void</code> return type. If there is any primitive * type, you have to wrap that value with the corresponding wrapper object * and, in this case, you may not pass <code>null</code>. * * @param ms * This method's signature. * @param args * The actual arguments of this method call. The array must be * indexed in the same order as the parameters were declared. You * mustn't use this constructor if the method signature declares * no parameters. */ public MockedCall(MethodSignature ms, Object[] args) { // checkSignature(ms, hasParams, isVoid) checkSignature(ms, true, true); signature = ms; this.args = new Object[signature.numberOfParameters()]; setArgs(args); } /** * Creates an object to represent the method call defined by the specified * method signature and return value. Use this constructor if the method * declares no parameters. If the return value is a primitive type, you have * to wrap that value with the corresponding wrapper object and, in this * case, you may not pass <code>null</code>. * * @param ms * This method's signature. * @param retVal * The return value of this method call. You mustn't use this * constructor if the method signature declares a * <code>void</code> return type. */ public MockedCall(MethodSignature ms, Object retVal) { // checkSignature(ms, hasParams, isVoid) checkSignature(ms, false, false); signature = ms; this.args = new Object[signature.numberOfParameters()]; setResult(retVal); } /** * Creates an object to represent the method call defined by the specified * method signature. Use this constructor if the method declares a * <code>void</code> return type and no parameters. * * @param ms * This method's signature. */ public MockedCall(MethodSignature ms) { // checkSignature(ms, hasParams, isVoid) checkSignature(ms, false, true); signature = ms; this.args = new Object[signature.numberOfParameters()]; } /** * Retrieves the return value of the method call represented by this object. * You mustn't call this method if the method signature declares a * <code>void</code> return type. * * @return The return value of the method call represented by this object. */ public Object getResult() { if (signature.isReturnTypeVoid()) { throw new IllegalArgumentException( "This method has a void return type."); } return result; } /** * Associates an exception to the method call represented by this object. * * @param t * The exception. */ public void setException(Throwable t) { if (t == null) { throw new NullPointerException("No exception."); } exception = t; } /** * Tells whether the method call represented by this object has an * exception. * * @return <code>true</code> if there's an exception, <code>false</code> * otherwise. */ public boolean hasException() { return exception != null; } /** * Retrieves the exception, if any, associated to the method call * represented by this object. This method will return <code>null</code> * if there's no exception associated to the method call represented by this * object. * * @see #hasException() * @return The exception, if any, associated to the method call represented * by this object. */ public Throwable getException() { return exception; } /** * Tells whether the passed <code>MockedCall</code> can be considered the * same as the current object. * * @param mc * Another <code>MockedCall</code>. * @return <code>true</code> if, and only if, <code>mc</code> and the * current object have both the same signature and the same actual * call arguments. */ public boolean isSameCall(MockedCall mc) { boolean b = false; if (mc != null && signature.equals(mc.signature)) { b = true; // B/c of setArgs and constructor implementations, we know that the // args array always have the same number of elements as specified // by the signature object (this number may also be 0: no params). for (int i = 0; i < args.length; ++i) { if (!isSameObject(args[i], mc.args[i])) { b = false; break; } } } return b; } /** * Overrides the same method in <code>Object</code> to return a string in * the format: "name(arg1, ..., argN)". * * @return A string representation of this method call. */ @Override public String toString() { StringBuffer buf = new StringBuffer(); buf.append(signature.getName()); buf.append("("); if (args.length != 0) { int i = 0; while (i < args.length - 1) { buf.append(args[i++]); buf.append(", "); } buf.append(args[i]); } buf.append(")"); return buf.toString(); } /** * Checks that <code>ms</code> is not <code>null</code> and conforms to * the options specified by the boolean parameters. If a check fails, a * runtime exception is thrown. * * @param ms * The method signature. * @param hasParams * Pass <code>true</code> to verify that the signature declares * parameters, <code>false</code> to verify that the signature * has no parameters. * @param isVoid * Pass <code>true</code> to verify that the signature declares * a <code>void</code> return type, <code>false</code> to * verify that the return type is not <code>void</code>. */ private void checkSignature(MethodSignature ms, boolean hasParams, boolean isVoid) { if (ms == null) { throw new NullPointerException("No method signature was provided."); } if (hasParams != ms.hasParameters()) { throw new IllegalArgumentException("Wrong parameters declaration."); } if (isVoid != ms.isReturnTypeVoid()) { throw new IllegalArgumentException("Wrong return type declaration."); } } /** * Sets the arguments of this method call. If there is any primitive type, * you have to wrap that value with the corresponding wrapper object and, in * this case, you may not pass <code>null</code>. You mustn't call this * method if the method signature declares no parameters. * * @param args * The actual arguments of this method call. The array must be * indexed in the same order as the parameters were declared. */ private void setArgs(Object[] args) { // Constructor has already checked that method has params. if (args == null || args.length != signature.numberOfParameters()) { throw new IllegalArgumentException("Wrong number of arguments."); } for (int i = 0; i < args.length; ++i) { if (!signature.isValidArgument(args[i], i)) { throw new IllegalArgumentException("Invalid argument type: " + i); } this.args[i] = args[i]; } } /** * Sets the result of this method call. If the method return type is a * primitive type, you have to wrap the value with the corresponding wrapper * object and, in this case, you may not pass <code>null</code>. You * mustn't call this method if the method signature declares a * <code>void</code> return type. * * @param result * The return value of this method call. */ private void setResult(Object result) { // Constructor has already checked that return type is not void. if (!signature.isValidReturnValue(result)) { throw new IllegalArgumentException("Invalid return type."); } this.result = result; } /** * Tells whether <code>p1</code> and <code>p2</code> may be considered * to be the same object. * * @param p1 * The first object. * @param p2 * The second object. * @return <code>true</code> if the passed objects hold the same state, * <code>false</code> otherwise. */ private boolean isSameObject(Object p1, Object p2) { boolean b = p1 == p2; if (!b) { if (p1 == null) { b = false; } else { // Both p1 and p2 are not null. b = p2.equals(p1); } } return b; } }