Browse Source

DataBinder allows for adding custom Formatters as alternative to PropertyEditors (including per-field formatters)

Includes a generic FormatterPropertyEditorAdapter plus Number conversion support in TypeConverterDelegate.

Issue: SPR-7773
Issue: SPR-6069
pull/763/merge
Juergen Hoeller 10 years ago
parent
commit
02da2e85ee
  1. 11
      spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java
  2. 76
      spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java
  3. 53
      spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java
  4. 63
      spring-context/src/main/java/org/springframework/validation/DataBinder.java
  5. 263
      spring-context/src/test/java/org/springframework/validation/DataBinderTests.java

11
spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -34,6 +34,7 @@ import org.springframework.core.convert.ConversionFailedException; @@ -34,6 +34,7 @@ import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.NumberUtils;
import org.springframework.util.StringUtils;
/**
@ -204,7 +205,7 @@ class TypeConverterDelegate { @@ -204,7 +205,7 @@ class TypeConverterDelegate {
if (Object.class.equals(requiredType)) {
return (T) convertedValue;
}
if (requiredType.isArray()) {
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
@ -257,6 +258,11 @@ class TypeConverterDelegate { @@ -257,6 +258,11 @@ class TypeConverterDelegate {
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class<Number>) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null
@ -339,7 +345,6 @@ class TypeConverterDelegate { @@ -339,7 +345,6 @@ class TypeConverterDelegate {
catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Field [" + convertedValue + "] isn't an enum value", ex);
}
}
}

76
spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
/*
* Copyright 2002-2015 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.format.support;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.format.Formatter;
import org.springframework.util.Assert;
/**
* Adapter that bridges between {@link Formatter} and {@link PropertyEditor}.
*
* @author Juergen Hoeller
* @since 4.2
*/
public class FormatterPropertyEditorAdapter extends PropertyEditorSupport {
private final Formatter<Object> formatter;
/**
* Create a new {@code FormatterPropertyEditorAdapter} for the given {@link Formatter}.
* @param formatter the {@link Formatter} to wrap
*/
@SuppressWarnings("unchecked")
public FormatterPropertyEditorAdapter(Formatter<?> formatter) {
Assert.notNull(formatter, "Formatter must not be null");
this.formatter = (Formatter<Object>) formatter;
}
/**
* Determine the {@link Formatter}-declared field type.
* @return the field type declared in the wrapped {@link Formatter} implementation
* (never {@code null})
* @throws IllegalArgumentException if the {@link Formatter}-declared field type
* cannot be inferred
*/
public Class<?> getFieldType() {
return FormattingConversionService.getFieldType(this.formatter);
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
setValue(this.formatter.parse(text, LocaleContextHolder.getLocale()));
}
catch (ParseException ex) {
throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex);
}
}
@Override
public String getAsText() {
return this.formatter.print(getValue(), LocaleContextHolder.getLocale());
}
}

