Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/main/java/org/mitre/caasd/commons/lambda/CheckedCallable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.mitre.caasd.commons.lambda;

import java.util.concurrent.Callable;

/**
* A CheckedCallable is similar to a {@link Callable} EXCEPT it throws a checked exception.
* <p>
* Unfortunately, CheckedCallables can obfuscate code because they require using try-catch blocks.
* This class and the convenience functions in {@link Uncheck}, allow you to improve the readability
* of some code (assuming you are willing to demote all checked exceptions to RuntimeExceptions)
* <p>
* For example:
*
* <pre>{@code
* //code WITHOUT these utilities -- is harder to read and write.
* try {
* return myThrowingCallable();
* } catch (AnnoyingCheckedException ex) {
* throw DemotedException.demote(ex);
* }})
*
*
* //code WITH these utilities -- is easier to read and write.
* return Uncheck.call(() -> myThrowingCallable());
* }</pre>
*/
@FunctionalInterface
public interface CheckedCallable<V> {

V call() throws Exception;
}
29 changes: 29 additions & 0 deletions src/main/java/org/mitre/caasd/commons/lambda/CheckedRunnable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.mitre.caasd.commons.lambda;

/**
* A CheckedRunnable is similar to a {@link Runnable} EXCEPT it throws a checked exception.
* <p>
* Unfortunately, CheckedRunnables can obfuscate code because they require using try-catch blocks.
* This class and the convenience functions in {@link Uncheck}, allow you to improve the readability
* of some code (assuming you are willing to demote all checked exceptions to RuntimeExceptions)
* <p>
* For example:
*
* <pre>{@code
* //code WITHOUT these utilities -- is harder to read and write.
* try {
* myThrowingFunction();
* } catch (AnnoyingCheckedException ex) {
* throw DemotedException.demote(ex);
* }})
*
*
* //code WITH these utilities -- is easier to read and write.
* Uncheck.run(() -> myThrowingFunction());
* }</pre>
*/
@FunctionalInterface
public interface CheckedRunnable {

void run() throws Exception;
}
42 changes: 38 additions & 4 deletions src/main/java/org/mitre/caasd/commons/lambda/Uncheck.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@
import org.mitre.caasd.commons.util.DemotedException;

/**
* Collection of convenience methods to shorten the syntax when used in
* {@link Stream} operators:
* Uncheck contains a batch of convenience methods to remove try/catch blocks. These methods
* "demote" checked exceptions into RuntimeExceptions whose cause is the original checked
* exception.
* <p>
* Demoting exception shortens syntax and greatly simplifies {@link Stream} operators:
*
* <pre>{@code
* // remove the "ick" from needed try/catch blocks in the filter and map stages of this pipeline.
* stream
* .filter(Uncheck.pred(x -> checkedTest(x)))
* .map(Uncheck.func(x -> checkedFn(x)))
Expand Down Expand Up @@ -93,7 +97,7 @@ public static <T> Predicate<T> pred(CheckedPredicate<T> pred) {
* {@link Consumer}. This method is for simplifying stream processing pipelines.
*
* @param consumer A consumer that throws a checked exception
* @param <T> The generic type
* @param <T> The generic type
*
* @return A plain Consumer that may emit DemotedExceptions
* @throws DemotedException when the original CheckedConsumer would have thrown a checked
Expand Down Expand Up @@ -163,7 +167,7 @@ public static <T> BinaryOperator<T> biOp(CheckedBinaryOperator<T> binaryOperator
* {@link Supplier}. This method is for simplifying stream processing pipelines.
*
* @param checkedSupplier A supplier that throws a checked exception
* @param <T> The supplied type
* @param <T> The supplied type
*
* @return A plain Supplier that may emit DemotedExceptions
* @throws DemotedException when the original CheckedSupplier would have thrown a checked
Expand All @@ -181,4 +185,34 @@ public static <T> Supplier<T> supplier(CheckedSupplier<T> checkedSupplier) {
}
};
}

/**
* Wraps this "Runnable" with a try/catch block that demotes all checked exceptions.
*
* @param checkedRunnable A Runnable that can throw a checked exception
*/
public static void run(CheckedRunnable checkedRunnable) {
try {
checkedRunnable.run();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw DemotedException.demote(e);
}
}

/**
* Wraps this "Callable" with a try/catch block that demotes all checked exceptions.
*
* @param checkedCallable A Callable that can throw a checked exception
*/
public static <T> T call(CheckedCallable<T> checkedCallable) {
try {
return checkedCallable.call();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw DemotedException.demote(e);
}
}
}
48 changes: 48 additions & 0 deletions src/test/java/org/mitre/caasd/commons/lambda/UncheckTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.text.ParseException;
import java.util.ArrayList;
Expand Down Expand Up @@ -213,4 +214,51 @@ private Color color(String s) throws CheckedIllegalArgumentException {
throw new CheckedIllegalArgumentException(e);
}
}

@Test
void testCheckedRunnable() {

// test Uncheck.run(() -> throwCheckedException());
DemotedException ex1 = assertThrows(DemotedException.class, () -> Uncheck.run(() -> throwCheckedException()));

assertInstanceOf(ParseException.class, ex1.getCause());

// test Uncheck.run(() -> throwRuntimeException());
//
// RuntimeExceptions are not demoted ...
assertThrows(UnsupportedOperationException.class, () -> Uncheck.run(() -> throwRuntimeException()));
}

@Test
void testCheckedCallable() {

// test Uncheck.run(() -> throwCheckedException());
DemotedException ex1 =
assertThrows(DemotedException.class, () -> Uncheck.call(() -> throwCheckedException_callable()));

assertInstanceOf(ParseException.class, ex1.getCause());

// test Uncheck.call(() -> throwRuntimeException_callable());
//
// RuntimeExceptions are not demoted ...
assertThrows(UnsupportedOperationException.class, () -> Uncheck.call(() -> throwRuntimeException_callable()));
}

void throwCheckedException() throws ParseException {
throw new ParseException("I always fail", 0);
}

void throwRuntimeException() throws Exception {
throw new UnsupportedOperationException("I always fail");
}

String throwCheckedException_callable() throws ParseException {
throwCheckedException();
return "never get here";
}

String throwRuntimeException_callable() throws Exception {
throwRuntimeException();
return "never get here";
}
}
Loading