Add CharSequenceToObjectConverter support
Update `ApplicationConversionService` with support for converting `CharSequence` source types by using existing `String` based converters. The addition is primarily to allow `ConfigTreePropertySource` values to be converted correctly. Closes gh-24171
This commit is contained in:
@@ -122,6 +122,13 @@ public class ApplicationConversionService extends FormattingConversionService {
|
||||
registry.addConverter(new InputStreamSourceToByteArrayConverter());
|
||||
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
|
||||
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
|
||||
if (registry instanceof ConversionService) {
|
||||
addApplicationConverters(registry, (ConversionService) registry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addApplicationConverters(ConverterRegistry registry, ConversionService conversionService) {
|
||||
registry.addConverter(new CharSequenceToObjectConverter(conversionService));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
|
||||
/**
|
||||
* {@link ConditionalGenericConverter} to convert {@link CharSequence} type by delegating
|
||||
* to existing {@link String} converters.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CharSequenceToObjectConverter implements ConditionalGenericConverter {
|
||||
|
||||
private static final TypeDescriptor STRING = TypeDescriptor.valueOf(String.class);
|
||||
|
||||
private static final Set<ConvertiblePair> TYPES;
|
||||
|
||||
private final ThreadLocal<Boolean> disable = new ThreadLocal<>();
|
||||
|
||||
static {
|
||||
TYPES = Collections.singleton(new ConvertiblePair(CharSequence.class, Object.class));
|
||||
}
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
CharSequenceToObjectConverter(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
return TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (sourceType.getType() == String.class || this.disable.get() == Boolean.TRUE) {
|
||||
return false;
|
||||
}
|
||||
this.disable.set(Boolean.TRUE);
|
||||
try {
|
||||
return !this.conversionService.canConvert(sourceType, targetType)
|
||||
&& this.conversionService.canConvert(STRING, targetType);
|
||||
}
|
||||
finally {
|
||||
this.disable.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return this.conversionService.convert(source.toString(), STRING, targetType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link CharSequenceToObjectConverter}
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CharSequenceToObjectConverterTests {
|
||||
|
||||
@ConversionServiceTest
|
||||
void convertWhenCanConvertViaToString(ConversionService conversionService) {
|
||||
assertThat(conversionService.convert(new StringBuilder("1"), Integer.class)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@ConversionServiceTest
|
||||
void convertWhenCanConvertDirectlySkipsStringConversion(ConversionService conversionService) {
|
||||
assertThat(conversionService.convert(new String("1"), Long.class)).isEqualTo(1);
|
||||
System.out.println(conversionService.getClass());
|
||||
if (!ConversionServiceArguments.isApplicationConversionService(conversionService)) {
|
||||
assertThat(conversionService.convert(new StringBuilder("1"), Long.class)).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream<? extends Arguments> conversionServices() {
|
||||
return ConversionServiceArguments.with((conversionService) -> {
|
||||
conversionService.addConverter(new StringToIntegerConverter());
|
||||
conversionService.addConverter(new StringToLongConverter());
|
||||
conversionService.addConverter(new CharSequenceToLongConverter());
|
||||
conversionService.addConverter(new CharSequenceToObjectConverter(conversionService));
|
||||
});
|
||||
}
|
||||
|
||||
static class StringToIntegerConverter implements Converter<String, Integer> {
|
||||
|
||||
@Override
|
||||
public Integer convert(String source) {
|
||||
return Integer.valueOf(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class StringToLongConverter implements Converter<String, Long> {
|
||||
|
||||
@Override
|
||||
public Long convert(String source) {
|
||||
return Long.valueOf(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CharSequenceToLongConverter implements Converter<CharSequence, Long> {
|
||||
|
||||
@Override
|
||||
public Long convert(CharSequence source) {
|
||||
return Long.valueOf(source.toString()) + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,6 +57,13 @@ final class ConversionServiceArguments {
|
||||
"Application conversion service")));
|
||||
}
|
||||
|
||||
static boolean isApplicationConversionService(ConversionService conversionService) {
|
||||
if (conversionService instanceof NamedConversionService) {
|
||||
return isApplicationConversionService(((NamedConversionService) conversionService).delegate);
|
||||
}
|
||||
return conversionService instanceof ApplicationConversionService;
|
||||
}
|
||||
|
||||
static class NamedConversionService implements ConversionService {
|
||||
|
||||
private final ConversionService delegate;
|
||||
|
||||
@@ -79,7 +79,7 @@ class ConfigTreePropertySourceTests {
|
||||
@Test
|
||||
void getPropertyNamesFromFlatReturnsPropertyNames() throws Exception {
|
||||
ConfigTreePropertySource propertySource = getFlatPropertySource();
|
||||
assertThat(propertySource.getPropertyNames()).containsExactly("a", "b", "c");
|
||||
assertThat(propertySource.getPropertyNames()).containsExactly("a", "b", "c", "one");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,6 +154,7 @@ class ConfigTreePropertySourceTests {
|
||||
assertThat(environment.getProperty("b")).isEqualTo("B");
|
||||
assertThat(environment.getProperty("c", InputStreamSource.class).getInputStream()).hasContent("C");
|
||||
assertThat(environment.getProperty("c", byte[].class)).contains('C');
|
||||
assertThat(environment.getProperty("one", Integer.class)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -227,6 +228,7 @@ class ConfigTreePropertySourceTests {
|
||||
addProperty("a", "A");
|
||||
addProperty("b", "B");
|
||||
addProperty("c", "C");
|
||||
addProperty("one", "1");
|
||||
return new ConfigTreePropertySource("test", this.directory);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user