53
spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -67,12 +67,7 @@ public class FormattingConversionService extends GenericConversionService @@ -67,12 +67,7 @@ public class FormattingConversionService extends GenericConversionService
@Override
public void addFormatter(Formatter<?> formatter) {
Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
if (fieldType == null) {
throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter [" +
formatter.getClass().getName() + "]; does the formatter parameterize the <T> generic type?");
}
addFormatterForFieldType(fieldType, formatter);
addFormatterForFieldType(getFieldType(formatter), formatter);
}
@Override
@ -88,14 +83,8 @@ public class FormattingConversionService extends GenericConversionService @@ -88,14 +83,8 @@ public class FormattingConversionService extends GenericConversionService
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory annotationFormatterFactory) {
Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
GenericTypeResolver.resolveTypeArgument(annotationFormatterFactory.getClass(), AnnotationFormatterFactory.class);
if (annotationType == null) {
throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from AnnotationFormatterFactory [" +
annotationFormatterFactory.getClass().getName() + "]; does the factory parameterize the <A extends Annotation> generic type?");
}
public void addFormatterForFieldAnnotation(AnnotationFormatterFactory<? extends Annotation> annotationFormatterFactory) {
Class<? extends Annotation> annotationType = getAnnotationType(annotationFormatterFactory);
if (this.embeddedValueResolver != null && annotationFormatterFactory instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) annotationFormatterFactory).setEmbeddedValueResolver(this.embeddedValueResolver);
}
@ -107,6 +96,28 @@ public class FormattingConversionService extends GenericConversionService @@ -107,6 +96,28 @@ public class FormattingConversionService extends GenericConversionService
}
static Class<?> getFieldType(Formatter<?> formatter) {
Class<?> fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
if (fieldType == null) {
throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter [" +
formatter.getClass().getName() + "]; does the formatter parameterize the <T> generic type?");
}
return fieldType;
}
@SuppressWarnings("unchecked")
static Class<? extends Annotation> getAnnotationType(AnnotationFormatterFactory<? extends Annotation> factory) {
Class<? extends Annotation> annotationType = (Class<? extends Annotation>)
GenericTypeResolver.resolveTypeArgument(factory.getClass(), AnnotationFormatterFactory.class);
if (annotationType == null) {
throw new IllegalArgumentException("Unable to extract parameterized Annotation type argument from " +
"AnnotationFormatterFactory [" + factory.getClass().getName() +
"]; does the factory parameterize the <A extends Annotation> generic type?");
}
return annotationType;
}
private static class PrinterConverter implements GenericConverter {
private final Class<?> fieldType;
@ -148,7 +159,7 @@ public class FormattingConversionService extends GenericConversionService @@ -148,7 +159,7 @@ public class FormattingConversionService extends GenericConversionService
@Override
public String toString() {
return this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer;
return (this.fieldType.getName() + " -> " + String.class.getName() + " : " + this.printer);
}
}
@ -197,7 +208,7 @@ public class FormattingConversionService extends GenericConversionService @@ -197,7 +208,7 @@ public class FormattingConversionService extends GenericConversionService
@Override
public String toString() {
return String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser;
return (String.class.getName() + " -> " + this.fieldType.getName() + ": " + this.parser);
}
}
@ -249,8 +260,8 @@ public class FormattingConversionService extends GenericConversionService @@ -249,8 +260,8 @@ public class FormattingConversionService extends GenericConversionService
@Override
public String toString() {
return "@" + this.annotationType.getName() + " " + this.fieldType.getName() + " -> " +
String.class.getName() + ": " + this.annotationFormatterFactory;
return ("@" + this.annotationType.getName() + " " + this.fieldType.getName() + " -> " +
String.class.getName() + ": " + this.annotationFormatterFactory);
}
}
@ -302,8 +313,8 @@ public class FormattingConversionService extends GenericConversionService @@ -302,8 +313,8 @@ public class FormattingConversionService extends GenericConversionService
@Override
public String toString() {
return String.class.getName() + " -> @" + this.annotationType.getName() + " " +
this.fieldType.getName() + ": " + this.annotationFormatterFactory;
return (String.class.getName() + " -> @" + this.annotationType.getName() + " " +
this.fieldType.getName() + ": " + this.annotationFormatterFactory);
}
}

63
spring-context/src/main/java/org/springframework/validation/DataBinder.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2015 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.
@ -42,6 +42,8 @@ import org.springframework.beans.TypeConverter; @@ -42,6 +42,8 @@ import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.Formatter;
import org.springframework.format.support.FormatterPropertyEditorAdapter;
import org.springframework.lang.UsesJava8;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@ -553,6 +555,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { @@ -553,6 +555,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
return Collections.unmodifiableList(this.validators);
}
//---------------------------------------------------------------------
// Implementation of PropertyEditorRegistry/TypeConverter interface
//---------------------------------------------------------------------
@ -576,6 +579,64 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { @@ -576,6 +579,64 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
return this.conversionService;
}
/**
* Add a custom formatter, applying it to all fields matching the
* {@link Formatter}-declared type.
* <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
* @param formatter the formatter to add, generically declared for a specific type
* @since 4.2
* @see #registerCustomEditor(Class, PropertyEditor)
*/
public void addCustomFormatter(Formatter<?> formatter) {
FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter);
}
/**
* Add a custom formatter for the field type specified in {@link Formatter} class,
* applying it to the specified fields only, if any, or otherwise to all fields.
* <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
* @param formatter the formatter to add, generically declared for a specific type
* @param fields the fields to apply the formatter to, or none if to be applied to all
* @since 4.2
* @see #registerCustomEditor(Class, String, PropertyEditor)
*/
public void addCustomFormatter(Formatter<?> formatter, String... fields) {
FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
Class<?> fieldType = adapter.getFieldType();
if (ObjectUtils.isEmpty(fields)) {
getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter);
}
else {
for (String field : fields) {
getPropertyEditorRegistry().registerCustomEditor(fieldType, field, adapter);
}
}
}
/**
* Add a custom formatter, applying it to the specified field types only, if any,
* or otherwise to all fields matching the {@link Formatter}-declared type.
* <p>Registers a corresponding {@link PropertyEditor} adapter underneath the covers.
* @param formatter the formatter to add (does not need to generically declare a
* field type if field types are explicitly specified as parameters)
* @param fieldTypes the field types to apply the formatter to, or none if to be
* derived from the given {@link Formatter} implementation class
* @since 4.2
* @see #registerCustomEditor(Class, PropertyEditor)
*/
public void addCustomFormatter(Formatter<?> formatter, Class<?>... fieldTypes) {
FormatterPropertyEditorAdapter adapter = new FormatterPropertyEditorAdapter(formatter);
if (ObjectUtils.isEmpty(fieldTypes)) {
getPropertyEditorRegistry().registerCustomEditor(adapter.getFieldType(), adapter);
}
else {
for (Class<?> fieldType : fieldTypes) {
getPropertyEditorRegistry().registerCustomEditor(fieldType, adapter);
}
}
}
@Override
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor);

