/* * util.mocks.MockedCall * *------------------------------------------------------------------------------ * Copyright (C) 2006 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ 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 = (mc != null && signature.equals(mc.signature)); if (b) { //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. */ 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) //Then p2 can't be null. b = false; else //Both p1 and p2 are not null. b = p2.equals(p1); } return b; } }