/*
* util.mocks.MethodSignature
*
*------------------------------------------------------------------------------
* 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;
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.
*/
public boolean equals(Object methodSignature)
{
boolean b = (methodSignature != null &&
MethodSignature.class.equals(methodSignature.getClass()));
if (b) {
MethodSignature ms = (MethodSignature) methodSignature;
b = (ms.visibility == visibility &&
ms.returnType.equals(returnType) &&
ms.name.equals(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());
}
}