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

org.ibatis.cglib.ClassInfo Maven / Gradle / Ivy

Go to download

The jBATIS persistence framework will help you to significantly reduce the amount of Java code that you normally need to access a relational database. iBATIS simply maps JavaBeans to SQL statements using a very simple XML descriptor.

The newest version!
/*
 *  Copyright 2004 Clinton Begin
 *
 *  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.ibatis.cglib;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * This class represents a cached set of class definition information that allows for easy mapping between property
 * names and getter/setter methods.
 */
public class ClassInfo {

    private static boolean cacheEnabled = true;
    private static final Map, ClassInfo> CLASS_INFO_MAP = new HashMap, ClassInfo>();
    private static final Map, ClassInfo> CLASS_INFO_MAP_STRICT = new HashMap, ClassInfo>();

    private String className;
    private boolean strict;
    private Map setMethods = new LinkedHashMap();
    private Map getMethods = new LinkedHashMap();
    private Map> setTypes = new LinkedHashMap>();
    private Map> getTypes = new LinkedHashMap>();
    private Constructor defaultConstructor;
    private List propertyNames = new ArrayList();

    private ClassInfo(Class clazz, boolean strict) {
        if (clazz.getEnclosingClass() != null && !Modifier.isStatic(clazz.getModifiers())) {
            throw new RuntimeException("Not top-level or static class: " + clazz.getName());
        }
        if (!Modifier.isPublic(clazz.getModifiers())) {
            throw new RuntimeException("Not public class: " + clazz.getName());
        }
        this.strict = strict;
        className = clazz.getName();
        addDefaultConstructor(clazz);
        addGetMethods(clazz);
        addSetMethods(clazz);
        addFields(clazz);
        if (strict) {
            Set set = new LinkedHashSet();
            set.addAll(getMethods.keySet());
            set.retainAll(setMethods.keySet());
            HashMap _setMethods = new LinkedHashMap();
            HashMap _getMethods = new LinkedHashMap();
            HashMap> _setTypes = new LinkedHashMap>();
            HashMap> _getTypes = new LinkedHashMap>();
            for (String key : set) {
                if (getTypes.get(key) != setTypes.get(key)) {
                    continue;
                }
                _getMethods.put(key, getMethods.get(key));
                _setMethods.put(key, setMethods.get(key));
                _getTypes.put(key, getTypes.get(key));
                _setTypes.put(key, setTypes.get(key));
            }
            getMethods = _getMethods;
            setMethods = _setMethods;
            getTypes = _getTypes;
            setTypes = _setTypes;
            Set attrs = new HashSet();
            for (String key : _getMethods.keySet()) {
                attrs.add(_getMethods.get(key).getName());
            }
            propertyNames.addAll(attrs);
        } else {
            Set attrs = new HashSet();
            for (String key : getMethods.keySet()) {
                attrs.add(getMethods.get(key).getName());
            }
            for (String key : setMethods.keySet()) {
                attrs.add(setMethods.get(key).getName());
            }
            propertyNames.addAll(attrs);
        }
        Collections.sort(propertyNames, new Comparator() {
            public int compare(String o1, String o2) {
                return o1.toLowerCase(Locale.ENGLISH).compareTo(o2.toLowerCase(Locale.ENGLISH));
            }
        });
    }

    public List getPropertyNames() {
        return Collections.unmodifiableList(propertyNames);
    }

    private void addDefaultConstructor(Class clazz) {
        try {
            Constructor constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            this.defaultConstructor = constructor;
        } catch (Exception e) {
        }
    }

