forked from SteamWar/SteamWar
149 lines
6.5 KiB
Java
149 lines
6.5 KiB
Java
/*
|
|
* This file is a part of the SteamWar software.
|
|
*
|
|
* Copyright (C) 2020 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package de.steamwar.command;
|
|
|
|
import de.steamwar.command.mapper.BooleanMapper;
|
|
import de.steamwar.command.mapper.NumberMapper;
|
|
import lombok.experimental.UtilityClass;
|
|
|
|
import java.lang.annotation.Annotation;
|
|
import java.lang.annotation.Repeatable;
|
|
import java.lang.reflect.AnnotatedElement;
|
|
import java.lang.reflect.Method;
|
|
import java.util.*;
|
|
import java.util.function.ToIntFunction;
|
|
|
|
@UtilityClass
|
|
public class CommandUtils {
|
|
|
|
final Map<String, AbstractTypeMapper<?, ?>> MAPPER_FUNCTIONS = new HashMap<>();
|
|
|
|
final Map<String, AbstractTypeValidator<?, ?>> VALIDATOR_FUNCTIONS = new HashMap<>();
|
|
|
|
final Map<String, AbstractTypeSupplier<?, ?>> SUPPLIER_FUNCTIONS = new HashMap<>();
|
|
|
|
static final AbstractTypeMapper<Object, String> STRING_MAPPER = new AbstractTypeMapper<>() {
|
|
@Override
|
|
public String map(Object sender, PreviousArguments previousArguments, String s) {
|
|
return s;
|
|
}
|
|
|
|
@Override
|
|
public Collection<String> tabComplete(Object sender, PreviousArguments previousArguments, String s) {
|
|
return Collections.singletonList(s);
|
|
}
|
|
};
|
|
|
|
static {
|
|
addMapper(boolean.class, Boolean.class, new BooleanMapper());
|
|
addMapper(int.class, Integer.class, new NumberMapper(Integer::parseInt, false));
|
|
addMapper(long.class, Long.class, new NumberMapper(Long::parseLong, false));
|
|
addMapper(float.class, Float.class, new NumberMapper(Float::parseFloat, true));
|
|
addMapper(double.class, Double.class, new NumberMapper(Double::parseDouble, true));
|
|
MAPPER_FUNCTIONS.put(String.class.getTypeName(), STRING_MAPPER);
|
|
}
|
|
|
|
private static void addMapper(Class<?> clazz, Class<?> alternativeClazz, AbstractTypeMapper<?, ?> mapper) {
|
|
MAPPER_FUNCTIONS.put(clazz.getTypeName(), mapper);
|
|
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
|
|
}
|
|
|
|
public static <K, T> void addMapper(Class<T> clazz, AbstractTypeMapper<K, T> mapper) {
|
|
addMapper(clazz.getTypeName(), mapper);
|
|
}
|
|
|
|
public static <T> void addMapper(String name, AbstractTypeMapper<T, ?> mapper) {
|
|
MAPPER_FUNCTIONS.putIfAbsent(name, mapper);
|
|
}
|
|
|
|
public static <T> void addValidator(Class<T> clazz, AbstractTypeValidator<T, ?> validator) {
|
|
addValidator(clazz.getTypeName(), validator);
|
|
}
|
|
|
|
public static <T> void addValidator(String name, AbstractTypeValidator<T, ?> validator) {
|
|
VALIDATOR_FUNCTIONS.putIfAbsent(name, validator);
|
|
}
|
|
|
|
public static <T> void addSupplier(Class<T> clazz, AbstractTypeSupplier<T, ?> supplier) {
|
|
addSupplier(clazz.getTypeName(), supplier);
|
|
}
|
|
|
|
public static <T> void addSupplier(String name, AbstractTypeSupplier<T, ?> supplier) {
|
|
SUPPLIER_FUNCTIONS.putIfAbsent(name, supplier);
|
|
}
|
|
|
|
public static List<Annotation> getAnnotations(AnnotatedElement annotatedElement) {
|
|
List<Annotation> annotationList = new ArrayList<>();
|
|
for (Annotation annotation : annotatedElement.getAnnotations()) {
|
|
try {
|
|
Method method = annotation.annotationType().getMethod("value");
|
|
Class<?> returnType = method.getReturnType();
|
|
if (!returnType.isArray()) {
|
|
annotationList.add(annotation);
|
|
continue;
|
|
}
|
|
Class<?> innerReturnType = returnType.getComponentType();
|
|
if (!(innerReturnType.isAnnotation() && innerReturnType.isAnnotationPresent(Repeatable.class))) {
|
|
annotationList.add(annotation);
|
|
continue;
|
|
}
|
|
Repeatable repeatable = innerReturnType.getAnnotation(Repeatable.class);
|
|
Class<? extends Annotation> containerType = repeatable.value();
|
|
if (containerType == returnType) {
|
|
throw new UnsupportedOperationException("Repeatable annotation must have a container annotation");
|
|
}
|
|
try {
|
|
Annotation[] innerAnnotations = (Annotation[]) method.invoke(annotation);
|
|
Collections.addAll(annotationList, innerAnnotations);
|
|
} catch (Exception e) {
|
|
annotationList.add(annotation);
|
|
}
|
|
} catch (NoSuchMethodException e) {
|
|
annotationList.add(annotation);
|
|
}
|
|
}
|
|
annotationList.removeIf(anno -> !anno.annotationType().isAnnotationPresent(Handler.Implementation.class));
|
|
return annotationList;
|
|
}
|
|
|
|
public static <T extends Handler> List<Annotation> getAnnotations(AnnotatedElement annotatedElement, Class<T> type) {
|
|
List<Annotation> annotations = getAnnotations(annotatedElement);
|
|
annotations.removeIf(annotation -> {
|
|
Handler.Implementation implementation = annotation.annotationType().getAnnotation(Handler.Implementation.class);
|
|
return implementation == null || !type.isAssignableFrom(implementation.value());
|
|
});
|
|
return annotations;
|
|
}
|
|
|
|
public static ToIntFunction<Number> createComparator(String type, Handler.TypeWrapper clazz, int iValue, long lValue, float fValue, double dValue) {
|
|
if (clazz.is(int.class) || clazz.is(Integer.class)) {
|
|
return number -> Integer.compare(number.intValue(), iValue);
|
|
} else if (clazz.is(long.class) || clazz.is(Long.class)) {
|
|
return number -> Long.compare(number.longValue(), lValue);
|
|
} else if (clazz.is(float.class) || clazz.is(Float.class)) {
|
|
return number -> Float.compare(number.floatValue(), fValue);
|
|
} else if (clazz.is(double.class) || clazz.is(Double.class)) {
|
|
return number -> Double.compare(number.doubleValue(), dValue);
|
|
} else {
|
|
throw new IllegalArgumentException(type + " annotation is not supported for " + clazz);
|
|
}
|
|
}
|
|
}
|