initial JSR-303 Bean Validation support; revised ConversionService and FormatterRegistry

This commit is contained in:
Juergen Hoeller
2009-09-07 23:58:42 +00:00
parent f9f9b431a6
commit a86a698e5b
72 changed files with 1808 additions and 848 deletions

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2002-2009 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.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ReflectionUtils;
/**
* {@link TypeDescriptor} extension that exposes additional annotations
* as conversion metadata: namely, annotations on other accessor methods
* (getter/setter) and on the underlying field, if found.
*
* @author Juergen Hoeller
* @since 3.0
*/
class BeanTypeDescriptor extends TypeDescriptor {
private final PropertyDescriptor propertyDescriptor;
private Annotation[] cachedAnnotations;
/**
* Create a new BeanTypeDescriptor for the given bean property.
* @param methodParameter the target method parameter
* @param propertyDescriptor the corresponding JavaBean PropertyDescriptor
*/
public BeanTypeDescriptor(MethodParameter methodParameter, PropertyDescriptor propertyDescriptor) {
super(methodParameter);
this.propertyDescriptor = propertyDescriptor;
}
@Override
public Annotation[] getAnnotations() {
Annotation[] anns = this.cachedAnnotations;
if (anns == null) {
Field underlyingField = ReflectionUtils.findField(
getMethodParameter().getMethod().getDeclaringClass(), this.propertyDescriptor.getName());
Map<Class, Annotation> annMap = new LinkedHashMap<Class, Annotation>();
if (underlyingField != null) {
for (Annotation ann : underlyingField.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method targetMethod = getMethodParameter().getMethod();
Method writeMethod = this.propertyDescriptor.getWriteMethod();
Method readMethod = this.propertyDescriptor.getReadMethod();
if (writeMethod != null && writeMethod != targetMethod) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
if (readMethod != null && readMethod != targetMethod) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
for (Annotation ann : targetMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
anns = annMap.values().toArray(new Annotation[annMap.size()]);
this.cachedAnnotations = anns;
}
return anns;
}
}

View File

@@ -333,10 +333,10 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd != null) {
if (pd.getReadMethod() != null) {
return new TypeDescriptor(new MethodParameter(pd.getReadMethod(), -1));
return new BeanTypeDescriptor(new MethodParameter(pd.getReadMethod(), -1), pd);
}
else if (pd.getWriteMethod() != null) {
return new TypeDescriptor(new MethodParameter(pd.getWriteMethod(), 0));
return new BeanTypeDescriptor(BeanUtils.getWriteMethodParameter(pd), pd);
}
}
}
@@ -947,7 +947,7 @@ public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWra
writeMethod.invoke(object, value);
return null;
}
},acc);
}, acc);
} catch (PrivilegedActionException ex) {
throw ex.getException();
}

View File

@@ -89,7 +89,8 @@ public interface PropertyAccessor {
Class getPropertyType(String propertyName) throws BeansException;
/**
* Return a type descriptor for the specified property.
* Return a type descriptor for the specified property:
* preferably from the read method, falling back to the write method.
* @param propertyName the property to check
* (may be a nested path and/or an indexed/mapped property)
* @return the property type for the particular property,

View File

@@ -165,13 +165,17 @@ class TypeConverterDelegate {
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null &&
conversionService.canConvert(convertedValue.getClass(), requiredType)) {
if (editor == null && conversionService != null && convertedValue != null) {
TypeDescriptor typeDesc;
if (methodParam != null) {
return (T) conversionService.convert(convertedValue, new TypeDescriptor(methodParam));
typeDesc = (descriptor != null ?
new BeanTypeDescriptor(methodParam, descriptor) : new TypeDescriptor(methodParam));
}
else {
return conversionService.convert(convertedValue, requiredType);
typeDesc = TypeDescriptor.valueOf(requiredType);
}
if (conversionService.canConvert(convertedValue.getClass(), typeDesc)) {
return (T) conversionService.convert(convertedValue, typeDesc);
}
}
@@ -353,21 +357,26 @@ class TypeConverterDelegate {
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) {
if (convertedValue instanceof String) {
if (editor != null) {
// 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);
}
}
else {
// Unsynchronized access to non-shared editor instance.
return doConvertTextValue(oldValue, newTextValue, editor);
else if (String.class.equals(requiredType)) {
returnValue = convertedValue;
}
}