ExceptionConverter.java
package org.codefilarete.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import org.codefilarete.tool.Reflections;
import org.codefilarete.tool.StringAppender;
import org.codefilarete.tool.collection.Arrays;
import org.codefilarete.tool.collection.Iterables;
import org.codefilarete.tool.exception.Exceptions;
import org.codefilarete.tool.exception.NotImplementedException;
/**
* A tool class to convert some exceptions from default JDK to a clearer one, well ... hope so !
*
* @author Guillaume Mary
*/
public class ExceptionConverter {
protected RuntimeException convertException(Throwable t, Object target, AbstractReflector reflector, Object... args) {
if (t instanceof NullPointerException) {
NullPointerException nullPointerException = new NullPointerException(
"Cannot invoke " + getReflectorDescription(reflector) + " on null instance");
nullPointerException.initCause(t); // initial NPE is added for more trace
return nullPointerException;
} else if (t instanceof InvocationTargetException || t instanceof IllegalAccessException) {
return Exceptions.asRuntimeException(t);
} else if (t instanceof IllegalArgumentException) {
if ("wrong number of arguments".equals(t.getMessage())) {
return convertWrongNumberOfArguments(reflector, args);
} else if ("object is not an instance of declaring class".equals(t.getMessage())) {
return convertObjectIsNotAnInstanceOfDeclaringClass(target, reflector);
} else if (t.getMessage().startsWith("Can not set")) {
return convertCannotSetFieldToObject(target, reflector, args.length > 0 ? args[0] : null);
} else if ("argument type mismatch".equals(t.getMessage())) {
return convertArgumentTypeMismatch((IllegalArgumentException) t, reflector, args);
} else {
return Exceptions.asRuntimeException(t);
}
} else {
return Exceptions.asRuntimeException(t);
}
}
private IllegalArgumentException convertWrongNumberOfArguments(AbstractReflector reflector, Object... args) {
String message = "wrong number of arguments for " + getReflectorDescription(reflector);
if (reflector instanceof AccessorByMethod) {
message += giveMessageForWrongNumberOfArguments(((AccessorByMethod) reflector).getGetter(), args);
}
return new IllegalArgumentException(message);
}
public String giveMessageForWrongNumberOfArguments(Method method, Object[] args) {
TypeAppender parameterFormatter = new TypeAppender(100);
parameterFormatter.ccat(method.getParameterTypes(), ", ");
return ": expected " + parameterFormatter.toString()
+ " but " + (Arrays.isEmpty(args) ? "none" : new StringAppender(100).ccat(args, ", ").wrap("(", ")"))
+ " was given";
}
private IllegalArgumentException convertObjectIsNotAnInstanceOfDeclaringClass(Object target, AbstractReflector reflector) {
String message = "object is not an instance of declaring class";
if (reflector instanceof AccessorByMember || reflector instanceof MutatorByMember) {
Member member;
if (reflector instanceof AccessorByMember) {
member = ((AccessorByMember) reflector).getGetter();
} else {
member = ((MutatorByMember) reflector).getSetter();
}
Class<?> declaringClass = member.getDeclaringClass();
message += giveMessageForConvertObjectIsNotAnInstanceOfDeclaringClass(target, declaringClass);
}
return new IllegalArgumentException(message);
}
public String giveMessageForConvertObjectIsNotAnInstanceOfDeclaringClass(Object target, Class<?> declaringClass) {
return ": expected " + Reflections.toString(declaringClass) + " but " + Reflections.toString(target.getClass()) + " was given";
}
private RuntimeException convertCannotSetFieldToObject(Object target, AbstractReflector reflector, Object arg) {
// Modifying default message because it's not really understandable "Can not set ... to ... "
Field getter;
if (reflector instanceof AccessorByField) {
getter = ((AccessorByField) reflector).getGetter();
} else if (reflector instanceof MutatorByField) {
getter = ((MutatorByField) reflector).getSetter();
} else {
// this should never happen because this method only handle field access which are handled by previous ifs
throw new NotImplementedException(reflector.getClass() + " is not handled by this convertor");
}
// 2 cases happen here: Object is of wrong type and has no matching field, or boxing of value is wrong
// (cf https://docs.oracle.com/javase/tutorial/reflect/member/fieldTrouble.html) but we can't distinguish cases
if (Reflections.findField(target.getClass(), getter.getName()) == null) {
return new IllegalArgumentException("Field " + Reflections.toString(getter) + " doesn't exist in " + Reflections.toString(target.getClass()));
} else if (!target.getClass().isAssignableFrom(getter.getDeclaringClass())) {
return new IllegalArgumentException("Field " + Reflections.toString(getter) + " cannot be applied on instance of type " + Reflections.toString(target.getClass()));
} else if (!getter.getType().isInstance(arg)) {
return new IllegalArgumentException("Field " + Reflections.toString(getter)
+ " of type " + Reflections.toString(getter.getType()) + " is not compatible with " + (arg == null ? "null" : Reflections.toString(arg.getClass())));
} else {
return new RuntimeException("Can not set " + arg + " to " + target);
}
}
private IllegalArgumentException convertArgumentTypeMismatch(IllegalArgumentException t, AbstractReflector reflector, Object... args) {
if (reflector instanceof MutatorByMethod) {
TypeAppender parameterFormatter = new TypeAppender(100);
parameterFormatter.cat(getReflectorDescription(reflector), " expects ")
.ccat(((MutatorByMethod) reflector).getMethod().getParameterTypes(), ", ")
.cat(" as argument, but ")
.ccat(Iterables.collectToList(Arrays.asList(args), Object::getClass), ", ")
.cat(" was given");
throw new IllegalArgumentException(parameterFormatter.toString());
} else {
// actually I'm not sure that something else than a MutatorByMethod can raise an "argument type mismatch" exception
// so this code may be never get called
throw t;
}
}
private String getReflectorDescription(AbstractReflector reflector) {
if (reflector instanceof AbstractAccessor) {
return ((AbstractAccessor) reflector).getGetterDescription();
}
if (reflector instanceof AbstractMutator) {
return ((AbstractMutator) reflector).getSetterDescription();
}
throw new IllegalArgumentException("Unknown reflector " + reflector);
}
private static class TypeAppender extends StringAppender {
private TypeAppender(int capacity) {
super(capacity);
}
@Override
public StringAppender cat(Object s) {
return s instanceof Class ? super.cat(Reflections.toString((Class) s)) : super.cat(s);
}
}
}