metaclasshelper.java

来自「Groovy动态语言 运行在JVM中的动态语言 可以方便的处理业务逻辑变化大的业」· Java 代码 · 共 985 行 · 第 1/3 页

JAVA
985
字号
/*
 * Copyright 2005 John G. Wilson
 *
 * 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 java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.wrappers.Wrapper;

/**
 * @author John Wilson
 * @author Jochen Theodorou
 */
public class MetaClassHelper {

    public static final Object[] EMPTY_ARRAY = {};
    public static Class[] EMPTY_TYPE_ARRAY = {};
    protected 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;
    
    public static boolean accessibleToConstructor(final Class at, final Constructor constructor) {
        boolean accessible = false;
        if (Modifier.isPublic(constructor.getModifiers())) {
            accessible = true;
        }
        else if (Modifier.isPrivate(constructor.getModifiers())) {
            accessible = at.getName().equals(constructor.getName());
        }
        else if ( Modifier.isProtected(constructor.getModifiers()) ) {
            if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) {
                accessible = true;
            }
            else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) {
                accessible = false;
            }
            else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) {
                accessible = false;
            }
            else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) {
                accessible = true;
            }
            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 {
            if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null ) {
                accessible = true;
            }
            else if ( at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null ) {
                accessible = false;
            }
            else if ( at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null ) {
                accessible = false;
            }
            else if ( at.getPackage().equals(constructor.getDeclaringClass().getPackage()) ) {
                accessible = true;
            }
        }
        return accessible;
    }
    
    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] = new Boolean(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] = new Character(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] = new Byte(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] = new Integer(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] = new Short(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] = new Long(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] = new Double(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] = new Float(array[i]);
            }
        }
        
        return ret;
    }
    
    
    /**
     * @param list
     * @param parameterType
     */
    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).intValue());
                }
                else if (obj instanceof Double) {
                    Array.setDouble(objArray, i, ((Double) obj).doubleValue());
                }
                else if (obj instanceof Boolean) {
                    Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
                }
                else if (obj instanceof Long) {
                    Array.setLong(objArray, i, ((Long) obj).longValue());
                }
                else if (obj instanceof Float) {
                    Array.setFloat(objArray, i, ((Float) obj).floatValue());
                }
                else if (obj instanceof Character) {
                    Array.setChar(objArray, i, ((Character) obj).charValue());
                }
                else if (obj instanceof Byte) {
                    Array.setByte(objArray, i, ((Byte) obj).byteValue());
                }
                else if (obj instanceof Short) {
                    Array.setShort(objArray, i, ((Short) obj).shortValue());
                }
            }
            else {
                Array.set(objArray, i, obj);
            }
        }
        return objArray;
    }
    
    protected static Class autoboxType(Class type) {
        if (type.isPrimitive()) {
            if (type == int.class) {
                return Integer.class;
            }
            else if (type == double.class) {
                return Double.class;
            }
            else if (type == long.class) {
                return Long.class;
            }
            else if (type == boolean.class) {
                return Boolean.class;
            }
            else if (type == float.class) {
                return Float.class;
            }
            else if (type == char.class) {
                return Character.class;
            }
            else if (type == byte.class) {
                return Byte.class;
            }
            else if (type == short.class) {
                return Short.class;
            }
        }
        return type;
    }
    
    private static 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 int[][] primitiveDistanceTable = {
        //              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*/{ 14,     15,     12,     13,     10,     11,         8,      9,      0,          1,      2,      3,      4,      5,          6,      7,          },
        /*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,          },
        /*Numer*/{      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 primitiveDistanceTable[toIndex][fromIndex];
    }
    
    private static int getMaximumInterfaceDistance(Class c, Class interfaceClass) {
        if (c==interfaceClass) return 0;
        Class[] interfaces = c.getInterfaces();
        int max = 0;
        for (int i=0; i<interfaces.length; i++) {
            int sub = 0;
            if (interfaces[i].isAssignableFrom(c)) {
                sub = 1+ getMaximumInterfaceDistance(interfaces[i],interfaceClass);
            }
            max = Math.max(max,sub);
        }
        return max;
    }
    
    public static long calculateParameterDistance(Class[] arguments, Class[] parameters) {
        int objectDistance=0, interfaceDistance=0;
        for (int i=0; i<arguments.length; i++) {
            if (parameters[i]==arguments[i]) continue;
            
            if (parameters[i].isInterface()) {
                objectDistance+=primitives.length;
                interfaceDistance += getMaximumInterfaceDistance(arguments[i],parameters[i]);
                continue;
            }
            
            if (arguments[i]!=null) {
                int pd = getPrimitiveDistance(parameters[i],arguments[i]);
                if (pd!=-1) {
                    objectDistance += pd;
                    continue;
                }
                
                // add one to dist to be sure interfaces are prefered
                objectDistance += primitives.length+1;
                Class clazz = autoboxType(arguments[i]);
                while (clazz!=null) {
                    if (clazz==parameters[i]) break;
                    if (clazz==GString.class && parameters[i]==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 prefered over a more
                // specific type
                // remove one to dist to be sure Object is prefered
                objectDistance--;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?