package tc.oc.commons.core.exception;
import java.util.Optional;
import javax.annotation.Nullable;
import com.google.common.base.Throwables;
import tc.oc.commons.core.reflect.TypeParameterCache;
import tc.oc.commons.core.util.StackTrace;
import tc.oc.commons.core.util.ThrowingRunnable;
import tc.oc.commons.core.util.ThrowingSupplier;
/**
* An object that handles exceptions of some specified type. Essentially, this is
* an encapsulated catch block.
*
* TODO: Merge with {@link tc.oc.exception.ExceptionHandler}
*/
public interface ExceptionHandler<T extends Throwable> {
TypeParameterCache<ExceptionHandler, Throwable> CACHE_T = new TypeParameterCache<>(ExceptionHandler.class, "T");
default Class<T> exceptionType() {
return (Class<T>) CACHE_T.resolveRaw(getClass());
}
/**
* Handle the given exception in some way. If a source is given, it is some executable
* object from which the exception was thrown. The stack trace from the source object
* will be the origin of that object, which may be entirely different than the trace
* from the exception.
*/
void handleException(T throwable, @Nullable Object source, @Nullable StackTrace trace);
default void handleException(T throwable, @Nullable Object source) {
handleException(throwable, source, null);
}
default void handleException(T throwable) {
handleException(throwable, null);
}
default void run(ThrowingRunnable<T> block) {
try {
block.runThrows();
} catch(Throwable ex) {
final Class<T> type = exceptionType();
if(type.isInstance(ex)) {
handleException(type.cast(ex), block);
} else {
throw Throwables.propagate(ex);
}
}
}
default <U> Optional<U> get(ThrowingSupplier<U, T> block) {
try {
return Optional.of(block.getThrows());
} catch(Throwable ex) {
final Class<T> type = exceptionType();
if(type.isInstance(ex)) {
handleException(type.cast(ex), block);
return Optional.empty();
} else {
throw Throwables.propagate(ex);
}
}
}
default <U> Optional<U> flatGet(ThrowingSupplier<Optional<U>, T> block) {
return get(block).orElse(Optional.empty());
}
}