This is an automated email from the ASF dual-hosted git repository. benw pushed a commit to branch TAP5-2743 in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
commit c90d9d802924d88d7060573871a2cac76f9dcd79 Author: Ben Weidig <b...@netzgut.net> AuthorDate: Sun Dec 4 15:50:00 2022 +0100 TAP-2743: Add "compute" method to PerThreadValue<T> --- .../tapestry5/ioc/services/PerThreadValue.java | 65 ++++++++++ .../groovy/ioc/specs/PerThreadValueSpec.groovy | 140 +++++++++++++++++++++ .../ioc/specs/PerthreadManagerImplSpec.groovy | 2 - 3 files changed, 205 insertions(+), 2 deletions(-) diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PerThreadValue.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PerThreadValue.java index bce9097a0..4c1c3b73c 100644 --- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PerThreadValue.java +++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/PerThreadValue.java @@ -14,6 +14,10 @@ package org.apache.tapestry5.ioc.services; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + /** * Provides access to per-thread (and, by extension, per-request) data, managed by the {@link PerthreadManager}. * A PerThreadValue stores a particular type of information. @@ -43,4 +47,65 @@ public interface PerThreadValue<T> * Sets the current per-thread value, then returns that value. */ T set(T newValue); + + /** + * If no value is currently stored (checked by {@link #exists()}), the value + * provided by the supplier function is set and return. + * Otherwise, the current value is returned. + * + * @param fn the value supplier function + * @return The current (existing or computed) value + * @throws NullPointerException if the supplier function is null + * @since 5.8.3 + */ + default T computeIfAbsent(Supplier<? extends T> fn) { + Objects.requireNonNull(fn); + if (exists()) { + return get(); + } + + T newValue = fn.get(); + set(newValue); + + return newValue; + } + + /** + * If a value is currently stored (checked by {@link #exists()}), this value + * is used to compute a new one with the given mapping function. + * Otherwise, null is returned. + * + * @param fn the mapping function to compute the new value + * @return The new computed value, or null if none was present + * @throws NullPointerException if the mapping function is null + * @since 5.8.3 + */ + default T computeIfPresent(Function<? super T, ? extends T> fn) { + Objects.requireNonNull(fn); + if (!exists()) { + return null; + } + + T newValue = fn.apply(get()); + set(newValue); + + return newValue; + } + + /** + * Computes a new value with the help of the current one, which is returned. + * + * @param fn the mapping function to compute the new value + * @return The new computed value + * @throws NullPointerException if the mapping function is null + * @since 5.8.3 + */ + default T compute(Function<? super T, ? extends T> fn) { + Objects.requireNonNull(fn); + + T newValue = fn.apply(get()); + set(newValue); + + return newValue; + } } diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/PerThreadValueSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/PerThreadValueSpec.groovy new file mode 100644 index 000000000..2afbb03e0 --- /dev/null +++ b/tapestry-ioc/src/test/groovy/ioc/specs/PerThreadValueSpec.groovy @@ -0,0 +1,140 @@ +package ioc.specs + +import java.util.function.Supplier + +import org.apache.tapestry5.ioc.internal.services.PerthreadManagerImpl +import org.apache.tapestry5.ioc.services.PerThreadValue +import org.slf4j.Logger + +import spock.lang.Specification + +class PerThreadValueSpec extends Specification { + + def manager + + def setup(){ + Logger logger = Mock() + manager = new PerthreadManagerImpl(logger) + } + + def "computeIfAbsent - no value"() { + given: + def newValue = "a computed value" + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.computeIfAbsent({ newValue }) + + + then: + perThreadValue.exists() == true + perThreadValue.get() == newValue + } + + def "computeIfAbsent - pre-existing value"() { + given: + def currentValue = "inital value" + def newValue = "a computed value" + PerThreadValue<String> perThreadValue = manager.createValue(); + perThreadValue.set(currentValue) + + when: + perThreadValue.computeIfAbsent({ newValue }) + + then: + perThreadValue.exists() == true + perThreadValue.get() == currentValue + perThreadValue.get() != newValue + } + + def "computeIfAbsent - supplier null"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.computeIfAbsent(null) + + then: + thrown NullPointerException + } + + def "computeIfPresent - no value"() { + given: + def currentValue = "inital value" + def newValue = "a computed value" + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.computeIfPresent({ current -> newValue }) + + then: + perThreadValue.exists() == false + perThreadValue.get() == null + } + + def "computeIfPresent - pre-existing value"() { + given: + def currentValue = "inital value" + def newValue = "a computed value" + PerThreadValue<String> perThreadValue = manager.createValue(); + perThreadValue.set(currentValue) + + when: + perThreadValue.computeIfPresent({ current -> newValue }) + + then: + perThreadValue.get() != currentValue + perThreadValue.get() == newValue + } + + def "computeIfPresent- mapper null"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.computeIfPresent(null) + + then: + thrown NullPointerException + } + + def "compute - no value"() { + given: + def newValue = "a computed value" + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.compute({ current -> newValue }) + + then: + perThreadValue.exists() == true + perThreadValue.get() == newValue + } + + def "compute - pre-existing value"() { + given: + def currentValue = "inital value" + def newValue = "a computed value" + PerThreadValue<String> perThreadValue = manager.createValue(); + perThreadValue.set(currentValue) + + when: + perThreadValue.compute({ current -> newValue }) + + then: + perThreadValue.get() != currentValue + perThreadValue.get() == newValue + } + + def "compute - mapper null"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.compute(null) + + then: + thrown NullPointerException + } + +} \ No newline at end of file diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/PerthreadManagerImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/PerthreadManagerImplSpec.groovy index b349a7f16..a49f535ca 100644 --- a/tapestry-ioc/src/test/groovy/ioc/specs/PerthreadManagerImplSpec.groovy +++ b/tapestry-ioc/src/test/groovy/ioc/specs/PerthreadManagerImplSpec.groovy @@ -178,6 +178,4 @@ class PerthreadManagerImplSpec extends Specification { !value.exists() } - - }