All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.groovy.reflection.CachedClass Maven / Gradle / Ivy

There is a newer version: 1.5.8
Show newest version
/*
 * 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 groovy.lang.MetaClass;
import groovy.lang.MetaMethod;
import org.codehaus.groovy.classgen.BytecodeHelper;
import org.codehaus.groovy.runtime.Reflector;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;

import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

/**
 * @author Alex.Tkachman
 */
public class CachedClass {
    private CachedClass cachedSuperClass;

    private static final MetaMethod[] EMPTY = new MetaMethod[0];

    int hashCode;

    private Reflector reflector;

    private volatile Object metaClassForClass; // either MetaClass or SoftReference

    private CachedField[] fields;
    private CachedConstructor[] constructors;
    private CachedMethod[] methods;
    private final Class cachedClass;
    private MetaMethod[] newMetaMethods = EMPTY;
    public  CachedMethod [] mopMethods;
    public static final CachedClass[] EMPTY_ARRAY = new CachedClass[0];
    private Object staticMetaClassField;
    private static final Object NONE = new Object();

    public Set getInterfaces() {
        if (interfaces == null)  {
            interfaces = new HashSet (0);

            if (getCachedClass().isInterface())
              interfaces.add(this);

            Class[] classes = getCachedClass().getInterfaces();
            for (int i = 0; i < classes.length; i++) {
                final CachedClass aClass = ReflectionCache.getCachedClass(classes[i]);
                if (!interfaces.contains(aClass))
                  interfaces.addAll(aClass.getInterfaces());
            }

            final CachedClass superClass = getCachedSuperClass();
            if (superClass != null)
              interfaces.addAll(superClass.getInterfaces());
        }
        return interfaces;
    }

    private Set ownInterfaces;

    public Set getOwnInterfaces() {
        if (ownInterfaces == null)  {
            ownInterfaces = new HashSet (0);

            if (getCachedClass().isInterface())
              ownInterfaces.add(this);

            Class[] classes = getCachedClass().getInterfaces();
            for (int i = 0; i < classes.length; i++) {
                final CachedClass aClass = ReflectionCache.getCachedClass(classes[i]);
                if (!ownInterfaces.contains(aClass))
                  ownInterfaces.addAll(aClass.getInterfaces());
            }

            final CachedClass superClass = getCachedSuperClass();
            if (superClass != null)
              ownInterfaces.addAll(superClass.getInterfaces());
        }
        return ownInterfaces;
    }

