From 8c70acc3c30ccc48b783c69897b4ee35af1ec82b Mon Sep 17 00:00:00 2001 From: Chris Bono Date: Thu, 9 Jun 2022 17:53:16 -0500 Subject: [PATCH] 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 --- .../context/properties/PropertyMapper.java | 24 ++++++++++- .../properties/PropertyMapperTests.java | 43 ++++++++++++++++++- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/PropertyMapper.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/PropertyMapper.java index 7fc758eea4..e18731e46b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/PropertyMapper.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/PropertyMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -18,6 +18,7 @@ package org.springframework.boot.context.properties; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -52,6 +53,7 @@ import org.springframework.util.function.SingletonSupplier; * * @author Phillip Webb * @author Artsiom Yudovin + * @author Chris Bono * @since 2.0.0 */ public final class PropertyMapper { @@ -280,7 +282,7 @@ public final class PropertyMapper { /** * Complete the mapping by passing any non-filtered value to the specified - * consumer. + * consumer. The method is designed to be used with mutable objects. * @param consumer the consumer that should accept the value if it's not been * filtered */ @@ -292,6 +294,24 @@ public final class PropertyMapper { } } + /** + * Complete the mapping for any non-filtered value by apply the given function to + * an existing instance and returning a new one. For filtered values, the + * {@code instance} parameter is returned unchanged. The method is designed to be + * used with immutable objects. + * @param the result type + * @param instance the current instance + * @param mapper the mapping function + * @return a new mapped instance or the original instance + * @since 3.0.0 + */ + public R to(R instance, BiFunction mapper) { + Assert.notNull(instance, "Instance must not be null"); + Assert.notNull(mapper, "Mapper must not be null"); + T value = this.supplier.get(); + return (!this.predicate.test(value)) ? instance : mapper.apply(instance, value); + } + /** * Complete the mapping by creating a new instance from the non-filtered value. * @param the resulting type diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/PropertyMapperTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/PropertyMapperTests.java index 392b024731..dec021e504 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/PropertyMapperTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/PropertyMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 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. @@ -29,6 +29,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * * @author Phillip Webb * @author Artsiom Yudovin + * @author Chris Bono */ class PropertyMapperTests { @@ -207,6 +208,20 @@ class PropertyMapperTests { assertThat(result).isEqualTo("123"); } + @Test + void toImmutableReturnsNewInstance() { + Immutable instance = this.map.from("Spring").toInstance(Immutable::of); + instance = this.map.from("123").as(Integer::valueOf).to(instance, Immutable::withAge); + assertThat(instance).hasToString("Spring 123"); + } + + @Test + void toImmutableWhenFilteredReturnsOriginalInstance() { + Immutable instance = this.map.from("Spring").toInstance(Immutable::of); + instance = this.map.from("123").when("345"::equals).as(Integer::valueOf).to(instance, Immutable::withAge); + assertThat(instance).hasToString("Spring null"); + } + static class Count implements Supplier { private final Supplier source; @@ -257,4 +272,30 @@ class PropertyMapperTests { } + static class Immutable { + + private final String name; + + private final Integer age; + + Immutable(String name, Integer age) { + this.name = name; + this.age = age; + } + + public Immutable withAge(Integer age) { + return new Immutable(this.name, age); + } + + @Override + public String toString() { + return "%s %s".formatted(this.name, this.age); + } + + static Immutable of(String name) { + return new Immutable(name, null); + } + + } + }