Improve error messages

This commit is contained in:
2025-11-24 10:00:45 +01:00
parent e9d814bc95
commit 3d6c50938c
31 changed files with 336 additions and 203 deletions
@@ -185,7 +185,7 @@ public abstract class AbstractCommand<T> {
handlerMap.get(runPriority).forEach((dataMethodAnnotationTriple, handlerMethods) -> {
handlerMethods.forEach(annotationHandlerMethod -> {
try {
annotationHandlerMethod.check(dataMethodAnnotationTriple.c, new Handler.MethodWrapper.ForMethod(dataMethodAnnotationTriple.b), dataMethodAnnotationTriple.a);
annotationHandlerMethod.check(new Handler.AnnotationWrapper.ForAnnotation<>(dataMethodAnnotationTriple.c), new Handler.MethodWrapper.ForMethod(dataMethodAnnotationTriple.b), dataMethodAnnotationTriple.a);
} catch (Exception e) {
throw new UnsupportedOperationException("Method check failed for " + dataMethodAnnotationTriple.b.getName(), e);
}
@@ -30,6 +30,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
public interface Handler {
@@ -63,8 +64,12 @@ public interface Handler {
}
class HandlerException extends Exception {
public HandlerException(String message) {
@Getter
private final CodePlace codePlace;
public HandlerException(String message, CodePlace codePlace) {
super(message);
this.codePlace = codePlace;
}
}
@@ -106,7 +111,7 @@ public interface Handler {
}
interface HandlerMethod<T extends Annotation> extends Handler {
void check(T annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException;
void check(AnnotationWrapper<T> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException;
int getRunPriority();
@@ -114,7 +119,7 @@ public interface Handler {
}
interface HandlerParameter<T extends Annotation, A, B> extends Handler {
void check(T annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException;
void check(AnnotationWrapper<T> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException;
default boolean needsParentTypeMapper() {
return false;
@@ -139,6 +144,26 @@ public interface Handler {
}
}
@Getter
class CodePlace {
private final MethodWrapper method;
private final ParameterWrapper parameter;
private final AnnotationWrapper<?>[] annotations;
public CodePlace(MethodWrapper method, AnnotationWrapper<?>... annotations) {
this.method = method;
this.parameter = null;
this.annotations = annotations;
}
public CodePlace(ParameterWrapper parameter, AnnotationWrapper<?>... annotations) {
this.method = null;
this.parameter = parameter;
this.annotations = annotations;
}
}
interface MethodWrapper {
String getName();
@@ -186,11 +211,11 @@ public interface Handler {
boolean isAnnotationPresent(Class<? extends Annotation> annotation);
<A extends Annotation> A getAnnotation(Class<A> annotation);
<A extends Annotation> AnnotationWrapper<A> getAnnotation(Class<A> annotation);
String getName();
List<Annotation> getAnnotations();
List<AnnotationWrapper<?>> getAnnotations();
class ForParameter implements ParameterWrapper {
@@ -217,8 +242,8 @@ public interface Handler {
}
@Override
public <A extends Annotation> A getAnnotation(Class<A> annotation) {
return parameter.getAnnotation(annotation);
public <A extends Annotation> AnnotationWrapper<A> getAnnotation(Class<A> annotation) {
return new AnnotationWrapper.ForAnnotation<>(parameter.getAnnotation(annotation));
}
@Override
@@ -227,8 +252,11 @@ public interface Handler {
}
@Override
public List<Annotation> getAnnotations() {
return CommandUtils.getAnnotations(parameter);
public List<AnnotationWrapper<?>> getAnnotations() {
return CommandUtils.getAnnotations(parameter)
.stream()
.map(AnnotationWrapper.ForAnnotation::new)
.collect(Collectors.toList());
}
}
}
@@ -320,4 +348,23 @@ public interface Handler {
}
}
}
interface AnnotationWrapper<A extends Annotation> {
A getAnnotation();
class ForAnnotation<A extends Annotation> implements AnnotationWrapper<A> {
private A annotation;
public ForAnnotation(A annotation) {
this.annotation = annotation;
}
@Override
public A getAnnotation() {
return this.annotation;
}
}
}
}
@@ -36,12 +36,12 @@ public final class AllowNullHandler {
public static final class Impl implements Handler.HandlerParameter<AllowNull, Object, Object> {
@Override
public void check(AllowNull annotation, MethodWrapper method, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<AllowNull> annotation, MethodWrapper method, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("AllowNull annotation cannot be used on the first parameter");
throw new HandlerException("AllowNull annotation cannot be used on the first parameter", new CodePlace(parameter, annotation));
}
if (parameter.getType().isPrimitive()) {
throw new HandlerException("AllowNull annotation cannot be used on primitive types");
throw new HandlerException("AllowNull annotation cannot be used on primitive types", new CodePlace(parameter));
}
}
}
@@ -47,15 +47,15 @@ public final class ArrayLengthHandler {
public static final class Impl implements Handler.HandlerParameter<ArrayLength, Object, Object> {
@Override
public void check(ArrayLength arrayLength, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<ArrayLength> arrayLength, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("ArrayLength annotation cannot be used on first parameter");
throw new HandlerException("ArrayLength annotation cannot be used on first parameter", new CodePlace(parameter, arrayLength));
}
if (!parameter.getType().isArray()) {
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not an array");
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not an array", new CodePlace(parameter));
}
if (arrayLength.min() > arrayLength.max()) {
throw new HandlerException("Min ArrayLength cannot be smaller than Max ArrayLength");
if (arrayLength.getAnnotation().min() > arrayLength.getAnnotation().max()) {
throw new HandlerException("Min ArrayLength cannot be smaller than Max ArrayLength", new CodePlace(parameter, arrayLength));
}
}
}
@@ -31,12 +31,12 @@ public final class CachedHandler {
public static final class Impl implements Handler.HandlerMethod<Cached> {
@Override
public void check(Cached annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Cached> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() != 0) {
throw new HandlerException("Cached method must have no parameters");
throw new HandlerException("Cached method must have no parameters", new CodePlace(method));
}
if (!method.getReturnType().isAssignableTo(AbstractTypeMapper.class)) {
throw new HandlerException("Cached method must return AbstractTypeMapper");
throw new HandlerException("Cached method must return AbstractTypeMapper", new CodePlace(method));
}
}
@@ -31,12 +31,12 @@ public final class ClassMapperHandler {
public static final class Impl implements Handler.HandlerMethod<ClassMapper> {
@Override
public void check(ClassMapper annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<ClassMapper> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() != 0) {
throw new HandlerException("ClassMapper method must have no parameters");
throw new HandlerException("ClassMapper method must have no parameters", new CodePlace(method));
}
if (!method.getReturnType().isAssignableTo(AbstractTypeMapper.class)) {
throw new HandlerException("ClassMapper method must return AbstractTypeMapper");
throw new HandlerException("ClassMapper method must return AbstractTypeMapper", new CodePlace(method));
}
}
@@ -31,12 +31,12 @@ public final class ClassValidatorHandler {
public static final class Impl implements Handler.HandlerMethod<ClassValidator> {
@Override
public void check(ClassValidator annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<ClassValidator> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() != 0) {
throw new HandlerException("ClassValidator method must have no parameters");
throw new HandlerException("ClassValidator method must have no parameters", new CodePlace(method));
}
if (!method.getReturnType().isAssignableTo(AbstractTypeValidator.class)) {
throw new HandlerException("ClassValidator method must return AbstractValidator");
throw new HandlerException("ClassValidator method must return AbstractValidator", new CodePlace(method));
}
}
@@ -35,12 +35,12 @@ public final class EndsWithHandler {
public static final class Impl implements Handler.HandlerParameter<EndsWith, Object, String> {
@Override
public void check(EndsWith annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<EndsWith> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("EndsWith annotation cannot be used on first parameter");
throw new HandlerException("EndsWith annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
if (!parameter.getType().is(String.class)) {
throw new HandlerException("EndsWith annotation cannot be used on String parameter");
throw new HandlerException("EndsWith annotation cannot be used on String parameter", new CodePlace(parameter));
}
}
@@ -31,12 +31,12 @@ public final class ErrorMessageHandler {
public static final class Impl implements Handler.HandlerParameter<ErrorMessage, Object, Object> {
@Override
public void check(ErrorMessage errorMessage, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<ErrorMessage> errorMessage, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("ErrorMessage annotation cannot be used on first parameter");
throw new HandlerException("ErrorMessage annotation cannot be used on first parameter", new CodePlace(parameter, errorMessage));
}
if (!errorMessage.allowEAs() && !(parameter.isVarArgs() || parameter.getType().isArray())) {
throw new HandlerException("ErrorMessage allowESs cannot be used on non array or varargs parameter");
if (!errorMessage.getAnnotation().allowEAs() && !(parameter.isVarArgs() || parameter.getType().isArray())) {
throw new HandlerException("ErrorMessage allowESs cannot be used on non array or varargs parameter", new CodePlace(parameter));
}
}
@@ -44,9 +44,9 @@ public final class GreedyHandler {
public static final class Impl implements Handler.HandlerParameter<Greedy, Object, Object> {
@Override
public void check(Greedy annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Greedy> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (!parameter.getType().isArray() || parameter.isVarArgs()) {
throw new HandlerException("Greedy annotation cannot be used on non array parameters");
throw new HandlerException("Greedy annotation cannot be used on non array parameters", new CodePlace(parameter));
}
}
}
@@ -35,9 +35,9 @@ public final class LengthHandler {
public static final class Impl implements Handler.HandlerParameter<Length, Object, Object> {
@Override
public void check(Length annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Length> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Length annotation cannot be used on first parameter");
throw new HandlerException("Length annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
}
@@ -32,28 +32,28 @@ public final class MapperHandler {
public static final class Impl implements Handler.HandlerMethod<Mapper>, Handler.HandlerParameter<Mapper, Object, Object> {
@Override
public void check(Mapper annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Mapper> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() != 0) {
throw new HandlerException("Mapper method must have no parameters");
throw new HandlerException("Mapper method must have no parameters", new CodePlace(method));
}
if (!method.getReturnType().isAssignableTo(AbstractTypeMapper.class)) {
throw new HandlerException("Mapper method must return AbstractTypeMapper");
throw new HandlerException("Mapper method must return AbstractTypeMapper", new CodePlace(method));
}
if (annotation.value() != null && !annotation.value().isEmpty()) {
if (annotation.getAnnotation().value() != null && !annotation.getAnnotation().value().isEmpty()) {
return;
}
// TODO: Implement! for AnnotationProcessor as well!
if (method instanceof MethodWrapper.ForMethod) {
Optional<Class<?>> genericType = getGenericTypeOfReturn(((MethodWrapper.ForMethod) method).getMethod());
if (annotation.type() == void.class) {
if (annotation.getAnnotation().type() == void.class) {
if (genericType.isEmpty()) {
throw new HandlerException("Please supply a class type to the Mapper");
throw new HandlerException("Please supply a class type to the Mapper", new CodePlace(method));
}
} else {
if (genericType.isPresent() && !annotation.type().isAssignableFrom(genericType.get())) {
throw new HandlerException("Supplied type does not conform to actual type");
if (genericType.isPresent() && !annotation.getAnnotation().type().isAssignableFrom(genericType.get())) {
throw new HandlerException("Supplied type does not conform to actual type", new CodePlace(method));
}
}
}
@@ -77,12 +77,12 @@ public final class MapperHandler {
}
@Override
public void check(Mapper annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Mapper> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Mapper annotation cannot be used on first parameter");
throw new HandlerException("Mapper annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
if (!dataCheckable.hasMapper(annotation.value())) {
throw new HandlerException("Mapper '" + annotation.value() + "' not found");
if (!dataCheckable.hasMapper(annotation.getAnnotation().value())) {
throw new HandlerException("Mapper '" + annotation.getAnnotation().value() + "' not found", new CodePlace(parameter));
}
}
@@ -34,41 +34,41 @@ public final class MaxHandler {
public static final class Impl implements Handler.HandlerParameter<Max, Object, Number> {
@Override
public void check(Max max, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Max> max, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Max annotation cannot be used on first parameter");
throw new HandlerException("Max annotation cannot be used on first parameter", new CodePlace(parameter, max));
}
TypeWrapper type = parameter.getType();
if (parameter.isVarArgs()) type = type.getComponentType();
if (!type.is(int.class) && !type.is(Integer.class) && !type.is(long.class) && !type.is(Long.class) && !type.is(float.class) && !type.is(Float.class) && !type.is(double.class) && !type.is(Double.class)) {
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.");
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.", new CodePlace(parameter));
}
if (!parameter.isAnnotationPresent(Min.class)) {
return;
}
Min min = parameter.getAnnotation(Min.class);
AnnotationWrapper<Min> min = parameter.getAnnotation(Min.class);
Number minValue;
if (type.is(int.class) || type.is(Integer.class)) {
minValue = min.intValue();
minValue = min.getAnnotation().intValue();
} else if (type.is(long.class) || type.is(Long.class)) {
minValue = min.longValue();
minValue = min.getAnnotation().longValue();
} else if (type.is(float.class) || type.is(Float.class)) {
minValue = min.floatValue();
minValue = min.getAnnotation().floatValue();
} else if (type.is(double.class) || type.is(Double.class)) {
minValue = min.doubleValue();
minValue = min.getAnnotation().doubleValue();
} else {
throw new SecurityException();
}
ToIntFunction<Number> maxComparator = CommandUtils.createComparator("Max", type, max.intValue(), max.longValue(), max.floatValue(), max.doubleValue());
if (!(min.inclusive() && max.inclusive()) && maxComparator.applyAsInt(minValue) == 0) {
throw new HandlerException("Min and Max cannot be equal if not both are inclusive");
ToIntFunction<Number> maxComparator = CommandUtils.createComparator("Max", type, max.getAnnotation().intValue(), max.getAnnotation().longValue(), max.getAnnotation().floatValue(), max.getAnnotation().doubleValue());
if (!(min.getAnnotation().inclusive() && max.getAnnotation().inclusive()) && maxComparator.applyAsInt(minValue) == 0) {
throw new HandlerException("Min and Max cannot be equal if not both are inclusive", new CodePlace(parameter, max, min));
} else if (maxComparator.applyAsInt(minValue) > 0) {
throw new HandlerException("Max must be bigger then Min");
throw new HandlerException("Max must be bigger then Min", new CodePlace(parameter, max, min));
}
}
@@ -36,38 +36,38 @@ public final class MaxReferenceHandler {
public static final class Impl implements Handler.HandlerParameter<Max.Reference, Object, Number> {
@Override
public void check(Max.Reference annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Max.Reference> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Max.Reference annotation cannot be used on first parameter");
throw new HandlerException("Max.Reference annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
TypeWrapper type = parameter.getType();
if (type.isArray()) type = type.getComponentType();
if (!type.is(int.class) && !type.is(Integer.class) && !type.is(long.class) && !type.is(Long.class) && !type.is(float.class) && !type.is(Float.class) && !type.is(double.class) && !type.is(Double.class)) {
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.");
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.", new CodePlace(parameter));
}
ParameterWrapper[] parameters = methodWrapper.getParameters();
for (int i = index - 1; i >= 1; i--) {
ParameterWrapper toCheck = parameters[i];
Name name = toCheck.getAnnotation(Name.class);
AnnotationWrapper<Name> name = toCheck.getAnnotation(Name.class);
String parameterName;
if (name == null) {
if (name.getAnnotation() == null) {
parameterName = toCheck.getName();
} else {
parameterName = name.value();
parameterName = name.getAnnotation().value();
}
if (!parameterName.equals(annotation.value())) {
if (!parameterName.equals(annotation.getAnnotation().value())) {
continue;
}
TypeWrapper parameterType = toCheck.getType();
if (!parameterType.isAssignableTo(type)) {
throw new HandlerException("Parameter type being referenced cannot be assigned here!");
throw new HandlerException("Parameter type being referenced cannot be assigned here!", new CodePlace(toCheck, name));
} else {
return;
}
}
throw new HandlerException("Parameter with name '" + annotation.value() + "' does not exist.");
throw new HandlerException("Parameter with name '" + annotation.getAnnotation().value() + "' does not exist.", new CodePlace(parameter, annotation));
}
@Override
@@ -33,9 +33,9 @@ public final class MinHandler {
public static final class Impl implements Handler.HandlerParameter<Min, Object, Number> {
@Override
public void check(Min annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Min> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Min annotation cannot be used on first parameter");
throw new HandlerException("Min annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
TypeWrapper type = parameter.getType();
if (parameter.isVarArgs()) type = type.getComponentType();
@@ -43,7 +43,7 @@ public final class MinHandler {
if (type.is(long.class) || type.is(Long.class)) return;
if (type.is(float.class) || type.is(Float.class)) return;
if (type.is(double.class) || type.is(Double.class)) return;
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.");
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.", new CodePlace(parameter));
}
@Override
@@ -36,38 +36,38 @@ public final class MinReferenceHandler {
public static final class Impl implements Handler.HandlerParameter<Min.Reference, Object, Number> {
@Override
public void check(Min.Reference annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Min.Reference> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Min.Reference annotation cannot be used on first parameter");
throw new HandlerException("Min.Reference annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
TypeWrapper type = parameter.getType();
if (type.isArray()) type = type.getComponentType();
if (!type.is(int.class) && !type.is(Integer.class) && !type.is(long.class) && !type.is(Long.class) && !type.is(float.class) && !type.is(Float.class) && !type.is(double.class) && !type.is(Double.class)) {
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.");
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a number.", new CodePlace(parameter));
}
ParameterWrapper[] parameters = methodWrapper.getParameters();
for (int i = index - 1; i >= 1; i--) {
ParameterWrapper toCheck = parameters[i];
Name name = toCheck.getAnnotation(Name.class);
AnnotationWrapper<Name> name = toCheck.getAnnotation(Name.class);
String parameterName;
if (name == null) {
if (name.getAnnotation() == null) {
parameterName = toCheck.getName();
} else {
parameterName = name.value();
parameterName = name.getAnnotation().value();
}
if (!parameterName.equals(annotation.value())) {
if (!parameterName.equals(annotation.getAnnotation().value())) {
continue;
}
TypeWrapper parameterType = toCheck.getType();
if (!parameterType.isAssignableTo(type)) {
throw new HandlerException("Parameter type being referenced cannot be assigned here!");
throw new HandlerException("Parameter type being referenced cannot be assigned here!", new CodePlace(toCheck, name));
} else {
return;
}
}
throw new HandlerException("Parameter with name '" + annotation.value() + "' does not exist.");
throw new HandlerException("Parameter with name '" + annotation.getAnnotation().value() + "' does not exist.", new CodePlace(parameter, annotation));
}
@Override
@@ -35,12 +35,12 @@ public final class OptionalValueHandler {
public static final class Impl implements Handler.HandlerParameter<OptionalValue, Object, Object> {
@Override
public void check(OptionalValue annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<OptionalValue> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("OptionalValue annotation cannot be used on the first parameter");
throw new HandlerException("OptionalValue annotation cannot be used on the first parameter", new CodePlace(parameter, annotation));
}
if (parameter.isVarArgs() || parameter.getType().isArray()) {
throw new HandlerException("OptionalValue annotation cannot be used on varargs or array parameters");
throw new HandlerException("OptionalValue annotation cannot be used on varargs or array parameters", new CodePlace(parameter));
}
}
@@ -31,12 +31,12 @@ public final class RegexHandler {
public static final class Impl implements Handler.HandlerParameter<Regex, Object, String> {
@Override
public void check(Regex annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Regex> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Regex annotation cannot be used on first parameter");
throw new HandlerException("Regex annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
if (!parameter.getType().is(String.class)) {
throw new HandlerException("Regex annotation cannot be used on String parameter");
throw new HandlerException("Regex annotation cannot be used on String parameter", new CodePlace(parameter));
}
}
@@ -19,14 +19,11 @@
package de.steamwar.command.handler;
import de.steamwar.command.CommandUtils;
import de.steamwar.command.Handler;
import de.steamwar.command.annotations.Register;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
public final class RegisterHandler {
@@ -34,24 +31,24 @@ public final class RegisterHandler {
public static final class Impl implements Handler.HandlerMethod<Register> {
@Override
public void check(Register annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Register> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() == 0) {
throw new HandlerException("Register method must have at least one parameter");
throw new HandlerException("Register method must have at least one parameter", new CodePlace(method));
}
if (!method.getReturnType().is(void.class)) {
throw new HandlerException("Register method must return void");
throw new HandlerException("Register method must return void", new CodePlace(method));
}
// TODO: Implement hasExecutorMapper!
if (method instanceof MethodWrapper.ForMethod) {
if (!dataCheckable.hasExecutorMapper(((MethodWrapper.ForMethod) method).getMethod().getParameterTypes()[0])) {
throw new HandlerException("Register method first parameter must have a executor mapper registered");
throw new HandlerException("Register method first parameter must have a executor mapper registered", new CodePlace(method));
}
}
ParameterWrapper[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
ParameterWrapper parameter = parameters[i];
List<Annotation> annotationList = parameter.getAnnotations();
List<AnnotationWrapper<?>> annotationList = parameter.getAnnotations();
if (annotationList.isEmpty()) {
if (i == 0) {
continue;
@@ -62,27 +59,27 @@ public final class RegisterHandler {
type = type.getComponentType();
}
if (!type.isEnum() && !dataCheckable.hasMapper(type.getName())) {
throw new HandlerException("Register method parameter " + parameter.getName() + " must be annotated or have a type mapper");
throw new HandlerException("Register method parameter " + parameter.getName() + " must be annotated or have a type mapper", new CodePlace(parameter));
}
} else {
for (Annotation annotation1 : annotationList) {
getParameterHandler(annotation1).check(annotation1, method, parameter, i, dataCheckable);
for (AnnotationWrapper<?> annotation1 : annotationList) {
getParameterHandler(parameter, annotation1).check(annotation1, method, parameter, i, dataCheckable);
}
}
}
}
public static HandlerParameter getParameterHandler(Annotation annotation) throws HandlerException {
Implementation handler = annotation.annotationType().getAnnotation(Implementation.class);
public static HandlerParameter getParameterHandler(ParameterWrapper parameter, AnnotationWrapper<?> annotation) throws HandlerException {
Implementation handler = annotation.getAnnotation().annotationType().getAnnotation(Implementation.class);
Handler handlerObject;
try {
handlerObject = handler.value().getConstructor().newInstance();
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException e) {
throw new HandlerException("Handler " + handler.value().getName() + " cannot be used to check the argument validity");
throw new HandlerException("Handler " + handler.value().getName() + " cannot be used to check the argument validity", new CodePlace(parameter, annotation));
}
if (!(handlerObject instanceof HandlerParameter)) {
throw new HandlerException("Handler " + handlerObject.getClass().getName() + " is not a HandlerParameter");
throw new HandlerException("Handler " + handlerObject.getClass().getName() + " is not a HandlerParameter", new CodePlace(parameter, annotation));
}
return (HandlerParameter) handlerObject;
}
@@ -35,12 +35,12 @@ public final class StartsWithHandler {
public static final class Impl implements Handler.HandlerParameter<StartsWith, Object, String> {
@Override
public void check(StartsWith annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<StartsWith> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("StartsWith annotation cannot be used on first parameter");
throw new HandlerException("StartsWith annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
if (!parameter.getType().is(String.class)) {
throw new HandlerException("StartsWith annotation cannot be used on String parameter");
throw new HandlerException("StartsWith annotation cannot be used on String parameter", new CodePlace(parameter));
}
}
@@ -34,16 +34,16 @@ public final class StaticValueHandler {
public static final class Impl implements Handler.HandlerParameter<StaticValue, Object, Object> {
@Override
public void check(StaticValue annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<StaticValue> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("StaticValue annotation cannot be used on first parameter");
throw new HandlerException("StaticValue annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
TypeWrapper type = parameter.getType();
if (type.is(String.class)) return;
if (type.is(int.class) || type.is(Integer.class)) return;
if (type.is(long.class) || type.is(Long.class)) return;
if (type.is(boolean.class) || type.is(Boolean.class)) return;
throw new HandlerException("StaticValue parameter must be String, int, long or boolean");
throw new HandlerException("StaticValue parameter must be String, int, long or boolean", new CodePlace(parameter));
}
@Override
@@ -33,28 +33,28 @@ public final class SupplierHandler {
public static final class Impl implements Handler.HandlerMethod<Supplier>, Handler.HandlerParameter<Supplier, Object, Object> {
@Override
public void check(Supplier annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Supplier> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() != 0) {
throw new HandlerException("Supplier method must have no parameters");
throw new HandlerException("Supplier method must have no parameters", new CodePlace(method));
}
if (!method.getReturnType().isAssignableTo(AbstractTypeSupplier.class)) {
throw new HandlerException("Supplier method must return AbstractTypeSupplier");
throw new HandlerException("Supplier method must return AbstractTypeSupplier", new CodePlace(method));
}
if (annotation.value() != null && !annotation.value().isEmpty()) {
if (annotation.getAnnotation().value() != null && !annotation.getAnnotation().value().isEmpty()) {
return;
}
// TODO: Implement! for AnnotationProcessor as well!
if (method instanceof MethodWrapper.ForMethod) {
Optional<Class<?>> genericType = getGenericTypeOfReturn(((MethodWrapper.ForMethod) method).getMethod());
if (annotation.type() == void.class) {
if (annotation.getAnnotation().type() == void.class) {
if (genericType.isEmpty()) {
throw new UnsupportedOperationException("Please supply a class type to the Supplier");
throw new HandlerException("Please supply a class type to the Supplier", new CodePlace(method));
}
} else {
if (genericType.isPresent() && !annotation.type().isAssignableFrom(genericType.get())) {
throw new UnsupportedOperationException("Supplied type does not conform to actual type");
if (genericType.isPresent() && !annotation.getAnnotation().type().isAssignableFrom(genericType.get())) {
throw new HandlerException("Supplied type does not conform to actual type", new CodePlace(method));
}
}
}
@@ -78,22 +78,23 @@ public final class SupplierHandler {
}
@Override
public void check(Supplier annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Supplier> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Supplier annotation cannot be used on the first parameter");
throw new HandlerException("Supplier annotation cannot be used on the first parameter", new CodePlace(parameter, annotation));
}
String s = annotation.value() != null && !annotation.value().isEmpty() ? annotation.value() : parameter.getType().getTypeName();
String s = annotation.getAnnotation().value() != null && !annotation.getAnnotation().value().isEmpty() ? annotation.getAnnotation().value() : parameter.getType().getTypeName();
if (!dataCheckable.hasSupplier(s)) {
throw new HandlerException("Supplier '" + annotation.value() + "' not found");
throw new HandlerException("Supplier '" + annotation.getAnnotation().value() + "' not found", new CodePlace(parameter));
}
boolean hasDisallowedAnnotations = parameter.getAnnotations()
.stream()
.filter(anno -> !(anno instanceof Supplier))
.filter(anno -> !(anno instanceof AllowNull))
.filter(anno -> !(anno instanceof Name))
.anyMatch(anno -> anno.getClass().isAnnotationPresent(Implementation.class));
.filter(anno -> !(anno.getAnnotation() instanceof Supplier))
.filter(anno -> !(anno.getAnnotation() instanceof AllowNull))
.filter(anno -> !(anno.getAnnotation() instanceof Name))
.anyMatch(anno -> anno.getAnnotation().annotationType().isAnnotationPresent(Implementation.class));
if (hasDisallowedAnnotations) {
throw new HandlerException("Only AllowNull or Name can be used in conjunction with Supplier annotation");
// TODO: Improve annotation handling!
throw new HandlerException("Only AllowNull or Name can be used in conjunction with Supplier annotation", new CodePlace(parameter));
}
}
}
@@ -40,9 +40,9 @@ public final class TabFilterHandler {
public static final class Impl implements Handler.HandlerParameter<TabFilter, Object, Object> {
@Override
public void check(TabFilter annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<TabFilter> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("TabFilter annotation cannot be used on first parameter");
throw new HandlerException("TabFilter annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
}
}
@@ -36,12 +36,12 @@ public final class UniqueHandler {
public static final class Impl implements Handler.HandlerParameter<Unique, Object, Object> {
@Override
public void check(Unique annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Unique> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Unique annotation cannot be used on the first parameter");
throw new HandlerException("Unique annotation cannot be used on the first parameter", new CodePlace(parameter, annotation));
}
if (!parameter.getType().isArray() && !parameter.isVarArgs()) {
throw new HandlerException("Unique annotation cannot be used on non array types");
throw new HandlerException("Unique annotation cannot be used on non array types", new CodePlace(parameter));
}
}
}
@@ -32,28 +32,28 @@ public final class ValidatorHandler {
public static final class Impl implements Handler.HandlerMethod<Validator>, Handler.HandlerParameter<Validator, Object, Object> {
@Override
public void check(Validator annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Validator> annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException {
if (method.getParameterCount() != 0) {
throw new HandlerException("Validator method must have no parameters");
throw new HandlerException("Validator method must have no parameters", new CodePlace(method));
}
if (!method.getReturnType().isAssignableTo(AbstractTypeValidator.class)) {
throw new HandlerException("Validator method must return AbstractValidator");
throw new HandlerException("Validator method must return AbstractValidator", new CodePlace(method));
}
if (annotation.value() != null && !annotation.value().isEmpty()) {
if (annotation.getAnnotation().value() != null && !annotation.getAnnotation().value().isEmpty()) {
return;
}
// TODO: Implement! for AnnotationProcessor as well!
if (method instanceof MethodWrapper.ForMethod) {
Optional<Class<?>> genericType = getGenericTypeOfReturn(((MethodWrapper.ForMethod) method).getMethod());
if (annotation.type() == void.class) {
if (annotation.getAnnotation().type() == void.class) {
if (genericType.isEmpty()) {
throw new HandlerException("Please supply a class type to the Validator");
throw new HandlerException("Please supply a class type to the Validator", new CodePlace(method));
}
} else {
if (genericType.isPresent() && !annotation.type().isAssignableFrom(genericType.get())) {
throw new HandlerException("Supplied type does not conform to actual type");
if (genericType.isPresent() && !annotation.getAnnotation().type().isAssignableFrom(genericType.get())) {
throw new HandlerException("Supplied type does not conform to actual type", new CodePlace(method));
}
}
}
@@ -77,10 +77,10 @@ public final class ValidatorHandler {
}
@Override
public void check(Validator annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
String s = annotation.value() != null && !annotation.value().isEmpty() ? annotation.value() : parameter.getType().getTypeName();
public void check(AnnotationWrapper<Validator> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
String s = annotation.getAnnotation().value() != null && !annotation.getAnnotation().value().isEmpty() ? annotation.getAnnotation().value() : parameter.getType().getTypeName();
if (!dataCheckable.hasValidator(s)) {
throw new HandlerException("Validator '" + annotation.value() + "' not found");
throw new HandlerException("Validator '" + annotation.getAnnotation().value() + "' not found", new CodePlace(parameter, annotation));
}
}
@@ -33,16 +33,16 @@ public final class ValuesHandler {
public static final class Impl implements Handler.HandlerParameter<Values, Object, Object> {
@Override
public void check(Values annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Values> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Values annotation cannot be used on first parameter");
throw new HandlerException("Values annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
TypeWrapper type = parameter.getType();
if (type.is(String.class)) return;
if (type.is(int.class) || type.is(Integer.class)) return;
if (type.is(long.class) || type.is(Long.class)) return;
if (type.is(boolean.class) || type.is(Boolean.class)) return;
throw new HandlerException("Values parameter must be String, int, long or boolean");
throw new HandlerException("Values parameter must be String, int, long or boolean", new CodePlace(parameter));
}
@Override
@@ -36,39 +36,39 @@ public class ValuesReferenceHandler {
public static final class Impl implements Handler.HandlerParameter<Values.Reference, Object, Object> {
@Override
public void check(Values.Reference annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
public void check(AnnotationWrapper<Values.Reference> annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException {
if (index == 0) {
throw new HandlerException("Values.Reference annotation cannot be used on first parameter");
throw new HandlerException("Values.Reference annotation cannot be used on first parameter", new CodePlace(parameter, annotation));
}
TypeWrapper type = parameter.getType();
if (type.isArray()) type = type.getComponentType();
if (!type.is(int.class) && !type.is(Integer.class) && !type.is(long.class) && !type.is(Long.class) && !type.is(String.class)) {
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a String or Int or Long.");
throw new HandlerException("Parameter " + index + " of " + methodWrapper.getName() + " is not a String or Int or Long.", new CodePlace(parameter));
}
ParameterWrapper[] parameters = methodWrapper.getParameters();
for (int i = index - 1; i >= 1; i--) {
ParameterWrapper toCheck = parameters[i];
Name name = toCheck.getAnnotation(Name.class);
AnnotationWrapper<Name> name = toCheck.getAnnotation(Name.class);
String parameterName;
if (name == null) {
if (name.getAnnotation() == null) {
parameterName = toCheck.getName();
} else {
parameterName = name.value();
parameterName = name.getAnnotation().value();
}
if (!parameterName.equals(annotation.value())) {
if (!parameterName.equals(annotation.getAnnotation().value())) {
continue;
}
TypeWrapper toCheckType = toCheck.getType();
if (toCheckType.isArray()) toCheckType = toCheckType.getComponentType();
if (!toCheckType.is(type)) {
throw new HandlerException("Parameter type being referenced cannot be assigned here!");
throw new HandlerException("Parameter type being referenced cannot be assigned here!", new CodePlace(toCheck, name));
} else {
return;
}
}
throw new HandlerException("Parameter with name '" + annotation.value() + "' does not exist.");
throw new HandlerException("Parameter with name '" + annotation.getAnnotation().value() + "' does not exist.", new CodePlace(parameter, annotation));
}
@Override