263
spring-context/src/test/java/org/springframework/validation/DataBinderTests.java

@ -35,7 +35,7 @@ import java.util.Map; @@ -35,7 +35,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import junit.framework.TestCase;
import org.junit.Test;
import org.springframework.beans.InvalidPropertyException;
import org.springframework.beans.MutablePropertyValues;
@ -59,13 +59,16 @@ import org.springframework.tests.sample.beans.TestBean; @@ -59,13 +59,16 @@ import org.springframework.tests.sample.beans.TestBean;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.junit.Assert.*;
/**
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
*/
public class DataBinderTests extends TestCase {
public class DataBinderTests {
@Test
public void testBindingNoErrors() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -99,7 +102,8 @@ public class DataBinderTests extends TestCase { @@ -99,7 +102,8 @@ public class DataBinderTests extends TestCase {
assertTrue(!other.equals(binder.getBindingResult()));
}
public void testedBindingWithDefaultConversionNoErrors() throws Exception {
@Test
public void testBindingWithDefaultConversionNoErrors() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
assertTrue(binder.isIgnoreUnknownFields());
@ -114,7 +118,8 @@ public class DataBinderTests extends TestCase { @@ -114,7 +118,8 @@ public class DataBinderTests extends TestCase {
assertTrue(rod.isJedi());
}
public void testedNestedBindingWithDefaultConversionNoErrors() throws Exception {
@Test
public void testNestedBindingWithDefaultConversionNoErrors() throws Exception {
TestBean rod = new TestBean(new TestBean());
DataBinder binder = new DataBinder(rod, "person");
assertTrue(binder.isIgnoreUnknownFields());
@ -129,6 +134,7 @@ public class DataBinderTests extends TestCase { @@ -129,6 +134,7 @@ public class DataBinderTests extends TestCase {
assertTrue(((TestBean) rod.getSpouse()).isJedi());
}
@Test
public void testBindingNoErrorsNotIgnoreUnknown() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -147,6 +153,7 @@ public class DataBinderTests extends TestCase { @@ -147,6 +153,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingNoErrorsWithInvalidField() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -163,6 +170,7 @@ public class DataBinderTests extends TestCase { @@ -163,6 +170,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingNoErrorsWithIgnoreInvalid() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -174,6 +182,7 @@ public class DataBinderTests extends TestCase { @@ -174,6 +182,7 @@ public class DataBinderTests extends TestCase {
binder.bind(pvs);
}
@Test
public void testBindingWithErrors() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -235,6 +244,7 @@ public class DataBinderTests extends TestCase { @@ -235,6 +244,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingWithSystemFieldError() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -251,6 +261,7 @@ public class DataBinderTests extends TestCase { @@ -251,6 +261,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingWithErrorsAndCustomEditors() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -317,6 +328,7 @@ public class DataBinderTests extends TestCase { @@ -317,6 +328,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingWithCustomEditorOnObjectField() {
BeanWithObjectProperty tb = new BeanWithObjectProperty();
DataBinder binder = new DataBinder(tb);
@ -327,6 +339,7 @@ public class DataBinderTests extends TestCase { @@ -327,6 +339,7 @@ public class DataBinderTests extends TestCase {
assertEquals(new Integer(1), tb.getObject());
}
@Test
public void testBindingWithFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@ -358,6 +371,7 @@ public class DataBinderTests extends TestCase { @@ -358,6 +371,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingErrorWithFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@ -380,12 +394,13 @@ public class DataBinderTests extends TestCase { @@ -380,12 +394,13 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingErrorWithStringFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
FormattingConversionService conversionService = new FormattingConversionService();
DefaultConversionService.addDefaultConverters(conversionService);
conversionService.addFormatterForFieldType(String.class, new Formatter<String>() {
conversionService.addFormatter(new Formatter<String>() {
@Override
public String parse(String text, Locale locale) throws ParseException {
throw new ParseException(text, 0);
@ -404,6 +419,7 @@ public class DataBinderTests extends TestCase { @@ -404,6 +419,7 @@ public class DataBinderTests extends TestCase {
assertEquals("test", binder.getBindingResult().getFieldValue("name"));
}
@Test
public void testBindingWithFormatterAgainstList() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
@ -425,6 +441,7 @@ public class DataBinderTests extends TestCase { @@ -425,6 +441,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingErrorWithFormatterAgainstList() {
BeanWithIntegerList tb = new BeanWithIntegerList();
DataBinder binder = new DataBinder(tb);
@ -447,6 +464,7 @@ public class DataBinderTests extends TestCase { @@ -447,6 +464,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingWithFormatterAgainstFields() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@ -479,6 +497,7 @@ public class DataBinderTests extends TestCase { @@ -479,6 +497,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingErrorWithFormatterAgainstFields() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
@ -502,6 +521,78 @@ public class DataBinderTests extends TestCase { @@ -502,6 +521,78 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testBindingWithCustomFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
binder.addCustomFormatter(new NumberStyleFormatter(), Float.class);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("myFloat", "1,2");
LocaleContextHolder.setLocale(Locale.GERMAN);
try {
binder.bind(pvs);
assertEquals(new Float(1.2), tb.getMyFloat());
assertEquals("1,2", binder.getBindingResult().getFieldValue("myFloat"));
PropertyEditor editor = binder.getBindingResult().findEditor("myFloat", Float.class);
assertNotNull(editor);
editor.setValue(new Float(1.4));
assertEquals("1,4", editor.getAsText());
editor = binder.getBindingResult().findEditor("myFloat", null);
assertNotNull(editor);
editor.setAsText("1,6");
assertTrue(((Number) editor.getValue()).floatValue() == 1.6f);
}
finally {
LocaleContextHolder.resetLocaleContext();
}
}
@Test
public void testBindingErrorWithCustomFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
binder.addCustomFormatter(new NumberStyleFormatter());
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("myFloat", "1x2");
LocaleContextHolder.setLocale(Locale.GERMAN);
try {
binder.bind(pvs);
assertEquals(new Float(0.0), tb.getMyFloat());
assertEquals("1x2", binder.getBindingResult().getFieldValue("myFloat"));
assertTrue(binder.getBindingResult().hasFieldErrors("myFloat"));
}
finally {
LocaleContextHolder.resetLocaleContext();
}
}
@Test
public void testBindingErrorWithCustomStringFormatter() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb);
binder.addCustomFormatter(new Formatter<String>() {
@Override
public String parse(String text, Locale locale) throws ParseException {
throw new ParseException(text, 0);
}
@Override
public String print(String object, Locale locale) {
return object;
}
});
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "test");
binder.bind(pvs);
assertTrue(binder.getBindingResult().hasFieldErrors("name"));
assertEquals("test", binder.getBindingResult().getFieldValue("name"));
}
@Test
public void testBindingWithAllowedFields() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
@ -516,6 +607,7 @@ public class DataBinderTests extends TestCase { @@ -516,6 +607,7 @@ public class DataBinderTests extends TestCase {
assertTrue("did not change age", rod.getAge() == 0);
}
@Test
public void testBindingWithDisallowedFields() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
@ -533,6 +625,7 @@ public class DataBinderTests extends TestCase { @@ -533,6 +625,7 @@ public class DataBinderTests extends TestCase {
assertEquals("age", disallowedFields[0]);
}
@Test
public void testBindingWithAllowedAndDisallowedFields() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
@ -551,6 +644,7 @@ public class DataBinderTests extends TestCase { @@ -551,6 +644,7 @@ public class DataBinderTests extends TestCase {
assertEquals("age", disallowedFields[0]);
}
@Test
public void testBindingWithOverlappingAllowedAndDisallowedFields() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
@ -569,6 +663,7 @@ public class DataBinderTests extends TestCase { @@ -569,6 +663,7 @@ public class DataBinderTests extends TestCase {
assertEquals("age", disallowedFields[0]);
}
@Test
public void testBindingWithAllowedFieldsUsingAsterisks() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -595,6 +690,7 @@ public class DataBinderTests extends TestCase { @@ -595,6 +690,7 @@ public class DataBinderTests extends TestCase {
assertTrue("Same object", tb.equals(rod));
}
@Test
public void testBindingWithAllowedAndDisallowedMapFields() throws Exception {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod);
@ -622,6 +718,7 @@ public class DataBinderTests extends TestCase { @@ -622,6 +718,7 @@ public class DataBinderTests extends TestCase {
/**
* Tests for required field, both null, non-existing and empty strings.
*/
@Test
public void testBindingWithRequiredFields() throws Exception {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@ -652,6 +749,7 @@ public class DataBinderTests extends TestCase { @@ -652,6 +749,7 @@ public class DataBinderTests extends TestCase {
assertEquals("", br.getFieldValue("spouse.name"));
}
@Test
public void testBindingWithRequiredMapFields() throws Exception {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@ -671,6 +769,7 @@ public class DataBinderTests extends TestCase { @@ -671,6 +769,7 @@ public class DataBinderTests extends TestCase {
assertEquals("required", br.getFieldError("someMap[key4]").getCode());
}
@Test
public void testBindingWithNestedObjectCreation() throws Exception {
TestBean tb = new TestBean();
@ -691,6 +790,32 @@ public class DataBinderTests extends TestCase { @@ -691,6 +790,32 @@ public class DataBinderTests extends TestCase {
assertEquals("test", tb.getSpouse().getName());
}
@Test
public void testCustomEditorWithOldValueAccess() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(String.class, null, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (getValue() == null || !text.equalsIgnoreCase(getValue().toString())) {
setValue(text);
}
}
});
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "value");
binder.bind(pvs);
assertEquals("value", tb.getName());
pvs = new MutablePropertyValues();
pvs.add("name", "vaLue");
binder.bind(pvs);
assertEquals("value", tb.getName());
}
@Test
public void testCustomEditorForSingleProperty() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@ -730,6 +855,7 @@ public class DataBinderTests extends TestCase { @@ -730,6 +855,7 @@ public class DataBinderTests extends TestCase {
assertEquals("spouse.name", binder.getBindingResult().getFieldError("spouse.*").getField());
}
@Test
public void testCustomEditorForPrimitiveProperty() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -739,6 +865,7 @@ public class DataBinderTests extends TestCase { @@ -739,6 +865,7 @@ public class DataBinderTests extends TestCase {
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Integer(99));
}
@Override
public String getAsText() {
return "argh";
@ -753,6 +880,7 @@ public class DataBinderTests extends TestCase { @@ -753,6 +880,7 @@ public class DataBinderTests extends TestCase {
assertEquals(99, tb.getAge());
}
@Test
public void testCustomEditorForAllStringProperties() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -762,6 +890,7 @@ public class DataBinderTests extends TestCase { @@ -762,6 +890,7 @@ public class DataBinderTests extends TestCase {
public void setAsText(String text) throws IllegalArgumentException {
setValue("prefix" + text);
}
@Override
public String getAsText() {
return ((String) getValue()).substring(6);
@ -784,30 +913,104 @@ public class DataBinderTests extends TestCase { @@ -784,30 +913,104 @@ public class DataBinderTests extends TestCase {
assertEquals("prefixvalue", tb.getTouchy());
}
public void testCustomEditorWithOldValueAccess() {
@Test
public void testCustomFormatterForSingleProperty() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
DataBinder binder = new DataBinder(tb, "tb");
binder.registerCustomEditor(String.class, null, new PropertyEditorSupport() {
binder.addCustomFormatter(new Formatter<String>() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (getValue() == null || !text.equalsIgnoreCase(getValue().toString())) {
setValue(text);
public String parse(String text, Locale locale) throws ParseException {
return "prefix" + text;
}
@Override
public String print(String object, Locale locale) {
return object.substring(6);
}
});
}, "name");
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "value");
pvs.add("touchy", "value");
pvs.add("spouse.name", "sue");
binder.bind(pvs);
assertEquals("value", tb.getName());
pvs = new MutablePropertyValues();
pvs.add("name", "vaLue");
binder.getBindingResult().rejectValue("name", "someCode", "someMessage");
binder.getBindingResult().rejectValue("touchy", "someCode", "someMessage");
binder.getBindingResult().rejectValue("spouse.name", "someCode", "someMessage");
assertEquals("", binder.getBindingResult().getNestedPath());
assertEquals("value", binder.getBindingResult().getFieldValue("name"));
assertEquals("prefixvalue", binder.getBindingResult().getFieldError("name").getRejectedValue());
assertEquals("prefixvalue", tb.getName());
assertEquals("value", binder.getBindingResult().getFieldValue("touchy"));
assertEquals("value", binder.getBindingResult().getFieldError("touchy").getRejectedValue());
assertEquals("value", tb.getTouchy());
assertTrue(binder.getBindingResult().hasFieldErrors("spouse.*"));
assertEquals(1, binder.getBindingResult().getFieldErrorCount("spouse.*"));
assertEquals("spouse.name", binder.getBindingResult().getFieldError("spouse.*").getField());
}
@Test
public void testCustomFormatterForPrimitiveProperty() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.addCustomFormatter(new Formatter<Integer>() {
@Override
public Integer parse(String text, Locale locale) throws ParseException {
return 99;
}
@Override
public String print(Integer object, Locale locale) {
return "argh";
}
}, "age");
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("age", "");
binder.bind(pvs);
assertEquals("value", tb.getName());
assertEquals("argh", binder.getBindingResult().getFieldValue("age"));
assertEquals(99, tb.getAge());
}
@Test
public void testCustomFormatterForAllStringProperties() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
binder.addCustomFormatter(new Formatter<String>() {
@Override
public String parse(String text, Locale locale) throws ParseException {
return "prefix" + text;
}
@Override
public String print(String object, Locale locale) {
return object.substring(6);
}
});
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "value");
pvs.add("touchy", "value");
binder.bind(pvs);
binder.getBindingResult().rejectValue("name", "someCode", "someMessage");
binder.getBindingResult().rejectValue("touchy", "someCode", "someMessage");
assertEquals("value", binder.getBindingResult().getFieldValue("name"));
assertEquals("prefixvalue", binder.getBindingResult().getFieldError("name").getRejectedValue());
assertEquals("prefixvalue", tb.getName());
assertEquals("value", binder.getBindingResult().getFieldValue("touchy"));
assertEquals("prefixvalue", binder.getBindingResult().getFieldError("touchy").getRejectedValue());
assertEquals("prefixvalue", tb.getTouchy());
}
@Test
public void testJavaBeanPropertyConventions() {
Book book = new Book();
DataBinder binder = new DataBinder(book);
@ -831,6 +1034,7 @@ public class DataBinderTests extends TestCase { @@ -831,6 +1034,7 @@ public class DataBinderTests extends TestCase {
assertEquals(0, book.getNInStock());
}
@Test
public void testValidatorNoErrors() {
TestBean tb = new TestBean();
tb.setAge(33);
@ -894,6 +1098,7 @@ public class DataBinderTests extends TestCase { @@ -894,6 +1098,7 @@ public class DataBinderTests extends TestCase {
assertTrue(!errors.hasFieldErrors("name"));
}
@Test
public void testValidatorWithErrors() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@ -962,6 +1167,7 @@ public class DataBinderTests extends TestCase { @@ -962,6 +1167,7 @@ public class DataBinderTests extends TestCase {
assertEquals(new Integer(0), (errors.getFieldErrors("spouse.age").get(0)).getRejectedValue());
}
@Test
public void testValidatorWithErrorsAndCodesPrefix() {
TestBean tb = new TestBean();
tb.setSpouse(new TestBean());
@ -1033,6 +1239,7 @@ public class DataBinderTests extends TestCase { @@ -1033,6 +1239,7 @@ public class DataBinderTests extends TestCase {
assertEquals(new Integer(0), (errors.getFieldErrors("spouse.age").get(0)).getRejectedValue());
}
@Test
public void testValidatorWithNestedObjectNull() {
TestBean tb = new TestBean();
Errors errors = new BeanPropertyBindingResult(tb, "tb");
@ -1051,6 +1258,7 @@ public class DataBinderTests extends TestCase { @@ -1051,6 +1258,7 @@ public class DataBinderTests extends TestCase {
assertEquals(null, (errors.getFieldErrors("spouse").get(0)).getRejectedValue());
}
@Test
public void testNestedValidatorWithoutNestedPath() {
TestBean tb = new TestBean();
tb.setName("XXX");
@ -1064,6 +1272,7 @@ public class DataBinderTests extends TestCase { @@ -1064,6 +1272,7 @@ public class DataBinderTests extends TestCase {
assertEquals("tb", (errors.getGlobalErrors().get(0)).getObjectName());
}
@Test
public void testBindingStringArrayToIntegerSet() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1091,6 +1300,7 @@ public class DataBinderTests extends TestCase { @@ -1091,6 +1300,7 @@ public class DataBinderTests extends TestCase {
assertNull(tb.getSet());
}
@Test
public void testBindingNullToEmptyCollection() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1103,6 +1313,7 @@ public class DataBinderTests extends TestCase { @@ -1103,6 +1313,7 @@ public class DataBinderTests extends TestCase {
assertTrue(tb.getSet().isEmpty());
}
@Test
public void testBindingToIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1141,6 +1352,7 @@ public class DataBinderTests extends TestCase { @@ -1141,6 +1352,7 @@ public class DataBinderTests extends TestCase {
assertEquals("NOT_ROD", errors.getFieldError("map[key1].name").getCodes()[6]);
}
@Test
public void testBindingToNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
@ -1178,6 +1390,7 @@ public class DataBinderTests extends TestCase { @@ -1178,6 +1390,7 @@ public class DataBinderTests extends TestCase {
assertEquals("NOT_ROD", errors.getFieldError("array[0].nestedIndexedBean.list[0].name").getCodes()[8]);
}
@Test
public void testEditorForNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
@ -1203,6 +1416,7 @@ public class DataBinderTests extends TestCase { @@ -1203,6 +1416,7 @@ public class DataBinderTests extends TestCase {
assertEquals("test2", binder.getBindingResult().getFieldValue("array[1].nestedIndexedBean.list[1].name"));
}
@Test
public void testSpecificEditorForNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
@ -1228,6 +1442,7 @@ public class DataBinderTests extends TestCase { @@ -1228,6 +1442,7 @@ public class DataBinderTests extends TestCase {
assertEquals("test2", binder.getBindingResult().getFieldValue("array[1].nestedIndexedBean.list[1].name"));
}
@Test
public void testInnerSpecificEditorForNestedIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
tb.getArray()[0].setNestedIndexedBean(new IndexedTestBean());
@ -1253,6 +1468,7 @@ public class DataBinderTests extends TestCase { @@ -1253,6 +1468,7 @@ public class DataBinderTests extends TestCase {
assertEquals("test2", binder.getBindingResult().getFieldValue("array[1].nestedIndexedBean.list[1].name"));
}
@Test
public void testDirectBindingToIndexedField() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1305,6 +1521,7 @@ public class DataBinderTests extends TestCase { @@ -1305,6 +1521,7 @@ public class DataBinderTests extends TestCase {
assertEquals("NOT_NULL", errors.getFieldError("map[key0]").getCodes()[4]);
}
@Test
public void testDirectBindingToEmptyIndexedFieldWithRegisteredSpecificEditor() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1335,6 +1552,7 @@ public class DataBinderTests extends TestCase { @@ -1335,6 +1552,7 @@ public class DataBinderTests extends TestCase {
assertEquals("NOT_NULL", errors.getFieldError("map[key0]").getCodes()[5]);
}
@Test
public void testDirectBindingToEmptyIndexedFieldWithRegisteredGenericEditor() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1365,6 +1583,7 @@ public class DataBinderTests extends TestCase { @@ -1365,6 +1583,7 @@ public class DataBinderTests extends TestCase {
assertEquals("NOT_NULL", errors.getFieldError("map[key0]").getCodes()[5]);
}
@Test
public void testCustomEditorWithSubclass() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1398,6 +1617,7 @@ public class DataBinderTests extends TestCase { @@ -1398,6 +1617,7 @@ public class DataBinderTests extends TestCase {
assertEquals("arraya", errors.getFieldValue("array[0]"));
}
@Test
public void testBindToStringArrayWithArrayEditor() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1416,6 +1636,7 @@ public class DataBinderTests extends TestCase { @@ -1416,6 +1636,7 @@ public class DataBinderTests extends TestCase {
assertEquals("b2", tb.getStringArray()[1]);
}
@Test
public void testBindToStringArrayWithComponentEditor() {
TestBean tb = new TestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1434,6 +1655,7 @@ public class DataBinderTests extends TestCase { @@ -1434,6 +1655,7 @@ public class DataBinderTests extends TestCase {
assertEquals("Xb2", tb.getStringArray()[1]);
}
@Test
public void testBindingErrors() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -1460,6 +1682,7 @@ public class DataBinderTests extends TestCase { @@ -1460,6 +1682,7 @@ public class DataBinderTests extends TestCase {
assertEquals("Field Person Age did not have correct type", msg);
}
@Test
public void testAddAllErrors() {
TestBean rod = new TestBean();
DataBinder binder = new DataBinder(rod, "person");
@ -1478,6 +1701,7 @@ public class DataBinderTests extends TestCase { @@ -1478,6 +1701,7 @@ public class DataBinderTests extends TestCase {
assertEquals("badName", nameError.getCode());
}
@Test
public void testBindingWithResortedList() {
IndexedTestBean tb = new IndexedTestBean();
DataBinder binder = new DataBinder(tb, "tb");
@ -1495,6 +1719,7 @@ public class DataBinderTests extends TestCase { @@ -1495,6 +1719,7 @@ public class DataBinderTests extends TestCase {
assertEquals(tb1.getName(), binder.getBindingResult().getFieldValue("list[1].name"));
}
@Test
public void testRejectWithoutDefaultMessage() throws Exception {
TestBean tb = new TestBean();
tb.setName("myName");
@ -1512,6 +1737,7 @@ public class DataBinderTests extends TestCase { @@ -1512,6 +1737,7 @@ public class DataBinderTests extends TestCase {
assertEquals("invalid field", ms.getMessage(ex.getFieldError("age"), Locale.US));
}
@Test
public void testBindExceptionSerializable() throws Exception {
SerializablePerson tb = new SerializablePerson();
tb.setName("myName");
@ -1540,6 +1766,7 @@ public class DataBinderTests extends TestCase { @@ -1540,6 +1766,7 @@ public class DataBinderTests extends TestCase {
assertEquals("myName", ex2.getFieldValue("name"));
}
@Test
public void testTrackDisallowedFields() throws Exception {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@ -1559,6 +1786,7 @@ public class DataBinderTests extends TestCase { @@ -1559,6 +1786,7 @@ public class DataBinderTests extends TestCase {
assertEquals("beanName", disallowedFields[0]);
}
@Test
public void testAutoGrowWithinDefaultLimit() throws Exception {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@ -1570,6 +1798,7 @@ public class DataBinderTests extends TestCase { @@ -1570,6 +1798,7 @@ public class DataBinderTests extends TestCase {
assertEquals(5, testBean.getFriends().size());
}
@Test
public void testAutoGrowBeyondDefaultLimit() throws Exception {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@ -1586,6 +1815,7 @@ public class DataBinderTests extends TestCase { @@ -1586,6 +1815,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testAutoGrowWithinCustomLimit() throws Exception {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@ -1598,6 +1828,7 @@ public class DataBinderTests extends TestCase { @@ -1598,6 +1828,7 @@ public class DataBinderTests extends TestCase {
assertEquals(5, testBean.getFriends().size());
}
@Test
public void testAutoGrowBeyondCustomLimit() throws Exception {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@ -1615,6 +1846,7 @@ public class DataBinderTests extends TestCase { @@ -1615,6 +1846,7 @@ public class DataBinderTests extends TestCase {
}
}
@Test
public void testNestedGrowingList() {
Form form = new Form();
DataBinder binder = new DataBinder(form, "form");
@ -1630,6 +1862,7 @@ public class DataBinderTests extends TestCase { @@ -1630,6 +1862,7 @@ public class DataBinderTests extends TestCase {
assertEquals(2, list.size());
}
@Test
public void testFieldErrorAccessVariations() throws Exception {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");

Loading…
Cancel
Save