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()
 
   }
-
-
 }

Reply via email to