    private Set interfaces;

    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);

        for (Iterator it = getInterfaces().iterator(); it.hasNext(); ) {
            CachedClass inf = (CachedClass) it.next();
            ReflectionCache.isAssignableFrom(klazz, inf.cachedClass);
        }

        for (CachedClass cur = this; cur != null; cur = cur.getCachedSuperClass()) {
            ReflectionCache.setAssignableFrom(cur.cachedClass, klazz);
        }
    }

    public synchronized CachedClass getCachedSuperClass() {
        if (cachedSuperClass == null) {
            if (!isArray)
              cachedSuperClass = ReflectionCache.getCachedClass(getCachedClass().getSuperclass());
            else
              if (cachedClass.getComponentType().isPrimitive() || cachedClass.getComponentType() == Object.class)
                cachedSuperClass = ReflectionCache.OBJECT_CLASS;
              else
                cachedSuperClass = ReflectionCache.OBJECT_ARRAY_CLASS;
        }

        return cachedSuperClass;
    }

    public synchronized CachedMethod[] getMethods() {
        if (methods == null) {
            final Method[] declaredMethods = (Method[])
               AccessController.doPrivileged(new PrivilegedAction/**/() {
                   public /*Method[]*/ Object run() {
                       return getCachedClass().getDeclaredMethods();
                   }
               });
            ArrayList methods = new ArrayList(declaredMethods.length);
            ArrayList mopMethods = new ArrayList(declaredMethods.length);
            for (int i = 0; i != declaredMethods.length; ++i) {
                final CachedMethod cachedMethod = new CachedMethod(this, declaredMethods[i]);
                final String name = cachedMethod.getName();

                if (name.indexOf('+') >= 0) {
                    // Skip Synthetic methods inserted by JDK 1.5 compilers and later
                    continue;
                } /*else if (Modifier.isAbstract(reflectionMethod.getModifiers())) {
                   continue;
                }*/

                if (name.startsWith("this$") || name.startsWith("super$"))
                  mopMethods.add(cachedMethod);
                else
                  methods.add(cachedMethod);
            }
            this.methods = (CachedMethod[]) methods.toArray(new CachedMethod[methods.size()]);
            Arrays.sort(this.methods);

            final CachedClass superClass = getCachedSuperClass();
            if (superClass != null) {
                superClass.getMethods();
                final CachedMethod[] superMopMethods = superClass.mopMethods;
                for (int i = 0; i != superMopMethods.length; ++i)
                  mopMethods.add(superMopMethods[i]);
            }
            this.mopMethods = (CachedMethod[]) mopMethods.toArray(new CachedMethod[mopMethods.size()]);
            Arrays.sort(this.mopMethods, CachedMethodComparatorByName.INSTANCE);
        }
        return methods;
    }

    public synchronized CachedField[] getFields() {
        if (fields == null) {

            final Field[] declaredFields = (Field[])
               AccessController.doPrivileged(new PrivilegedAction/**/() {
                   public /*Field[]*/ Object run() {
                       return getCachedClass().getDeclaredFields();
                   }
               });
            fields = new CachedField[declaredFields.length];
            for (int i = 0; i != fields.length; ++i)
                fields[i] = new CachedField(this, declaredFields[i]);
        }
        return fields;
    }

    public synchronized CachedConstructor[] getConstructors() {
        if (constructors == null) {
            final Constructor[] declaredConstructors = (Constructor[])
               AccessController.doPrivileged(new PrivilegedAction/**/() {
                   public /*Constructor[]*/ Object run() {
                       return getCachedClass().getDeclaredConstructors();
                   }
               });
            constructors = new CachedConstructor[declaredConstructors.length];
            for (int i = 0; i != constructors.length; ++i)
                constructors[i] = new CachedConstructor(this, declaredConstructors[i]);
        }
        return constructors;
    }

    public CachedMethod searchMethods(String name, CachedClass[] parameterTypes) {
        CachedMethod[] methods = getMethods();

        CachedMethod res = null;
        for (int i = 0; i < methods.length; i++) {
            CachedMethod m = methods[i];
            if (m.getName().equals(name)
                    && ReflectionCache.arrayContentsEq(parameterTypes, m.getParameterTypes())
                    && (res == null || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return res;
    }

    public int getModifiers() {
        return modifiers;
    }

    public Object coerceArgument(Object argument) {
        return argument;
    }
    
    public int getSuperClassDistance() {
        synchronized (getCachedClass()) {
            if (distance == -1) {
                int distance = 0;
                for (Class klazz= getCachedClass(); klazz != null; klazz = klazz.getSuperclass()) {
                    distance++;
                }
                this.distance = distance;
            }
            return distance;
        }
    }

    public int hashCode() {
        if (hashCode == 0) {
          hashCode = super.hashCode();
          if (hashCode == 0)
            hashCode = 0xcafebebe;
        }
        return hashCode;
    }

    public boolean isPrimitive() {
        return isPrimitive;
    }

    public boolean isVoid() {
        return getCachedClass() == void.class;
    }

    public void box(BytecodeHelper helper) {
        helper.box(getCachedClass());
    }

    public void unbox(BytecodeHelper helper) {
        helper.unbox(getCachedClass());
    }

    public boolean isInterface() {
        return isInterface;
    }

    public void doCast(BytecodeHelper helper) {
        helper.doCast(getCachedClass());
    }

    public String getName() {
        return getCachedClass().getName();
    }

    public String getTypeDescription() {
        return BytecodeHelper.getTypeDescription(getCachedClass());
    }

    public synchronized Reflector getReflector() {
        /*if (reflector == null) {
            final MetaClassRegistry metaClassRegistry = MetaClassRegistryImpl.getInstance(MetaClassRegistryImpl.LOAD_DEFAULT);
            reflector = ((MetaClassRegistryImpl)metaClassRegistry).loadReflector(getCachedClass(), Arrays.asList(getMethods()));
        }*/
        return reflector;
    }

    public final Class getCachedClass() {
        return cachedClass;
    }

    public MetaMethod[] getNewMetaMethods() {
        return newMetaMethods;
    }

    public void setNewMopMethods(ArrayList arr) {
        newMetaMethods = (MetaMethod[]) arr.toArray(new MetaMethod[arr.size()]);
    }

    public synchronized void setStaticMetaClassField(MetaClass mc) {
        if (staticMetaClassField == NONE)
          return;
        if (staticMetaClassField == null) {
            final CachedField[] cachedFields = getFields();
            for (int i = 0; i < cachedFields.length; i++) {
                CachedField cachedField = cachedFields[i];
                if (cachedField.getName().startsWith("$staticMetaClass") && cachedField.getType() == MetaClass.class && cachedField.isStatic()) {
                    staticMetaClassField = cachedField;
                    break;
                }
            }
        }
        if (staticMetaClassField == null) {
          staticMetaClassField = NONE;
          return;
        }

        ((CachedField)staticMetaClassField).setProperty(null,mc);
    }

    public MetaClass getMetaClassForClass() {
        Object cur = metaClassForClass;
        if (cur == null)
            return null;
        if (cur instanceof SoftReference) {
            SoftReference softReference = (SoftReference) cur;
            return (MetaClass) softReference.get();
        }
        return (MetaClass) metaClassForClass;
    }

    public void setMetaClassForClass(MetaClass metaClassForClass, boolean isConst) {
        if (isConst || metaClassForClass == null)
            this.metaClassForClass = metaClassForClass;
        else
            this.metaClassForClass = new SoftReference(metaClassForClass);
        setStaticMetaClassField(metaClassForClass);
    }

    public boolean isAssignableFrom(Class argument) {
        return argument == null || ReflectionCache.isAssignableFrom(getCachedClass(), argument);
    }

    boolean isDirectlyAssignable(Object argument) {
        return ReflectionCache.isAssignableFrom(getCachedClass(), argument.getClass());
    }

    public static class CachedMethodComparatorByName implements Comparator {
        public static final Comparator INSTANCE = new CachedMethodComparatorByName();

        public int compare(Object o1, Object o2) {
            return ((CachedMethod)o1).getName().compareTo(((CachedMethod)o2).getName());
        }
    }

    public static class CachedMethodComparatorWithString implements Comparator {
        public static final Comparator INSTANCE = new CachedMethodComparatorWithString();

        public int compare(Object o1, Object o2) {
            return ((CachedMethod)o1).getName().compareTo((String)o2);
        }
    }

    public String toString() {
        return cachedClass.toString();
    }

    public static class NumberCachedClass extends CachedClass {

        NumberCachedClass(Class klazz) {
            super(klazz);
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof Number) {
                return coerceNumber(argument);
            }
            return argument;

        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return classToTransformFrom == null
                    || Number.class.isAssignableFrom(classToTransformFrom)
                    || classToTransformFrom == Byte.TYPE
                    || classToTransformFrom == Short.TYPE
                    || classToTransformFrom == Integer.TYPE
                    || classToTransformFrom == Long.TYPE
                    || classToTransformFrom == Float.TYPE
                    || classToTransformFrom == Double.TYPE
                        ;
        }

        private Object coerceNumber(Object argument) {
            Class param = getCachedClass();
            if (param == Byte.class /*|| param == Byte.TYPE*/) {
                argument = new Byte(((Number) argument).byteValue());
            } else if (param == BigInteger.class) {
                argument = new BigInteger(String.valueOf((Number) argument));
            }

            return argument;
        }
    }

    public static class IntegerCachedClass extends NumberCachedClass {  // int, Integer
        IntegerCachedClass(Class klazz) {
            super(klazz);
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof Integer) {
                return argument;
            }

            return new Integer(((Number) argument).intValue());
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument == null || argument instanceof Integer;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return classToTransformFrom == null
                || classToTransformFrom == Integer.class
                || classToTransformFrom == Short.class
                || classToTransformFrom == Byte.class
                || classToTransformFrom == BigInteger.class
                || classToTransformFrom == Integer.TYPE
                || classToTransformFrom == Short.TYPE
                || classToTransformFrom == Byte.TYPE;
        }
    }

    public static class BigIntegerCachedClass extends NumberCachedClass {
        BigIntegerCachedClass(Class klazz) {
            super(klazz);
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof BigInteger;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return classToTransformFrom == null
                || classToTransformFrom == Integer.class
                || classToTransformFrom == Short.class
                || classToTransformFrom == Byte.class
                || classToTransformFrom == BigInteger.class
                || classToTransformFrom == Long.class
                || classToTransformFrom == Integer.TYPE
                || classToTransformFrom == Short.TYPE
                || classToTransformFrom == Byte.TYPE
                || classToTransformFrom == Long.TYPE;
        }
    }

    public static class ByteCachedClass extends NumberCachedClass {
        ByteCachedClass(Class klazz) {
            super(klazz);
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof Byte) {
                return argument;
            }

            return new Byte(((Number) argument).byteValue());
        }

        public boolean isDirectlyAssignable(Object argument) {
            return argument instanceof Short;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return classToTransformFrom == null
                || classToTransformFrom == Byte.class
                || classToTransformFrom == Byte.TYPE;
        }
    }

    public static class ShortCachedClass extends NumberCachedClass {
        ShortCachedClass(Class klazz) {
            super(klazz);
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof Short) {
                return argument;
            }

            return new Short(((Number) argument).shortValue());
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof Short;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return classToTransformFrom == null
                || classToTransformFrom == Short.class
                || classToTransformFrom == Byte.class
                || classToTransformFrom == Short.TYPE
                || classToTransformFrom == Byte.TYPE;
        }
    }

    public static class LongCachedClass extends NumberCachedClass {
        LongCachedClass(Class klazz) {
            super(klazz);
        }


        public Object coerceArgument(Object argument) {
            if (argument instanceof Long) {
                return argument;
            }

            return new Long(((Number) argument).longValue());
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof Long;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return  classToTransformFrom == null
                    || classToTransformFrom == Integer.class
                    || classToTransformFrom == Long.class
                    || classToTransformFrom == Short.class
                    || classToTransformFrom == Byte.class
                    || classToTransformFrom == Integer.TYPE
                    || classToTransformFrom == Long.TYPE
                    || classToTransformFrom == Short.TYPE
                    || classToTransformFrom == Byte.TYPE;
        }
    }

    public static class FloatCachedClass extends NumberCachedClass {
        FloatCachedClass(Class klazz) {
            super(klazz);
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof Float) {
                return argument;
            }

            Float res = new Float(((Number) argument).floatValue());
            if (argument instanceof BigDecimal && res.isInfinite()) {
                throw new IllegalArgumentException(Float.class + " out of range while converting from BigDecimal");
            }
            return res;
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof Float;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return  classToTransformFrom == null
                    || classToTransformFrom == Float.class
                    || classToTransformFrom == Integer.class
                    || classToTransformFrom == Long.class
                    || classToTransformFrom == Short.class
                    || classToTransformFrom == Byte.class
                    || classToTransformFrom == Float.TYPE
                    || classToTransformFrom == Integer.TYPE
                    || classToTransformFrom == Long.TYPE
                    || classToTransformFrom == Short.TYPE
                    || classToTransformFrom == Byte.TYPE;
        }
    }

    public static class DoubleCachedClass extends NumberCachedClass { // Double, double
        DoubleCachedClass(Class klazz) {
            super(klazz);
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof Double;
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof Double) {
                return argument;
            }

            Double res = new Double(((Number) argument).doubleValue());
            if (argument instanceof BigDecimal && res.isInfinite()) {
                throw new IllegalArgumentException(Double.class + " out of range while converting from BigDecimal");
            }
            return res;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return  classToTransformFrom == null
                    || classToTransformFrom == Double.class
                    || classToTransformFrom == Integer.class
                    || classToTransformFrom == Long.class
                    || classToTransformFrom == Short.class
                    || classToTransformFrom == Byte.class
                    || classToTransformFrom == Float.class
                    || classToTransformFrom == Double.TYPE
                    || classToTransformFrom == Integer.TYPE
                    || classToTransformFrom == Long.TYPE
                    || classToTransformFrom == Short.TYPE
                    || classToTransformFrom == Byte.TYPE
                    || classToTransformFrom == Float.TYPE
                    || classToTransformFrom == BigDecimal.class
                    || classToTransformFrom == BigInteger.class;
        }
    }

    public static class BigDecimalCachedClass extends DoubleCachedClass {
        BigDecimalCachedClass(Class klazz) {
            super(klazz);
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof BigDecimal;
        }

        public Object coerceArgument(Object argument) {
            if (argument instanceof BigDecimal) {
                return argument;
            }

            return new BigDecimal(((Number) argument).doubleValue());
        }
    }

    public static class StringCachedClass extends CachedClass {
        private static final Class STRING_CLASS = String.class;
        private static final Class GSTRING_CLASS = GString.class;

        StringCachedClass() {
            super(STRING_CLASS);
        }

        boolean isDirectlyAssignable(Object argument) {
            return argument instanceof String;
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return  classToTransformFrom == null
                  || classToTransformFrom == STRING_CLASS
                  || ReflectionCache.isAssignableFrom(GSTRING_CLASS,classToTransformFrom);
        }

        public Object coerceArgument(Object argument) {
            return argument instanceof GString ? argument.toString() : argument;
        }
    }

    public static class BooleanCachedClass extends CachedClass {
        BooleanCachedClass(Class klazz) {
            super(klazz);
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return classToTransformFrom == null
                  || classToTransformFrom == Boolean.class
                  || classToTransformFrom == Boolean.TYPE;
        }
    }

    public static class CharacterCachedClass extends CachedClass {
        public CharacterCachedClass(Class klazz) {
            super(klazz);
        }

        public boolean isAssignableFrom(Class classToTransformFrom) {
            return  classToTransformFrom == null
                  ||classToTransformFrom == Character.class
                  ||classToTransformFrom == Character.TYPE;
        }
    }

    public static class ArrayCachedClass extends CachedClass {
        ArrayCachedClass(Class klazz) {
            super(klazz);
        }

        public Object coerceArgument(Object argument) {
            Class argumentClass = argument.getClass();
            if (argumentClass.getName().charAt(0) != '[') return argument;
            Class argumentComponent = argumentClass.getComponentType();

            Class paramComponent = getCachedClass().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;
            } else if (paramComponent==Object.class && argumentComponent.isPrimitive()){
                argument = DefaultTypeTransformation.primitiveArrayBox(argument);
            }
            return argument;
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy