This commit is contained in:
Nabil Fawwaz Elqayyim 2025-10-07 23:10:35 +03:00 committed by GitHub
commit 7d0851340d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 126 additions and 12 deletions

View File

@ -16,6 +16,8 @@
package org.springframework.core.env;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.jspecify.annotations.Nullable;
@ -44,10 +46,43 @@ public class MapPropertySource extends EnumerablePropertySource<Map<String, Obje
super(name, source);
}
/**
* Returns the value associated with the given property name.
* <p>
* First, this method checks for an exact match in the underlying {@code source} map.
* If not found, it attempts to reconstruct a {@link List} from sequentially indexed keys
* (e.g. {@code name[0]}, {@code name[1]}, ...), stopping at the first missing index.
* <p>
* Values that implement {@link CharSequence} are converted to plain {@link String} instances.
*
* @param name the property name to resolve
* @return the resolved value, or {@code null} if not found
*/
@Override
public @Nullable Object getProperty(String name) {
return this.source.get(name);
Object directMatch = this.source.get(name);
if (directMatch != null) {
return directMatch;
}
List<Object> collectedValues = new ArrayList<>();
for (int index = 0; ; index++) {
String indexedKey = name + "[" + index + "]";
if (!this.source.containsKey(indexedKey)) {
break;
}
Object rawIndexedValue = this.source.get(indexedKey);
if (rawIndexedValue instanceof CharSequence cs) {
collectedValues.add(cs.toString());
} else {
collectedValues.add(rawIndexedValue);
}
}
return collectedValues.isEmpty() ? null : collectedValues;
}
@Override

View File

@ -16,11 +16,7 @@
package org.springframework.test.context.support;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.stream.Stream;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.Test;
@ -37,11 +33,16 @@ import org.springframework.mock.env.MockEnvironment;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.test.context.TestPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.entry;
import java.io.Serial;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
@ -306,6 +307,84 @@ class TestPropertySourceUtilsTests {
.withMessageContaining("'inlinedProperties' must not be null");
}
@Test
void returnsListOfStringsFromIndexedKeys() {
Map<String, Object> source = Collections.unmodifiableMap(new HashMap<>() {
@Serial
private static final long serialVersionUID = 5698617178562090885L;
{
put("first.second[0]", "i");
put("first.second[1]", "love");
put("first.second[2]", "spring");
}
});
PropertySource<?> propertySource = new MapPropertySource("test", source);
Object result = propertySource.getProperty("first.second");
assertThat(result)
.isInstanceOf(List.class)
.asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactly("i", "love", "spring");
}
@Test
void returnsListOfMixedTypesFromIndexedKeys() {
Map<String, Object> source = Collections.unmodifiableMap(new HashMap<>() {
@Serial
private static final long serialVersionUID = 5698617178562090885L;
{
put("first.second[0]", "i");
put("first.second[1]", "love");
put("first.second[2]", "spring");
put("first.second[3]", 7);
}
});
PropertySource<?> propertySource = new MapPropertySource("test", source);
Object result = propertySource.getProperty("first.second");
assertThat(result)
.isInstanceOf(List.class)
.asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactly("i", "love", "spring", 7);
}
@Test
void returnsListOfIntegersFromIndexedKeys() {
Map<String, Object> source = Collections.unmodifiableMap(new HashMap<>() {
@Serial
private static final long serialVersionUID = 5698617178562090885L;
{
put("first.second[0]", 1);
put("first.second[1]", 2);
put("first.second[2]", 3);
}
});
PropertySource<?> propertySource = new MapPropertySource("test", source);
Object result = propertySource.getProperty("first.second");
assertThat(result)
.isInstanceOf(List.class)
.asInstanceOf(InstanceOfAssertFactories.LIST)
.containsExactly(1, 2, 3);
}
@Test
void returnsNullWhenNoDirectMatchAndNoIndexedKeys() {
Map<String, Object> sourceMap = new HashMap<>();
PropertySource<?> ps = new MapPropertySource("test", sourceMap);
Object result = ps.getProperty("first.second");
assertThat(result).isNull();
}
private static void assertMergedTestPropertySources(Class<?> testClass, String[] expectedLocations,
String[] expectedProperties) {