TAP5-2743: PerThreadValue<T> convenience methods
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 bce9097..5b23069 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 @@
* 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 0000000..3687b74
--- /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 b349a7f..a49f535 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 @@
!value.exists()
}
-
-
}