net.femtoparsec.jnlmin.utils.PropertySetter.java Source code

Java tutorial

Introduction

Here is the source code for net.femtoparsec.jnlmin.utils.PropertySetter.java

Source

/*
 * @copyright Copyright (c) 2012, Bastien Aracil
 *    All rights reserved.
 *    New BSD license. See http://en.wikipedia.org/wiki/Bsd_license
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *       * The name of Bastien Aracil may not be used to endorse or promote products
 *         derived from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *    DISCLAIMED. IN NO EVENT SHALL BASTIEN ARACIL BE LIABLE FOR ANY
 *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package net.femtoparsec.jnlmin.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author Bastien Aracil
 * Date: 06/12/12
 */
public class PropertySetter {

    private static Map<Class<?>, Reference<PropertySetter>> CACHE = new HashMap<Class<?>, Reference<PropertySetter>>();

    public static PropertySetter getPropertySetter(Class beanClass) {
        cleanCache();

        Reference<PropertySetter> reference = CACHE.get(beanClass);

        PropertySetter result = reference == null ? null : reference.get();
        if (result == null) {
            result = new PropertySetter(beanClass);
            CACHE.put(beanClass, new SoftReference<PropertySetter>(result));
        }
        return result;
    }

    private static void cleanCache() {
        Iterator<Map.Entry<Class<?>, Reference<PropertySetter>>> iterator = CACHE.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Class<?>, Reference<PropertySetter>> entry = iterator.next();
            if (entry.getValue().get() == null) {
                iterator.remove();
            }
        }
    }

    public static void clearCache() {
        CACHE.clear();
    }

    private final Map<String, Map<Class<?>, Method>> unaryMethods;

    private final Set<Key> checkedSetters;

    private final Map<Key, Method> setters;

    private final Class<?> beanClass;

    public PropertySetter(Class beanClass) {
        this.beanClass = beanClass;
        this.unaryMethods = ReflectUtils.findUnaryMethods(beanClass);
        this.checkedSetters = new HashSet<Key>();
        this.setters = new HashMap<Key, Method>();
    }

    public void setProperty(Object bean, String propertyName, Class<?> propertyClass, Object propertyValue)
            throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Validate.notNull(bean, "bean");

        if (bean.getClass() != beanClass) {
            throw new IllegalArgumentException(
                    String.format("Invalid bean class '%s'. Excepted '%s'", bean.getClass(), beanClass));
        }

        Method method = getSetter(propertyName, propertyClass);
        method.invoke(bean, propertyValue);
    }

    private Method getSetter(String propertyName, Class<?> propertyClass) throws NoSuchMethodException {
        final String setterName = this.getSetterName(propertyName);
        final Key key = new Key(propertyName, propertyClass);

        Method method = this.setters.get(key);
        if (method == null && !checkedSetters.contains(key)) {
            Map<Class<?>, Method> candidates = this.unaryMethods.get(setterName);
            if (candidates != null) {
                Class<?> closestClass = ReflectUtils.findClosestClass(propertyClass, candidates.keySet());
                if (closestClass != null) {
                    method = candidates.get(closestClass);
                }
            }
            this.setters.put(key, method);
            this.checkedSetters.add(key);
        }

        if (method == null) {
            throw new NoSuchMethodException(String.format("%s(%s)", setterName, propertyClass));
        }

        return method;
    }

    private String getSetterName(String propertyName) {
        return String.format("set%s", StringUtils.capitalize(propertyName));
    }

    private static class Key {

        private final String propertyName;

        private final Class<?> propertyClass;

        private Key(String propertyName, Class<?> propertyClass) {
            this.propertyName = Validate.notNull(propertyName, "propertyName");
            this.propertyClass = Validate.notNull(propertyClass, "propertyClass");
        }

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

            Key key = (Key) o;

            return propertyClass.equals(key.propertyClass) && propertyName.equals(key.propertyName);
        }

        @Override
        public int hashCode() {
            int result = propertyName.hashCode();
            result = 31 * result + propertyClass.hashCode();
            return result;
        }
    }
}