initial JSR-303 Bean Validation support; revised ConversionService and FormatterRegistry
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user