Files
SteamWar/CommandFramework/CommandFrameworkBase/src/de/steamwar/command/CommandUtils.java
T

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);
}
}
}