diff --git a/spring-context/src/main/java/org/springframework/format/FormatterRegistry.java b/spring-context/src/main/java/org/springframework/format/FormatterRegistry.java index 0d1024bb4b5..94591b90458 100644 --- a/spring-context/src/main/java/org/springframework/format/FormatterRegistry.java +++ b/spring-context/src/main/java/org/springframework/format/FormatterRegistry.java @@ -29,6 +29,24 @@ import org.springframework.core.convert.converter.ConverterRegistry; */ public interface FormatterRegistry extends ConverterRegistry { + /** + * Adds a Printer to print fields of a specific type. + * The field type is implied by the parameterized Printer instance. + * @param printer the printer to add + * @since 5.2 + * @see #addFormatter(Formatter) + */ + void addPrinter(Printer printer); + + /** + * Adds a Parser to parse fields of a specific type. + * The field type is implied by the parameterized Parser instance. + * @param parser the parser to add + * @since 5.2 + * @see #addFormatter(Formatter) + */ + void addParser(Parser parser); + /** * Adds a Formatter to format fields of a specific type. * The field type is implied by the parameterized Formatter instance. diff --git a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java index 45793aca5e8..661c198fd49 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -37,6 +37,8 @@ import org.springframework.format.FormatterRegistry; import org.springframework.format.Parser; import org.springframework.format.Printer; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.util.StringValueResolver; @@ -65,6 +67,18 @@ public class FormattingConversionService extends GenericConversionService } + @Override + public void addPrinter(Printer printer) { + Class fieldType = getFieldType(printer, Printer.class); + addConverter(new PrinterConverter(fieldType, printer, this)); + } + + @Override + public void addParser(Parser parser) { + Class fieldType = getFieldType(parser, Parser.class); + addConverter(new ParserConverter(fieldType, parser, this)); + } + @Override public void addFormatter(Formatter formatter) { addFormatterForFieldType(getFieldType(formatter), formatter); @@ -97,15 +111,18 @@ public class FormattingConversionService extends GenericConversionService static Class getFieldType(Formatter formatter) { - Class fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class); - if (fieldType == null && formatter instanceof DecoratingProxy) { + return getFieldType(formatter, Formatter.class); + } + + private static Class getFieldType(T instance, Class genericInterface) { + Class fieldType = GenericTypeResolver.resolveTypeArgument(instance.getClass(), genericInterface); + if (fieldType == null && instance instanceof DecoratingProxy) { fieldType = GenericTypeResolver.resolveTypeArgument( - ((DecoratingProxy) formatter).getDecoratedClass(), Formatter.class); - } - if (fieldType == null) { - throw new IllegalArgumentException("Unable to extract the parameterized field type from Formatter [" + - formatter.getClass().getName() + "]; does the class parameterize the generic type?"); + ((DecoratingProxy) instance).getDecoratedClass(), genericInterface); } + Assert.notNull(fieldType, () -> "Unable to extract the parameterized field type from " + + ClassUtils.getShortName(genericInterface) + " [" + instance.getClass().getName() + + "]; does the class parameterize the generic type?"); return fieldType; } diff --git a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java index b31b751ed87..50e9de1fab0 100644 --- a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java +++ b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java @@ -346,8 +346,25 @@ public class FormattingConversionServiceTests { @Test public void introspectedFormatter() throws ParseException { - formattingService.addFormatter(new NumberStyleFormatter()); - assertThat(formattingService.convert(null, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class))).isNull(); + formattingService.addFormatter(new NumberStyleFormatter("#,#00.0#")); + assertThat(formattingService.convert(123, String.class)).isEqualTo("123.0"); + assertThat(formattingService.convert("123.0", Integer.class)).isEqualTo(123); + } + + @Test + public void introspectedPrinter() throws ParseException { + formattingService.addPrinter(new NumberStyleFormatter("#,#00.0#")); + assertThat(formattingService.convert(123, String.class)).isEqualTo("123.0"); + assertThatExceptionOfType(ConversionFailedException.class).isThrownBy(() -> + assertThat(formattingService.convert("123.0", Integer.class)).isNull()) + .withCauseInstanceOf(NumberFormatException.class); + } + + @Test + public void introspectedParser() throws ParseException { + formattingService.addParser(new NumberStyleFormatter("#,#00.0#")); + assertThat(formattingService.convert("123.0", Integer.class)).isEqualTo(123); + assertThat(formattingService.convert(123, String.class)).isEqualTo("123"); } @Test