/* * Copyright 2002-2016 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.validation; import java.beans.PropertyEditor; import org.springframework.beans.BeanUtils; import org.springframework.beans.ConfigurablePropertyAccessor; import org.springframework.beans.PropertyAccessorUtils; import org.springframework.beans.PropertyEditorRegistry; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.ConvertingPropertyEditorAdapter; import org.springframework.util.Assert; /** * Abstract base class for {@link BindingResult} implementations that work with * Spring's {@link org.springframework.beans.PropertyAccessor} mechanism. * Pre-implements field access through delegation to the corresponding * PropertyAccessor methods. * * @author Juergen Hoeller * @since 2.0 * @see #getPropertyAccessor() * @see org.springframework.beans.PropertyAccessor * @see org.springframework.beans.ConfigurablePropertyAccessor */ @SuppressWarnings("serial") public abstract class AbstractPropertyBindingResult extends AbstractBindingResult { private transient ConversionService conversionService; /** * Create a new AbstractPropertyBindingResult instance. * @param objectName the name of the target object * @see DefaultMessageCodesResolver */ protected AbstractPropertyBindingResult(String objectName) { super(objectName); } public void initConversion(ConversionService conversionService) { Assert.notNull(conversionService, "ConversionService must not be null"); this.conversionService = conversionService; if (getTarget() != null) { getPropertyAccessor().setConversionService(conversionService); } } /** * Returns the underlying PropertyAccessor. * @see #getPropertyAccessor() */ @Override public PropertyEditorRegistry getPropertyEditorRegistry() { return getPropertyAccessor(); } /** * Returns the canonical property name. * @see org.springframework.beans.PropertyAccessorUtils#canonicalPropertyName */ @Override protected String canonicalFieldName(String field) { return PropertyAccessorUtils.canonicalPropertyName(field); } /** * Determines the field type from the property type. * @see #getPropertyAccessor() */ @Override public Class<?> getFieldType(String field) { return getPropertyAccessor().getPropertyType(fixedField(field)); } /** * Fetches the field value from the PropertyAccessor. * @see #getPropertyAccessor() */ @Override protected Object getActualFieldValue(String field) { return getPropertyAccessor().getPropertyValue(field); } /** * Formats the field value based on registered PropertyEditors. * @see #getCustomEditor */ @Override protected Object formatFieldValue(String field, Object value) { String fixedField = fixedField(field); // Try custom editor... PropertyEditor customEditor = getCustomEditor(fixedField); if (customEditor != null) { customEditor.setValue(value); String textValue = customEditor.getAsText(); // If the PropertyEditor returned null, there is no appropriate // text representation for this value: only use it if non-null. if (textValue != null) { return textValue; } } if (this.conversionService != null) { // Try custom converter... TypeDescriptor fieldDesc = getPropertyAccessor().getPropertyTypeDescriptor(fixedField); TypeDescriptor strDesc = TypeDescriptor.valueOf(String.class); if (fieldDesc != null && this.conversionService.canConvert(fieldDesc, strDesc)) { return this.conversionService.convert(value, fieldDesc, strDesc); } } return value; } /** * Retrieve the custom PropertyEditor for the given field, if any. * @param fixedField the fully qualified field name * @return the custom PropertyEditor, or {@code null} */ protected PropertyEditor getCustomEditor(String fixedField) { Class<?> targetType = getPropertyAccessor().getPropertyType(fixedField); PropertyEditor editor = getPropertyAccessor().findCustomEditor(targetType, fixedField); if (editor == null) { editor = BeanUtils.findEditorByConvention(targetType); } return editor; } /** * This implementation exposes a PropertyEditor adapter for a Formatter, * if applicable. */ @Override public PropertyEditor findEditor(String field, Class<?> valueType) { Class<?> valueTypeForLookup = valueType; if (valueTypeForLookup == null) { valueTypeForLookup = getFieldType(field); } PropertyEditor editor = super.findEditor(field, valueTypeForLookup); if (editor == null && this.conversionService != null) { TypeDescriptor td = null; if (field != null) { TypeDescriptor ptd = getPropertyAccessor().getPropertyTypeDescriptor(fixedField(field)); if (valueType == null || valueType.isAssignableFrom(ptd.getType())) { td = ptd; } } if (td == null) { td = TypeDescriptor.valueOf(valueTypeForLookup); } if (this.conversionService.canConvert(TypeDescriptor.valueOf(String.class), td)) { editor = new ConvertingPropertyEditorAdapter(this.conversionService, td); } } return editor; } /** * Provide the PropertyAccessor to work with, according to the * concrete strategy of access. * <p>Note that a PropertyAccessor used by a BindingResult should * always have its "extractOldValueForEditor" flag set to "true" * by default, since this is typically possible without side effects * for model objects that serve as data binding target. * @see ConfigurablePropertyAccessor#setExtractOldValueForEditor */ public abstract ConfigurablePropertyAccessor getPropertyAccessor(); }