org.apache.bval.util.PropertyAccess.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.bval.util.PropertyAccess.java

Source

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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.apache.bval.util;

import org.apache.bval.util.reflection.Reflection;
import org.apache.commons.beanutils.PropertyUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.ElementType;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;

/**
 * Description: Undefined dynamic strategy (FIELD or METHOD access) Uses PropertyUtils or tries to determine field to
 * access the value<br/>
 */
public class PropertyAccess extends AccessStrategy {
    private final Class<?> beanClass;
    private final String propertyName;
    private Field rememberField;

    /**
     * Create a new PropertyAccess instance.
     * 
     * @param clazz
     * @param propertyName
     */
    public PropertyAccess(Class<?> clazz, String propertyName) {
        this.beanClass = clazz;
        this.propertyName = propertyName;
    }

    /**
     * {@inheritDoc}
     */
    public ElementType getElementType() {
        return rememberField != null ? ElementType.FIELD : ElementType.METHOD;
    }

    private static Object getPublicProperty(Object bean, String property)
            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        if (bean instanceof Map<?, ?>) {
            return ((Map<?, ?>) bean).get(property);
        } else { // supports DynaBean and standard Objects
            return PropertyUtils.getSimpleProperty(bean, property);
        }
    }

    /**
     * Get a named property from <code>bean</code>.
     * 
     * @param bean
     * @param propertyName
     * @return Object found
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     */
    public static Object getProperty(Object bean, String propertyName)
            throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        return new PropertyAccess(bean.getClass(), propertyName).get(bean);
    }

    /**
     * {@inheritDoc}
     */
    public String toString() {
        return "Property{" + beanClass.getName() + '.' + propertyName + '}';
    }

    /**
     * {@inheritDoc}
     */
    public Type getJavaType() {
        Type result = getTypeInner();
        return result == null ? Object.class : result;
    }

    /**
     * Learn whether this {@link PropertyAccess} references a known property.
     * 
     * @return boolean
     */
    public boolean isKnown() {
        return getTypeInner() != null;
    }

    /**
     * Find out what, if any, type can be calculated.
     * 
     * @return type found or <code>null</code>
     */
    private Type getTypeInner() {
        if (rememberField != null) {
            return rememberField.getGenericType();
        }
        Method readMethod = getPropertyReadMethod(propertyName, beanClass);
        if (readMethod != null) {
            return readMethod.getGenericReturnType();
        }
        Field fld = getField(propertyName, beanClass);
        if (fld != null) {
            cacheField(fld);
            return rememberField.getGenericType();
        }
        return null;
    }

    private static Method getPropertyReadMethod(String propertyName, Class<?> beanClass) {
        for (PropertyDescriptor each : PropertyUtils.getPropertyDescriptors(beanClass)) {
            if (each.getName().equals(propertyName)) {
                return each.getReadMethod();
            }
        }
        return null;
    }

    private static Field getField(String propertyName, Class<?> beanClass) {
        try { // try public field
            return beanClass.getField(propertyName);
        } catch (NoSuchFieldException ex2) {
            // search for private/protected field up the hierarchy
            Class<?> theClass = beanClass;
            while (theClass != null) {
                try {
                    return theClass.getDeclaredField(propertyName);
                } catch (NoSuchFieldException ex3) {
                    // do nothing
                }
                theClass = theClass.getSuperclass();
            }
        }
        return null;
    }

    private static Object readField(Field field, Object bean) throws IllegalAccessException {
        final boolean mustUnset = Reflection.setAccessible(field, true);
        try {
            return field.get(bean);
        } finally {
            if (mustUnset) {
                Reflection.setAccessible(field, false);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public String getPropertyName() {
        return propertyName;
    }

    /**
     * {@inheritDoc}
     */
    public Object get(Object bean) {
        try {
            if (rememberField != null) { // cache field of previous access
                return readField(rememberField, bean);
            }
            try { // try public method
                return getPublicProperty(bean, propertyName);
            } catch (NoSuchMethodException ex) {
                return getFieldValue(bean);
            }
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            throw new IllegalArgumentException("cannot access " + propertyName, e);
        }
    }

    private Object getFieldValue(Object bean) throws IllegalAccessException {
        Field field = getField(propertyName, beanClass);
        if (field != null) {
            cacheField(field);
            return readField(rememberField, bean);
        }
        throw new IllegalArgumentException("cannot access field " + propertyName);
    }

    private void cacheField(Field field) {
        this.rememberField = field;
    }

    /**
     * {@inheritDoc}
     */
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        PropertyAccess that = (PropertyAccess) o;

        return beanClass.equals(that.beanClass) && propertyName.equals(that.propertyName);
    }

    /**
     * {@inheritDoc}
     */
    public int hashCode() {
        int result;
        result = beanClass.hashCode();
        result = 31 * result + propertyName.hashCode();
        return result;
    }
}