This is an automated email from the ASF dual-hosted git repository. benw pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
The following commit(s) were added to refs/heads/master by this push: new 9501c0fe2 TAP5-2743: PerThreadValue<T> convenience methods 9501c0fe2 is described below commit 9501c0fe2b983ed20de2dcd6e1dd9b612af9f877 Author: Ben Weidig <benwei...@users.noreply.github.com> AuthorDate: Sat Apr 22 13:02:48 2023 +0200 TAP5-2743: PerThreadValue<T> convenience methods --- .../tapestry5/ioc/services/PerThreadValue.java | 84 +++++++++ .../groovy/ioc/specs/PerThreadValueSpec.groovy | 188 +++++++++++++++++++++ .../ioc/specs/PerthreadManagerImplSpec.groovy | 2 - 3 files changed, 272 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..5b23069a7 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,11 @@ package org.apache.tapestry5.ioc.services; +import java.util.Objects; +import java.util.function.Consumer; +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 +48,83 @@ 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; + } + + /** + * If a value is set, performs the given action with it, otherwise it + * does nothing. + * + * @param action performed action if a value is set + * @throws NullPointerException if the action is null + * @since 5.8.3 + */ + default void ifSet(Consumer<? super T> action) { + Objects.requireNonNull(action); + + if (!exists()) { + return; + } + + action.accept(get()); + } } 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..3687b7479 --- /dev/null +++ b/tapestry-ioc/src/test/groovy/ioc/specs/PerThreadValueSpec.groovy @@ -0,0 +1,188 @@ +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 = "initial 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 = "initial 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 = "initial 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 = "initial 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 + } + + def "ifSet - value is set"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + perThreadValue.set("a value"); + def hasRun = false + + when: + perThreadValue.ifSet { hasRun = true } + + then: + hasRun == true + } + + def "ifSet - no value set"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + def hasRun = false + + when: + perThreadValue.ifSet { hasRun = true } + + then: + hasRun == false + } + + def "ifSet - action is null with no value set"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + + when: + perThreadValue.ifSet(null) + + then: + thrown NullPointerException + } + + def "ifSet - action is null with value set"() { + given: + PerThreadValue<String> perThreadValue = manager.createValue(); + perThreadValue.set("a value") + + when: + perThreadValue.ifSet(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() } - - }