/* ===================================================================
* 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;
}
}