/*
* 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;
}
}