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

com.squeakysand.commons.beans.BeanHelper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010-2012 Craig S. Dickson (http://craigsdickson.com)
 *
 * 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.squeakysand.commons.beans;

import com.squeakysand.commons.lang.ToStringHelper;
import com.squeakysand.commons.util.CollectionUtils;
import com.squeakysand.commons.util.FilteredList;
import com.squeakysand.commons.util.Predicate;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class providing common functionality related to working with JavaBeans.
 * 
 * @author Craig S. Dickson
 */
public final class BeanHelper {

    private static class PropertyMapNamePredicate implements Predicate> {

        private List propertyNames;

        public PropertyMapNamePredicate(String... propertyNames) {
            this.propertyNames = Arrays.asList(propertyNames);
        }

        @Override
        public boolean evaluate(Entry entry) {
            return propertyNames.contains(entry.getKey());
        }

    }

    private static Logger LOG = LoggerFactory.getLogger(BeanHelper.class);

    public static void copyBeanProperties(Object sourceBean, Object targetBean) throws BeanHelperException {
        Map sourcePropertyValues = getPropertyValues(sourceBean, true);
        Set sourcePropertyNames = sourcePropertyValues.keySet();
        List targetWriteableProperties = getWriteableProperties(targetBean.getClass());
        for (PropertyDescriptor writeableProperty : targetWriteableProperties) {
            String targetPropertyName = writeableProperty.getName();
            if (sourcePropertyNames.contains(targetPropertyName)) {
                Method setter = writeableProperty.getWriteMethod();
                Object propertyValue = sourcePropertyValues.get(targetPropertyName);
                try {
                    setter.invoke(targetBean, propertyValue);
                } catch (IllegalAccessException e) {
                    throw new BeanHelperException(e.getMessage(), e);
                } catch (IllegalArgumentException e) {
                    throw new BeanHelperException(e.getMessage(), e);
                } catch (InvocationTargetException e) {
                    throw new BeanHelperException(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * Generates a String representation of the state of the JavaBean passed in. Includes only the read-write properties
     * (equivalent to calling getPropertyString(o, false)).
     * 
     * @param bean
     *            the JavaBean.
     * @return a formatted String representing the values of the JavaBean properties of the bean.
     */
    public static String getPropertiesString(Object bean) {
        return getPropertiesString(bean, false);
    }

    /**
     * Generates a String representation of the state of the JavaBean passed in.
     * 
     * @param bean
     *            the JavaBean.
     * @param includeReadOnlyProperties
     *            indicates if read-only properties should be included.
     * @return a formatted String representing the values of the JavaBean properties of the bean.
     */
    public static String getPropertiesString(Object bean, boolean includeReadOnlyProperties) {
        if (bean == null) {
            throw new IllegalArgumentException("parameter 'bean' cannot be null");
        }
        Map properties = null;
        try {
            properties = BeanHelper.getPropertyValues(bean, includeReadOnlyProperties);
        } catch (BeanHelperException e) {
            LOG.error(e.getMessage(), e);
        }
        StringBuilder sb = new StringBuilder("{");
        if (properties != null) {
            for (String key : properties.keySet()) {
                sb.append(key);
                sb.append(" : ");
                Object value = properties.get(key);
                String valueAsString = ToStringHelper.toString(value);
                sb.append(valueAsString);
                sb.append(", ");
            }
            // chop off the last comma separator
            if (sb.length() > 1) {
                sb.setLength(sb.length() - 2);
            }
        }
        sb.append("}");
        return sb.toString();
    }

    public static PropertyDescriptor getPropertyDescriptor(Class klass, String propertyName) throws BeanHelperException {
        PropertyDescriptor result = null;
        List descriptors = getPropertyDescriptors(klass);
        for (PropertyDescriptor descriptor : descriptors) {
            if (descriptor.getName().equals(propertyName)) {
                result = descriptor;
                break;
            }
        }
        return result;
    }

    /**
     * @param element
     *            a setter or getter method.
     * @return the associated PropertyDescriptor.
     */
    public static PropertyDescriptor getPropertyDescriptor(ExecutableElement element) throws BeanHelperException {
        PropertyDescriptor result = null;
        String propertyName = getPropertyName(element);
        LOG.debug("for element " + element.getSimpleName() + " property name is " + propertyName);
        if (propertyName != null) {
            String className = ((TypeElement) element.getEnclosingElement()).getQualifiedName().toString();
            Class klass = null;
            try {
                klass = Thread.currentThread().getContextClassLoader().loadClass(className);
            } catch (ClassNotFoundException e) {
                System.out.println("cnfe: " + e.getMessage());
                LOG.error(e.getMessage(), e);
                throw new BeanHelperException(e);
            }
            List properties = getPropertyDescriptors(klass);
            for (PropertyDescriptor pd : properties) {
                System.out.println("checking pd " + ToStringHelper.toString(pd));
                if (pd.getName().equals(propertyName)) {
                    result = pd;
                    break;
                }
            }
        }
        System.out.println("for element " + element.getSimpleName() + " pd is " + ToStringHelper.toString(result));
        return result;
    }

    public static PropertyDescriptor getPropertyDescriptor(Object bean, String propertyName) throws BeanHelperException {
        return getPropertyDescriptor(bean.getClass(), propertyName);
    }

    public static List getPropertyDescriptors(Class klass) throws BeanHelperException {
        return getPropertyDescriptors(klass, PropertyDescriptorFilter.Include.ALL);
    }

    public static List getPropertyDescriptors(Class klass, PropertyDescriptorFilter.Include includedProperties)
                    throws BeanHelperException {
        PropertyDescriptor[] propertyDescriptors;
        try {
            propertyDescriptors = Introspector.getBeanInfo(klass).getPropertyDescriptors();
        } catch (IntrospectionException e) {
            throw new BeanHelperException(e);
        }
        List result = Arrays.asList(propertyDescriptors);
        switch (includedProperties) {
        case READABLE:
            result = new FilteredList(result, PropertyDescriptorFilter.READABLE);
            break;
        case WRITEABLE:
            result = new FilteredList(result, PropertyDescriptorFilter.WRITEABLE);
            break;
        case READ_WRITE:
            result = new FilteredList(result, PropertyDescriptorFilter.READ_WRITE);
            break;
        default:
            // the only other option is ALL, so we just return the result list "as is"
        }
        return result;
    }

    public static String getPropertyName(ExecutableElement element) {
        String propertyName = null;
        String methodName = element.getSimpleName().toString();
        LOG.debug("for element " + element.getSimpleName() + " method name is " + methodName);
        if (isPropertyGetter(element)) {
            if (methodName.startsWith("is")) {
                propertyName = Introspector.decapitalize(methodName.substring(2));
            } else {
                propertyName = Introspector.decapitalize(methodName.substring(3));
            }
        } else if (isPropertySetter(element)) {
            LOG.debug("for element " + element.getSimpleName() + " is a property setter");
            propertyName = Introspector.decapitalize(methodName.substring(3));
            LOG.debug("for element " + element.getSimpleName() + " raw property name " + methodName.substring(3));
        }
        return propertyName;
    }

    public static List getPropertyNames(Class klass) throws BeanHelperException {
        return getPropertyNames(klass, PropertyDescriptorFilter.Include.ALL);
    }

    public static List getPropertyNames(Class klass, PropertyDescriptorFilter.Include includedProperties) throws BeanHelperException {
        List result = new ArrayList();
        List properties = getPropertyDescriptors(klass, includedProperties);
        for (PropertyDescriptor descriptor : properties) {
            result.add(descriptor.getName());
        }
        // ignore the 'class' property inherited from the Object class
        result.remove("class");
        Collections.sort(result);
        return result;
    }

    public static Object getPropertyValue(Object bean, String propertyName) throws BeanHelperException {
        Object result = null;
        PropertyDescriptor descriptor = getPropertyDescriptor(bean, propertyName);
        if (descriptor == null) {
            throw new BeanHelperException(String.format("property %s not found for class %s", propertyName, bean.getClass()));
        }
        Method getter = descriptor.getReadMethod();
        if (getter == null) {
            throw new BeanHelperException(String.format("property %s is not readable for class %s", propertyName, bean.getClass()));
        }
        try {
            result = getter.invoke(bean, (Object[]) null);
        } catch (IllegalAccessException e) {
            throw new BeanHelperException(e.getMessage(), e);
        } catch (IllegalArgumentException e) {
            throw new BeanHelperException(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            throw new BeanHelperException(e.getMessage(), e);
        }
        return result;
    }

    /**
     * Introspects the passed in object and returns a map of the bean's property names and their current values.
     * 
     * @param bean
     *            the JavaBean to introspect.
     * @return a Map, keys are the property names, values are the current values of those properties.
     * @throws com.squeakysand.commons.beans.BeanHelperException
     *             if there are any problems during the introspection (e.g. security issues etc).
     */
    public static Map getPropertyValues(Object bean) throws BeanHelperException {
        return getPropertyValues(bean, true);
    }

    /**
     * Introspects the passed in object and returns a map of the bean's property names and their current values.
     * 
     * @param bean
     *            the JavaBean to introspect.
     * @param includeReadOnlyProperties
     *            indicates if read-only properties (i.e. properties that do not have a setter method) should be
     *            included in the result.
     * @return a Map, keys are the property names, values are the current values of those properties.
     * @throws com.squeakysand.commons.beans.BeanHelperException
     *             if there are any problems during the introspection (e.g. security issues etc).
     */
    public static Map getPropertyValues(Object bean, boolean includeReadOnlyProperties) throws BeanHelperException {
        Map result = new TreeMap();
        List properties = null;
        Class beanClass = bean.getClass();
        if (includeReadOnlyProperties) {
            properties = getPropertyDescriptors(beanClass, PropertyDescriptorFilter.Include.READABLE);
        } else {
            properties = getPropertyDescriptors(beanClass, PropertyDescriptorFilter.Include.READ_WRITE);
        }
        for (PropertyDescriptor descriptor : properties) {
            try {
                Method getter = descriptor.getReadMethod();
                Object value = getter.invoke(bean, (Object[]) null);
                result.put(descriptor.getName(), value);
            } catch (IllegalAccessException e) {
                LOG.info("unable to access property '{}' of class {} via reflection - change logging level to debug for more info", descriptor.getName(),
                                beanClass.getName());
                LOG.debug(e.getLocalizedMessage(), e);
            } catch (InvocationTargetException e) {
                LOG.info("unable to access property '{}' of class {} via reflection - change logging level to debug for more info", descriptor.getName(),
                                beanClass.getName());
                LOG.debug(e.getLocalizedMessage(), e);
            }
        }
        // ignore the 'class' property inherited from the Object class
        result.remove("class");
        return result;
    }

    public static Map getPropertyValues(Object bean, boolean includeReadOnlyProperties, String... propertyNameFilter) throws BeanHelperException {
        Map allPropertyValues = getPropertyValues(bean, includeReadOnlyProperties);
        return CollectionUtils.filter(allPropertyValues, new PropertyMapNamePredicate(propertyNameFilter));
    }

    public static Map getPropertyValues(Object bean, String... propertyNameFilter) throws BeanHelperException {
        return getPropertyValues(bean, true, propertyNameFilter);
    }

    public static List getReadableProperties(Class klass) throws BeanHelperException {
        return getPropertyDescriptors(klass, PropertyDescriptorFilter.Include.READABLE);
    }

    public static List getSetterMethods(Class klass) throws BeanHelperException {
        List result = new ArrayList();
        List writeableProperties = getWriteableProperties(klass);
        for (PropertyDescriptor propertyDescriptor : writeableProperties) {
            Method setterMethod = propertyDescriptor.getWriteMethod();
            result.add(setterMethod);
        }
        return result;
    }

    public static List getWriteableProperties(Class klass) throws BeanHelperException {
        return getPropertyDescriptors(klass, PropertyDescriptorFilter.Include.WRITEABLE);
    }

    public static boolean isPropertyGetter(ExecutableElement element) {
        boolean result = false;
        if ((element.getKind() == ElementKind.METHOD) && element.getModifiers().contains(Modifier.PUBLIC)) {
            String methodName = element.getSimpleName().toString();
            if (methodName.startsWith("is") && element.getReturnType().getKind().equals(TypeKind.BOOLEAN) && element.getParameters().isEmpty()) {
                result = true;
            } else if (methodName.startsWith("get") && element.getParameters().isEmpty()) {
                result = true;
            }
        }
        return result;
    }

    public static boolean isPropertySetter(ExecutableElement element) {
        boolean result = false;
        System.out.println("checking property setter status of " + element.getSimpleName());
        if ((element.getKind() == ElementKind.METHOD) && element.getModifiers().contains(Modifier.PUBLIC)) {
            System.out.println("for element " + element.getSimpleName() + " is a method and is public");
            String methodName = element.getSimpleName().toString();
            System.out.println("method namme: " + methodName + ", return type: " + element.getReturnType().getKind() + " equals " + TypeKind.VOID + " "
                            + element.getReturnType().equals(TypeKind.VOID) + ", parameter size: " + element.getParameters().size());
            if (methodName.startsWith("set") && element.getReturnType().getKind().equals(TypeKind.VOID) && (element.getParameters().size() == 1)) {
                result = true;
            }
        }
        return result;
    }

    private BeanHelper() {
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy