/*
 * Copyright 2002-2018 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
 *
 *      https://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.lang.Nullable;
import org.springframework.util.Assert;

Abstract base class for BindingResult implementations that work with Spring's PropertyAccessor mechanism. Pre-implements field access through delegation to the corresponding PropertyAccessor methods.
Author:Juergen Hoeller
See Also:
Since:2.0
/** * 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 { @Nullable private transient ConversionService conversionService;
Create a new AbstractPropertyBindingResult instance.
Params:
  • objectName – the name of the target object
See Also:
/** * 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 Also:
  • getPropertyAccessor()
/** * Returns the underlying PropertyAccessor. * @see #getPropertyAccessor() */
@Override public PropertyEditorRegistry getPropertyEditorRegistry() { return (getTarget() != null ? getPropertyAccessor() : null); }
Returns the canonical property name.
See Also:
  • canonicalPropertyName.canonicalPropertyName
/** * 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 Also:
  • getPropertyAccessor()
/** * Determines the field type from the property type. * @see #getPropertyAccessor() */
@Override @Nullable public Class<?> getFieldType(@Nullable String field) { return (getTarget() != null ? getPropertyAccessor().getPropertyType(fixedField(field)) : super.getFieldType(field)); }
Fetches the field value from the PropertyAccessor.
See Also:
  • getPropertyAccessor()
/** * Fetches the field value from the PropertyAccessor. * @see #getPropertyAccessor() */
@Override @Nullable protected Object getActualFieldValue(String field) { return getPropertyAccessor().getPropertyValue(field); }
Formats the field value based on registered PropertyEditors.
See Also:
  • getCustomEditor
/** * Formats the field value based on registered PropertyEditors. * @see #getCustomEditor */
@Override protected Object formatFieldValue(String field, @Nullable 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.
Params:
  • fixedField – the fully qualified field name
Returns:the custom PropertyEditor, or null
/** * Retrieve the custom PropertyEditor for the given field, if any. * @param fixedField the fully qualified field name * @return the custom PropertyEditor, or {@code null} */
@Nullable 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.
/** * This implementation exposes a PropertyEditor adapter for a Formatter, * if applicable. */
@Override @Nullable public PropertyEditor findEditor(@Nullable String field, @Nullable 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 && getTarget() != null) { TypeDescriptor ptd = getPropertyAccessor().getPropertyTypeDescriptor(fixedField(field)); if (ptd != null && (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.

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 Also:
  • setExtractOldValueForEditor.setExtractOldValueForEditor
/** * 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(); }