|
17 | 17 | package org.springframework.boot.context.properties.bind;
|
18 | 18 |
|
19 | 19 | import java.net.InetAddress;
|
| 20 | +import java.util.AbstractMap; |
20 | 21 | import java.util.ArrayList;
|
21 | 22 | import java.util.Collections;
|
22 | 23 | import java.util.HashMap;
|
23 | 24 | import java.util.LinkedHashMap;
|
24 | 25 | import java.util.List;
|
25 | 26 | import java.util.Map;
|
26 | 27 | import java.util.Properties;
|
| 28 | +import java.util.Set; |
27 | 29 | import java.util.stream.Collectors;
|
28 | 30 |
|
29 | 31 | import org.junit.jupiter.api.Test;
|
|
33 | 35 |
|
34 | 36 | import org.springframework.boot.context.properties.bind.BinderTests.ExampleEnum;
|
35 | 37 | import org.springframework.boot.context.properties.bind.BinderTests.JavaBean;
|
| 38 | +import org.springframework.boot.context.properties.bind.MapBinderTests.CustomMapWithoutDefaultCtor.CustomMap; |
36 | 39 | import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
37 | 40 | import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
38 | 41 | import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
@@ -78,7 +81,7 @@ class MapBinderTests {
|
78 | 81 |
|
79 | 82 | private final List<ConfigurationPropertySource> sources = new ArrayList<>();
|
80 | 83 |
|
81 |
| - private Binder binder = new Binder(this.sources); |
| 84 | + private final Binder binder = new Binder(this.sources); |
82 | 85 |
|
83 | 86 | @Test
|
84 | 87 | void bindToMapShouldReturnPopulatedMap() {
|
@@ -315,15 +318,13 @@ void bindToMapWithPlaceholdersShouldBeGreedyForScalars() {
|
315 | 318 | TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, "foo=boo");
|
316 | 319 | MockConfigurationPropertySource source = new MockConfigurationPropertySource("foo.aaa.bbb.ccc", "baz-${foo}");
|
317 | 320 | this.sources.add(source);
|
318 |
| - this.binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment)); |
319 |
| - Map<String, ExampleEnum> result = this.binder.bind("foo", Bindable.mapOf(String.class, ExampleEnum.class)) |
320 |
| - .get(); |
| 321 | + Binder binder = new Binder(this.sources, new PropertySourcesPlaceholdersResolver(environment)); |
| 322 | + Map<String, ExampleEnum> result = binder.bind("foo", Bindable.mapOf(String.class, ExampleEnum.class)).get(); |
321 | 323 | assertThat(result).containsEntry("aaa.bbb.ccc", ExampleEnum.BAZ_BOO);
|
322 | 324 | }
|
323 | 325 |
|
324 | 326 | @Test
|
325 | 327 | void bindToMapWithNoPropertiesShouldReturnUnbound() {
|
326 |
| - this.binder = new Binder(this.sources); |
327 | 328 | BindResult<Map<String, ExampleEnum>> result = this.binder.bind("foo",
|
328 | 329 | Bindable.mapOf(String.class, ExampleEnum.class));
|
329 | 330 | assertThat(result.isBound()).isFalse();
|
@@ -624,6 +625,18 @@ void bindToMapWithPlaceholdersShouldResolve() {
|
624 | 625 | assertThat(map).containsKey("bcd");
|
625 | 626 | }
|
626 | 627 |
|
| 628 | + @Test |
| 629 | + void bindToCustomMapWithoutCtorAndConverterShouldResolve() { |
| 630 | + DefaultConversionService conversionService = new DefaultConversionService(); |
| 631 | + conversionService.addConverter(new CustomMapConverter()); |
| 632 | + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); |
| 633 | + source.put("foo.custom-map", "value"); |
| 634 | + this.sources.add(source); |
| 635 | + Binder binder = new Binder(this.sources, null, conversionService, null); |
| 636 | + CustomMapWithoutDefaultCtor result = binder.bind("foo", Bindable.of(CustomMapWithoutDefaultCtor.class)).get(); |
| 637 | + assertThat(result.getCustomMap().getSource()).isEqualTo("value"); |
| 638 | + } |
| 639 | + |
627 | 640 | private <K, V> Bindable<Map<K, V>> getMapBindable(Class<K> keyGeneric, ResolvableType valueType) {
|
628 | 641 | ResolvableType keyType = ResolvableType.forClass(keyGeneric);
|
629 | 642 | return Bindable.of(ResolvableType.forClassWithGenerics(Map.class, keyType, valueType));
|
@@ -761,6 +774,48 @@ void setAddresses(Map<String, ? extends List<? extends InetAddress>> addresses)
|
761 | 774 |
|
762 | 775 | }
|
763 | 776 |
|
| 777 | + static class CustomMapWithoutDefaultCtor { |
| 778 | + |
| 779 | + private final CustomMap customMap; |
| 780 | + |
| 781 | + CustomMapWithoutDefaultCtor(CustomMap customMap) { |
| 782 | + this.customMap = customMap; |
| 783 | + } |
| 784 | + |
| 785 | + CustomMap getCustomMap() { |
| 786 | + return this.customMap; |
| 787 | + } |
| 788 | + |
| 789 | + static final class CustomMap extends AbstractMap<String, Object> { |
| 790 | + |
| 791 | + private final String source; |
| 792 | + |
| 793 | + CustomMap(String source) { |
| 794 | + this.source = source; |
| 795 | + } |
| 796 | + |
| 797 | + @Override |
| 798 | + public Set<Entry<String, Object>> entrySet() { |
| 799 | + return Collections.emptySet(); |
| 800 | + } |
| 801 | + |
| 802 | + String getSource() { |
| 803 | + return this.source; |
| 804 | + } |
| 805 | + |
| 806 | + } |
| 807 | + |
| 808 | + } |
| 809 | + |
| 810 | + private static final class CustomMapConverter implements Converter<String, CustomMap> { |
| 811 | + |
| 812 | + @Override |
| 813 | + public CustomMap convert(String source) { |
| 814 | + return new CustomMap(source); |
| 815 | + } |
| 816 | + |
| 817 | + } |
| 818 | + |
764 | 819 | private static final class InvocationArgument<T> implements Answer<T> {
|
765 | 820 |
|
766 | 821 | private final int index;
|
|
0 commit comments