/* * 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 . */ 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> MAPPER_FUNCTIONS = new HashMap<>(); final Map> VALIDATOR_FUNCTIONS = new HashMap<>(); final Map> SUPPLIER_FUNCTIONS = new HashMap<>(); static final AbstractTypeMapper STRING_MAPPER = new AbstractTypeMapper<>() { @Override public String map(Object sender, PreviousArguments previousArguments, String s) { return s; } @Override public Collection 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 void addMapper(Class clazz, AbstractTypeMapper mapper) { addMapper(clazz.getTypeName(), mapper); } public static void addMapper(String name, AbstractTypeMapper mapper) { MAPPER_FUNCTIONS.putIfAbsent(name, mapper); } public static void addValidator(Class clazz, AbstractTypeValidator validator) { addValidator(clazz.getTypeName(), validator); } public static void addValidator(String name, AbstractTypeValidator validator) { VALIDATOR_FUNCTIONS.putIfAbsent(name, validator); } public static void addSupplier(Class clazz, AbstractTypeSupplier supplier) { addSupplier(clazz.getTypeName(), supplier); } public static void addSupplier(String name, AbstractTypeSupplier supplier) { SUPPLIER_FUNCTIONS.putIfAbsent(name, supplier); } public static List getAnnotations(AnnotatedElement annotatedElement) { List 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 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 List getAnnotations(AnnotatedElement annotatedElement, Class type) { List 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 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); } } }