Polish "Add Printer and Parser beans to conversion service"
Extract common registration code and make use of the Spring Framework registration methods. See gh-17064
This commit is contained in:
parent
955eaa87ae
commit
9db20313a1
|
@ -17,7 +17,6 @@
|
||||||
package org.springframework.boot.autoconfigure.web.reactive;
|
package org.springframework.boot.autoconfigure.web.reactive;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -38,20 +37,14 @@ import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceCh
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||||
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.convert.ParserConverter;
|
import org.springframework.boot.convert.ApplicationConversionService;
|
||||||
import org.springframework.boot.convert.PrinterConverter;
|
|
||||||
import org.springframework.boot.web.codec.CodecCustomizer;
|
import org.springframework.boot.web.codec.CodecCustomizer;
|
||||||
import org.springframework.boot.web.reactive.filter.OrderedHiddenHttpMethodFilter;
|
import org.springframework.boot.web.reactive.filter.OrderedHiddenHttpMethodFilter;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.core.convert.converter.GenericConverter;
|
|
||||||
import org.springframework.format.Formatter;
|
|
||||||
import org.springframework.format.FormatterRegistry;
|
import org.springframework.format.FormatterRegistry;
|
||||||
import org.springframework.format.Parser;
|
|
||||||
import org.springframework.format.Printer;
|
|
||||||
import org.springframework.format.support.FormattingConversionService;
|
import org.springframework.format.support.FormattingConversionService;
|
||||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
@ -180,37 +173,13 @@ public class WebFluxAutoConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addFormatters(FormatterRegistry registry) {
|
public void addFormatters(FormatterRegistry registry) {
|
||||||
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
|
ApplicationConversionService.addBeans(registry, this.beanFactory);
|
||||||
registry.addConverter(converter);
|
|
||||||
}
|
|
||||||
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
|
|
||||||
registry.addConverter(converter);
|
|
||||||
}
|
|
||||||
for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
|
|
||||||
registry.addFormatter(formatter);
|
|
||||||
}
|
|
||||||
for (Printer<?> printer : getBeansOfType(Printer.class)) {
|
|
||||||
if (!(printer instanceof Formatter<?>)) {
|
|
||||||
registry.addConverter(new PrinterConverter(printer));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Parser<?> parser : getBeansOfType(Parser.class)) {
|
|
||||||
if (!(parser instanceof Formatter<?>)) {
|
|
||||||
registry.addConverter(new ParserConverter(parser));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> Collection<T> getBeansOfType(Class<T> type) {
|
|
||||||
return this.beanFactory.getBeansOfType(type).values();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
|
private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
|
||||||
if (this.resourceHandlerRegistrationCustomizer != null) {
|
if (this.resourceHandlerRegistrationCustomizer != null) {
|
||||||
this.resourceHandlerRegistrationCustomizer.customize(registration);
|
this.resourceHandlerRegistrationCustomizer.customize(registration);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.web.servlet;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
@ -55,8 +54,7 @@ import org.springframework.boot.autoconfigure.web.ResourceProperties;
|
||||||
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
|
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
|
||||||
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.convert.ParserConverter;
|
import org.springframework.boot.convert.ApplicationConversionService;
|
||||||
import org.springframework.boot.convert.PrinterConverter;
|
|
||||||
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
|
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
|
||||||
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
|
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
|
||||||
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
|
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
|
||||||
|
@ -68,16 +66,11 @@ import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.core.convert.converter.GenericConverter;
|
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.task.AsyncTaskExecutor;
|
import org.springframework.core.task.AsyncTaskExecutor;
|
||||||
import org.springframework.format.Formatter;
|
|
||||||
import org.springframework.format.FormatterRegistry;
|
import org.springframework.format.FormatterRegistry;
|
||||||
import org.springframework.format.Parser;
|
|
||||||
import org.springframework.format.Printer;
|
|
||||||
import org.springframework.format.support.FormattingConversionService;
|
import org.springframework.format.support.FormattingConversionService;
|
||||||
import org.springframework.http.CacheControl;
|
import org.springframework.http.CacheControl;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
@ -302,29 +295,7 @@ public class WebMvcAutoConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addFormatters(FormatterRegistry registry) {
|
public void addFormatters(FormatterRegistry registry) {
|
||||||
for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
|
ApplicationConversionService.addBeans(registry, this.beanFactory);
|
||||||
registry.addConverter(converter);
|
|
||||||
}
|
|
||||||
for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
|
|
||||||
registry.addConverter(converter);
|
|
||||||
}
|
|
||||||
for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
|
|
||||||
registry.addFormatter(formatter);
|
|
||||||
}
|
|
||||||
for (Printer<?> printer : getBeansOfType(Printer.class)) {
|
|
||||||
if (!(printer instanceof Formatter<?>)) {
|
|
||||||
registry.addConverter(new PrinterConverter(printer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Parser<?> parser : getBeansOfType(Parser.class)) {
|
|
||||||
if (!(parser instanceof Formatter<?>)) {
|
|
||||||
registry.addConverter(new ParserConverter(parser));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> Collection<T> getBeansOfType(Class<T> type) {
|
|
||||||
return this.beanFactory.getBeansOfType(type).values();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -381,10 +381,9 @@ class WebFluxAutoConfigurationTests {
|
||||||
void customPrinterAndParserShouldBeRegisteredAsConverters() {
|
void customPrinterAndParserShouldBeRegisteredAsConverters() {
|
||||||
this.contextRunner.withUserConfiguration(ParserConfiguration.class, PrinterConfiguration.class)
|
this.contextRunner.withUserConfiguration(ParserConfiguration.class, PrinterConfiguration.class)
|
||||||
.run((context) -> {
|
.run((context) -> {
|
||||||
Foo foo = new Foo("bar");
|
ConversionService service = context.getBean(ConversionService.class);
|
||||||
ConversionService conversionService = context.getBean(ConversionService.class);
|
assertThat(service.convert(new Example("spring", new Date()), String.class)).isEqualTo("spring");
|
||||||
assertThat(conversionService.convert(foo, String.class)).isEqualTo("bar");
|
assertThat(service.convert("boot", Example.class)).extracting(Example::getName).isEqualTo("boot");
|
||||||
assertThat(conversionService.convert("bar", Foo.class)).extracting(Foo::toString).isEqualTo("bar");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,17 +563,8 @@ class WebFluxAutoConfigurationTests {
|
||||||
static class PrinterConfiguration {
|
static class PrinterConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Printer<Foo> fooPrinter() {
|
public Printer<Example> examplePrinter() {
|
||||||
return new FooPrinter();
|
return new ExamplePrinter();
|
||||||
}
|
|
||||||
|
|
||||||
private static class FooPrinter implements Printer<Foo> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String print(Foo foo, Locale locale) {
|
|
||||||
return foo.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -583,34 +573,42 @@ class WebFluxAutoConfigurationTests {
|
||||||
static class ParserConfiguration {
|
static class ParserConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Parser<Foo> fooParser() {
|
public Parser<Example> exampleParser() {
|
||||||
return new FooParser();
|
return new ExampleParser();
|
||||||
}
|
|
||||||
|
|
||||||
private static class FooParser implements Parser<Foo> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Foo parse(String source, Locale locale) {
|
|
||||||
return new Foo(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Foo {
|
static final class Example {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
Foo(String name) {
|
private Example(String name, Date date) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getName() {
|
||||||
public String toString() {
|
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ExamplePrinter implements Printer<Example> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String print(Example example, Locale locale) {
|
||||||
|
return example.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleParser implements Parser<Example> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Example parse(String source, Locale locale) {
|
||||||
|
return new Example(source, new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -779,10 +779,9 @@ class WebMvcAutoConfigurationTests {
|
||||||
void customPrinterAndParserShouldBeRegisteredAsConverters() {
|
void customPrinterAndParserShouldBeRegisteredAsConverters() {
|
||||||
this.contextRunner.withUserConfiguration(ParserConfiguration.class, PrinterConfiguration.class)
|
this.contextRunner.withUserConfiguration(ParserConfiguration.class, PrinterConfiguration.class)
|
||||||
.run((context) -> {
|
.run((context) -> {
|
||||||
Foo foo = new Foo("bar");
|
ConversionService service = context.getBean(ConversionService.class);
|
||||||
ConversionService conversionService = context.getBean(ConversionService.class);
|
assertThat(service.convert(new Example("spring", new Date()), String.class)).isEqualTo("spring");
|
||||||
assertThat(conversionService.convert(foo, String.class)).isEqualTo("bar");
|
assertThat(service.convert("boot", Example.class)).extracting(Example::getName).isEqualTo("boot");
|
||||||
assertThat(conversionService.convert("bar", Foo.class)).extracting(Foo::toString).isEqualTo("bar");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1110,17 +1109,8 @@ class WebMvcAutoConfigurationTests {
|
||||||
static class PrinterConfiguration {
|
static class PrinterConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Printer<Foo> fooPrinter() {
|
public Printer<Example> examplePrinter() {
|
||||||
return new FooPrinter();
|
return new ExamplePrinter();
|
||||||
}
|
|
||||||
|
|
||||||
private static class FooPrinter implements Printer<Foo> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String print(Foo foo, Locale locale) {
|
|
||||||
return foo.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1129,34 +1119,42 @@ class WebMvcAutoConfigurationTests {
|
||||||
static class ParserConfiguration {
|
static class ParserConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Parser<Foo> fooParser() {
|
public Parser<Example> exampleParser() {
|
||||||
return new FooParser();
|
return new ExampleParser();
|
||||||
}
|
|
||||||
|
|
||||||
private static class FooParser implements Parser<Foo> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Foo parse(String source, Locale locale) {
|
|
||||||
return new Foo(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class Foo {
|
static final class Example {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
Foo(String name) {
|
private Example(String name, Date date) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getName() {
|
||||||
public String toString() {
|
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ExamplePrinter implements Printer<Example> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String print(Example example, Locale locale) {
|
||||||
|
return example.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleParser implements Parser<Example> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Example parse(String source, Locale locale) {
|
||||||
|
return new Example(source, new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2018 the original author or authors.
|
* Copyright 2012-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,11 +16,20 @@
|
||||||
|
|
||||||
package org.springframework.boot.convert;
|
package org.springframework.boot.convert;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.core.convert.converter.ConverterRegistry;
|
import org.springframework.core.convert.converter.ConverterRegistry;
|
||||||
|
import org.springframework.core.convert.converter.GenericConverter;
|
||||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
|
import org.springframework.format.Formatter;
|
||||||
import org.springframework.format.FormatterRegistry;
|
import org.springframework.format.FormatterRegistry;
|
||||||
|
import org.springframework.format.Parser;
|
||||||
|
import org.springframework.format.Printer;
|
||||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||||
import org.springframework.format.support.FormattingConversionService;
|
import org.springframework.format.support.FormattingConversionService;
|
||||||
import org.springframework.util.StringValueResolver;
|
import org.springframework.util.StringValueResolver;
|
||||||
|
@ -134,4 +143,36 @@ public class ApplicationConversionService extends FormattingConversionService {
|
||||||
registry.addFormatter(new IsoOffsetFormatter());
|
registry.addFormatter(new IsoOffsetFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add {@link GenericConverter}, {@link Converter}, {@link Printer}, {@link Parser}
|
||||||
|
* and {@link Formatter} beans from the specified context.
|
||||||
|
* @param registry the service to register beans with
|
||||||
|
* @param beanFactory the bean factory to get the beans from
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
public static void addBeans(FormatterRegistry registry, ListableBeanFactory beanFactory) {
|
||||||
|
Set<Object> beans = new LinkedHashSet<>();
|
||||||
|
beans.addAll(beanFactory.getBeansOfType(GenericConverter.class).values());
|
||||||
|
beans.addAll(beanFactory.getBeansOfType(Converter.class).values());
|
||||||
|
beans.addAll(beanFactory.getBeansOfType(Printer.class).values());
|
||||||
|
beans.addAll(beanFactory.getBeansOfType(Parser.class).values());
|
||||||
|
for (Object bean : beans) {
|
||||||
|
if (bean instanceof GenericConverter) {
|
||||||
|
registry.addConverter((GenericConverter) bean);
|
||||||
|
}
|
||||||
|
else if (bean instanceof Converter) {
|
||||||
|
registry.addConverter((Converter<?, ?>) bean);
|
||||||
|
}
|
||||||
|
else if (bean instanceof Formatter) {
|
||||||
|
registry.addFormatter((Formatter<?>) bean);
|
||||||
|
}
|
||||||
|
else if (bean instanceof Printer) {
|
||||||
|
registry.addPrinter((Printer<?>) bean);
|
||||||
|
}
|
||||||
|
else if (bean instanceof Parser) {
|
||||||
|
registry.addParser((Parser<?>) bean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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.boot.convert;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
|
||||||
import org.springframework.core.DecoratingProxy;
|
|
||||||
import org.springframework.core.GenericTypeResolver;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.core.convert.converter.GenericConverter;
|
|
||||||
import org.springframework.format.Parser;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Converter} to convert from a {@link String} to {@code <T>} using the underlying
|
|
||||||
* {@link Parser}{@code <T>}.
|
|
||||||
*
|
|
||||||
* @author Dmytro Nosan
|
|
||||||
* @since 2.2.0
|
|
||||||
*/
|
|
||||||
public class ParserConverter implements GenericConverter {
|
|
||||||
|
|
||||||
private final Class<?> type;
|
|
||||||
|
|
||||||
private final Parser<?> parser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@code Converter} to convert {@code String} to a {@code T} via parser.
|
|
||||||
* @param parser parses {@code String} to a {@code T}
|
|
||||||
*/
|
|
||||||
public ParserConverter(Parser<?> parser) {
|
|
||||||
Assert.notNull(parser, "Parser must not be null");
|
|
||||||
this.type = getType(parser);
|
|
||||||
this.parser = parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
|
||||||
return Collections.singleton(new ConvertiblePair(String.class, this.type));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
|
||||||
String value = (String) source;
|
|
||||||
if (!StringUtils.hasText(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return this.parser.parse(value, LocaleContextHolder.getLocale());
|
|
||||||
}
|
|
||||||
catch (ParseException ex) {
|
|
||||||
throw new IllegalArgumentException("Value [" + value + "] can not be parsed", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.class.getName() + " -> " + this.type.getName() + " : " + this.parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getType(Parser<?> parser) {
|
|
||||||
Class<?> type = GenericTypeResolver.resolveTypeArgument(parser.getClass(), Parser.class);
|
|
||||||
if (type == null && parser instanceof DecoratingProxy) {
|
|
||||||
type = GenericTypeResolver.resolveTypeArgument(((DecoratingProxy) parser).getDecoratedClass(),
|
|
||||||
Parser.class);
|
|
||||||
}
|
|
||||||
if (type == null) {
|
|
||||||
throw new IllegalArgumentException("Unable to extract the parameterized type from Parser: '"
|
|
||||||
+ parser.getClass().getName() + "'. Does the class parameterize the <T> generic type?");
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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.boot.convert;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
|
||||||
import org.springframework.core.DecoratingProxy;
|
|
||||||
import org.springframework.core.GenericTypeResolver;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.core.convert.converter.GenericConverter;
|
|
||||||
import org.springframework.format.Printer;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link Converter} to convert {@code <T>} to a {@link String} using the underlying
|
|
||||||
* {@link Printer}{@code <T>}.
|
|
||||||
*
|
|
||||||
* @author Dmytro Nosan
|
|
||||||
* @since 2.2.0
|
|
||||||
*/
|
|
||||||
public class PrinterConverter implements GenericConverter {
|
|
||||||
|
|
||||||
private final Printer printer;
|
|
||||||
|
|
||||||
private final Class<?> type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a {@code Converter} to convert {@code T} to a {@code String} via printer.
|
|
||||||
* @param printer prints {@code T} to a {@code String}
|
|
||||||
*/
|
|
||||||
public PrinterConverter(Printer<?> printer) {
|
|
||||||
Assert.notNull(printer, "Printer must not be null");
|
|
||||||
this.type = getType(printer);
|
|
||||||
this.printer = printer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
|
||||||
return Collections.singleton(new ConvertiblePair(this.type, String.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
|
||||||
if (source == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return this.printer.print(source, LocaleContextHolder.getLocale());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return this.type.getName() + " -> " + String.class.getName() + " : " + this.printer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Class<?> getType(Printer<?> printer) {
|
|
||||||
Class<?> type = GenericTypeResolver.resolveTypeArgument(printer.getClass(), Printer.class);
|
|
||||||
if (type == null && printer instanceof DecoratingProxy) {
|
|
||||||
type = GenericTypeResolver.resolveTypeArgument(((DecoratingProxy) printer).getDecoratedClass(),
|
|
||||||
Printer.class);
|
|
||||||
}
|
|
||||||
if (type == null) {
|
|
||||||
throw new IllegalArgumentException("Unable to extract the parameterized type from Printer: '"
|
|
||||||
+ printer.getClass().getName() + "'. Does the class parameterize the <T> generic type?");
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-2019 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.boot.convert;
|
||||||
|
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.core.convert.converter.GenericConverter;
|
||||||
|
import org.springframework.format.Formatter;
|
||||||
|
import org.springframework.format.FormatterRegistry;
|
||||||
|
import org.springframework.format.Parser;
|
||||||
|
import org.springframework.format.Printer;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ApplicationConversionService}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
class ApplicationConversionServiceTests {
|
||||||
|
|
||||||
|
private FormatterRegistry registry = mock(FormatterRegistry.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addBeansWhenHasGenericConverterBeanAddConverter() {
|
||||||
|
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
|
ExampleGenericConverter.class)) {
|
||||||
|
ApplicationConversionService.addBeans(this.registry, context);
|
||||||
|
verify(this.registry).addConverter(context.getBean(ExampleGenericConverter.class));
|
||||||
|
verifyNoMoreInteractions(this.registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addBeansWhenHasConverterBeanAddConverter() {
|
||||||
|
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExampleConverter.class)) {
|
||||||
|
ApplicationConversionService.addBeans(this.registry, context);
|
||||||
|
verify(this.registry).addConverter(context.getBean(ExampleConverter.class));
|
||||||
|
verifyNoMoreInteractions(this.registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addBeansWhenHasFormatterBeanAddsOnlyFormatter() {
|
||||||
|
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExampleFormatter.class)) {
|
||||||
|
ApplicationConversionService.addBeans(this.registry, context);
|
||||||
|
verify(this.registry).addFormatter(context.getBean(ExampleFormatter.class));
|
||||||
|
verifyNoMoreInteractions(this.registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addBeansWhenHasPrinterBeanAddPrinter() {
|
||||||
|
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExamplePrinter.class)) {
|
||||||
|
ApplicationConversionService.addBeans(this.registry, context);
|
||||||
|
verify(this.registry).addPrinter(context.getBean(ExamplePrinter.class));
|
||||||
|
verifyNoMoreInteractions(this.registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addBeansWhenHasParserBeanAddParser() {
|
||||||
|
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(ExampleParser.class)) {
|
||||||
|
ApplicationConversionService.addBeans(this.registry, context);
|
||||||
|
verify(this.registry).addParser(context.getBean(ExampleParser.class));
|
||||||
|
verifyNoMoreInteractions(this.registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleGenericConverter implements GenericConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleConverter implements Converter<String, Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer convert(String source) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleFormatter implements Formatter<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String print(Integer object, Locale locale) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer parse(String text, Locale locale) throws ParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExampleParser implements Parser<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer parse(String text, Locale locale) throws ParseException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ExamplePrinter implements Printer<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String print(Integer object, Locale locale) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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.boot.convert;
|
|
||||||
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.aop.framework.ProxyFactory;
|
|
||||||
import org.springframework.core.convert.ConversionFailedException;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
|
||||||
import org.springframework.format.Parser;
|
|
||||||
import org.springframework.util.unit.DataSize;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link ParserConverter}.
|
|
||||||
*
|
|
||||||
* @author Dmytro Nosan
|
|
||||||
*/
|
|
||||||
class ParserConverterTests {
|
|
||||||
|
|
||||||
private final DefaultConversionService conversionService = new DefaultConversionService();
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void addParsers() {
|
|
||||||
this.conversionService.addConverter(new ParserConverter(new DurationParser()));
|
|
||||||
this.conversionService
|
|
||||||
.addConverter(new ParserConverter(((Parser<?>) new ProxyFactory(new DataSizeParser()).getProxy())));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void convertStringToDataSize() {
|
|
||||||
assertThat(convert("1KB", DataSize.class)).isEqualTo(DataSize.ofKilobytes(1));
|
|
||||||
assertThat(convert("", DataSize.class)).isNull();
|
|
||||||
assertThat(convert(null, DataSize.class)).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void convertStringToDuration() {
|
|
||||||
assertThat(convert("PT1S", Duration.class)).isEqualTo(Duration.ofSeconds(1));
|
|
||||||
assertThat(convert(null, Duration.class)).isNull();
|
|
||||||
assertThat(convert("", Duration.class)).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldFailParserGenericCanNotBeResolved() {
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> this.conversionService.addConverter(new ParserConverter((source, locale) -> "")))
|
|
||||||
.withMessageContaining("Unable to extract the parameterized type from Parser");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldFailParserThrowsParserException() {
|
|
||||||
this.conversionService.addConverter(new ParserConverter(new ObjectParser()));
|
|
||||||
assertThatExceptionOfType(ConversionFailedException.class).isThrownBy(() -> convert("Text", Object.class))
|
|
||||||
.withCauseInstanceOf(IllegalArgumentException.class)
|
|
||||||
.withMessageContaining("Value [Text] can not be parsed");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T convert(String source, Class<T> type) {
|
|
||||||
return type.cast(this.conversionService.convert(source, TypeDescriptor.valueOf(String.class),
|
|
||||||
TypeDescriptor.valueOf(type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DataSizeParser implements Parser<DataSize> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataSize parse(String value, Locale locale) {
|
|
||||||
return DataSize.parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DurationParser implements Parser<Duration> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Duration parse(String value, Locale locale) {
|
|
||||||
return Duration.parse(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ObjectParser implements Parser<Object> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object parse(String source, Locale locale) throws ParseException {
|
|
||||||
throw new ParseException("", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2012-2019 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.boot.convert;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.aop.framework.ProxyFactory;
|
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
|
||||||
import org.springframework.format.Printer;
|
|
||||||
import org.springframework.util.unit.DataSize;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link PrinterConverter}.
|
|
||||||
*
|
|
||||||
* @author Dmytro Nosan
|
|
||||||
*/
|
|
||||||
class PrinterConverterTests {
|
|
||||||
|
|
||||||
private final DefaultConversionService conversionService = new DefaultConversionService();
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
void addPrinters() {
|
|
||||||
this.conversionService.addConverter(new PrinterConverter(new DurationPrinter()));
|
|
||||||
this.conversionService
|
|
||||||
.addConverter(new PrinterConverter(((Printer<?>) new ProxyFactory(new DataSizePrinter()).getProxy())));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void convertDataSizeToString() {
|
|
||||||
assertThat(convert(DataSize.ofKilobytes(1), DataSize.class)).isEqualTo("1024B");
|
|
||||||
assertThat(convert(null, DataSize.class)).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void convertDurationToString() {
|
|
||||||
assertThat(convert(Duration.ofSeconds(1), Duration.class)).isEqualTo("PT1S");
|
|
||||||
assertThat(convert(null, Duration.class)).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldFailPrinterGenericCanNotBeResolved() {
|
|
||||||
assertThatIllegalArgumentException()
|
|
||||||
.isThrownBy(() -> this.conversionService.addConverter(new PrinterConverter((source, locale) -> "")))
|
|
||||||
.withMessageContaining("Unable to extract the parameterized type from Printer");
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> String convert(T source, Class<T> type) {
|
|
||||||
return (String) this.conversionService.convert(source, TypeDescriptor.valueOf(type),
|
|
||||||
TypeDescriptor.valueOf(String.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DataSizePrinter implements Printer<DataSize> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String print(DataSize dataSize, Locale locale) {
|
|
||||||
return dataSize.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DurationPrinter implements Printer<Duration> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String print(Duration duration, Locale locale) {
|
|
||||||
return duration.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue