blob: ea5bc70e5d8c6ef975427fc4249965793363f405 [file] [log] [blame]
// Copyright 2008 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry.enhance;
import org.apache.tapestry.enhance.javassist.ClassFabUtils;
import java.lang.reflect.Method;
/**
* JDK 1.4 based version of {@link MethodSignature}.
*/
public class MethodSignatureImpl implements MethodSignature
{
protected int _hashCode = -1;
protected Class _returnType;
protected String _name;
protected Class[] _parameterTypes;
protected Class[] _exceptionTypes;
public MethodSignatureImpl(Class returnType, String name,
Class[] parameterTypes, Class[] exceptionTypes)
{
_returnType = returnType;
_name = name;
_parameterTypes = parameterTypes;
_exceptionTypes = exceptionTypes;
}
public MethodSignatureImpl(Method m)
{
this(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes());
}
public Class[] getExceptionTypes()
{
return _exceptionTypes;
}
public String getName()
{
return _name;
}
public Class[] getParameterTypes()
{
return _parameterTypes;
}
public Class getReturnType()
{
return _returnType;
}
public int hashCode()
{
if (_hashCode == -1)
{
_hashCode = _returnType.hashCode();
_hashCode = 31 * _hashCode + _name.hashCode();
int count = count(_parameterTypes);
for (int i = 0; i < count; i++)
_hashCode = 31 * _hashCode + _parameterTypes[i].hashCode();
count = count(_exceptionTypes);
for (int i = 0; i < count; i++)
_hashCode = 31 * _hashCode + _exceptionTypes[i].hashCode();
}
return _hashCode;
}
protected static int count(Object[] array)
{
return array == null ? 0 : array.length;
}
/**
* Returns true if the other object is an instance of MethodSignature with identical values for
* return type, name, parameter types and exception types.
*/
public boolean equals(Object o)
{
if (o == null || !(o instanceof MethodSignatureImpl))
return false;
MethodSignatureImpl ms = (MethodSignatureImpl) o;
if (_returnType != ms._returnType)
return false;
if (!_name.equals(ms._name))
return false;
if (mismatch(_parameterTypes, ms._parameterTypes))
return false;
return !mismatch(_exceptionTypes, ms._exceptionTypes);
}
protected boolean mismatch(Class[] a1, Class[] a2)
{
int a1Count = count(a1);
int a2Count = count(a2);
if (a1Count != a2Count)
return true;
// Hm. What if order is important (for exceptions)? We're really saying here that they
// were derived from the name Method.
for (int i = 0; i < a1Count; i++)
{
if (!a1[i].isAssignableFrom(a2[i]))
return true;
}
return false;
}
public String toString()
{
StringBuffer buffer = new StringBuffer();
buffer.append(ClassFabUtils.getJavaClassName(_returnType));
buffer.append(" ");
buffer.append(_name);
buffer.append("(");
for (int i = 0; i < count(_parameterTypes); i++)
{
if (i > 0)
buffer.append(", ");
buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i]));
}
buffer.append(")");
for (int i = 0; i < count(_exceptionTypes); i++)
{
if (i == 0)
buffer.append(" throws ");
else
buffer.append(", ");
buffer.append(_exceptionTypes[i].getName());
}
return buffer.toString();
}
public String getUniqueId()
{
StringBuffer buffer = new StringBuffer(_name);
buffer.append("(");
for (int i = 0; i < count(_parameterTypes); i++)
{
if (i > 0)
buffer.append(",");
buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i]));
}
buffer.append(")");
return buffer.toString();
}
public boolean isGeneric()
{
return false;
}
public boolean isOverridingSignatureOf(MethodSignature ms)
{
if (!(ms instanceof MethodSignatureImpl))
return false;
MethodSignatureImpl sig = (MethodSignatureImpl)ms;
if (!sig._returnType.isAssignableFrom(_returnType))
return false;
if (!_name.equals(sig._name))
return false;
if (mismatch(_parameterTypes, sig._parameterTypes))
return false;
return exceptionsEncompass(sig._exceptionTypes);
}
/**
* The nuts and bolts of checking that another method signature's exceptions are a subset of
* this signature's.
*/
protected boolean exceptionsEncompass(Class[] otherExceptions)
{
int ourCount = count(_exceptionTypes);
int otherCount = count(otherExceptions);
// If we have no exceptions, then ours encompass theirs only if they
// have no exceptions, either.
if (ourCount == 0)
return otherCount == 0;
boolean[] matched = new boolean[otherCount];
int unmatched = otherCount;
for (int i = 0; i < ourCount && unmatched > 0; i++)
{
for (int j = 0; j < otherCount; j++)
{
// Ignore exceptions that have already been matched
if (matched[j])
continue;
// When one of our exceptions is a super-class of one of their exceptions,
// then their exceptions is matched.
if (_exceptionTypes[i].isAssignableFrom(otherExceptions[j]))
{
matched[j] = true;
unmatched--;
}
}
}
return unmatched == 0;
}
}