|
Groovy example source code file (MetaClassHelper.java)
The Groovy MetaClassHelper.java source code/* * 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.runtime; import groovy.lang.Closure; import groovy.lang.GString; import groovy.lang.GroovyRuntimeException; import groovy.lang.MetaMethod; import org.codehaus.groovy.reflection.CachedClass; import org.codehaus.groovy.util.FastArray; import org.codehaus.groovy.reflection.ParameterTypes; import org.codehaus.groovy.reflection.ReflectionCache; import org.codehaus.groovy.runtime.wrappers.Wrapper; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * @author John Wilson * @author Jochen Theodorou */ public class MetaClassHelper { public static final Object[] EMPTY_ARRAY = {}; public static final Class[] EMPTY_TYPE_ARRAY = {}; public static final Object[] ARRAY_WITH_NULL = {null}; protected static final Logger LOG = Logger.getLogger(MetaClassHelper.class.getName()); private static final int MAX_ARG_LEN = 12; private static final int OBJECT_SHIFT = 23, INTERFACE_SHIFT = 0, PRIMITIVE_SHIFT = 21, VARGS_SHIFT = 44; /* dist binary layout: * 0-20: interface * 21-22: primitive dist * 23-43: object dist * 44-48: vargs penalty */ public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; public static boolean accessibleToConstructor(final Class at, final Constructor constructor) { boolean accessible = false; final int modifiers = constructor.getModifiers(); if (Modifier.isPublic(modifiers)) { accessible = true; } else if (Modifier.isPrivate(modifiers)) { accessible = at.getName().equals(constructor.getName()); } else if (Modifier.isProtected(modifiers)) { Boolean isAccessible = checkCompatiblePackages(at, constructor); if (isAccessible != null) { accessible = isAccessible; } else { boolean flag = false; Class clazz = at; while (!flag && clazz != null) { if (clazz.equals(constructor.getDeclaringClass())) { flag = true; break; } if (clazz.equals(Object.class)) { break; } clazz = clazz.getSuperclass(); } accessible = flag; } } else { Boolean isAccessible = checkCompatiblePackages(at, constructor); if (isAccessible != null) { accessible = isAccessible; } } return accessible; } private static Boolean checkCompatiblePackages(Class at, Constructor constructor) { if (at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null) { return Boolean.TRUE; } if (at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null) { return Boolean.FALSE; } if (at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null) { return Boolean.FALSE; } if (at.getPackage().equals(constructor.getDeclaringClass().getPackage())) { return Boolean.TRUE; } return null; } public static Object[] asWrapperArray(Object parameters, Class componentType) { Object[] ret = null; if (componentType == boolean.class) { boolean[] array = (boolean[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == char.class) { char[] array = (char[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == byte.class) { byte[] array = (byte[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == int.class) { int[] array = (int[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == short.class) { short[] array = (short[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == long.class) { long[] array = (long[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == double.class) { double[] array = (double[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } else if (componentType == float.class) { float[] array = (float[]) parameters; ret = new Object[array.length]; for (int i = 0; i < array.length; i++) { ret[i] = array[i]; } } return ret; } /** * @param list the original list * @param parameterType the resulting array type * @return the constructed array */ public static Object asPrimitiveArray(List list, Class parameterType) { Class arrayType = parameterType.getComponentType(); Object objArray = Array.newInstance(arrayType, list.size()); for (int i = 0; i < list.size(); i++) { Object obj = list.get(i); if (arrayType.isPrimitive()) { if (obj instanceof Integer) { Array.setInt(objArray, i, (Integer) obj); } else if (obj instanceof Double) { Array.setDouble(objArray, i, (Double) obj); } else if (obj instanceof Boolean) { Array.setBoolean(objArray, i, (Boolean) obj); } else if (obj instanceof Long) { Array.setLong(objArray, i, (Long) obj); } else if (obj instanceof Float) { Array.setFloat(objArray, i, (Float) obj); } else if (obj instanceof Character) { Array.setChar(objArray, i, (Character) obj); } else if (obj instanceof Byte) { Array.setByte(objArray, i, (Byte) obj); } else if (obj instanceof Short) { Array.setShort(objArray, i, (Short) obj); } } else { Array.set(objArray, i, obj); } } return objArray; } private static final Class[] PRIMITIVES = { byte.class, Byte.class, short.class, Short.class, int.class, Integer.class, long.class, Long.class, BigInteger.class, float.class, Float.class, double.class, Double.class, BigDecimal.class, Number.class, Object.class }; private static final int[][] PRIMITIVE_DISTANCE_TABLE = { // byte Byte short Short int Integer long Long BigInteger float Float double Double BigDecimal, Number, Object /* byte*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,}, /*Byte*/{1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,}, /*short*/{14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,}, /*Short*/{14, 15, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,}, /*int*/{14, 15, 12, 13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,}, /*Integer*/{14, 15, 12, 13, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,}, /*long*/{14, 15, 12, 13, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,}, /*Long*/{14, 15, 12, 13, 10, 11, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9,}, /*BigInteger*/{9, 10, 7, 8, 5, 6, 3, 4, 0, 14, 15, 12, 13, 11, 1, 2,}, /*float*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 0, 1, 2, 3, 4, 5, 6,}, /*Float*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 1, 0, 2, 3, 4, 5, 6,}, /*double*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 0, 1, 2, 3, 4,}, /*Double*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 1, 0, 2, 3, 4,}, /*BigDecimal*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 0, 1, 2,}, /*Number*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 0, 1,}, /*Object*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 1, 0,}, }; private static int getPrimitiveIndex(Class c) { for (byte i = 0; i < PRIMITIVES.length; i++) { if (PRIMITIVES[i] == c) return i; } return -1; } private static int getPrimitiveDistance(Class from, Class to) { // we know here that from!=to, so a distance of 0 is never valid // get primitive type indexes int fromIndex = getPrimitiveIndex(from); int toIndex = getPrimitiveIndex(to); if (fromIndex == -1 || toIndex == -1) return -1; return PRIMITIVE_DISTANCE_TABLE[toIndex][fromIndex]; } private static int getMaximumInterfaceDistance(Class c, Class interfaceClass) { // -1 means a mismatch if (c == null) return -1; // 0 means a direct match if (c == interfaceClass) return 0; Class[] interfaces = c.getInterfaces(); int max = -1; for (Class anInterface : interfaces) { int sub = getMaximumInterfaceDistance(anInterface, interfaceClass); // we need to keep the -1 to track the mismatch, a +1 // by any means could let it look like a direct match // we want to add one, because there is an interface between // the interface we search for and the interface we are in. if (sub != -1) sub++; // we are interested in the longest path only max = Math.max(max, sub); } // we do not add one for super classes, only for interfaces int superClassMax = getMaximumInterfaceDistance(c.getSuperclass(), interfaceClass); return Math.max(max, superClassMax); } private static long calculateParameterDistance(Class argument, CachedClass parameter) { /** * note: when shifting with 32 bit, you should only shift on a long. If you do * that with an int, then i==(i<<32), which means you loose the shift * information */ if (parameter.getTheClass() == argument) return 0; if (parameter.isInterface()) { return getMaximumInterfaceDistance(argument, parameter.getTheClass()) << INTERFACE_SHIFT; } long objectDistance = 0; if (argument != null) { long pd = getPrimitiveDistance(parameter.getTheClass(), argument); if (pd != -1) return pd << PRIMITIVE_SHIFT; // add one to dist to be sure interfaces are preferred objectDistance += PRIMITIVES.length + 1; Class clazz = ReflectionCache.autoboxType(argument); while (clazz != null) { if (clazz == parameter.getTheClass()) break; if (clazz == GString.class && parameter.getTheClass() == String.class) { objectDistance += 2; break; } clazz = clazz.getSuperclass(); objectDistance += 3; } } else { // choose the distance to Object if a parameter is null // this will mean that Object is preferred over a more // specific type Class clazz = parameter.getTheClass(); if (clazz.isPrimitive()) { objectDistance += 2; } else { while (clazz != Object.class) { clazz = clazz.getSuperclass(); objectDistance += 2; } } } return objectDistance << OBJECT_SHIFT; } public static long calculateParameterDistance(Class[] arguments, ParameterTypes pt) { CachedClass[] parameters = pt.getParameterTypes(); if (parameters.length == 0) return 0; long ret = 0; int noVargsLength = parameters.length - 1; // if the number of parameters does not match we have // a vargs usage // // case A: arguments.length<parameters.length // // In this case arguments.length is always equal to // noVargsLength because only the last parameter // might be a optional vargs parameter // // VArgs penalty: 1l // // case B: arguments.length>parameters.length // // In this case all arguments with a index bigger than // paramMinus1 are part of the vargs, so a // distance calculation needs to be done against // parameters[noVargsLength].getComponentType() // // VArgs penalty: 2l+arguments.length-parameters.length // // case C: arguments.length==parameters.length && // isAssignableFrom( parameters[noVargsLength], // arguments[noVargsLength] ) // // In this case we have no vargs, so calculate directly // // VArgs penalty: 0l // // case D: arguments.length==parameters.length && // !isAssignableFrom( parameters[noVargsLength], // arguments[noVargsLength] ) // // In this case we have a vargs case again, we need // to calculate arguments[noVargsLength] against // parameters[noVargsLength].getComponentType // // VArgs penalty: 2l // // This gives: VArgs_penalty(C)<VArgs_penalty(A) // VArgs_penalty(A)<VArgs_penalty(D) // VArgs_penalty(D)<VArgs_penalty(B) /** * In general we want to match the signature that allows us to use * as less arguments for the vargs part as possible. That means the * longer signature usually wins if both signatures are vargs, while * vargs looses always against a signature without vargs. * * A vs B : * def foo(Object[] a) {1} -> case B * def foo(a,b,Object[] c) {2} -> case A * assert foo(new Object(),new Object()) == 2 * --> A preferred over B * * A vs C : * def foo(Object[] a) {1} -> case B * def foo(a,b) {2} -> case C * assert foo(new Object(),new Object()) == 2 * --> C preferred over A * * A vs D : * def foo(Object[] a) {1} -> case D * def foo(a,Object[] b) {2} -> case A * assert foo(new Object()) == 2 * --> A preferred over D * * This gives C<A(); tmpClass = argClass = arguments[i].getClass(); for (; tmpClass != Object.class; tmpClass = tmpClass.getSuperclass()) { intfs.addAll(Arrays.asList(tmpClass.getInterfaces())); } if (intfs.contains(fallback)) { tmpCount++; } } } // all arg classes implement interface fallback, so use that as the array component type if (tmpCount == arguments.length - offset) { baseClass = fallback; } } Object result = makeArray(null, baseClass, arguments.length - offset); System.arraycopy(arguments, offset, result, 0, arguments.length - offset); return result; } public static Object makeArray(Object obj, Class secondary, int length) { Class baseClass = secondary; if (obj != null) { baseClass = obj.getClass(); } /*if (GString.class.isAssignableFrom(baseClass)) { baseClass = GString.class; }*/ return Array.newInstance(baseClass, length); } public static GroovyRuntimeException createExceptionText(String init, MetaMethod method, Object object, Object[] args, Throwable reason, boolean setReason) { return new GroovyRuntimeException( init + method + " on: " + object + " with arguments: " + InvokerHelper.toString(args) + " reason: " + reason, setReason ? reason : null); } protected static String getClassName(Object object) { if (object == null) return null; return (object instanceof Class) ? ((Class) object).getName() : object.getClass().getName(); } /** * Returns a callable object for the given method name on the object. * The object acts like a Closure in that it can be called, like a closure * and passed around - though really its a method pointer, not a closure per se. * * @param object the object containing the method * @param methodName the method of interest * @return the resulting closure-like method pointer */ public static Closure getMethodPointer(Object object, String methodName) { return new MethodClosure(object, methodName); } public static boolean isAssignableFrom(Class classToTransformTo, Class classToTransformFrom) { if (classToTransformTo == classToTransformFrom) return true; if (classToTransformFrom == null) return true; if (classToTransformTo == Object.class) return true; classToTransformTo = ReflectionCache.autoboxType(classToTransformTo); classToTransformFrom = ReflectionCache.autoboxType(classToTransformFrom); if (classToTransformTo == classToTransformFrom) return true; // note: there is no coercion for boolean and char. Range matters, precision doesn't if (classToTransformTo == Integer.class) { if (classToTransformFrom == Integer.class || classToTransformFrom == Short.class || classToTransformFrom == Byte.class || classToTransformFrom == BigInteger.class) return true; } else if (classToTransformTo == Double.class) { if (classToTransformFrom == Double.class || classToTransformFrom == Integer.class || classToTransformFrom == Long.class || classToTransformFrom == Short.class || classToTransformFrom == Byte.class || classToTransformFrom == Float.class || classToTransformFrom == BigDecimal.class || classToTransformFrom == BigInteger.class) return true; } else if (classToTransformTo == BigDecimal.class) { if (classToTransformFrom == Double.class || classToTransformFrom == Integer.class || classToTransformFrom == Long.class || classToTransformFrom == Short.class || classToTransformFrom == Byte.class || classToTransformFrom == Float.class || classToTransformFrom == BigDecimal.class || classToTransformFrom == BigInteger.class) return true; } else if (classToTransformTo == BigInteger.class) { if (classToTransformFrom == Integer.class || classToTransformFrom == Long.class || classToTransformFrom == Short.class || classToTransformFrom == Byte.class || classToTransformFrom == BigInteger.class) return true; } else if (classToTransformTo == Long.class) { if (classToTransformFrom == Long.class || classToTransformFrom == Integer.class || classToTransformFrom == Short.class || classToTransformFrom == Byte.class) return true; } else if (classToTransformTo == Float.class) { if (classToTransformFrom == Float.class || classToTransformFrom == Integer.class || classToTransformFrom == Long.class || classToTransformFrom == Short.class || classToTransformFrom == Byte.class) return true; } else if (classToTransformTo == Short.class) { if (classToTransformFrom == Short.class || classToTransformFrom == Byte.class) return true; } else if (classToTransformTo == String.class) { if (classToTransformFrom == String.class || GString.class.isAssignableFrom(classToTransformFrom)) { return true; } } return ReflectionCache.isAssignableFrom(classToTransformTo, classToTransformFrom); } public static boolean isGenericSetMethod(MetaMethod method) { return (method.getName().equals("set")) && method.getParameterTypes().length == 2; } protected static boolean isSuperclass(Class clazz, Class superclass) { while (clazz != null) { if (clazz == superclass) return true; clazz = clazz.getSuperclass(); } return false; } public static boolean parametersAreCompatible(Class[] arguments, Class[] parameters) { if (arguments.length != parameters.length) return false; for (int i = 0; i < arguments.length; i++) { if (!isAssignableFrom(parameters[i], arguments[i])) return false; } return true; } public static void logMethodCall(Object object, String methodName, Object[] arguments) { String className = getClassName(object); String logname = "methodCalls." + className + "." + methodName; Logger objLog = Logger.getLogger(logname); if (!objLog.isLoggable(Level.FINER)) return; StringBuffer msg = new StringBuffer(methodName); msg.append("("); if (arguments != null) { for (int i = 0; i < arguments.length;) { msg.append(normalizedValue(arguments[i])); if (++i < arguments.length) { msg.append(","); } } } msg.append(")"); objLog.logp(Level.FINER, className, msg.toString(), "called from MetaClass.invokeMethod"); } protected static String normalizedValue(Object argument) { String value; try { value = argument.toString(); if (value.length() > MAX_ARG_LEN) { value = value.substring(0, MAX_ARG_LEN - 2) + ".."; } if (argument instanceof String) { value = "\'" + value + "\'"; } } catch (Exception e) { value = shortName(argument); } return value; } protected static String shortName(Object object) { if (object == null || object.getClass() == null) return "unknownClass"; String name = getClassName(object); if (name == null) return "unknownClassName"; // *very* defensive... int lastDotPos = name.lastIndexOf('.'); if (lastDotPos < 0 || lastDotPos >= name.length() - 1) return name; return name.substring(lastDotPos + 1); } public static Class[] wrap(Class[] classes) { Class[] wrappedArguments = new Class[classes.length]; for (int i = 0; i < wrappedArguments.length; i++) { Class c = classes[i]; if (c == null) continue; if (c.isPrimitive()) { if (c == Integer.TYPE) { c = Integer.class; } else if (c == Byte.TYPE) { c = Byte.class; } else if (c == Long.TYPE) { c = Long.class; } else if (c == Double.TYPE) { c = Double.class; } else if (c == Float.TYPE) { c = Float.class; } } else if (isSuperclass(c, GString.class)) { c = String.class; } wrappedArguments[i] = c; } return wrappedArguments; } public static boolean sameClasses(Class[] params, Object[] arguments, boolean weakNullCheck) { if (params.length != arguments.length) return false; for (int i = params.length - 1; i >= 0; i--) { Object arg = arguments[i]; if (arg == null) { if (!weakNullCheck) return false; } else { if (params[i] != arg.getClass() && (!(arg instanceof Wrapper) || params[i] != ((Wrapper) arg).getType())) return false; } } return true; } public static boolean sameClasses(Class[] params, Object[] arguments) { if (params.length != arguments.length) return false; for (int i = params.length - 1; i >= 0; i--) { Object arg = arguments[i]; if (arg == null) { if (params[i] != null) return false; } else { if (params[i] != arg.getClass() && !(arg instanceof Wrapper && params[i] == ((Wrapper) arg).getType())) return false; } } return true; } public static boolean sameClasses(Class[] params) { if (params.length != 0) return false; return true; } public static boolean sameClasses(Class[] params, Object arg1) { if (params.length != 1) return false; if (arg1 == null || (params[0] != arg1.getClass() && (!(arg1 instanceof Wrapper) || params[0] != ((Wrapper) arg1).getType()))) return false; return true; } public static boolean sameClasses(Class[] params, Object arg1, Object arg2) { if (params.length != 2) return false; if (arg1 == null || (params[0] != arg1.getClass() && (!(arg1 instanceof Wrapper) || params[0] != ((Wrapper) arg1).getType()))) return false; if (arg2 == null || (params[1] != arg2.getClass() && (!(arg2 instanceof Wrapper) || params[1] != ((Wrapper) arg2).getType()))) return false; return true; } public static boolean sameClasses(Class[] params, Object arg1, Object arg2, Object arg3) { if (params.length != 3) return false; if (arg1 == null || (params[0] != arg1.getClass() && (!(arg1 instanceof Wrapper) || params[0] != ((Wrapper) arg1).getType()))) return false; if (arg2 == null || (params[1] != arg2.getClass() && (!(arg2 instanceof Wrapper) || params[1] != ((Wrapper) arg2).getType()))) return false; if (arg3 == null || (params[2] != arg3.getClass() && (!(arg3 instanceof Wrapper) || params[2] != ((Wrapper) arg3).getType()))) return false; return true; } public static boolean sameClasses(Class[] params, Object arg1, Object arg2, Object arg3, Object arg4) { if (params.length != 4) return false; if (arg1 == null || (params[0] != arg1.getClass() && (!(arg1 instanceof Wrapper) || params[0] != ((Wrapper) arg1).getType()))) return false; if (arg2 == null || (params[1] != arg2.getClass() && (!(arg2 instanceof Wrapper) || params[1] != ((Wrapper) arg2).getType()))) return false; if (arg3 == null || (params[2] != arg3.getClass() && (!(arg3 instanceof Wrapper) || params[2] != ((Wrapper) arg3).getType()))) return false; if (arg4 == null || (params[3] != arg4.getClass() && (!(arg4 instanceof Wrapper) || params[3] != ((Wrapper) arg4).getType()))) return false; return true; } public static boolean sameClass(Class[] params, Object arg) { return !(arg == null || (params[0] != arg.getClass() && (!(arg instanceof Wrapper) || params[0] != ((Wrapper) arg).getType()))); } public static Class[] castArgumentsToClassArray(Object[] argTypes) { if (argTypes == null) return EMPTY_CLASS_ARRAY; Class[] classes = new Class[argTypes.length]; for (int i = 0; i < argTypes.length; i++) { Object argType = argTypes[i]; if (argType instanceof Class) { classes[i] = (Class) argType; } else if (argType == null) { classes[i] = null; } else { // throw new IllegalArgumentException("Arguments to method [respondsTo] must be of type java.lang.Class!"); classes[i] = argType.getClass(); } } return classes; } public static void unwrap(Object[] arguments) { // // Temp code to ignore wrapped parameters // The New MOP will deal with these properly // for (int i = 0; i != arguments.length; i++) { if (arguments[i] instanceof Wrapper) { arguments[i] = ((Wrapper) arguments[i]).unwrap(); } } } } Other Groovy examples (source code examples)Here is a short list of links related to this Groovy MetaClassHelper.java source code file: |
... this post is sponsored by my books ... | |
![]() #1 New Release! |
![]() FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.