org.codefilarete.tool.exception.Exceptions Maven / Gradle / Ivy
package org.codefilarete.tool.exception;
import java.util.NoSuchElementException;
import java.util.function.Predicate;
import org.codefilarete.tool.collection.Iterables;
import org.codefilarete.tool.collection.ReadOnlyIterator;
/**
*
* @author Guillaume Mary
*/
public interface Exceptions {
/**
* Convert a {@link Throwable} into a {@link RuntimeException}. Do nothing if the {@link Throwable} is already a {@link RuntimeException},
* else instantiate a {@link RuntimeException} with the exception as the init cause.
* Please use with caution because doing this can be considered as a bad practice.
*
* @param t any kinf of exception
* @return the {@link Throwable} itself if it's already a {@link RuntimeException},
* else a {@link RuntimeException} which cause is the {@link Throwable} argument
*/
static RuntimeException asRuntimeException(Throwable t) {
if (t instanceof RuntimeException) {
return (RuntimeException) t;
} else {
return new RuntimeException(t);
}
}
static T findExceptionInCauses(Throwable t, Class throwableClass) {
return (T) findExceptionInCauses(t, new ClassExceptionFilter<>(throwableClass));
}
static T findExceptionInCauses(Throwable t, Class throwableClass, String message) {
return (T) findExceptionInCauses(t, new ClassAndMessageExceptionFilter<>(throwableClass, message));
}
static T findExceptionInCauses(Throwable t, Class throwableClass, Predicate messageAccepter) {
return (T) findExceptionInCauses(t, new ClassAndMessageExceptionFilter<>(throwableClass, messageAccepter));
}
/**
* Look up a {@link Throwable} in the causes hierarchy of the {@link Throwable} argument according to a {@link ExceptionFilter}
*
* @param t the initial stack error
* @param filter a filter
* @return null if not found
*/
static Throwable findExceptionInCauses(Throwable t, final ExceptionFilter filter) {
return Iterables.stream(new ExceptionCauseIterator(t)).filter(filter::accept).findAny().orElse(null);
}
/**
* Iterator on {@link Throwable} causes (and itself)
*/
class ExceptionCauseIterator extends ReadOnlyIterator {
private Throwable currentThrowable;
private boolean hasNext = false;
public ExceptionCauseIterator(Throwable throwable) {
this.currentThrowable = throwable;
}
@Override
public boolean hasNext() {
return hasNext = currentThrowable != null;
}
@Override
public Throwable next() {
if (!hasNext) {
// this is necessary to be compliant with Iterator#next(..) contract
throw new NoSuchElementException();
}
Throwable next = currentThrowable;
currentThrowable = currentThrowable.getCause();
return next;
}
}
interface ExceptionFilter {
boolean accept(Throwable t);
}
class ClassExceptionFilter implements ExceptionFilter {
private final Class targetClass;
public ClassExceptionFilter(Class c) {
this.targetClass = c;
}
public boolean accept(Throwable t) {
return targetClass.isAssignableFrom(t.getClass());
}
}
class ClassAndMessageExceptionFilter extends ClassExceptionFilter {
private final Predicate messageAccepter;
public ClassAndMessageExceptionFilter(Class c, String message) {
super(c);
this.messageAccepter = message::equalsIgnoreCase;
}
public ClassAndMessageExceptionFilter(Class c, Predicate messageAccepter) {
super(c);
this.messageAccepter = messageAccepter;
}
@Override
public boolean accept(Throwable t) {
return super.accept(t) && this.messageAccepter.test(t.getMessage());
}
}
}