com.sworddance.beans.PropertyAdaptor.java Source code

Java tutorial

Introduction

Here is the source code for com.sworddance.beans.PropertyAdaptor.java

Source

/*
 * 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 com.sworddance.beans;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import com.sworddance.util.ApplicationGeneralException;
import com.sworddance.util.ApplicationNullPointerException;

import static org.apache.commons.lang.StringUtils.*;

/**
 * Used to manage dynamic access to a property of a specific class.
 *
 * @author Howard Lewis Ship
 * @author patmoore
 */
public class PropertyAdaptor {
    private String propertyName;

    private Method getter;

    private Method setter;

    public PropertyAdaptor(String propertyName) {
        this.propertyName = propertyName;
    }

    public PropertyAdaptor(String propertyName, Method readMethod, Method writeMethod) {
        this.propertyName = propertyName;
        this.getter = readMethod;
        this.setter = writeMethod;
    }

    /**
     * @return the name of the method used to read the property, or null if the property is not
     * readable.
     */
    public String getReadMethodName() {
        return getter == null ? null : getter.getName();
    }

    /**
     * @return the name of the method used to write the property, or null if the property is not
     * writable.
     */
    public String getWriteMethodName() {
        return setter == null ? null : setter.getName();
    }

    public String getPropertyName() {
        return propertyName;
    }

    /**
     * Updates the property of the target object.
     * @param <T>
     *
     * @param target the object to update
     * @param value the value to be stored into the target object property
     * @return value returned by the set operation ( usually void )
     */
    @SuppressWarnings("unchecked")
    public <T> T write(Object target, Object value) {
        if (setter == null) {
            throw new ApplicationGeneralException("No set" + this.propertyName + "()");
        } else {

            try {
                return (T) setter.invoke(target, new Object[] { value });
            } catch (IllegalArgumentException e) {
                throw new IllegalArgumentException(setter.toGenericString(), e);
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException(setter.toGenericString(), e);
            } catch (InvocationTargetException e) {
                throw new IllegalArgumentException(setter.toGenericString(), e.getCause());
            }
        }
    }

    public void smartWrite(Object target, String value) {
        Object convertedValue = convertValueForAssignment(target, value);

        write(target, convertedValue);
    }

    /** @since 1.1 */
    private Object convertValueForAssignment(Object target, String value) {
        if (value == null || getReturnType().isInstance(value)) {
            return value;
        }

        PropertyEditor e = PropertyEditorManager.findEditor(getReturnType());

        if (e == null) {
            Object convertedValue = instantiateViaStringConstructor(value);

            if (convertedValue != null) {
                return convertedValue;
            }

            throw new ApplicationGeneralException("noPropertyEditor(" + propertyName + "+)" + target.getClass());
        }

        try {
            e.setAsText(value);

            return e.getValue();
        } catch (Exception ex) {
            throw new ApplicationGeneralException(
                    "unableToConvert(" + value + ", " + getReturnType() + ", " + propertyName + ", " + target, ex);
        }
    }

    /**
     * Checks to see if this adaptor's property type has a public constructor that takes a single
     * String argument.
     */
    private Object instantiateViaStringConstructor(String value) {
        try {
            Constructor<?> c = getReturnType().getConstructor(new Class[] { String.class });

            return c.newInstance(new Object[] { value });
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * @return true if there's a write method for the property.
     */
    public boolean isWritable() {
        return setter != null;
    }

    /**
     * Reads the property of the target object.
     *
     * @param target the object to read a property from
     * @return property's value
     */
    public Object read(Object target) {
        if (getter == null) {
            throw new ApplicationGeneralException("no get" + propertyName + "()");
        } else if (target == null) {
            throw new ApplicationNullPointerException("target to read property " + propertyName + " from is null");
        } else {

            try {
                return getter.invoke(target);

            } catch (IllegalArgumentException e) {
                throw new IllegalArgumentException(getter.toGenericString()
                        + " is not allowed to be called on an object of type =" + target.getClass(), e);
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException(getter.toGenericString()
                        + " (private/protected method?) is not allowed to be called on an object of type ="
                        + target.getClass(), e);
            } catch (InvocationTargetException e) {
                throw new IllegalArgumentException(getter.toGenericString() + " target is a " + target.getClass(),
                        e.getCause());
            }
        }
    }

    /**
     * @return true if there's a read method for the property.
     */
    public boolean isReadable() {
        return getter != null;
    }

    /**
     * @return the returnType
     */
    public Class<?> getReturnType() {
        return getGetter().getReturnType();
    }

    /**
     * @param getter the getter to set
     */
    public void setGetter(Method getter) {
        this.getter = getter;
    }

    public void setGetter(Class<?> clazz, Class<?>... parameterTypes) {
        setGetter(getMethod(clazz, parameterTypes));
    }

    /**
     * Get a the Getter method with the given parameter types (usually only a single parameter)
     * @param clazz
     * @param propertyName
     * @param parameterTypes
     * @return the getter method.
     */
    private Method getMethod(Class<?> clazz, Class<?>... parameterTypes) {
        if (propertyName == null) {
            throw new IllegalArgumentException("propertyName cannot be null");
        }
        String capitalize = capitalize(propertyName);
        for (String methodName : Arrays.asList(propertyName, "get" + capitalize, "is" + capitalize)) {
            try {
                return clazz.getMethod(methodName, parameterTypes);
            } catch (SecurityException e) {
                //                    throw new IllegalArgumentException(clazz+"."+propertyName+ " " + StringUtils.join(parameterTypes), e);
            } catch (NoSuchMethodException e) {
                //                    throw new IllegalArgumentException(clazz+"."+propertyName+ " " + StringUtils.join(parameterTypes), e);
            }
        }
        throw new IllegalArgumentException(clazz + "." + propertyName + " " + join(parameterTypes));
    }

    /**
     * @return the getter
     */
    public Method getGetter() {
        return getter;
    }

    /**
     * @param setter the setter to set
     */
    public void setSetter(Method setter) {
        this.setter = setter;
    }

    /**
     * when constructing a PropertyMethodChain, we don't always want the setter to be available
     * because only the 'leaf' property should be writable.
     * @param clazz
     * @return true if setter was found.
     */
    public boolean initSetter(Class<?> clazz) {
        try {
            setSetter(clazz.getMethod("set" + capitalize(propertyName), this.getReturnType()));
            return true;
        } catch (SecurityException e) {
            // oh well..
            return false;
        } catch (NoSuchMethodException e) {
            // oh well..
            return false;
        }
    }

    /**
     * @return the setter
     */
    public Method getSetter() {
        return setter;
    }

    @Override
    public String toString() {
        return getter.getName();
    }

    /**
     * @return {@link #isReadable()} || {@link #isWritable()}
     */
    public boolean isExists() {
        return this.isReadable() || this.isWritable();
    }
}