Skip to content

Commit 4d466c3

Browse files
committed
Merge branch '3.3.x'
Closes gh-41564
2 parents 9ff53f8 + 8e82aad commit 4d466c3

File tree

4 files changed

+55
-4
lines changed

4 files changed

+55
-4
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.context.properties.bind;
1818

1919
import java.util.Collection;
20+
import java.util.EnumMap;
2021
import java.util.Map;
2122
import java.util.Properties;
2223
import java.util.function.Supplier;
@@ -65,8 +66,7 @@ protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> targe
6566
}
6667
}
6768
}
68-
Map<Object, Object> map = CollectionFactory
69-
.createMap((target.getValue() != null) ? Map.class : target.getType().resolve(Object.class), 0);
69+
Map<Object, Object> map = createMap(target);
7070
for (ConfigurationPropertySource source : getContext().getSources()) {
7171
if (!ConfigurationPropertyName.EMPTY.equals(name)) {
7272
source = source.filter(name::isAncestorOf);
@@ -76,6 +76,15 @@ protected Object bindAggregate(ConfigurationPropertyName name, Bindable<?> targe
7676
return map.isEmpty() ? null : map;
7777
}
7878

79+
private Map<Object, Object> createMap(Bindable<?> target) {
80+
Class<?> mapType = (target.getValue() != null) ? Map.class : target.getType().resolve(Object.class);
81+
if (EnumMap.class.isAssignableFrom(mapType)) {
82+
Class<?> keyType = target.getType().asMap().getGeneric(0).resolve();
83+
return CollectionFactory.createMap(mapType, keyType, 0);
84+
}
85+
return CollectionFactory.createMap(mapType, 0);
86+
}
87+
7988
private boolean hasDescendants(ConfigurationPropertyName name) {
8089
for (ConfigurationPropertySource source : getContext().getSources()) {
8190
if (source.containsDescendantOf(name) == ConfigurationPropertyState.PRESENT) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import java.util.ArrayList;
2626
import java.util.Collection;
2727
import java.util.Collections;
28+
import java.util.EnumMap;
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Optional;
@@ -156,6 +157,10 @@ private <T> T getNewDefaultValueInstanceIfPossible(Binder.Context context, Resol
156157
if (Collection.class.isAssignableFrom(resolved)) {
157158
return (T) CollectionFactory.createCollection(resolved, 0);
158159
}
160+
if (EnumMap.class.isAssignableFrom(resolved)) {
161+
Class<?> keyType = type.asMap().getGeneric(0).resolve();
162+
return (T) CollectionFactory.createMap(resolved, keyType, 0);
163+
}
159164
if (Map.class.isAssignableFrom(resolved)) {
160165
return (T) CollectionFactory.createMap(resolved, 0);
161166
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/MapBinderTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.AbstractMap;
2121
import java.util.ArrayList;
2222
import java.util.Collections;
23+
import java.util.EnumMap;
2324
import java.util.HashMap;
2425
import java.util.LinkedHashMap;
2526
import java.util.List;
@@ -79,6 +80,9 @@ class MapBinderTests {
7980
private static final Bindable<Map<String, String[]>> STRING_ARRAY_MAP = Bindable.mapOf(String.class,
8081
String[].class);
8182

83+
private static final Bindable<EnumMap<ExampleEnum, String>> EXAMPLE_ENUM_STRING_ENUM_MAP = Bindable
84+
.of(ResolvableType.forClassWithGenerics(EnumMap.class, ExampleEnum.class, String.class));
85+
8286
private final List<ConfigurationPropertySource> sources = new ArrayList<>();
8387

8488
private final Binder binder = new Binder(this.sources);
@@ -637,6 +641,17 @@ void bindToCustomMapWithoutCtorAndConverterShouldResolve() {
637641
assertThat(result.getCustomMap().getSource()).isEqualTo("value");
638642
}
639643

644+
@Test
645+
void bindToEnumMapShouldBind() {
646+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
647+
source.put("props.foo-bar", "value");
648+
this.sources.add(source);
649+
Binder binder = new Binder(this.sources, null, null, null);
650+
EnumMap<ExampleEnum, String> result = binder.bind("props", EXAMPLE_ENUM_STRING_ENUM_MAP).get();
651+
assertThat(result).hasSize(1).containsEntry(ExampleEnum.FOO_BAR, "value");
652+
653+
}
654+
640655
private <K, V> Bindable<Map<K, V>> getMapBindable(Class<K> keyGeneric, ResolvableType valueType) {
641656
ResolvableType keyType = ResolvableType.forClass(keyGeneric);
642657
return Bindable.of(ResolvableType.forClassWithGenerics(Map.class, keyType, valueType));

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.nio.file.Paths;
2222
import java.time.LocalDate;
2323
import java.util.ArrayList;
24+
import java.util.EnumMap;
2425
import java.util.List;
2526
import java.util.Map;
2627
import java.util.Objects;
@@ -306,6 +307,13 @@ void bindWhenMapParametersWithEmptyDefaultValueShouldReturnEmptyInstance() {
306307
assertThat(bound.getMapValue()).isEmpty();
307308
}
308309

310+
@Test
311+
void bindWhenEnumMapParametersWithEmptyDefaultValueShouldReturnEmptyInstance() {
312+
NestedConstructorBeanWithEmptyDefaultValueForEnumMapTypes bound = this.binder.bindOrCreate("foo",
313+
Bindable.of(NestedConstructorBeanWithEmptyDefaultValueForEnumMapTypes.class));
314+
assertThat(bound.getMapValue()).isEmpty();
315+
}
316+
309317
@Test
310318
void bindWhenArrayParameterWithEmptyDefaultValueShouldReturnEmptyInstance() {
311319
NestedConstructorBeanWithEmptyDefaultValueForArrayTypes bound = this.binder.bindOrCreate("foo",
@@ -781,6 +789,20 @@ Map<String, String> getMapValue() {
781789

782790
}
783791

792+
static class NestedConstructorBeanWithEmptyDefaultValueForEnumMapTypes {
793+
794+
private final EnumMap<ExampleEnum, String> mapValue;
795+
796+
NestedConstructorBeanWithEmptyDefaultValueForEnumMapTypes(@DefaultValue EnumMap<ExampleEnum, String> mapValue) {
797+
this.mapValue = mapValue;
798+
}
799+
800+
EnumMap<ExampleEnum, String> getMapValue() {
801+
return this.mapValue;
802+
}
803+
804+
}
805+
784806
static class NestedConstructorBeanWithEmptyDefaultValueForArrayTypes {
785807

786808
private final String[] arrayValue;

0 commit comments

Comments
 (0)