/* * This file is a part of the SteamWar software. * * Copyright (C) 2023 SteamWar.de-Serverteam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package de.steamwar.command; import lombok.Getter; import java.lang.annotation.*; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.function.Function; public interface Handler { default Optional> getGenericTypeOfReturn(Method method) { Type type; try { type = method.getGenericReturnType(); } catch (Exception e) { return Optional.empty(); } if (!(type instanceof ParameterizedType)) { return Optional.empty(); } ParameterizedType parameterizedType = (ParameterizedType) type; Type[] generics = parameterizedType.getActualTypeArguments(); if (generics.length != 1 && generics.length != 2) { return Optional.empty(); } Type genericType = generics[generics.length - 1]; try { return Optional.of(Class.forName(genericType.getTypeName())); } catch (Exception e) { return Optional.empty(); } } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) @interface Implementation { Class value(); } class HandlerException extends Exception { public HandlerException(String message) { super(message); } } interface DataCheckable { boolean hasExecutorMapper(Class clazz); boolean hasMapper(String key); boolean hasValidator(String key); boolean hasSupplier(String key); } interface DataReadable { Function getExecutorMapper(Class clazz); AbstractTypeMapper getMapper(String key); AbstractTypeValidator getValidator(String key); AbstractTypeSupplier getSupplier(String key); } interface DataWritable { Function getExecutorMapper(Class clazz); void addMapper(String key, boolean local, AbstractTypeMapper mapper); void addValidator(String key, boolean local, AbstractTypeValidator validator); void addSupplier(String key, boolean local, AbstractTypeSupplier supplier); /** * Invoking the same method twice will result in the exact same result. */ T invoke(Method method); void addCommand(Method method, String[] subCommand, String[] description, boolean noTabComplete); } interface HandlerMethod extends Handler { default void check(T annotation, Method method, DataCheckable dataCheckable) throws Exception { check(annotation, new MethodWrapper.ForMethod(method), dataCheckable); } default void check(T annotation, MethodWrapper method, DataCheckable dataCheckable) throws HandlerException { } int getRunPriority(); void run(T annotation, Method method, DataWritable dataWritable); } interface HandlerParameter extends Handler { default void check(T annotation, Parameter parameter, int index, DataCheckable dataCheckable) throws Exception { check(annotation, new MethodWrapper.ForMethod((Method) parameter.getDeclaringExecutable()), new ParameterWrapper.ForParameter(parameter), index, dataCheckable); } default void check(T annotation, MethodWrapper methodWrapper, ParameterWrapper parameter, int index, DataCheckable dataCheckable) throws HandlerException { } default boolean needsParentTypeMapper() { return false; } /** * Null values are allowed to be returned. */ default AbstractTypeMapper getTypeMapper(T annotation, Parameter parameter, int index, DataReadable dataReadable, AbstractTypeMapper parentTypeMapper) { return null; } default int getValidatorPriority() { return 0; } /** * Null values are allowed to be returned. */ default AbstractTypeValidator getValidator(T annotation, Parameter parameter, int index, DataReadable dataReadable) { return null; } } interface MethodWrapper { String getName(); int getParameterCount(); TypeWrapper getReturnType(); ParameterWrapper[] getParameters(); class ForMethod implements MethodWrapper { @Getter private final Method method; public ForMethod(Method method) { this.method = method; } @Override public String getName() { return method.getName(); } @Override public int getParameterCount() { return method.getParameterCount(); } @Override public TypeWrapper getReturnType() { return new TypeWrapper.ForClass(method.getReturnType()); } @Override public ParameterWrapper[] getParameters() { return Arrays.stream(method.getParameters()).map(ParameterWrapper.ForParameter::new).toArray(ParameterWrapper[]::new); } } } interface ParameterWrapper { TypeWrapper getType(); boolean isVarArgs(); boolean isAnnotationPresent(Class annotation); A getAnnotation(Class annotation); String getName(); List getAnnotations(); class ForParameter implements ParameterWrapper { @Getter private final Parameter parameter; public ForParameter(Parameter parameter) { this.parameter = parameter; } @Override public TypeWrapper getType() { return new TypeWrapper.ForClass(this.parameter.getType()); } @Override public boolean isVarArgs() { return parameter.isVarArgs(); } @Override public boolean isAnnotationPresent(Class annotation) { return parameter.isAnnotationPresent(annotation); } @Override public A getAnnotation(Class annotation) { return parameter.getAnnotation(annotation); } @Override public String getName() { return parameter.getName(); } @Override public List getAnnotations() { return CommandUtils.getAnnotations(parameter); } } } interface TypeWrapper { boolean isAssignableTo(Class clazz); boolean isAssignableTo(TypeWrapper type); boolean isPrimitive(); boolean isArray(); boolean isEnum(); boolean is(Class clazz); boolean is(TypeWrapper type); TypeWrapper getComponentType(); String getName(); class ForClass implements TypeWrapper { @Getter private final Class clazz; public ForClass(Class clazz) { this.clazz = clazz; } @Override public boolean isAssignableTo(Class clazz) { return clazz.isAssignableFrom(this.clazz); } @Override public boolean isAssignableTo(TypeWrapper type) { if (type instanceof ForClass) { return ((ForClass) type).getClazz().isAssignableFrom(clazz); } return false; } @Override public boolean isPrimitive() { return clazz.isPrimitive(); } @Override public boolean isArray() { return clazz.isArray(); } @Override public boolean isEnum() { return clazz.isEnum(); } @Override public boolean is(Class clazz) { return this.clazz == clazz; } @Override public boolean is(TypeWrapper type) { if (type instanceof ForClass) { return ((ForClass) type).getClazz() == this.clazz; } return false; } @Override public TypeWrapper getComponentType() { return new TypeWrapper.ForClass(this.clazz.getComponentType()); } @Override public String getName() { return this.clazz.getName(); } } } }