This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push:
new 389fb37 Add Memoizer(Function) and Memoizer(Function, boolean).
389fb37 is described below
commit 389fb37c56d82473ae925d3c8cbb1d4c40819722
Author: Gary Gregory <[email protected]>
AuthorDate: Mon Mar 21 10:00:41 2022 -0400
Add Memoizer(Function) and Memoizer(Function, boolean).
---
src/changes/changes.xml | 1 +
.../apache/commons/lang3/concurrent/Memoizer.java | 34 +++++++++-
...moizerTest.java => MemoizerComputableTest.java} | 32 ++++-----
...MemoizerTest.java => MemoizerFunctionTest.java} | 76 +++++++++++-----------
4 files changed, 89 insertions(+), 54 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index e0197e3..c13545e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -123,6 +123,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary
Gregory">Add JavaVersion.JAVA_18.</action>
<action type="add" dev="ggregory" due-to="Gary
Gregory">Add TimeZones.toTimeZone(TimeZone).</action>
<action type="add" dev="ggregory" due-to="Gary
Gregory">Add FutureTasks.</action>
+ <action type="add" dev="ggregory" due-to="Gary
Gregory">Add Memoizer(Function) and Memoizer(Function, boolean).</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Dependabot,
Gary Gregory">Bump spotbugs-maven-plugin from 4.2.0 to 4.5.0.0 #735, #808,
#822, #834.</action>
<action type="update" dev="ggregory" due-to="Dependabot,
XenoAmess">Bump actions/cache from v2.1.4 to v2.1.7 #742, #752, #764,
#833.</action>
diff --git a/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
b/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
index d53318d..6fccc82 100644
--- a/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
+++ b/src/main/java/org/apache/commons/lang3/concurrent/Memoizer.java
@@ -55,7 +55,7 @@ public class Memoizer<I, O> implements Computable<I, O> {
* Constructs a Memoizer for the provided Computable calculation.
* </p>
* <p>
- * If a calculation is thrown an exception for any reason, this exception
will be cached and returned for all future
+ * If a calculation throws an exception for any reason, this exception
will be cached and returned for all future
* calls with the provided parameter.
* </p>
*
@@ -82,6 +82,38 @@ public class Memoizer<I, O> implements Computable<I, O> {
/**
* <p>
+ * Constructs a Memoizer for the provided Function calculation.
+ * </p>
+ * <p>
+ * If a calculation throws an exception for any reason, this exception
will be cached and returned for all future
+ * calls with the provided parameter.
+ * </p>
+ *
+ * @param function the function whose results should be memorized
+ * @since 2.13.0
+ */
+ public Memoizer(final Function<I, O> function) {
+ this(function, false);
+ }
+
+ /**
+ * <p>
+ * Constructs a Memoizer for the provided Function calculation, with the
option of whether a Function that
+ * experiences an error should recalculate on subsequent calls or return
the same cached exception.
+ * </p>
+ *
+ * @param function the computation whose results should be memorized
+ * @param recalculate determines whether the computation should be
recalculated on subsequent calls if the previous call
+ * failed
+ * @since 2.13.0
+ */
+ public Memoizer(final Function<I, O> function, final boolean recalculate)
{
+ this.recalculate = recalculate;
+ this.mappingFunction = k -> FutureTasks.run(() -> function.apply(k));
+ }
+
+ /**
+ * <p>
* This method will return the result of the calculation and cache it, if
it has not previously been calculated.
* </p>
* <p>
diff --git
a/src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java
b/src/test/java/org/apache/commons/lang3/concurrent/MemoizerComputableTest.java
similarity index 99%
copy from src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java
copy to
src/test/java/org/apache/commons/lang3/concurrent/MemoizerComputableTest.java
index d45800d..d34bedb 100644
--- a/src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java
+++
b/src/test/java/org/apache/commons/lang3/concurrent/MemoizerComputableTest.java
@@ -25,7 +25,7 @@ import org.easymock.EasyMock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-public class MemoizerTest {
+public class MemoizerComputableTest {
private Computable<Integer, Integer> computable;
@@ -35,17 +35,6 @@ public class MemoizerTest {
}
@Test
- public void testOnlyCallComputableOnceIfDoesNotThrowException() throws
Exception {
- final Integer input = 1;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
- expect(computable.compute(input)).andReturn(input);
- replay(computable);
-
- assertEquals(input, memoizer.compute(input), "Should call computable
first time");
- assertEquals(input, memoizer.compute(input), "Should not call the
computable the second time");
- }
-
- @Test
public void testDefaultBehaviourNotToRecalculateExecutionExceptions()
throws Exception {
final Integer input = 1;
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
@@ -83,14 +72,14 @@ public class MemoizerTest {
}
@Test
- public void testWhenComputableThrowsRuntimeException() throws Exception {
+ public void testOnlyCallComputableOnceIfDoesNotThrowException() throws
Exception {
final Integer input = 1;
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
- final RuntimeException runtimeException = new RuntimeException("Some
runtime exception");
- expect(computable.compute(input)).andThrow(runtimeException);
+ expect(computable.compute(input)).andReturn(input);
replay(computable);
- assertThrows(RuntimeException.class, () -> memoizer.compute(input));
+ assertEquals(input, memoizer.compute(input), "Should call computable
first time");
+ assertEquals(input, memoizer.compute(input), "Should not call the
computable the second time");
}
@Test
@@ -103,4 +92,15 @@ public class MemoizerTest {
assertThrows(Error.class, () -> memoizer.compute(input));
}
+
+ @Test
+ public void testWhenComputableThrowsRuntimeException() throws Exception {
+ final Integer input = 1;
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
+ final RuntimeException runtimeException = new RuntimeException("Some
runtime exception");
+ expect(computable.compute(input)).andThrow(runtimeException);
+ replay(computable);
+
+ assertThrows(RuntimeException.class, () -> memoizer.compute(input));
+ }
}
diff --git
a/src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java
b/src/test/java/org/apache/commons/lang3/concurrent/MemoizerFunctionTest.java
similarity index 71%
rename from src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java
rename to
src/test/java/org/apache/commons/lang3/concurrent/MemoizerFunctionTest.java
index d45800d..d9419ee 100644
--- a/src/test/java/org/apache/commons/lang3/concurrent/MemoizerTest.java
+++
b/src/test/java/org/apache/commons/lang3/concurrent/MemoizerFunctionTest.java
@@ -21,86 +21,88 @@ import static org.easymock.EasyMock.replay;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.util.function.Function;
+
import org.easymock.EasyMock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-public class MemoizerTest {
+public class MemoizerFunctionTest {
- private Computable<Integer, Integer> computable;
+ private Function<Integer, Integer> function;
@BeforeEach
public void setUpComputableMock() {
- computable = EasyMock.mock(Computable.class);
- }
-
- @Test
- public void testOnlyCallComputableOnceIfDoesNotThrowException() throws
Exception {
- final Integer input = 1;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
- expect(computable.compute(input)).andReturn(input);
- replay(computable);
-
- assertEquals(input, memoizer.compute(input), "Should call computable
first time");
- assertEquals(input, memoizer.compute(input), "Should not call the
computable the second time");
+ function = EasyMock.mock(Function.class);
}
@Test
public void testDefaultBehaviourNotToRecalculateExecutionExceptions()
throws Exception {
final Integer input = 1;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
- final InterruptedException interruptedException = new
InterruptedException();
- expect(computable.compute(input)).andThrow(interruptedException);
- replay(computable);
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
+ final IllegalArgumentException interruptedException = new
IllegalArgumentException();
+ expect(function.apply(input)).andThrow(interruptedException);
+ replay(function);
assertThrows(Throwable.class, () -> memoizer.compute(input));
- assertThrows(IllegalStateException.class, () ->
memoizer.compute(input));
+ assertThrows(IllegalArgumentException.class, () ->
memoizer.compute(input));
}
@Test
public void testDoesNotRecalculateWhenSetToFalse() throws Exception {
final Integer input = 1;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable,
false);
- final InterruptedException interruptedException = new
InterruptedException();
- expect(computable.compute(input)).andThrow(interruptedException);
- replay(computable);
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function,
false);
+ final IllegalArgumentException interruptedException = new
IllegalArgumentException();
+ expect(function.apply(input)).andThrow(interruptedException);
+ replay(function);
assertThrows(Throwable.class, () -> memoizer.compute(input));
- assertThrows(IllegalStateException.class, () ->
memoizer.compute(input));
+ assertThrows(IllegalArgumentException.class, () ->
memoizer.compute(input));
}
@Test
public void testDoesRecalculateWhenSetToTrue() throws Exception {
final Integer input = 1;
final Integer answer = 3;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable,
true);
- final InterruptedException interruptedException = new
InterruptedException();
-
expect(computable.compute(input)).andThrow(interruptedException).andReturn(answer);
- replay(computable);
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function,
true);
+ final IllegalArgumentException interruptedException = new
IllegalArgumentException();
+
expect(function.apply(input)).andThrow(interruptedException).andReturn(answer);
+ replay(function);
assertThrows(Throwable.class, () -> memoizer.compute(input));
assertEquals(answer, memoizer.compute(input));
}
@Test
- public void testWhenComputableThrowsRuntimeException() throws Exception {
+ public void testOnlyCallComputableOnceIfDoesNotThrowException() throws
Exception {
final Integer input = 1;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
- final RuntimeException runtimeException = new RuntimeException("Some
runtime exception");
- expect(computable.compute(input)).andThrow(runtimeException);
- replay(computable);
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
+ expect(function.apply(input)).andReturn(input);
+ replay(function);
- assertThrows(RuntimeException.class, () -> memoizer.compute(input));
+ assertEquals(input, memoizer.compute(input), "Should call computable
first time");
+ assertEquals(input, memoizer.compute(input), "Should not call the
computable the second time");
}
@Test
public void testWhenComputableThrowsError() throws Exception {
final Integer input = 1;
- final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
final Error error = new Error();
- expect(computable.compute(input)).andThrow(error);
- replay(computable);
+ expect(function.apply(input)).andThrow(error);
+ replay(function);
assertThrows(Error.class, () -> memoizer.compute(input));
}
+
+ @Test
+ public void testWhenComputableThrowsRuntimeException() throws Exception {
+ final Integer input = 1;
+ final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
+ final RuntimeException runtimeException = new RuntimeException("Some
runtime exception");
+ expect(function.apply(input)).andThrow(runtimeException);
+ replay(function);
+
+ assertThrows(RuntimeException.class, () -> memoizer.compute(input));
+ }
}