alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Spring Framework example source code file (TypeConverterDelegate.java)

This example Spring Framework source code file (TypeConverterDelegate.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Spring Framework tags/keywords

bean, cannot, class, class, collection, collection, illegalargumentexception, iterator, javabean, map, methodparameter, object, object, propertyeditor, reflection, string, string, util

The Spring Framework TypeConverterDelegate.java source code

/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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 org.springframework.beans;

import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.CollectionFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.JdkVersion;
import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * Internal helper class for converting property values to target types.
 *
 * <p>Works on a given {@link PropertyEditorRegistrySupport} instance.
 * Used as a delegate by {@link BeanWrapperImpl} and {@link SimpleTypeConverter}.
 *
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 2.0
 * @see BeanWrapperImpl
 * @see SimpleTypeConverter
 */
class TypeConverterDelegate {

	private static final Log logger = LogFactory.getLog(TypeConverterDelegate.class);

	private static final Map unknownEditorTypes = Collections.synchronizedMap(new WeakHashMap());

	private final PropertyEditorRegistrySupport propertyEditorRegistry;

	private final Object targetObject;


	/**
	 * Create a new TypeConverterDelegate for the given editor registry.
	 * @param propertyEditorRegistry the editor registry to use
	 */
	public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry) {
		this(propertyEditorRegistry, null);
	}

	/**
	 * Create a new TypeConverterDelegate for the given editor registry and bean instance.
	 * @param propertyEditorRegistry the editor registry to use
	 * @param targetObject the target object to work on (as context that can be passed to editors)
	 */
	public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry, Object targetObject) {
		this.propertyEditorRegistry = propertyEditorRegistry;
		this.targetObject = targetObject;
	}


	/**
	 * Convert the value to the specified required type.
	 * @param newValue the proposed new value
	 * @param requiredType the type we must convert to
	 * (or <code>null if not known, for example in case of a collection element)
	 * @return the new value, possibly the result of type conversion
	 * @throws IllegalArgumentException if type conversion failed
	 */
	public Object convertIfNecessary(Object newValue, Class requiredType) throws IllegalArgumentException {
		return convertIfNecessary(null, null, newValue, requiredType, null, null);
	}

	/**
	 * Convert the value to the specified required type.
	 * @param newValue the proposed new value
	 * @param requiredType the type we must convert to
	 * (or <code>null if not known, for example in case of a collection element)
	 * @param methodParam the method parameter that is the target of the conversion
	 * (may be <code>null)
	 * @return the new value, possibly the result of type conversion
	 * @throws IllegalArgumentException if type conversion failed
	 */
	public Object convertIfNecessary(Object newValue, Class requiredType, MethodParameter methodParam)
			throws IllegalArgumentException {

		return convertIfNecessary(null, null, newValue, requiredType, null, methodParam);
	}

	/**
	 * Convert the value to the required type for the specified property.
	 * @param propertyName name of the property
	 * @param oldValue the previous value, if available (may be <code>null)
	 * @param newValue the proposed new value
	 * @param requiredType the type we must convert to
	 * (or <code>null if not known, for example in case of a collection element)
	 * @return the new value, possibly the result of type conversion
	 * @throws IllegalArgumentException if type conversion failed
	 */
	public Object convertIfNecessary(
			String propertyName, Object oldValue, Object newValue, Class requiredType)
			throws IllegalArgumentException {

		return convertIfNecessary(propertyName, oldValue, newValue, requiredType, null, null);
	}

	/**
	 * Convert the value to the required type for the specified property.
	 * @param oldValue the previous value, if available (may be <code>null)
	 * @param newValue the proposed new value
	 * @param descriptor the JavaBeans descriptor for the property
	 * @return the new value, possibly the result of type conversion
	 * @throws IllegalArgumentException if type conversion failed
	 */
	public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescriptor descriptor)
			throws IllegalArgumentException {

		return convertIfNecessary(
				descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), descriptor,
				BeanUtils.getWriteMethodParameter(descriptor));
	}


	/**
	 * Convert the value to the required type (if necessary from a String),
	 * for the specified property.
	 * @param propertyName name of the property
	 * @param oldValue the previous value, if available (may be <code>null)
	 * @param newValue the proposed new value
	 * @param requiredType the type we must convert to
	 * (or <code>null if not known, for example in case of a collection element)
	 * @param descriptor the JavaBeans descriptor for the property
	 * @param methodParam the method parameter that is the target of the conversion
	 * (may be <code>null)
	 * @return the new value, possibly the result of type conversion
	 * @throws IllegalArgumentException if type conversion failed
	 */
	protected Object convertIfNecessary(
			String propertyName, Object oldValue, Object newValue, Class requiredType,
			PropertyDescriptor descriptor, MethodParameter methodParam)
			throws IllegalArgumentException {

		Object convertedValue = newValue;

		// Custom editor for this type?
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		// Value not of required type?
		if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
			if (editor == null && descriptor != null) {
				if (JdkVersion.isAtLeastJava15()) {
					editor = descriptor.createPropertyEditor(this.targetObject);
				}
				else {
					Class editorClass = descriptor.getPropertyEditorClass();
					if (editorClass != null) {
						editor = (PropertyEditor) BeanUtils.instantiateClass(editorClass);
					}
				}
			}
			if (editor == null && requiredType != null) {
				// No custom editor -> check BeanWrapperImpl's default editors.
				editor = (PropertyEditor) this.propertyEditorRegistry.getDefaultEditor(requiredType);
				if (editor == null && !unknownEditorTypes.containsKey(requiredType)) {
					// No BeanWrapper default editor -> check standard JavaBean editors.
					editor = PropertyEditorManager.findEditor(requiredType);
					if (editor == null) {
						unknownEditorTypes.put(requiredType, Boolean.TRUE);
					}
				}
			}
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		}

		if (requiredType != null) {
			// Try to apply some standard type conversion rules if appropriate.

			if (convertedValue != null) {
				if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
					// We can stringify any primitive value...
					return convertedValue.toString();
				}
				else if (requiredType.isArray()) {
					// Array required -> apply appropriate conversion of elements.
					return convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
				}
				else if (convertedValue instanceof Collection && CollectionFactory.isApproximableCollectionType(requiredType)) {
					// Convert elements to target type, if determined.
					convertedValue = convertToTypedCollection((Collection) convertedValue, propertyName, methodParam);
				}
				else if (convertedValue instanceof Map && CollectionFactory.isApproximableMapType(requiredType)) {
					// Convert keys and values to respective target type, if determined.
					convertedValue = convertToTypedMap((Map) convertedValue, propertyName, methodParam);
				}
				else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
					String strValue = ((String) convertedValue).trim();
					if (JdkVersion.isAtLeastJava15() && requiredType.isEnum() && "".equals(strValue)) {
						// It's an empty enum identifier: reset the enum value to null.
						return null;
					}
					// Try field lookup as fallback: for JDK 1.5 enum or custom enum
					// with values defined as static fields. Resulting value still needs
					// to be checked, hence we don't return it right away.
					try {
						Field enumField = requiredType.getField(strValue);
						convertedValue = enumField.get(null);
					}
					catch (Throwable ex) {
						if (logger.isTraceEnabled()) {
							logger.trace("Field [" + convertedValue + "] isn't an enum value", ex);
						}
					}
				}
			}

			if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
				// Definitely doesn't match: throw IllegalArgumentException.
				throw new IllegalArgumentException("Cannot convert value of type [" +
						(newValue != null ? ClassUtils.getQualifiedName(newValue.getClass()) : null) +
						"] to required type [" + ClassUtils.getQualifiedName(requiredType) + "]" +
						(propertyName != null ? " for property '" + propertyName + "'" : "") +
						": no matching editors or conversion strategy found");
			}
		}

		return convertedValue;
	}

	/**
	 * Convert the value to the required type (if necessary from a String),
	 * using the given property editor.
	 * @param oldValue the previous value, if available (may be <code>null)
	 * @param newValue the proposed new value
	 * @param requiredType the type we must convert to
	 * (or <code>null if not known, for example in case of a collection element)
	 * @param editor the PropertyEditor to use
	 * @return the new value, possibly the result of type conversion
	 * @throws IllegalArgumentException if type conversion failed
	 */
	protected Object doConvertValue(Object oldValue, Object newValue, Class requiredType, PropertyEditor editor) {
		Object convertedValue = newValue;
		boolean sharedEditor = false;

		if (editor != null) {
			sharedEditor = this.propertyEditorRegistry.isSharedEditor(editor);
		}

		if (editor != null && !(convertedValue instanceof String)) {
			// Not a String -> use PropertyEditor's setValue.
			// With standard PropertyEditors, this will return the very same object;
			// we just want to allow special PropertyEditors to override setValue
			// for type conversion from non-String values to the required type.
			try {
				Object newConvertedValue = null;
				if (sharedEditor) {
					// Synchronized access to shared editor instance.
					synchronized (editor) {
						editor.setValue(convertedValue);
						newConvertedValue = editor.getValue();
					}
				}
				else {
					// Unsynchronized access to non-shared editor instance.
					editor.setValue(convertedValue);
					newConvertedValue = editor.getValue();
				}
				if (newConvertedValue != convertedValue) {
					convertedValue = newConvertedValue;
					// Reset PropertyEditor: It already did a proper conversion.
					// Don't use it again for a setAsText call.
					editor = null;
				}
			}
			catch (Exception ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
				}
				// Swallow and proceed.
			}
		}

		if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
			// Convert String array to a comma-separated String.
			// Only applies if no PropertyEditor converted the String array before.
			// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
			if (logger.isTraceEnabled()) {
				logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
			}
			convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
		}

		if (editor != null && convertedValue instanceof String) {
			// Use PropertyEditor's setAsText in case of a String value.
			if (logger.isTraceEnabled()) {
				logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
			}
			String newTextValue = (String) convertedValue;
			if (sharedEditor) {
				// Synchronized access to shared editor instance.
				synchronized (editor) {
					return doConvertTextValue(oldValue, newTextValue, editor);
				}
			}
			else {
				// Unsynchronized access to non-shared editor instance.
				return doConvertTextValue(oldValue, newTextValue, editor);
			}
		}

		return convertedValue;
	}

	/**
	 * Convert the given text value using the given property editor.
	 * @param oldValue the previous value, if available (may be <code>null)
	 * @param newTextValue the proposed text value
	 * @param editor the PropertyEditor to use
	 * @return the converted value
	 */
	protected Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
		try {
			editor.setValue(oldValue);
		}
		catch (Exception ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
			}
			// Swallow and proceed.
		}
		editor.setAsText(newTextValue);
		return editor.getValue();
	}

	protected Object convertToTypedArray(Object input, String propertyName, Class componentType) {
		if (input instanceof Collection) {
			// Convert Collection elements to array elements.
			Collection coll = (Collection) input;
			Object result = Array.newInstance(componentType, coll.size());
			int i = 0;
			for (Iterator it = coll.iterator(); it.hasNext(); i++) {
				Object value = convertIfNecessary(
						buildIndexedPropertyName(propertyName, i), null, it.next(), componentType);
				Array.set(result, i, value);
			}
			return result;
		}
		else if (input.getClass().isArray()) {
			// Convert array elements, if necessary.
			if (componentType.equals(input.getClass().getComponentType()) &&
					!this.propertyEditorRegistry.hasCustomEditorForElement(componentType, propertyName)) {
				return input;
			}
			int arrayLength = Array.getLength(input);
			Object result = Array.newInstance(componentType, arrayLength);
			for (int i = 0; i < arrayLength; i++) {
				Object value = convertIfNecessary(
						buildIndexedPropertyName(propertyName, i), null, Array.get(input, i), componentType);
				Array.set(result, i, value);
			}
			return result;
		}
		else {
			// A plain value: convert it to an array with a single component.
			Object result = Array.newInstance(componentType, 1);
			Object value = convertIfNecessary(
					buildIndexedPropertyName(propertyName, 0), null, input, componentType);
			Array.set(result, 0, value);
			return result;
		}
	}

	protected Collection convertToTypedCollection(
			Collection original, String propertyName, MethodParameter methodParam) {

		Class elementType = null;
		if (methodParam != null && JdkVersion.isAtLeastJava15()) {
			elementType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
		}
		if (elementType == null &&
				!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
			return original;
		}

		Collection convertedCopy = null;
		Iterator it = null;
		try {
			it = original.iterator();
			if (it == null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Collection of type [" + original.getClass().getName() +
							"] returned null Iterator - injecting original Collection as-is");
				}
				return original;
			}
			convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Cannot access Collection of type [" + original.getClass().getName() +
						"] - injecting original Collection as-is", ex);
			}
			return original;
		}
		boolean actuallyConverted = false;
		int i = 0;
		for (; it.hasNext(); i++) {
			Object element = it.next();
			String indexedPropertyName = buildIndexedPropertyName(propertyName, i);
			if (methodParam != null) {
				methodParam.increaseNestingLevel();
			}
			Object convertedElement =
					convertIfNecessary(indexedPropertyName, null, element, elementType, null, methodParam);
			if (methodParam != null) {
				methodParam.decreaseNestingLevel();
			}
			convertedCopy.add(convertedElement);
			actuallyConverted = actuallyConverted || (element != convertedElement);
		}
		return (actuallyConverted ? convertedCopy : original);
	}

	protected Map convertToTypedMap(Map original, String propertyName, MethodParameter methodParam) {
		Class keyType = null;
		Class valueType = null;
		if (methodParam != null && JdkVersion.isAtLeastJava15()) {
			keyType = GenericCollectionTypeResolver.getMapKeyParameterType(methodParam);
			valueType = GenericCollectionTypeResolver.getMapValueParameterType(methodParam);
		}
		if (keyType == null && valueType == null &&
				!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
			return original;
		}

		Map convertedCopy = null;
		Iterator it = null;
		try {
			it = original.entrySet().iterator();
			if (it == null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Map of type [" + original.getClass().getName() +
							"] returned null Iterator - injecting original Map as-is");
				}
			}
			convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
		}
		catch (Throwable ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Cannot access Map of type [" + original.getClass().getName() +
						"] - injecting original Map as-is", ex);
			}
			return original;
		}
		boolean actuallyConverted = false;
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			Object key = entry.getKey();
			Object value = entry.getValue();
			String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
			if (methodParam != null) {
				methodParam.increaseNestingLevel();
				methodParam.setTypeIndexForCurrentLevel(0);
			}
			Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, null, methodParam);
			if (methodParam != null) {
				methodParam.setTypeIndexForCurrentLevel(1);
			}
			Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, null, methodParam);
			if (methodParam != null) {
				methodParam.decreaseNestingLevel();
			}
			convertedCopy.put(convertedKey, convertedValue);
			actuallyConverted = actuallyConverted || (key != convertedKey) || (value != convertedValue);
		}
		return (actuallyConverted ? convertedCopy : original);
	}

	private String buildIndexedPropertyName(String propertyName, int index) {
		return (propertyName != null ?
				propertyName + PropertyAccessor.PROPERTY_KEY_PREFIX + index + PropertyAccessor.PROPERTY_KEY_SUFFIX :
				null);
	}

	private String buildKeyedPropertyName(String propertyName, Object key) {
		return (propertyName != null ?
				propertyName + PropertyAccessor.PROPERTY_KEY_PREFIX + key + PropertyAccessor.PROPERTY_KEY_SUFFIX :
				null);
	}

}

Other Spring Framework examples (source code examples)

Here is a short list of links related to this Spring Framework TypeConverterDelegate.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.