/*
* util.mocks.MethodSignature
*
* Copyright 2006 University of Dundee. All rights reserved.
* Use is subject to license terms supplied in LICENSE.txt
*/
package util.mocks;
import java.util.HashMap;
import java.util.Map;
/**
* Helper class to represent a method signature in a {@link MockedCall}.
* <p>
* The method signature has to be defined with exactly the same class objects as
* the original return type and parameter types use
* <code>int.class</code>, <code>boolean.class</code>, etc., for primitive
* types and <code>void.class</code> to specify a <code>void</code> return
* type.
* </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 MethodSignature {
/** Public visibility flag. */
public static final int PUBLIC = 0;
/** Package visibility flag. */
public static final int PACKAGE = 1;
/** Protected visibility flag. */
public static final int PROTECTED = 2;
/** Private visibility flag. */
public static final int PRIVATE = 3;
/**
* Maps each Java primitive type to the corresponding object wrapper type.
*/
private static final Map primitiveTypes = new HashMap();
static {
primitiveTypes.put(boolean.class, Boolean.class);
primitiveTypes.put(byte.class, Byte.class);
primitiveTypes.put(char.class, Character.class);
primitiveTypes.put(short.class, Short.class);
primitiveTypes.put(int.class, Integer.class);
primitiveTypes.put(long.class, Long.class);
primitiveTypes.put(float.class, Float.class);
primitiveTypes.put(double.class, Double.class);
}
/**
* Holds the visibility flag for this method. The constructor makes sure
* that the value is set to one of the static flags defined by this class.
*/
private int visibility;
/**
* The method return type. The constructor makes sure that it is not
* <code>null</code>.
*/
private Class returnType;
/**
* The method name. The constructor makes sure that it has at least one
* character.
*/
private String name;
/**
* The type of each method parameter, in the same order as they were
* declared. The constructor makes sure that this is either a <code>0</code>-length
* array (if the method declares no parameters) or, if the length is bigger
* than <code>0</code>, all elements are not <code>null</code>.
*/
private Class[] paramTypes;
/**
* Creates a new instance to represent a given method signature.
*
* @param visibility
* The method visibility. Must be one of the flags defined by
* this class.
* @param returnType
* The method return type. Mustn't be <code>null</code>. Pass
* <code>void.class</code>, if the method returns no value.
* @param name
* The method name. Must contain one character at least.
* @param paramTypes
* The type of each method parameter, in the same order as they
* were declared. Must have elements and those mustn't be
* <code>null</code>. For methods that have no paramters, use
* the other constructor.
*/
public MethodSignature(int visibility, Class returnType, String name,
Class[] paramTypes) {
if (visibility < PUBLIC || PRIVATE < visibility) {
throw new IllegalArgumentException("Invalid visibility argument.");
}
if (returnType == null) {
throw new IllegalArgumentException("Invalid returnType argument.");
}
if (name == null || name.length() == 0) {
throw new IllegalArgumentException("Invalid name argument.");
}
if (paramTypes == null) {
paramTypes = new Class[0];
}
for (int i = 0; i < paramTypes.length; ++i) {
if (paramTypes[i] == null) {
throw new IllegalArgumentException("Null paramType: " + i);
}
}
this.visibility = visibility;
this.returnType = returnType;
this.name = name;
this.paramTypes = paramTypes;
}
/**
* Creates a new instance to represent a given method signature. This
* constructor has to be used for methods that declare no parameter.
*
* @param visibility
* The method visibility. Must be one of the flags defined by
* this class.
* @param returnType
* The method return type. Mustn't be <code>null</code>. Pass
* <code>void.class</code>, if the method returns no value.
* @param name
* The method name. Must contain one character at least.
*/
public MethodSignature(int visibility, Class returnType, String name) {
this(visibility, returnType, name, null); // No-params method.
}
/**
* Returns the method name.
*
* @return See above.
*/
public String getName() {
return name;
}
/**
* Returns the number of parameters declared by the method. Returns
* <code>0</code> for a no-params method, that is, if
* {@link #hasParameters()} returns <code>false</code>.
*
* @return See above.
*/
public int numberOfParameters() {
return paramTypes.length;
}
/**
* Tells whether or not the method returns a value.
*
* @return <code>true</code> if the method return type is
* <code>void</code>, <code>false</code> otherwise.
*/
public boolean isReturnTypeVoid() {
return void.class == returnType;
}
/**
* Tells whether or not the type of the specified argument is a valid return
* type. Primitive types are matched against the corresponding wrapper type
* and, in this case, a <code>null</code> argument will never match.
* Non-primitive types are matched against the argument class and, in this
* case, a <code>null</code> argument will always match. This method will
* always return <code>false</code> in the case of a <code>void</code>
* return type.
*
* @param retVal
* The value whose type we want to test.
* @return <code>true</code> if <code>retVal</code> is compatible with
* the the return type of this method signature, <code>false</code>
* otherwise.
*/
public boolean isValidReturnValue(Object retVal) {
if (isReturnTypeVoid()) {
return false;
}
return matches(retVal, returnType);
}
/**
* Tells whether or not the method declares parmeters.
*
* @return <code>true</code> if the method declares parmeters,
* <code>false</code> otherwise.
*/
public boolean hasParameters() {
return paramTypes.length != 0;
}
/**
* Tells whether or not the type of the specified argument is a valid type
* for the <code>argIndex</code> method parameter. Primitive types are
* matched against the corresponding wrapper type and, in this case, a
* <code>null</code> argument will never match. Non-primitive types are
* matched against the argument class and, in this case, a <code>null</code>
* argument will always match.
*
* @param arg
* The value whose type we want to test.
* @param argIndex
* The index of the method parameter. Pass <code>0</code> for
* the first parameter, <code>1</code> for the second, and so
* on.
* @return <code>true</code> if <code>arg</code> is compatible with the
* the type of the <code>argIndex</code> method parameter in this
* method signature, <code>false</code> otherwise.
*/
public boolean isValidArgument(Object arg, int argIndex) {
if (argIndex < 0 || paramTypes.length < argIndex) {
return false;
}
return matches(arg, paramTypes[argIndex]);
}
/**
* Tells whether or not this object is the same as the specified argument.
*
* @param methodSignature
* The object to compare.
* @return <code>true</code> if this object and
* <code>methodSignature</code> hold the same state,
* <code>false</code> otherwise.
*/
@Override
public boolean equals(Object methodSignature) {
boolean b = methodSignature != null && methodSignature.getClass() == MethodSignature.class;
if (b) {
MethodSignature ms = (MethodSignature) methodSignature;
b = ms.visibility == visibility && ms.returnType == returnType
&& ms.name == name && ms.paramTypes.length == paramTypes.length;
if (b) {
for (int i = 0; i < paramTypes.length; ++i) {
if (paramTypes[i] != ms.paramTypes[i]) {
b = false;
break;
}
}
}
}
return b;
}
/**
* Tells whether or not the type of <code>value</code> matches the
* specified <code>type</code>. Primitive types are matched against the
* corresponding wrapper type and, in this case, a <code>null value</code>
* will never match. Non-primitive types are matched against the class of
* <code>value</code> and, in this case, a <code>null value</code> will
* always match.
*
* @param value
* The object whose type we want to match against
* <code>type</code>.
* @param type
* The type to match against.
* @return <code>true</code> for a successful match, <code>false</code>
* otherwise.
*/
private boolean matches(Object value, Class type) {
if (primitiveTypes.containsKey(type)) {
if (value == null) {
return false;
}
Class wrapperClass = (Class) primitiveTypes.get(type);
return wrapperClass == value.getClass();
}
// Else:
if (value == null) {
return true;
}
return type.isAssignableFrom(value.getClass());
}
}