    private void addGetMethods(Class cls) {
        Method[] methods = getClassMethods(cls);
        out: for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            for (Class e : method.getExceptionTypes()) {
                if (Exception.class.isAssignableFrom(e) && !RuntimeException.class.isAssignableFrom(e)) {
                    continue out;
                }
            }
            String name = method.getName();
            if (name.startsWith("get") && name.length() > 3) {
                if (method.getParameterTypes().length == 0) {
                    name = name.substring(3);
                    addGetMethod(dropCase(name), method);
                }
            } else if (name.startsWith("is") && name.length() > 2) {
                if (method.getParameterTypes().length == 0
                    && (method.getReturnType() == Boolean.class || method.getReturnType() == boolean.class)) {
                    name = name.substring(2);
                    addGetMethod(dropCase(name), method);
                }
            }
        }
    }

    void addGetMethod(String name, Method method) {
        MethodInvoker mi = new MethodInvoker(name, method, false);
        Class rt = method.getReturnType();
        // getMethods.put(name, mi);
        // getTypes.put(name, rt);
        {
            getMethods.put(name.toLowerCase(Locale.ENGLISH), mi);
            getTypes.put(name.toLowerCase(Locale.ENGLISH), rt);
        }
    }

    private void addSetMethods(Class cls) {
        Method[] methods = getClassMethods(cls);
        out: for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            for (Class e : method.getExceptionTypes()) {
                if (Exception.class.isAssignableFrom(e) && !RuntimeException.class.isAssignableFrom(e)) {
                    continue out;
                }
            }
            String name = method.getName();
            if (name.startsWith("set") && name.length() > 3) {
                if (method.getParameterTypes().length == 1) {
                    name = name.substring(3);
                    addSetMethod(dropCase(name), method);
                }
            }
        }
    }

    void addSetMethod(String name, Method method) {
        MethodInvoker mi = new MethodInvoker(name, method, true);
        Class pt = method.getParameterTypes()[0];
        // setMethods.put(name, mi);
        // setTypes.put(name, pt);
        {
            setMethods.put(name.toLowerCase(Locale.ENGLISH), mi);
            setTypes.put(name.toLowerCase(Locale.ENGLISH), pt);
        }
    }

    private void addFields(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (Modifier.isFinal(field.getModifiers())) {
                continue;
            }
            if (strict && Modifier.isStatic(field.getModifiers())) {
                continue;
            }

            Invoker setter = setMethods.get(field.getName().toLowerCase(Locale.ENGLISH));
            if (setter == null) {
                addSetField(field);
            } else {
                setter.fixAccess(field);
            }

            Invoker getter = getMethods.get(field.getName().toLowerCase(Locale.ENGLISH));
            if (getter == null) {
                addGetField(field);
            } else {
                getter.fixAccess(field);
            }
        }
        if (clazz.getSuperclass() != null) {
            addFields(clazz.getSuperclass());
        }
    }

    void addSetField(Field field) {
        // setMethods.put(field.getName(), new SetFieldInvoker(field));
        // setTypes.put(field.getName(), field.getType());
        if (Modifier.isPublic(field.getModifiers())) {
            setMethods.put(field.getName().toLowerCase(Locale.ENGLISH), new SetFieldInvoker(field));
            setTypes.put(field.getName().toLowerCase(Locale.ENGLISH), field.getType());
        }
    }

    void addGetField(Field field) {
        // getMethods.put(field.getName(), new GetFieldInvoker(field));
        // getTypes.put(field.getName(), field.getType());
        if (Modifier.isPublic(field.getModifiers())) {

            getMethods.put(field.getName().toLowerCase(Locale.ENGLISH), new GetFieldInvoker(field));
            getTypes.put(field.getName().toLowerCase(Locale.ENGLISH), field.getType());
        }
    }

    /**
     * This method returns an array containing all methods declared in this class and any superclass. We use this
     * method, instead of the simpler Class.getMethods(), because we want to look for private methods as well.
     *
     * @param cls
     *            The class
     * @return An array containing all methods in this class
     */
    private Method[] getClassMethods(Class cls) {
        Map methodSet = new LinkedHashMap();
        Class currentClass = cls;
        while (currentClass != null) {
            addUniqueMethods(methodSet, currentClass.getDeclaredMethods());

            // we also need to look for interface methods -
            // because the class may be abstract
            Class[] interfaces = currentClass.getInterfaces();
            for (int i = 0; i < interfaces.length; i++) {
                addUniqueMethods(methodSet, interfaces[i].getMethods());
            }

            currentClass = currentClass.getSuperclass();
        }

        Collection methods = methodSet.values();

        return (Method[]) methods.toArray(new Method[methods.size()]);
    }

    private void addUniqueMethods(Map mmap, Method[] methods) {
        for (Method m : methods) {
            if (strict && Modifier.isStatic(m.getModifiers())) {
                continue;
            }

            String name = m.getName();
            if ((name.startsWith("get") || name.startsWith("is") || name.startsWith("set")) && !m.isBridge()
                && Modifier.isPublic(m.getModifiers())) {
                if (!mmap.containsKey(name.toLowerCase(Locale.ENGLISH))) {
                    mmap.put(name.toLowerCase(Locale.ENGLISH), m);
                }
            }
        }
    }

    /**
     * Gets the name of the class the instance provides information for
     *
     * @return The class name
     */
    public String getClassName() {
        return className;
    }

    public Object instantiateClass() {
        if (defaultConstructor != null) {
            try {
                return defaultConstructor.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("Error instantiating class. Cause: " + e, e);
            }
        } else {
            throw new RuntimeException("Error instantiating class. No default constructor for class " + className);
        }
    }

    public Invoker getSetInvoker(String propertyName) {
        Invoker method = (Invoker) setMethods.get(propertyName.toLowerCase(Locale.ENGLISH));
        if (method == null) {
            throw new RuntimeException("No WRITEABLE property named '" + propertyName + "' in class '" + className + "'");
        }
        return method;
    }

    public Invoker getGetInvoker(String propertyName) {
        Invoker method = (Invoker) getMethods.get(propertyName.toLowerCase(Locale.ENGLISH));
        if (method == null) {
            throw new RuntimeException("No READABLE property named '" + propertyName + "' in class '" + className + "'");
        }
        return method;
    }

    /**
     * Gets the type for a property setter
     *
     * @param propertyName
     *            - the name of the property
     * @return The Class of the propery setter
     */
    public Class getSetterType(String propertyName) {
        Class clazz = (Class) setTypes.get(propertyName.toLowerCase(Locale.ENGLISH));
        if (clazz == null) {
            throw new RuntimeException("No WRITEABLE property named '" + propertyName + "' in class '" + className + "'");
        }
        return clazz;
    }

    /**
     * Gets the type for a property getter
     *
     * @param propertyName
     *            - the name of the property
     * @return The Class of the propery getter
     */
    public Class getGetterType(String propertyName) {
        Class clazz = getTypes.get(propertyName.toLowerCase(Locale.ENGLISH));
        if (clazz == null) {
            throw new RuntimeException("No READABLE property named '" + propertyName + "' in class '" + className + "'");
        }
        return clazz;
    }

    /**
     * Check to see if a class has a writeable property by name
     *
     * @param propertyName
     *            - the name of the property to check
     * @return True if the object has a writeable property by the name
     */
    public boolean hasWritableProperty(String propertyName) {
        return setMethods.containsKey(propertyName.toLowerCase(Locale.ENGLISH));
    }

    /**
     * Check to see if a class has a readable property by name
     *
     * @param propertyName
     *            - the name of the property to check
     * @return True if the object has a readable property by the name
     */
    public boolean hasReadableProperty(String propertyName) {
        return getMethods.containsKey(propertyName.toLowerCase(Locale.ENGLISH));
    }

    /**
     * Gets an instance of ClassInfo for the specified class.
     *
     * @param clazz
     *            The class for which to lookup the method cache.
     * @return The method cache for the class
     */
    public static ClassInfo getInstance(Class clazz) {
        return getInstance(clazz, false);
    }

    public static ClassInfo getInstance(Class clazz, boolean strict) {
        if (cacheEnabled) {
            ClassInfo cached = null;
            if (strict) {
                synchronized (CLASS_INFO_MAP_STRICT) {
                    cached = (ClassInfo) CLASS_INFO_MAP_STRICT.get(clazz);
                    if (cached == null) {
                        cached = new ClassInfo(clazz, strict);
                        CLASS_INFO_MAP_STRICT.put(clazz, cached);
                    }
                }
            } else {
                synchronized (CLASS_INFO_MAP) {
                    cached = (ClassInfo) CLASS_INFO_MAP.get(clazz);
                    if (cached == null) {
                        cached = new ClassInfo(clazz, strict);
                        CLASS_INFO_MAP.put(clazz, cached);
                    }
                }
            }
            return cached;
        } else {
            return new ClassInfo(clazz, strict);
        }
    }

    public static void setCacheEnabled(boolean cacheEnabled) {
        ClassInfo.cacheEnabled = cacheEnabled;
    }

    /**
     * Examines a Throwable object and gets it's root cause
     *
     * @param t
     *            - the exception to examine
     * @return The root cause
     */
    public static Throwable unwrapThrowable(Throwable t) {
        Throwable t2 = t;
        while (true) {
            if (t2 instanceof InvocationTargetException) {
                t2 = ((InvocationTargetException) t).getTargetException();
            } else if (t2 instanceof UndeclaredThrowableException) {
                t2 = ((UndeclaredThrowableException) t).getUndeclaredThrowable();
            } else {
                return t2;
            }
        }
    }

    public static String dropCase(String name) {
        int firstLower = -1;
        for (int i = 0; i < name.length(); i++) {
            char c = name.charAt(i);
            if (Character.isLowerCase(c)) {
                firstLower = i;
                break;
            }
        }
        if (firstLower == -1) {
            // empty or all upper case char
            return name.toLowerCase(Locale.ENGLISH);
        } else if (firstLower == 0) {
            // start with lower case char
            return name;
        } else if (firstLower == 1) {
            return name.substring(0, firstLower).toLowerCase(Locale.ENGLISH) + name.substring(firstLower);
        } else {
            return name.substring(0, firstLower - 1).toLowerCase(Locale.ENGLISH) + name.substring(firstLower - 1);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy