Skip to content

Commit 8c70acc

Browse files
onobcphilwebb
andcommitted
Add PropertyMapper.to(...) API designed for immutable instances
Add a new `to` method on `PropertyMapper` designed to work with immutable instances. The new method takes an existing instance and a mapping `BiFunction`. See gh-31323 Co-authored-by: Phillip Webb <pwebb@vmware.com>
1 parent e291b8f commit 8c70acc

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 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.
@@ -18,6 +18,7 @@
1818

1919
import java.util.NoSuchElementException;
2020
import java.util.Objects;
21+
import java.util.function.BiFunction;
2122
import java.util.function.Consumer;
2223
import java.util.function.Function;
2324
import java.util.function.Predicate;
@@ -52,6 +53,7 @@
5253
*
5354
* @author Phillip Webb
5455
* @author Artsiom Yudovin
56+
* @author Chris Bono
5557
* @since 2.0.0
5658
*/
5759
public final class PropertyMapper {
@@ -280,7 +282,7 @@ public Source<T> when(Predicate<T> predicate) {
280282

281283
/**
282284
* Complete the mapping by passing any non-filtered value to the specified
283-
* consumer.
285+
* consumer. The method is designed to be used with mutable objects.
284286
* @param consumer the consumer that should accept the value if it's not been
285287
* filtered
286288
*/
@@ -292,6 +294,24 @@ public void to(Consumer<T> consumer) {
292294
}
293295
}
294296

297+
/**
298+
* Complete the mapping for any non-filtered value by apply the given function to
299+
* an existing instance and returning a new one. For filtered values, the
300+
* {@code instance} parameter is returned unchanged. The method is designed to be
301+
* used with immutable objects.
302+
* @param <R> the result type
303+
* @param instance the current instance
304+
* @param mapper the mapping function
305+
* @return a new mapped instance or the original instance
306+
* @since 3.0.0
307+
*/
308+
public <R> R to(R instance, BiFunction<R, T, R> mapper) {
309+
Assert.notNull(instance, "Instance must not be null");
310+
Assert.notNull(mapper, "Mapper must not be null");
311+
T value = this.supplier.get();
312+
return (!this.predicate.test(value)) ? instance : mapper.apply(instance, value);
313+
}
314+
295315
/**
296316
* Complete the mapping by creating a new instance from the non-filtered value.
297317
* @param <R> the resulting type

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

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 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.
@@ -29,6 +29,7 @@
2929
*
3030
* @author Phillip Webb
3131
* @author Artsiom Yudovin
32+
* @author Chris Bono
3233
*/
3334
class PropertyMapperTests {
3435

@@ -207,6 +208,20 @@ void whenWhenValueMatchesShouldSupportChainedCalls() {
207208
assertThat(result).isEqualTo("123");
208209
}
209210

211+
@Test
212+
void toImmutableReturnsNewInstance() {
213+
Immutable instance = this.map.from("Spring").toInstance(Immutable::of);
214+
instance = this.map.from("123").as(Integer::valueOf).to(instance, Immutable::withAge);
215+
assertThat(instance).hasToString("Spring 123");
216+
}
217+
218+
@Test
219+
void toImmutableWhenFilteredReturnsOriginalInstance() {
220+
Immutable instance = this.map.from("Spring").toInstance(Immutable::of);
221+
instance = this.map.from("123").when("345"::equals).as(Integer::valueOf).to(instance, Immutable::withAge);
222+
assertThat(instance).hasToString("Spring null");
223+
}
224+
210225
static class Count<T> implements Supplier<T> {
211226

212227
private final Supplier<T> source;
@@ -257,4 +272,30 @@ String getName() {
257272

258273
}
259274

275+
static class Immutable {
276+
277+
private final String name;
278+
279+
private final Integer age;
280+
281+
Immutable(String name, Integer age) {
282+
this.name = name;
283+
this.age = age;
284+
}
285+
286+
public Immutable withAge(Integer age) {
287+
return new Immutable(this.name, age);
288+
}
289+
290+
@Override
291+
public String toString() {
292+
return "%s %s".formatted(this.name, this.age);
293+
}
294+
295+
static Immutable of(String name) {
296+
return new Immutable(name, null);
297+
}
298+
299+
}
300+
260301
}

0 commit comments

Comments
 (0)