/* | |
* Copyright 2003-2007 the original author or authors. | |
* | |
* 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.codehaus.groovy.reflection; | |
import groovy.lang.GString; | |
import java.lang.reflect.Method; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; | |
/** | |
* @author Alex.Tkachman | |
*/ | |
public class CachedClass { | |
private CachedClass cachedSuperClass; | |
private CachedMethod[] methods; | |
public final Class cachedClass; | |
public final boolean isArray; | |
public final boolean isPrimitive; | |
public final int modifiers; | |
int distance = -1; | |
public final boolean isInterface; | |
public final boolean isNumber; | |
CachedClass(Class klazz) { | |
cachedClass = klazz; | |
isArray = klazz.isArray(); | |
isPrimitive = klazz.isPrimitive(); | |
modifiers = klazz.getModifiers(); | |
isInterface = klazz.isInterface(); | |
isNumber = Number.class.isAssignableFrom(klazz); | |
} | |
public synchronized CachedClass getCachedSuperClass() { | |
if (cachedSuperClass == null) | |
cachedSuperClass = ReflectionCache.getCachedClass(cachedClass.getSuperclass()); | |
return cachedSuperClass; | |
} | |
public synchronized CachedMethod[] getMethods() { | |
if (methods == null) { | |
final Method[] declaredMethods = cachedClass.getDeclaredMethods(); | |
methods = new CachedMethod[declaredMethods.length]; | |
for (int i = 0; i != methods.length; ++i) | |
methods[i] = ReflectionCache.getCachedMethod(declaredMethods[i]); | |
} | |
return methods; | |
} | |
public Method searchMethods(String name, CachedClass[] parameterTypes) { | |
CachedMethod[] methods = getMethods(); | |
Method res = null; | |
for (int i = 0; i < methods.length; i++) { | |
CachedMethod m = methods[i]; | |
if (m.cachedMethod.getName().equals(name) | |
&& ReflectionCache.arrayContentsEq(parameterTypes, m.getParameterTypes()) | |
&& (res == null || res.getReturnType().isAssignableFrom(m.cachedMethod.getReturnType()))) | |
res = m.cachedMethod; | |
} | |
return res; | |
} | |
public int getModifiers() { | |
return modifiers; | |
} | |
/** | |
* Coerces a GString instance into String if needed | |
* | |
* @return the coerced argument | |
*/ | |
protected Object coerceGString(Object argument) { | |
if (cachedClass != String.class) return argument; | |
if (!(argument instanceof GString)) return argument; | |
return argument.toString(); | |
} | |
// PRECONDITION: | |
// !ReflectionCache.isAssignableFrom(parameterType, argument.getClass()) | |
protected Object coerceNumber(Object argument) { | |
if (argument instanceof Number && (isNumber || isPrimitive)) { // Number types | |
Object oldArgument = argument; | |
boolean wasDouble = false; | |
boolean wasFloat = false; | |
Class param = cachedClass; | |
if (param == Byte.class || param == Byte.TYPE) { | |
argument = new Byte(((Number) argument).byteValue()); | |
} else if (param == Double.class || param == Double.TYPE) { | |
wasDouble = true; | |
argument = new Double(((Number) argument).doubleValue()); | |
} else if (param == Float.class || param == Float.TYPE) { | |
wasFloat = true; | |
argument = new Float(((Number) argument).floatValue()); | |
} else if (param == Integer.class || param == Integer.TYPE) { | |
argument = new Integer(((Number) argument).intValue()); | |
} else if (param == Long.class || param == Long.TYPE) { | |
argument = new Long(((Number) argument).longValue()); | |
} else if (param == Short.class || param == Short.TYPE) { | |
argument = new Short(((Number) argument).shortValue()); | |
} else if (param == BigDecimal.class) { | |
argument = new BigDecimal(String.valueOf((Number) argument)); | |
} else if (param == BigInteger.class) { | |
argument = new BigInteger(String.valueOf((Number) argument)); | |
} | |
if (oldArgument instanceof BigDecimal) { | |
BigDecimal oldbd = (BigDecimal) oldArgument; | |
boolean throwException = false; | |
if (wasDouble) { | |
Double d = (Double) argument; | |
if (d.isInfinite()) throwException = true; | |
} else if (wasFloat) { | |
Float f = (Float) argument; | |
if (f.isInfinite()) throwException = true; | |
} else { | |
BigDecimal newbd = new BigDecimal(String.valueOf((Number) argument)); | |
throwException = !oldArgument.equals(newbd); | |
} | |
if (throwException) | |
throw new IllegalArgumentException(param + " out of range while converting from BigDecimal"); | |
} | |
} | |
return argument; | |
} | |
protected Object coerceArray(Object argument) { | |
if (!isArray) return argument; | |
Class argumentClass = argument.getClass(); | |
if (!argumentClass.isArray()) return argument; | |
Class paramComponent = cachedClass.getComponentType(); | |
if (paramComponent.isPrimitive()) { | |
if (paramComponent == boolean.class && argumentClass == Boolean[].class) { | |
argument = DefaultTypeTransformation.convertToBooleanArray(argument); | |
} else if (paramComponent == byte.class && argumentClass == Byte[].class) { | |
argument = DefaultTypeTransformation.convertToByteArray(argument); | |
} else if (paramComponent == char.class && argumentClass == Character[].class) { | |
argument = DefaultTypeTransformation.convertToCharArray(argument); | |
} else if (paramComponent == short.class && argumentClass == Short[].class) { | |
argument = DefaultTypeTransformation.convertToShortArray(argument); | |
} else if (paramComponent == int.class && argumentClass == Integer[].class) { | |
argument = DefaultTypeTransformation.convertToIntArray(argument); | |
} else if (paramComponent == long.class && | |
(argumentClass == Long[].class || argumentClass == Integer[].class)) { | |
argument = DefaultTypeTransformation.convertToLongArray(argument); | |
} else if (paramComponent == float.class && | |
(argumentClass == Float[].class || argumentClass == Integer[].class)) { | |
argument = DefaultTypeTransformation.convertToFloatArray(argument); | |
} else if (paramComponent == double.class && | |
(argumentClass == Double[].class || argumentClass == Float[].class | |
|| BigDecimal[].class.isAssignableFrom(argumentClass))) { | |
argument = DefaultTypeTransformation.convertToDoubleArray(argument); | |
} | |
} else if (paramComponent == String.class && argument instanceof GString[]) { | |
GString[] strings = (GString[]) argument; | |
String[] ret = new String[strings.length]; | |
for (int i = 0; i < strings.length; i++) { | |
ret[i] = strings[i].toString(); | |
} | |
argument = ret; | |
} | |
return argument; | |
} | |
public int getSuperClassDistance() { | |
synchronized (cachedClass) { | |
if (distance == -1) { | |
int distance = 0; | |
for (Class klazz=cachedClass; klazz != null; klazz = klazz.getSuperclass()) { | |
distance++; | |
} | |
this.distance = distance; | |
} | |
return distance; | |
} | |
} | |
} |