net.solarnetwork.util.CloningPropertyEditorRegistrar.java Source code

Java tutorial

Introduction

Here is the source code for net.solarnetwork.util.CloningPropertyEditorRegistrar.java

Source

/* ===================================================================
 * CloningPropertyEditorRegistrar.java
 * 
 * Created Aug 3, 2009 3:29:02 PM
 * 
 * Copyright (c) 2009 Solarnetwork.net Dev Team.
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ===================================================================
 * $Id$
 * ===================================================================
 */

package net.solarnetwork.util;

import java.beans.PropertyEditor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;

/**
 * PropertyEditorRegistrar implementation that creates new PropertyEditor instances
 * by cloning the ones configured on this class.
 * 
 * <p>This implementation makes it easy to cofnigure PropertyEditors that are wired up
 * to Spring components but need to be used in a thread-safe manner. Each PropertyEditor
 * must provide a no-argument <code>clone()</code> method to create fully-configured
 * copy of itself.</p>
 * 
 * <p>The configurable properties of this class are:</p>
 * 
 * <dl class="class-properties">
 *   <dt>propertyEditors</dt>
 *   <dd>Map of property names to associated PropertyEditor instances.</dd>
 *   
 *   <dt>classEditors</dt>
 *   <dd>Map of Class objects to associated PropertyEditor instances.</dd>
 *   
 *   <dt>lenient</dt>
 *   <dd>If <em>true</em> (the default) then if a configured PropertyEditor does
 *   not provide a public {@code clone()} method, this will configure the 
 *   PropertyEditor as-is, without creating a new copy. If <em>false</em> then
 *   a RuntimeException will be thrown if this situation is encountered.</dd>
 * </dl>
 *
 * @author matt
 * @version $Revision$ $Date$
 */
public class CloningPropertyEditorRegistrar implements PropertyEditorRegistrar {

    private Map<String, PropertyEditor> propertyEditors = null;
    private Map<Class<?>, PropertyEditor> classEditors = null;
    private boolean lenient = true;

    /* (non-Javadoc)
     * @see org.springframework.beans.PropertyEditorRegistrar#registerCustomEditors(org.springframework.beans.PropertyEditorRegistry)
     */
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        if (propertyEditors != null) {
            for (Map.Entry<String, PropertyEditor> me : propertyEditors.entrySet()) {
                registerEditor(registry, me);
            }
        }
        if (classEditors != null) {
            for (Map.Entry<Class<?>, PropertyEditor> me : classEditors.entrySet()) {
                registerEditor(registry, me);
            }
        }
    }

    /**
     * Set a {@link PropertyEditor} for a specific key.
     * 
     * @param key the key (property name)
     * @param editor the associated editor
     */
    public void setPropertyEditor(String key, PropertyEditor editor) {
        if (this.propertyEditors == null) {
            this.propertyEditors = new LinkedHashMap<String, PropertyEditor>();
        }
        this.propertyEditors.put(key, editor);
    }

    /**
     * Set a {@link PropertyEditor} for a specific Class.
     * 
     * @param clazz the Class
     * @param editor the associated editor
     */
    public void setPropertyEditor(Class<?> clazz, PropertyEditor editor) {
        if (this.classEditors == null) {
            this.classEditors = new LinkedHashMap<Class<?>, PropertyEditor>();
        }
        this.classEditors.put(clazz, editor);
    }

    private void registerEditor(PropertyEditorRegistry registry, Map.Entry<?, PropertyEditor> me) {
        PropertyEditor ed = me.getValue();
        Object key = me.getKey();
        try {
            Method m = ed.getClass().getMethod("clone", (Class[]) null);
            PropertyEditor copy = (PropertyEditor) m.invoke(ed, (Object[]) null);
            registerValue(registry, copy, key);
        } catch (NoSuchMethodException e) {
            if (lenient) {
                // fall back to non-cloned copy, assume it's thread safe!
                registerValue(registry, ed, key);
            } else {
                throw new RuntimeException(e);
            }
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    private void registerValue(PropertyEditorRegistry registry, PropertyEditor copy, Object key) {
        if (key instanceof Class<?>) {
            registry.registerCustomEditor((Class<?>) key, null, copy);
        } else {
            // assume key is String
            registry.registerCustomEditor(null, (String) key, copy);
        }
    }

    /**
     * @return the propertyEditors
     */
    public Map<String, PropertyEditor> getPropertyEditors() {
        return propertyEditors;
    }

    /**
     * @param propertyEditors the propertyEditors to set
     */
    public void setPropertyEditors(Map<String, PropertyEditor> propertyEditors) {
        this.propertyEditors = propertyEditors;
    }

    /**
     * @return the lenient
     */
    public boolean isLenient() {
        return lenient;
    }

    /**
     * @param lenient the lenient to set
     */
    public void setLenient(boolean lenient) {
        this.lenient = lenient;
    }

    /**
     * @return the classEditors
     */
    public Map<Class<?>, PropertyEditor> getClassEditors() {
        return classEditors;
    }

    /**
     * @param classEditors the classEditors to set
     */
    public void setClassEditors(Map<Class<?>, PropertyEditor> classEditors) {
        this.classEditors = classEditors;
    }

}