LinkageOptimizations #48
2
build.gradle
Normale Datei → Ausführbare Datei
2
build.gradle
Normale Datei → Ausführbare Datei
@ -81,6 +81,8 @@ dependencies {
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||
|
||||
compileOnly 'org.xerial:sqlite-jdbc:3.36.0'
|
||||
}
|
||||
|
||||
task buildResources {
|
||||
|
34
src/de/steamwar/ImplementationProvider.java
Normale Datei
34
src/de/steamwar/ImplementationProvider.java
Normale Datei
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class ImplementationProvider {
|
||||
private ImplementationProvider() {}
|
||||
|
||||
public static <T> T getImpl(String className) {
|
||||
try {
|
||||
return (T) Class.forName(className).getDeclaredConstructor().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
|
||||
throw new SecurityException("Could not load implementation", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -20,22 +20,26 @@
|
||||
package de.steamwar.command;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class AbstractSWCommand<T> {
|
||||
|
||||
private static final Map<Class<AbstractSWCommand<?>>, List<AbstractSWCommand<?>>> dependencyMap = new HashMap<>();
|
||||
|
||||
private Class<?> clazz; // This is used in createMappings()
|
||||
|
||||
private boolean initialized = false;
|
||||
protected final List<SubCommand<T>> commandList = new ArrayList<>();
|
||||
protected final List<SubCommand<T>> commandHelpList = new ArrayList<>();
|
||||
|
||||
private final Map<String, AbstractTypeMapper<T, ?>> localTypeMapper = new HashMap<>();
|
||||
private final Map<String, AbstractValidator<T, ?>> localValidators = new HashMap<>();
|
||||
@ -46,6 +50,13 @@ public abstract class AbstractSWCommand<T> {
|
||||
|
||||
protected AbstractSWCommand(Class<T> clazz, String command, String... aliases) {
|
||||
this.clazz = clazz;
|
||||
|
||||
PartOf partOf = this.getClass().getAnnotation(PartOf.class);
|
||||
if (partOf != null) {
|
||||
dependencyMap.computeIfAbsent((Class<AbstractSWCommand<?>>) partOf.value(), k -> new ArrayList<>()).add(this);
|
||||
return;
|
||||
}
|
||||
|
||||
createAndSafeCommand(command, aliases);
|
||||
unregister();
|
||||
register();
|
||||
@ -65,22 +76,16 @@ public abstract class AbstractSWCommand<T> {
|
||||
System.out.println(message.get());
|
||||
}
|
||||
|
||||
protected void sendMessage(T sender, String message, Object[] args) {}
|
||||
protected void sendMessage(T sender, String message, Object[] args) {
|
||||
}
|
||||
|
||||
protected final void execute(T sender, String alias, String[] args) {
|
||||
initialize();
|
||||
List<Runnable> errors = new ArrayList<>();
|
||||
try {
|
||||
if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) {
|
||||
if (!errors.isEmpty()) {
|
||||
errors.forEach(Runnable::run);
|
||||
return;
|
||||
}
|
||||
commandHelpList.stream().anyMatch(s -> s.invoke((ignore) -> {
|
||||
}, sender, alias, args));
|
||||
errors.forEach(Runnable::run);
|
||||
}
|
||||
} catch (CommandNoHelpException e) {
|
||||
// Ignored
|
||||
} catch (CommandFrameworkException e) {
|
||||
commandSystemError(sender, e);
|
||||
throw e;
|
||||
@ -96,79 +101,39 @@ public abstract class AbstractSWCommand<T> {
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.filter(s -> s.toLowerCase().startsWith(string))
|
||||
.filter(s -> s.toLowerCase().startsWith(string) || string.startsWith(s.toLowerCase()))
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
private synchronized void initialize() {
|
||||
if (initialized) return;
|
||||
createMapping();
|
||||
}
|
||||
|
||||
private synchronized void createMapping() {
|
||||
List<Method> methods = methods();
|
||||
List<Method> methods = methods().stream()
|
||||
.filter(this::validateMethod)
|
||||
.collect(Collectors.toList());
|
||||
for (Method method : methods) {
|
||||
Cached cached = method.getAnnotation(Cached.class);
|
||||
addMapper(Mapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> {
|
||||
this.<Mapper, AbstractTypeMapper<?, ?>>add(Mapper.class, method, (anno, typeMapper) -> {
|
||||
TabCompletionCache.add(typeMapper, cached);
|
||||
if (anno.local()) {
|
||||
localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper<T, ?>) typeMapper);
|
||||
} else {
|
||||
SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value(), typeMapper);
|
||||
}
|
||||
(anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value(), typeMapper);
|
||||
});
|
||||
addMapper(ClassMapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> {
|
||||
this.<ClassMapper, AbstractTypeMapper<?, ?>>add(ClassMapper.class, method, (anno, typeMapper) -> {
|
||||
TabCompletionCache.add(typeMapper, cached);
|
||||
if (anno.local()) {
|
||||
localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper<T, ?>) typeMapper);
|
||||
} else {
|
||||
SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), typeMapper);
|
||||
}
|
||||
(anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value().getName(), typeMapper);
|
||||
});
|
||||
addValidator(Validator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> {
|
||||
if (anno.local()) {
|
||||
localValidators.putIfAbsent(anno.value(), (AbstractValidator<T, ?>) validator);
|
||||
} else {
|
||||
SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value(), validator);
|
||||
}
|
||||
this.<Validator, AbstractValidator<?, ?>>add(Validator.class, method, (anno, validator) -> {
|
||||
(anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value(), validator);
|
||||
});
|
||||
addValidator(ClassValidator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> {
|
||||
if (anno.local()) {
|
||||
localValidators.putIfAbsent(anno.value().getTypeName(), (AbstractValidator<T, ?>) validator);
|
||||
} else {
|
||||
SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), validator);
|
||||
}
|
||||
this.<ClassValidator, AbstractValidator<?, ?>>add(ClassValidator.class, method, (anno, validator) -> {
|
||||
(anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value().getName(), validator);
|
||||
});
|
||||
}
|
||||
for (Method method : methods) {
|
||||
add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> {
|
||||
if (!anno.help()) return;
|
||||
boolean error = false;
|
||||
if (parameters.length != 2) {
|
||||
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many");
|
||||
error = true;
|
||||
}
|
||||
if (!parameters[parameters.length - 1].isVarArgs()) {
|
||||
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument");
|
||||
error = true;
|
||||
}
|
||||
if (parameters[parameters.length - 1].getType().getComponentType() != String.class) {
|
||||
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument");
|
||||
error = true;
|
||||
}
|
||||
if (error) return;
|
||||
commandHelpList.add(new SubCommand<>(this, method, anno.value(), new HashMap<>(), new HashMap<>(), true, null, anno.noTabComplete()));
|
||||
});
|
||||
|
||||
add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> {
|
||||
if (anno.help()) return;
|
||||
add(Register.class, method, true, (anno, parameters) -> {
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
Class<?> clazz = parameter.getType();
|
||||
if (parameter.isVarArgs() && i == parameters.length - 1) {
|
||||
clazz = parameter.getType().getComponentType();
|
||||
}
|
||||
if (parameter.isVarArgs()) clazz = clazz.getComponentType();
|
||||
Mapper mapper = parameter.getAnnotation(Mapper.class);
|
||||
if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) {
|
||||
continue;
|
||||
@ -179,66 +144,94 @@ public abstract class AbstractSWCommand<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, false, anno.description(), anno.noTabComplete()));
|
||||
commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, anno.description(), anno.noTabComplete()));
|
||||
});
|
||||
}
|
||||
|
||||
this.commandList.sort((o1, o2) -> {
|
||||
int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
|
||||
if (compare != 0) {
|
||||
return compare;
|
||||
} else {
|
||||
return Integer.compare(o1.comparableValue, o2.comparableValue);
|
||||
}
|
||||
});
|
||||
commandHelpList.sort((o1, o2) -> {
|
||||
int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
|
||||
if (compare != 0) {
|
||||
return compare;
|
||||
} else {
|
||||
return Integer.compare(o1.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0,
|
||||
o2.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0);
|
||||
}
|
||||
});
|
||||
if (dependencyMap.containsKey(this.getClass())) {
|
||||
dependencyMap.get(this.getClass()).forEach(abstractSWCommand -> {
|
||||
abstractSWCommand.localTypeMapper.putAll((Map) localTypeMapper);
|
||||
abstractSWCommand.localValidators.putAll((Map) localValidators);
|
||||
abstractSWCommand.initialize();
|
||||
commandList.addAll((Collection) abstractSWCommand.commandList);
|
||||
});
|
||||
}
|
||||
|
||||
Collections.sort(commandList);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private <T extends Annotation> void add(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, Parameter[]> consumer) {
|
||||
private boolean validateMethod(Method method) {
|
||||
if (!checkType(method.getAnnotations(), method.getReturnType(), false, annotation -> {
|
||||
CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class);
|
||||
if (methodMetaData == null) return (aClass, varArg) -> true;
|
||||
if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount())
|
||||
return (aClass, varArg) -> false;
|
||||
return (aClass, varArg) -> {
|
||||
Class<?>[] types = methodMetaData.value();
|
||||
if (types == null) return true;
|
||||
for (Class<?> type : types) {
|
||||
if (type.isAssignableFrom(aClass)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}, "The method '" + method + "'")) return false;
|
||||
boolean valid = true;
|
||||
for (Parameter parameter : method.getParameters()) {
|
||||
if (!checkType(parameter.getAnnotations(), parameter.getType(), parameter.isVarArgs(), annotation -> {
|
||||
CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class);
|
||||
if (parameterMetaData == null) return (aClass, varArg) -> true;
|
||||
Class<?> handler = parameterMetaData.handler();
|
||||
if (BiPredicate.class.isAssignableFrom(handler)) {
|
||||
try {
|
||||
return (BiPredicate<Class<?>, Boolean>) handler.getConstructor().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
||||
NoSuchMethodException e) {
|
||||
}
|
||||
}
|
||||
return (aClass, varArg) -> {
|
||||
if (varArg) aClass = aClass.getComponentType();
|
||||
Class<?>[] types = parameterMetaData.value();
|
||||
if (types == null) return true;
|
||||
for (Class<?> current : types) {
|
||||
if (current.isAssignableFrom(aClass)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}, "The parameter '" + parameter + "'")) valid = false;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
private boolean checkType(Annotation[] annotations, Class<?> clazz, boolean varArg, Function<Annotation, BiPredicate<Class<?>, Boolean>> toApplicableTypes, String warning) {
|
||||
boolean valid = true;
|
||||
for (Annotation annotation : annotations) {
|
||||
BiPredicate<Class<?>, Boolean> predicate = toApplicableTypes.apply(annotation);
|
||||
if (!predicate.test(clazz, varArg)) {
|
||||
commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'");
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
private <T extends Annotation> void add(Class<T> annotation, Method method, boolean firstParameter, BiConsumer<T, Parameter[]> consumer) {
|
||||
T[] anno = SWCommandUtils.getAnnotation(method, annotation);
|
||||
if (anno == null || anno.length == 0) return;
|
||||
|
||||
Parameter[] parameters = method.getParameters();
|
||||
if (!parameterTester.test(parameters.length)) {
|
||||
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many");
|
||||
return;
|
||||
}
|
||||
if (firstParameter && !clazz.isAssignableFrom(parameters[0].getType())) {
|
||||
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + clazz.getTypeName() + "'");
|
||||
return;
|
||||
}
|
||||
if (returnType != null && !returnType.isAssignableFrom(method.getReturnType())) {
|
||||
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'");
|
||||
return;
|
||||
}
|
||||
Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters));
|
||||
}
|
||||
|
||||
private <T extends Annotation> void addMapper(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractTypeMapper<?, ?>> consumer) {
|
||||
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
|
||||
private <T extends Annotation, K> void add(Class<T> annotation, Method method, BiConsumer<T, K> consumer) {
|
||||
add(annotation, method, false, (anno, parameters) -> {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
consumer.accept(anno, (AbstractTypeMapper<T, ?>) method.invoke(this));
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException(e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T extends Annotation> void addValidator(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractValidator<T, ?>> consumer) {
|
||||
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
|
||||
try {
|
||||
method.setAccessible(true);
|
||||
consumer.accept(anno, (AbstractValidator<T, ?>) method.invoke(this));
|
||||
consumer.accept(anno, (K) method.invoke(this));
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException(e.getMessage(), e);
|
||||
}
|
||||
@ -262,12 +255,29 @@ public abstract class AbstractSWCommand<T> {
|
||||
return methods;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface PartOf {
|
||||
Class<?> value();
|
||||
}
|
||||
|
||||
// --- Annotation for the command ---
|
||||
|
||||
/**
|
||||
* Annotation for registering a method as a command
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@Repeatable(Register.Registeres.class)
|
||||
@CommandMetaData.Method(value = void.class, minParameterCount = 1)
|
||||
protected @interface Register {
|
||||
|
||||
/**
|
||||
* Identifier of subcommand
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
@Deprecated
|
||||
boolean help() default false;
|
||||
|
||||
String[] description() default {};
|
||||
@ -276,6 +286,7 @@ public abstract class AbstractSWCommand<T> {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@CommandMetaData.Method(value = void.class, minParameterCount = 1)
|
||||
@interface Registeres {
|
||||
Register[] value();
|
||||
}
|
||||
@ -283,14 +294,41 @@ public abstract class AbstractSWCommand<T> {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
|
||||
@CommandMetaData.ImplicitTypeMapper(handler = Mapper.Handler.class)
|
||||
protected @interface Mapper {
|
||||
String value();
|
||||
|
||||
boolean local() default false;
|
||||
|
||||
class Handler<T> implements AbstractTypeMapper<T, Object> {
|
||||
|
||||
private AbstractTypeMapper<T, Object> inner;
|
||||
|
||||
public Handler(AbstractSWCommand.Mapper mapper, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
|
||||
inner = (AbstractTypeMapper<T, Object>) SWCommandUtils.getTypeMapper(mapper.value(), localTypeMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object map(T sender, PreviousArguments previousArguments, String s) {
|
||||
return inner.map(sender, previousArguments, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(T sender, Object value, MessageSender messageSender) {
|
||||
return inner.validate(sender, value, messageSender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
|
||||
return inner.tabCompletes(sender, previousArguments, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
|
||||
protected @interface ClassMapper {
|
||||
Class<?> value();
|
||||
|
||||
@ -299,30 +337,58 @@ public abstract class AbstractSWCommand<T> {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
|
||||
protected @interface Cached {
|
||||
long cacheDuration() default 5;
|
||||
|
||||
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||
|
||||
boolean global() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
@CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0)
|
||||
@CommandMetaData.ImplicitValidator(handler = Validator.Handler.class, order = 0)
|
||||
protected @interface Validator {
|
||||
String value() default "";
|
||||
|
||||
boolean local() default false;
|
||||
|
||||
boolean invert() default false;
|
||||
|
||||
class Handler<T> implements AbstractValidator<T, Object> {
|
||||
|
||||
private AbstractValidator<T, Object> inner;
|
||||
private boolean invert;
|
||||
|
||||
public Handler(AbstractSWCommand.Validator validator, Class<?> clazz, Map<String, AbstractValidator<T, ?>> localValidator) {
|
||||
inner = (AbstractValidator<T, Object>) SWCommandUtils.getValidator(validator, clazz, localValidator);
|
||||
invert = validator.invert();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(T sender, Object value, MessageSender messageSender) {
|
||||
return inner.validate(sender, value, messageSender) ^ invert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD})
|
||||
@CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0)
|
||||
protected @interface ClassValidator {
|
||||
Class<?> value();
|
||||
|
||||
boolean local() default false;
|
||||
}
|
||||
|
||||
// --- Implicit TypeMapper ---
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@CommandMetaData.Parameter({String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class})
|
||||
@CommandMetaData.ImplicitTypeMapper(handler = StaticValue.Handler.class)
|
||||
protected @interface StaticValue {
|
||||
String[] value();
|
||||
|
||||
@ -336,7 +402,49 @@ public abstract class AbstractSWCommand<T> {
|
||||
*/
|
||||
boolean allowISE() default false;
|
||||
|
||||
int[] falseValues() default { 0 };
|
||||
int[] falseValues() default {0};
|
||||
|
||||
class Handler<T> implements AbstractTypeMapper<T, Object> {
|
||||
|
||||
private AbstractTypeMapper inner;
|
||||
|
||||
public Handler(StaticValue staticValue, Class<?> clazz) {
|
||||
if (clazz == String.class) {
|
||||
inner = SWCommandUtils.createMapper(staticValue.value());
|
||||
return;
|
||||
}
|
||||
if (!staticValue.allowISE()) {
|
||||
throw new IllegalArgumentException("The parameter type '" + clazz.getTypeName() + "' is not supported by the StaticValue annotation");
|
||||
}
|
||||
if (clazz == boolean.class || clazz == Boolean.class) {
|
||||
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
|
||||
Set<Integer> falseValues = new HashSet<>();
|
||||
for (int i : staticValue.falseValues()) falseValues.add(i);
|
||||
inner = SWCommandUtils.createMapper(s -> {
|
||||
int index = tabCompletes.indexOf(s);
|
||||
return index == -1 ? null : !falseValues.contains(index);
|
||||
}, (commandSender, s) -> tabCompletes);
|
||||
} else if (clazz == int.class || clazz == Integer.class || clazz == long.class || clazz == Long.class) {
|
||||
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
|
||||
inner = SWCommandUtils.createMapper(s -> {
|
||||
Number index = tabCompletes.indexOf(s);
|
||||
return index.longValue() == -1 ? null : index;
|
||||
}, (commandSender, s) -> tabCompletes);
|
||||
} else {
|
||||
throw new IllegalArgumentException("The parameter type '" + clazz.getTypeName() + "' is not supported by the StaticValue annotation");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object map(T sender, PreviousArguments previousArguments, String s) {
|
||||
return inner.map(sender, previousArguments, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
|
||||
return inner.tabCompletes(sender, previousArguments, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ -353,8 +461,11 @@ public abstract class AbstractSWCommand<T> {
|
||||
boolean onlyUINIG() default false;
|
||||
}
|
||||
|
||||
// --- Implicit Validator ---
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = Integer.MAX_VALUE)
|
||||
protected @interface ErrorMessage {
|
||||
/**
|
||||
* Error message to be displayed when the parameter is invalid.
|
||||
@ -365,10 +476,219 @@ public abstract class AbstractSWCommand<T> {
|
||||
* This is the short form for 'allowEmptyArrays'.
|
||||
*/
|
||||
boolean allowEAs() default true;
|
||||
|
||||
class Handler<T> implements AbstractValidator<T, Object> {
|
||||
|
||||
private AbstractSWCommand.ErrorMessage errorMessage;
|
||||
|
||||
public Handler(AbstractSWCommand.ErrorMessage errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(T sender, Object value, MessageSender messageSender) {
|
||||
if (value == null) messageSender.send(errorMessage.value());
|
||||
if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
|
||||
messageSender.send(errorMessage.value());
|
||||
return false;
|
||||
}
|
||||
return value != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
protected @interface AllowNull {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
|
||||
@CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2)
|
||||
protected @interface Min {
|
||||
int intValue() default Integer.MIN_VALUE;
|
||||
|
||||
long longValue() default Long.MIN_VALUE;
|
||||
|
||||
float floatValue() default Float.MIN_VALUE;
|
||||
|
||||
double doubleValue() default Double.MIN_VALUE;
|
||||
|
||||
boolean inclusive() default true;
|
||||
|
||||
class Handler<T> implements AbstractValidator<T, Number> {
|
||||
|
||||
private int value;
|
||||
private Function<Number, Number> comparator;
|
||||
|
||||
public Handler(AbstractSWCommand.Min min, Class<?> clazz) {
|
||||
this.value = min.inclusive() ? 0 : 1;
|
||||
this.comparator = createComparator("Min", clazz, min.intValue(), min.longValue(), min.floatValue(), min.doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(T sender, Number value, MessageSender messageSender) {
|
||||
if (value == null) return true;
|
||||
return (comparator.apply(value).intValue()) >= this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
|
||||
@CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2)
|
||||
protected @interface Max {
|
||||
int intValue() default Integer.MAX_VALUE;
|
||||
|
||||
long longValue() default Long.MAX_VALUE;
|
||||
|
||||
float floatValue() default Float.MAX_VALUE;
|
||||
|
||||
double doubleValue() default Double.MAX_VALUE;
|
||||
|
||||
boolean inclusive() default true;
|
||||
|
||||
class Handler<T> implements AbstractValidator<T, Number> {
|
||||
|
||||
private int value;
|
||||
private Function<Number, Number> comparator;
|
||||
|
||||
public Handler(AbstractSWCommand.Max max, Class<?> clazz) {
|
||||
this.value = max.inclusive() ? 0 : -1;
|
||||
this.comparator = createComparator("Max", clazz, max.intValue(), max.longValue(), max.floatValue(), max.doubleValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(T sender, Number value, MessageSender messageSender) {
|
||||
if (value == null) return true;
|
||||
return (comparator.apply(value).intValue()) <= this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Function<Number, Number> createComparator(String type, Class<?> clazz, int iValue, long lValue, float fValue, double dValue) {
|
||||
if (clazz == int.class || clazz == Integer.class) {
|
||||
return number -> Integer.compare(number.intValue(), iValue);
|
||||
} else if (clazz == long.class || clazz == Long.class) {
|
||||
return number -> Long.compare(number.longValue(), lValue);
|
||||
} else if (clazz == float.class || clazz == Float.class) {
|
||||
return number -> Float.compare(number.floatValue(), fValue);
|
||||
} else if (clazz == double.class || clazz == Double.class) {
|
||||
return number -> Double.compare(number.doubleValue(), dValue);
|
||||
} else {
|
||||
throw new IllegalArgumentException(type + " annotation is not supported for " + clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@CommandMetaData.ImplicitTypeMapper(handler = Length.Handler.class)
|
||||
protected @interface Length {
|
||||
int min() default 0;
|
||||
|
||||
int max() default Integer.MAX_VALUE;
|
||||
|
||||
class Handler<T> implements AbstractTypeMapper<T, Object> {
|
||||
|
||||
private int min;
|
||||
private int max;
|
||||
private AbstractTypeMapper<T, Object> inner;
|
||||
|
||||
public Handler(Length length, AbstractTypeMapper<T, Object> inner) {
|
||||
this.min = length.min();
|
||||
this.max = length.max();
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object map(T sender, PreviousArguments previousArguments, String s) {
|
||||
if (s.length() < min || s.length() > max) return null;
|
||||
return inner.map(sender, previousArguments, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
|
||||
List<String> tabCompletes = inner.tabCompletes(sender, previousArguments, s)
|
||||
.stream()
|
||||
.filter(str -> str.length() >= min)
|
||||
.map(str -> str.substring(0, Math.min(str.length(), max)))
|
||||
.collect(Collectors.toList());
|
||||
if (s.length() < min) {
|
||||
tabCompletes.add(0, s);
|
||||
}
|
||||
return tabCompletes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String normalize(T sender, String s) {
|
||||
return inner.normalize(sender, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER})
|
||||
@CommandMetaData.Parameter(handler = ArrayLength.Type.class)
|
||||
@CommandMetaData.ImplicitTypeMapper(handler = ArrayLength.HandlerTypeMapper.class)
|
||||
@CommandMetaData.ImplicitValidator(handler = ArrayLength.HandlerValidator.class, order = 1)
|
||||
protected @interface ArrayLength {
|
||||
int min() default 0;
|
||||
|
||||
int max() default Integer.MAX_VALUE;
|
||||
|
||||
class Type implements BiPredicate<Class<?>, Boolean> {
|
||||
@Override
|
||||
public boolean test(Class<?> clazz, Boolean isVarArgs) {
|
||||
return clazz.isArray();
|
||||
}
|
||||
}
|
||||
|
||||
class HandlerTypeMapper<T> implements AbstractTypeMapper<T, Object> {
|
||||
|
||||
private int max;
|
||||
private AbstractTypeMapper<T, Object> inner;
|
||||
|
||||
public HandlerTypeMapper(ArrayLength arrayLength, AbstractTypeMapper<T, Object> inner) {
|
||||
this.max = arrayLength.max();
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object map(T sender, PreviousArguments previousArguments, String s) {
|
||||
return inner.map(sender, previousArguments, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
|
||||
Object[] mapped = previousArguments.getMappedArg(0);
|
||||
if (mapped.length >= max) return Collections.emptyList();
|
||||
return inner.tabCompletes(sender, previousArguments, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String normalize(T sender, String s) {
|
||||
return inner.normalize(sender, s);
|
||||
}
|
||||
}
|
||||
|
||||
class HandlerValidator<T> implements AbstractValidator<T, Object> {
|
||||
|
||||
private int min;
|
||||
private int max;
|
||||
|
||||
public HandlerValidator(ArrayLength arrayLength) {
|
||||
this.min = arrayLength.min();
|
||||
this.max = arrayLength.max();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(T sender, Object value, MessageSender messageSender) {
|
||||
if (value == null) return true;
|
||||
int length = Array.getLength(value);
|
||||
return length >= min && length <= max;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,38 @@ public interface AbstractTypeMapper<K, T> extends AbstractValidator<K, T> {
|
||||
/**
|
||||
* The CommandSender can be null!
|
||||
*/
|
||||
T map(K sender, String[] previousArguments, String s);
|
||||
@Deprecated
|
||||
default T map(K sender, String[] previousArguments, String s) {
|
||||
throw new IllegalArgumentException("This method is deprecated and should not be used anymore!");
|
||||
}
|
||||
|
||||
/**
|
||||
* The CommandSender can be null!
|
||||
*/
|
||||
default T map(K sender, PreviousArguments previousArguments, String s) {
|
||||
return map(sender, previousArguments.userArgs, s);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean validate(K sender, T value, MessageSender messageSender) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Collection<String> tabCompletes(K sender, String[] previousArguments, String s);
|
||||
@Deprecated
|
||||
default Collection<String> tabCompletes(K sender, String[] previousArguments, String s) {
|
||||
throw new IllegalArgumentException("This method is deprecated and should not be used anymore!");
|
||||
}
|
||||
|
||||
default Collection<String> tabCompletes(K sender, PreviousArguments previousArguments, String s) {
|
||||
return tabCompletes(sender, previousArguments.userArgs, s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the cache key by sender and user provided argument. <br>
|
||||
* Note: The CommandSender can be null! <br>
|
||||
* Returning null and the empty string are equivalent.
|
||||
*/
|
||||
default String normalize(K sender, String s) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,12 @@ public interface AbstractValidator<K, T> {
|
||||
*/
|
||||
boolean validate(K sender, T value, MessageSender messageSender);
|
||||
|
||||
@Deprecated
|
||||
default <C> Validator<C> validate(C value, MessageSender messageSender) {
|
||||
return new Validator<>(value, messageSender);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@RequiredArgsConstructor
|
||||
class Validator<C> {
|
||||
private final C value;
|
||||
|
@ -36,12 +36,6 @@ public class CommandFrameworkException extends RuntimeException {
|
||||
|
||||
private String message;
|
||||
|
||||
static CommandFrameworkException commandGetExceptions(String type, Class<?> clazzType, Executable executable, int index) {
|
||||
return new CommandFrameworkException(throwable -> {
|
||||
return CommandFrameworkException.class.getTypeName() + ": Error while getting " + type + " for " + clazzType.getTypeName() + " with parameter index " + index;
|
||||
}, null, throwable -> Stream.empty(), executable.getDeclaringClass().getTypeName() + "." + executable.getName() + "(Unknown Source)");
|
||||
}
|
||||
|
||||
static CommandFrameworkException commandPartExceptions(String type, Throwable cause, String current, Class<?> clazzType, Executable executable, int index) {
|
||||
return new CommandFrameworkException(e -> {
|
||||
return CommandFrameworkException.class.getTypeName() + ": Error while " + type + " (" + current + ") to type " + clazzType.getTypeName() + " with parameter index " + index;
|
||||
|
92
src/de/steamwar/command/CommandMetaData.java
Normale Datei
92
src/de/steamwar/command/CommandMetaData.java
Normale Datei
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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 java.lang.annotation.*;
|
||||
|
||||
public @interface CommandMetaData {
|
||||
|
||||
/**
|
||||
* This annotation is only for internal use.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@interface Method {
|
||||
Class<?>[] value();
|
||||
int minParameterCount() default 0;
|
||||
int maxParameterCount() default Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* This annotation denotes what types are allowed as parameter types the annotation annotated with can use.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@interface Parameter {
|
||||
Class<?>[] value() default {};
|
||||
Class<?> handler() default void.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* This annotation can be used in conjunction with a class that implements {@link AbstractTypeMapper} to
|
||||
* create a custom type mapper for a parameter. The class must have one of two constructors with the following
|
||||
* types:
|
||||
* <ul>
|
||||
* <li>Annotation this annotation annotates</li>
|
||||
* <li>{@link Class}</li>
|
||||
* <li>{@link AbstractTypeMapper}, optional, if not present only one per parameter</li>
|
||||
* <li>{@link java.util.Map} with types {@link String} and {@link AbstractValidator}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@interface ImplicitTypeMapper {
|
||||
/**
|
||||
* The validator class that should be used.
|
||||
*/
|
||||
Class<?> handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* This annotation can be used in conjunction with a class that implements {@link AbstractValidator} to
|
||||
* create a custom validator short hands for commands. The validator class must have one constructor with
|
||||
* one of the following types:
|
||||
* <ul>
|
||||
* <li>Annotation this annotation annotates</li>
|
||||
* <li>{@link Class}</li>
|
||||
* <li>{@link java.util.Map} with types {@link String} and {@link AbstractValidator}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@interface ImplicitValidator {
|
||||
/**
|
||||
* The validator class that should be used.
|
||||
*/
|
||||
Class<?> handler();
|
||||
|
||||
/**
|
||||
* Defines when this validator should be processed. Negative numbers denote that this will be
|
||||
* processed before {@link AbstractSWCommand.Validator} and positive numbers
|
||||
* denote that this will be processed after {@link AbstractSWCommand.Validator}.
|
||||
*/
|
||||
int order();
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import lombok.Setter;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -31,7 +32,8 @@ import java.util.function.Consumer;
|
||||
|
||||
class CommandPart<T> {
|
||||
|
||||
private static final String[] EMPTY_ARRAY = new String[0];
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
|
||||
@AllArgsConstructor
|
||||
private static class CheckArgumentResult {
|
||||
@ -41,7 +43,7 @@ class CommandPart<T> {
|
||||
|
||||
private AbstractSWCommand<T> command;
|
||||
private AbstractTypeMapper<T, ?> typeMapper;
|
||||
private AbstractValidator<T, Object> validator;
|
||||
private List<AbstractValidator<T, Object>> validators = new ArrayList<>();
|
||||
private Class<?> varArgType;
|
||||
private String optional;
|
||||
|
||||
@ -53,22 +55,25 @@ class CommandPart<T> {
|
||||
@Setter
|
||||
private boolean onlyUseIfNoneIsGiven = false;
|
||||
|
||||
@Setter
|
||||
private boolean allowNullValues = false;
|
||||
|
||||
private Parameter parameter;
|
||||
private int parameterIndex;
|
||||
|
||||
public CommandPart(AbstractSWCommand<T> command, AbstractTypeMapper<T, ?> typeMapper, AbstractValidator<T, Object> validator, Class<?> varArgType, String optional, Parameter parameter, int parameterIndex) {
|
||||
public CommandPart(AbstractSWCommand<T> command, AbstractTypeMapper<T, ?> typeMapper, Class<?> varArgType, String optional, Parameter parameter, int parameterIndex) {
|
||||
this.command = command;
|
||||
this.typeMapper = typeMapper;
|
||||
this.validator = validator;
|
||||
this.varArgType = varArgType;
|
||||
this.optional = optional;
|
||||
this.parameter = parameter;
|
||||
this.parameterIndex = parameterIndex;
|
||||
|
||||
validatePart();
|
||||
if (optional != null && varArgType != null) {
|
||||
throw new IllegalArgumentException("A vararg part can't have an optional part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void addValidator(AbstractValidator<T, Object> validator) {
|
||||
if (validator == null) return;
|
||||
validators.add(validator);
|
||||
}
|
||||
|
||||
public void setNext(CommandPart<T> next) {
|
||||
@ -78,41 +83,33 @@ class CommandPart<T> {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
private void validatePart() {
|
||||
if (optional != null && varArgType != null) {
|
||||
throw new IllegalArgumentException("A vararg part can't have an optional part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateArgumentArray(Consumer<Runnable> errors, List<Object> current, T sender, String[] args, int startIndex) {
|
||||
if (varArgType != null) {
|
||||
Object array = Array.newInstance(varArgType, args.length - startIndex);
|
||||
for (int i = startIndex; i < args.length; i++) {
|
||||
CheckArgumentResult validArgument = checkArgument(null, sender, args, i);
|
||||
if (!validArgument.success) {
|
||||
throw new CommandParseException();
|
||||
}
|
||||
CheckArgumentResult validArgument = checkArgument(null, sender, args, current, i);
|
||||
if (!validArgument.success) throw new CommandParseException();
|
||||
Array.set(array, i - startIndex, validArgument.value);
|
||||
}
|
||||
if (validator != null && !validator.validate(sender, array, (s, objects) -> {
|
||||
errors.accept(() -> command.sendMessage(sender, s, objects));
|
||||
})) {
|
||||
throw new CommandParseException();
|
||||
for (AbstractValidator<T, Object> validator : validators) {
|
||||
if (!validator.validate(sender, array, (s, objects) -> {
|
||||
errors.accept(() -> command.sendMessage(sender, s, objects));
|
||||
})) throw new CommandParseException();
|
||||
}
|
||||
current.add(array);
|
||||
return;
|
||||
}
|
||||
|
||||
CheckArgumentResult validArgument = checkArgument(errors, sender, args, startIndex);
|
||||
CheckArgumentResult validArgument = checkArgument(errors, sender, args, current, startIndex);
|
||||
if (!validArgument.success && optional == null) {
|
||||
throw new CommandParseException();
|
||||
}
|
||||
if (!validArgument.success) {
|
||||
if (!ignoreAsArgument) {
|
||||
if (!onlyUseIfNoneIsGiven) {
|
||||
current.add(typeMapper.map(sender, EMPTY_ARRAY, optional));
|
||||
current.add(typeMapper.map(sender, new PreviousArguments(EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY), optional));
|
||||
} else if (startIndex >= args.length) {
|
||||
current.add(typeMapper.map(sender, EMPTY_ARRAY, optional));
|
||||
current.add(typeMapper.map(sender, new PreviousArguments(EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY), optional));
|
||||
} else {
|
||||
throw new CommandParseException();
|
||||
}
|
||||
@ -127,18 +124,23 @@ class CommandPart<T> {
|
||||
}
|
||||
if (next != null) {
|
||||
next.generateArgumentArray(errors, current, sender, args, startIndex + 1);
|
||||
} else if (startIndex + 1 < args.length) {
|
||||
throw new CommandParseException();
|
||||
}
|
||||
}
|
||||
|
||||
public void generateTabComplete(List<String> current, T sender, String[] args, int startIndex) {
|
||||
public void generateTabComplete(List<String> current, T sender, String[] args, List<Object> mappedArgs, int startIndex) {
|
||||
if (varArgType != null) {
|
||||
List<Object> currentArgs = new ArrayList<>(mappedArgs);
|
||||
List<Object> varArgs = new ArrayList<>();
|
||||
for (int i = startIndex; i < args.length - 1; i++) {
|
||||
CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, i);
|
||||
if (!validArgument.success) {
|
||||
return;
|
||||
}
|
||||
CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, mappedArgs, i);
|
||||
if (!validArgument.success) return;
|
||||
varArgs.add(validArgument.value);
|
||||
}
|
||||
Collection<String> strings = tabCompletes(sender, args, args.length - 1);
|
||||
|
||||
currentArgs.add(varArgs.toArray());
|
||||
Collection<String> strings = tabCompletes(sender, args, currentArgs, args.length - 1);
|
||||
if (strings != null) {
|
||||
current.addAll(strings);
|
||||
}
|
||||
@ -146,56 +148,65 @@ class CommandPart<T> {
|
||||
}
|
||||
|
||||
if (args.length - 1 > startIndex) {
|
||||
CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, startIndex);
|
||||
CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, mappedArgs, startIndex);
|
||||
if (checkArgumentResult.success && next != null) {
|
||||
next.generateTabComplete(current, sender, args, startIndex + 1);
|
||||
if (!ignoreAsArgument) {
|
||||
mappedArgs.add(checkArgumentResult.value);
|
||||
}
|
||||
next.generateTabComplete(current, sender, args, mappedArgs, startIndex + 1);
|
||||
return;
|
||||
}
|
||||
if (optional != null && next != null) {
|
||||
next.generateTabComplete(current, sender, args, startIndex);
|
||||
next.generateTabComplete(current, sender, args, mappedArgs, startIndex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<String> strings = tabCompletes(sender, args, startIndex);
|
||||
Collection<String> strings = tabCompletes(sender, args, mappedArgs, startIndex);
|
||||
if (strings != null) {
|
||||
current.addAll(strings);
|
||||
}
|
||||
if (optional != null && next != null) {
|
||||
next.generateTabComplete(current, sender, args, startIndex);
|
||||
next.generateTabComplete(current, sender, args, mappedArgs, startIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> tabCompletes(T sender, String[] args, int startIndex) {
|
||||
return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> {
|
||||
private Collection<String> tabCompletes(T sender, String[] args, List<Object> mappedArgs, int startIndex) {
|
||||
return TabCompletionCache.tabComplete(sender, args[startIndex], (AbstractTypeMapper<Object, ?>) typeMapper, () -> {
|
||||
try {
|
||||
return typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]);
|
||||
return typeMapper.tabCompletes(sender, new PreviousArguments(Arrays.copyOf(args, startIndex), mappedArgs.toArray()), args[startIndex]);
|
||||
} catch (Throwable e) {
|
||||
throw CommandFrameworkException.commandPartExceptions("tabcompleting", e, args[startIndex], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private CheckArgumentResult checkArgument(Consumer<Runnable> errors, T sender, String[] args, int index) {
|
||||
private CheckArgumentResult checkArgument(Consumer<Runnable> errors, T sender, String[] args, List<Object> mappedArgs, int index) {
|
||||
Object value;
|
||||
try {
|
||||
value = typeMapper.map(sender, Arrays.copyOf(args, index), args[index]);
|
||||
value = typeMapper.map(sender, new PreviousArguments(Arrays.copyOf(args, index), mappedArgs.toArray()), args[index]);
|
||||
} catch (Exception e) {
|
||||
return new CheckArgumentResult(false, null);
|
||||
}
|
||||
if (validator != null && errors != null) {
|
||||
boolean success = true;
|
||||
for (AbstractValidator<T, Object> validator : validators) {
|
||||
try {
|
||||
if (!validator.validate(sender, value, (s, objects) -> {
|
||||
errors.accept(() -> {
|
||||
command.sendMessage(sender, s, objects);
|
||||
});
|
||||
})) {
|
||||
return new CheckArgumentResult(false, null);
|
||||
success = false;
|
||||
value = null;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
throw CommandFrameworkException.commandPartExceptions("validating", e, args[index], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex);
|
||||
}
|
||||
}
|
||||
return new CheckArgumentResult(allowNullValues || value != null, value);
|
||||
return new CheckArgumentResult(success, value);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return varArgType != null ? varArgType : parameter.getType();
|
||||
}
|
||||
}
|
||||
|
62
src/de/steamwar/command/PreviousArguments.java
Normale Datei
62
src/de/steamwar/command/PreviousArguments.java
Normale Datei
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PreviousArguments {
|
||||
|
||||
public final String[] userArgs;
|
||||
public final Object[] mappedArgs;
|
||||
|
||||
public PreviousArguments(String[] userArgs, Object[] mappedArgs) {
|
||||
this.userArgs = userArgs;
|
||||
this.mappedArgs = mappedArgs;
|
||||
}
|
||||
|
||||
public String getUserArg(int index) {
|
||||
return userArgs[userArgs.length - index - 1];
|
||||
}
|
||||
|
||||
public <T> T getMappedArg(int index) {
|
||||
return (T) mappedArgs[mappedArgs.length - index - 1];
|
||||
}
|
||||
|
||||
public <T> Optional<T> getFirst(Class<T> clazz) {
|
||||
for (Object o : mappedArgs) {
|
||||
if (clazz.isInstance(o)) {
|
||||
return Optional.of((T) o);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public <T> List<T> getAll(Class<T> clazz) {
|
||||
List<T> list = new ArrayList<>();
|
||||
for (Object o : mappedArgs) {
|
||||
if (clazz.isInstance(o)) {
|
||||
list.add((T) o);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@ import lombok.Getter;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.*;
|
||||
@ -42,12 +41,12 @@ public class SWCommandUtils {
|
||||
|
||||
private SWTypeMapperCreator swTypeMapperCreator = (mapper, tabCompleter) -> new AbstractTypeMapper<Object, Object>() {
|
||||
@Override
|
||||
public Object map(Object sender, String[] previousArguments, String s) {
|
||||
public Object map(Object sender, PreviousArguments previousArguments, String s) {
|
||||
return mapper.apply(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Object sender, String[] previousArguments, String s) {
|
||||
public Collection<String> tabCompletes(Object sender, PreviousArguments previousArguments, String s) {
|
||||
return ((BiFunction<Object, Object, Collection<String>>) tabCompleter).apply(sender, s);
|
||||
}
|
||||
};
|
||||
@ -58,10 +57,10 @@ public class SWCommandUtils {
|
||||
if (s.equalsIgnoreCase("false")) return false;
|
||||
return null;
|
||||
}, s -> Arrays.asList("true", "false")));
|
||||
addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat)));
|
||||
addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble)));
|
||||
addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt)));
|
||||
addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong)));
|
||||
addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat, true)));
|
||||
addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble, true)));
|
||||
addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt, false)));
|
||||
addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong, false)));
|
||||
MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList));
|
||||
}
|
||||
|
||||
@ -74,85 +73,7 @@ public class SWCommandUtils {
|
||||
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
|
||||
}
|
||||
|
||||
static <T> CommandPart<T> generateCommandPart(AbstractSWCommand<T> command, boolean help, String[] subCommand, Parameter[] parameters, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator) {
|
||||
CommandPart<T> first = null;
|
||||
CommandPart<T> current = null;
|
||||
for (String s : subCommand) {
|
||||
CommandPart commandPart = new CommandPart(command, createMapper(s), null, null, null, null, -1);
|
||||
commandPart.setIgnoreAsArgument(true);
|
||||
if (current != null) {
|
||||
current.setNext(commandPart);
|
||||
}
|
||||
current = commandPart;
|
||||
if (first == null) {
|
||||
first = current;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
AbstractTypeMapper<T, ?> typeMapper = getTypeMapper(parameter, localTypeMapper);
|
||||
AbstractValidator<T, Object> validator = (AbstractValidator<T, Object>) getValidator(parameter, localValidator);
|
||||
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
|
||||
AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class);
|
||||
AbstractSWCommand.AllowNull allowNull = parameter.getAnnotation(AbstractSWCommand.AllowNull.class);
|
||||
|
||||
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, validator, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i);
|
||||
commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG());
|
||||
commandPart.setAllowNullValues(allowNull != null);
|
||||
if (current != null) {
|
||||
current.setNext(commandPart);
|
||||
}
|
||||
current = commandPart;
|
||||
if (first == null) {
|
||||
first = current;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
|
||||
Class<?> clazz = parameter.getType();
|
||||
if (parameter.isVarArgs()) {
|
||||
clazz = clazz.getComponentType();
|
||||
}
|
||||
|
||||
AbstractSWCommand.ClassMapper classMapper = parameter.getAnnotation(AbstractSWCommand.ClassMapper.class);
|
||||
AbstractSWCommand.Mapper mapper = parameter.getAnnotation(AbstractSWCommand.Mapper.class);
|
||||
if (clazz.isEnum() && classMapper == null && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
|
||||
return createEnumMapper((Class<Enum<?>>) clazz);
|
||||
}
|
||||
|
||||
String name = clazz.getTypeName();
|
||||
if (classMapper != null) {
|
||||
name = classMapper.value().getTypeName();
|
||||
} else if (mapper != null) {
|
||||
name = mapper.value();
|
||||
} else {
|
||||
AbstractSWCommand.StaticValue staticValue = parameter.getAnnotation(AbstractSWCommand.StaticValue.class);
|
||||
if (staticValue != null) {
|
||||
if (parameter.getType() == String.class) {
|
||||
return createMapper(staticValue.value());
|
||||
}
|
||||
if (staticValue.allowISE()) {
|
||||
if ((parameter.getType() == boolean.class || parameter.getType() == Boolean.class)) {
|
||||
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
|
||||
Set<Integer> falseValues = new HashSet<>();
|
||||
for (int i : staticValue.falseValues()) falseValues.add(i);
|
||||
return createMapper(s -> {
|
||||
int index = tabCompletes.indexOf(s);
|
||||
return index == -1 ? null : !falseValues.contains(index);
|
||||
}, (commandSender, s) -> tabCompletes);
|
||||
}
|
||||
if ((parameter.getType() == int.class || parameter.getType() == Integer.class || parameter.getType() == long.class || parameter.getType() == Long.class) && staticValue.value().length >= 2) {
|
||||
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
|
||||
return createMapper(s -> {
|
||||
Number index = tabCompletes.indexOf(s);
|
||||
return index.longValue() == -1 ? null : index;
|
||||
}, (commandSender, s) -> tabCompletes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(String name, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
|
||||
AbstractTypeMapper<T, ?> typeMapper = localTypeMapper.getOrDefault(name, (AbstractTypeMapper<T, ?>) MAPPER_FUNCTIONS.getOrDefault(name, null));
|
||||
if (typeMapper == null) {
|
||||
throw new IllegalArgumentException("No mapper found for " + name);
|
||||
@ -160,45 +81,24 @@ public class SWCommandUtils {
|
||||
return typeMapper;
|
||||
}
|
||||
|
||||
public static <T> AbstractValidator<T, ?> getValidator(Parameter parameter, Map<String, AbstractValidator<T, ?>> localValidator) {
|
||||
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
|
||||
Class<?> clazz = parameter.getType();
|
||||
|
||||
AbstractSWCommand.ClassValidator classValidator = parameter.getAnnotation(AbstractSWCommand.ClassValidator.class);
|
||||
if (classValidator != null) {
|
||||
if (classValidator.value() != null) {
|
||||
return getValidator(classValidator.value().getTypeName(), localValidator);
|
||||
}
|
||||
return getValidator(clazz.getTypeName(), localValidator);
|
||||
if (parameter.isVarArgs()) {
|
||||
clazz = clazz.getComponentType();
|
||||
}
|
||||
|
||||
AbstractSWCommand.Validator validator = parameter.getAnnotation(AbstractSWCommand.Validator.class);
|
||||
if (validator != null) {
|
||||
if (validator.value() != null && !validator.value().isEmpty()) {
|
||||
return getValidator(validator.value(), localValidator);
|
||||
}
|
||||
return getValidator(clazz.getTypeName(), localValidator);
|
||||
if (clazz.isEnum() && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
|
||||
return createEnumMapper((Class<Enum<?>>) clazz);
|
||||
}
|
||||
|
||||
AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class);
|
||||
if (errorMessage != null) {
|
||||
return (AbstractValidator<T, Object>) (sender, value, messageSender) -> {
|
||||
if (value == null) messageSender.send(errorMessage.value());
|
||||
if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
|
||||
messageSender.send(errorMessage.value());
|
||||
return false;
|
||||
}
|
||||
return value != null;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
return getTypeMapper(clazz.getTypeName(), localTypeMapper);
|
||||
}
|
||||
|
||||
private static <T> AbstractValidator<T, ?> getValidator(String s, Map<String, AbstractValidator<T, ?>> localGuardChecker) {
|
||||
AbstractValidator<T, ?> validator = localGuardChecker.getOrDefault(s, (AbstractValidator<T, ?>) VALIDATOR_FUNCTIONS.getOrDefault(s, null));
|
||||
if (validator == null) {
|
||||
public static <T> AbstractValidator<T, ?> getValidator(AbstractSWCommand.Validator validator, Class<?> type, Map<String, AbstractValidator<T, ?>> localValidator) {
|
||||
String s = validator.value() != null && !validator.value().isEmpty() ? validator.value() : type.getTypeName();
|
||||
AbstractValidator<T, ?> concreteValidator = localValidator.getOrDefault(s, (AbstractValidator<T, ?>) VALIDATOR_FUNCTIONS.getOrDefault(s, null));
|
||||
if (concreteValidator == null) {
|
||||
throw new IllegalArgumentException("No validator found for " + s);
|
||||
}
|
||||
return validator;
|
||||
return concreteValidator;
|
||||
}
|
||||
|
||||
public static <K, T> void addMapper(Class<T> clazz, AbstractTypeMapper<K, T> mapper) {
|
||||
@ -255,10 +155,25 @@ public class SWCommandUtils {
|
||||
};
|
||||
}
|
||||
|
||||
private static Function<String, Collection<String>> numberCompleter(Function<String, ?> mapper) {
|
||||
return s -> numberMapper(mapper).apply(s) != null
|
||||
? Collections.singletonList(s)
|
||||
: Collections.emptyList();
|
||||
private static Function<String, Collection<String>> numberCompleter(Function<String, ?> mapper, boolean comma) {
|
||||
return s -> {
|
||||
if (numberMapper(mapper).apply(s) == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> strings = new ArrayList<>();
|
||||
if (s.length() == 0) {
|
||||
strings.add("-");
|
||||
} else {
|
||||
strings.add(s);
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
strings.add(s + i);
|
||||
}
|
||||
if (comma && (!s.contains(".") || !s.contains(","))) {
|
||||
strings.add(s + ".");
|
||||
}
|
||||
return strings;
|
||||
};
|
||||
}
|
||||
|
||||
static <T extends Annotation> T[] getAnnotation(Method method, Class<T> annotation) {
|
||||
|
@ -19,17 +19,20 @@
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SubCommand<T> {
|
||||
public class SubCommand<T> implements Comparable<SubCommand<T>> {
|
||||
|
||||
private AbstractSWCommand<T> abstractSWCommand;
|
||||
Method method;
|
||||
@ -39,11 +42,12 @@ public class SubCommand<T> {
|
||||
private Function<T, ?> senderFunction;
|
||||
AbstractValidator<T, T> validator;
|
||||
boolean noTabComplete;
|
||||
int comparableValue;
|
||||
|
||||
private Parameter[] parameters;
|
||||
|
||||
private CommandPart<T> commandPart;
|
||||
|
||||
SubCommand(AbstractSWCommand<T> abstractSWCommand, Method method, String[] subCommand, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, boolean help, String[] description, boolean noTabComplete) {
|
||||
SubCommand(AbstractSWCommand<T> abstractSWCommand, Method method, String[] subCommand, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, String[] description, boolean noTabComplete) {
|
||||
this.abstractSWCommand = abstractSWCommand;
|
||||
this.method = method;
|
||||
try {
|
||||
@ -55,17 +59,34 @@ public class SubCommand<T> {
|
||||
this.description = description;
|
||||
this.noTabComplete = noTabComplete;
|
||||
|
||||
Parameter[] parameters = method.getParameters();
|
||||
comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : -parameters.length;
|
||||
parameters = method.getParameters();
|
||||
AbstractSWCommand.Validator validator = parameters[0].getAnnotation(AbstractSWCommand.Validator.class);
|
||||
if (validator != null) {
|
||||
this.validator = (AbstractValidator<T, T>) SWCommandUtils.getValidator(validator, parameters[0].getType(), localValidator);
|
||||
}
|
||||
|
||||
validator = (AbstractValidator<T, T>) SWCommandUtils.getValidator(parameters[0], localValidator);
|
||||
|
||||
commandPart = SWCommandUtils.generateCommandPart(abstractSWCommand, help, subCommand, parameters, localTypeMapper, localValidator);
|
||||
commandPart = generateCommandPart(abstractSWCommand, subCommand, parameters, localTypeMapper, localValidator);
|
||||
|
||||
senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass());
|
||||
senderFunction = t -> parameters[0].getType().cast(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SubCommand<T> o) {
|
||||
int tLength = parameters.length + subCommand.length;
|
||||
int oLength = o.parameters.length + o.subCommand.length;
|
||||
|
||||
boolean tVarArgs = parameters[parameters.length - 1].isVarArgs();
|
||||
boolean oVarArgs = o.parameters[o.parameters.length - 1].isVarArgs();
|
||||
|
||||
if (tVarArgs) tLength *= -1;
|
||||
if (oVarArgs) oLength *= -1;
|
||||
|
||||
if (tVarArgs && oVarArgs) return Integer.compare(tLength, oLength);
|
||||
|
||||
return -Integer.compare(tLength, oLength);
|
||||
}
|
||||
|
||||
boolean invoke(Consumer<Runnable> errors, T sender, String alias, String[] args) {
|
||||
try {
|
||||
if (!senderPredicate.test(sender)) {
|
||||
@ -97,7 +118,7 @@ public class SubCommand<T> {
|
||||
objects.add(0, senderFunction.apply(sender));
|
||||
method.invoke(abstractSWCommand, objects.toArray());
|
||||
}
|
||||
} catch (CommandNoHelpException | CommandFrameworkException e) {
|
||||
} catch (CommandFrameworkException e) {
|
||||
throw e;
|
||||
} catch (CommandParseException e) {
|
||||
return false;
|
||||
@ -110,18 +131,155 @@ public class SubCommand<T> {
|
||||
}
|
||||
|
||||
List<String> tabComplete(T sender, String[] args) {
|
||||
if (validator != null) {
|
||||
if (!validator.validate(sender, sender, (s, objects) -> {
|
||||
// ignore
|
||||
})) {
|
||||
return null;
|
||||
}
|
||||
if (validator != null && !validator.validate(sender, sender, (s, objects) -> {})) {
|
||||
return null;
|
||||
}
|
||||
if (commandPart == null) {
|
||||
return null;
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
commandPart.generateTabComplete(list, sender, args, 0);
|
||||
commandPart.generateTabComplete(list, sender, args, new ArrayList<>(), 0);
|
||||
return list;
|
||||
}
|
||||
|
||||
private static <T> CommandPart<T> generateCommandPart(AbstractSWCommand<T> command, String[] subCommand, Parameter[] parameters, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator) {
|
||||
CommandPart<T> first = null;
|
||||
CommandPart<T> current = null;
|
||||
for (String s : subCommand) {
|
||||
CommandPart commandPart = new CommandPart(command, SWCommandUtils.createMapper(s), null, null, null, -1);
|
||||
commandPart.addValidator(NULL_FILTER);
|
||||
commandPart.setIgnoreAsArgument(true);
|
||||
if (current != null) {
|
||||
current.setNext(commandPart);
|
||||
}
|
||||
current = commandPart;
|
||||
if (first == null) {
|
||||
first = current;
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
Parameter parameter = parameters[i];
|
||||
AbstractTypeMapper<T, ?> typeMapper = handleImplicitTypeMapper(parameter, localTypeMapper);
|
||||
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
|
||||
AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class);
|
||||
|
||||
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i);
|
||||
commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG());
|
||||
handleImplicitTypeValidator(parameter, commandPart, localValidator);
|
||||
if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) {
|
||||
commandPart.addValidator((AbstractValidator<T, Object>) NULL_FILTER);
|
||||
}
|
||||
if (current != null) {
|
||||
current.setNext(commandPart);
|
||||
}
|
||||
current = commandPart;
|
||||
if (first == null) {
|
||||
first = current;
|
||||
}
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
private static <T> AbstractTypeMapper<T, Object> handleImplicitTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
|
||||
Class<?> type = parameter.getType();
|
||||
if (parameter.isVarArgs()) {
|
||||
type = type.getComponentType();
|
||||
}
|
||||
|
||||
Annotation[] annotations = parameter.getAnnotations();
|
||||
Constructor<?> sourceConstructor = null;
|
||||
Annotation sourceAnnotation = null;
|
||||
List<Constructor<?>> parentConstructors = new ArrayList<>();
|
||||
List<Annotation> parentAnnotations = new ArrayList<>();
|
||||
for (Annotation annotation : annotations) {
|
||||
CommandMetaData.ImplicitTypeMapper implicitTypeMapper = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitTypeMapper.class);
|
||||
if (implicitTypeMapper == null) continue;
|
||||
Class<?> clazz = implicitTypeMapper.handler();
|
||||
if (!AbstractTypeMapper.class.isAssignableFrom(clazz)) continue;
|
||||
Constructor<?>[] constructors = clazz.getConstructors();
|
||||
if (constructors.length != 1) continue;
|
||||
Constructor<?> constructor = constructors[0];
|
||||
if (needsTypeMapper(constructor)) {
|
||||
parentConstructors.add(constructor);
|
||||
parentAnnotations.add(annotation);
|
||||
} else {
|
||||
if (sourceAnnotation != null) {
|
||||
throw new IllegalArgumentException("Multiple source type mappers found for parameter " + parameter);
|
||||
}
|
||||
sourceConstructor = constructor;
|
||||
sourceAnnotation = annotation;
|
||||
}
|
||||
}
|
||||
|
||||
AbstractTypeMapper<T, Object> current;
|
||||
if (sourceAnnotation != null) {
|
||||
current = createInstance(sourceConstructor, sourceAnnotation, type, localTypeMapper);
|
||||
} else {
|
||||
current = (AbstractTypeMapper<T, Object>) SWCommandUtils.getTypeMapper(parameter, localTypeMapper);
|
||||
}
|
||||
for (int i = 0; i < parentConstructors.size(); i++) {
|
||||
Constructor<?> constructor = parentConstructors.get(i);
|
||||
Annotation annotation = parentAnnotations.get(i);
|
||||
current = createInstance(constructor, annotation, type, localTypeMapper, current);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
private static boolean needsTypeMapper(Constructor<?> constructor) {
|
||||
Class<?>[] parameterTypes = constructor.getParameterTypes();
|
||||
for (Class<?> parameterType : parameterTypes) {
|
||||
if (AbstractTypeMapper.class.isAssignableFrom(parameterType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> void handleImplicitTypeValidator(Parameter parameter, CommandPart<T> commandPart, Map<String, AbstractValidator<T, ?>> localValidator) {
|
||||
Annotation[] annotations = parameter.getAnnotations();
|
||||
Map<Integer, List<AbstractValidator<T, Object>>> validators = new HashMap<>();
|
||||
for (Annotation annotation : annotations) {
|
||||
CommandMetaData.ImplicitValidator implicitValidator = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitValidator.class);
|
||||
if (implicitValidator == null) continue;
|
||||
Class<?> clazz = implicitValidator.handler();
|
||||
if (!AbstractValidator.class.isAssignableFrom(clazz)) continue;
|
||||
Constructor<?>[] constructors = clazz.getConstructors();
|
||||
if (constructors.length != 1) continue;
|
||||
AbstractValidator<T, Object> validator = createInstance(constructors[0], annotation, commandPart.getType(), localValidator);
|
||||
validators.computeIfAbsent(implicitValidator.order(), integer -> new ArrayList<>()).add(validator);
|
||||
}
|
||||
List<Integer> keys = new ArrayList<>(validators.keySet());
|
||||
keys.sort(Integer::compareTo);
|
||||
for (Integer key : keys) {
|
||||
List<AbstractValidator<T, Object>> list = validators.get(key);
|
||||
for (AbstractValidator<T, Object> validator : list) {
|
||||
commandPart.addValidator(validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T createInstance(Constructor<?> constructor, Object... parameter) {
|
||||
Class<?>[] types = constructor.getParameterTypes();
|
||||
List<Object> objects = new ArrayList<>();
|
||||
for (Class<?> clazz : types) {
|
||||
boolean found = false;
|
||||
for (Object o : parameter) {
|
||||
if (clazz.isAssignableFrom(o.getClass())) {
|
||||
objects.add(o);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new RuntimeException("Could not find type " + clazz + " for constructor " + constructor);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return (T) constructor.newInstance(objects.toArray());
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final AbstractValidator<?, Object> NULL_FILTER = (sender, value, messageSender) -> value != null;
|
||||
}
|
||||
|
@ -51,25 +51,29 @@ public class TabCompletionCache {
|
||||
@AllArgsConstructor
|
||||
private static class Key {
|
||||
private Object sender;
|
||||
private String arg;
|
||||
private AbstractTypeMapper<?, ?> typeMapper;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
private static class TabCompletions {
|
||||
private AbstractSWCommand<?> command;
|
||||
private long timestamp;
|
||||
private Collection<String> tabCompletions;
|
||||
}
|
||||
|
||||
Collection<String> tabComplete(Object sender, AbstractTypeMapper<?, ?> typeMapper, AbstractSWCommand<?> command, Supplier<Collection<String>> tabCompleteSupplier) {
|
||||
Collection<String> tabComplete(Object sender, String arg, AbstractTypeMapper<Object, ?> typeMapper, Supplier<Collection<String>> tabCompleteSupplier) {
|
||||
if (!cached.contains(typeMapper)) return tabCompleteSupplier.get();
|
||||
Key key = global.contains(typeMapper) ? new Key(null, typeMapper) : new Key(sender, typeMapper);
|
||||
|
||||
String normalizedArg = typeMapper.normalize(sender, arg);
|
||||
if (normalizedArg == null) normalizedArg = "";
|
||||
Key key = new Key(global.contains(typeMapper) ? null : sender, normalizedArg, typeMapper);
|
||||
|
||||
TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> {
|
||||
return new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get());
|
||||
return new TabCompletions(System.currentTimeMillis(), tabCompleteSupplier.get());
|
||||
});
|
||||
if (tabCompletions.command != command || System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) {
|
||||
tabCompletions = new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get());
|
||||
tabCompletionCache.put(key, tabCompletions);
|
||||
|
||||
if (System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) {
|
||||
tabCompletions.tabCompletions = tabCompleteSupplier.get();
|
||||
}
|
||||
tabCompletions.timestamp = System.currentTimeMillis();
|
||||
return tabCompletions.tabCompletions;
|
||||
|
@ -285,6 +285,7 @@ public class LinkageProcessor extends AbstractProcessor {
|
||||
|
||||
private LinkageType resolveSingle(TypeMirror typeMirror) {
|
||||
String qualifier = typeMirror.toString();
|
||||
if (qualifier.contains("<")) qualifier = qualifier.substring(0, qualifier.indexOf('<'));
|
||||
qualifier = qualifier.substring(qualifier.lastIndexOf('.') + 1);
|
||||
try {
|
||||
return (LinkageType) Class.forName("de.steamwar.linkage.types." + qualifier + "_" + context.name()).getDeclaredConstructor().newInstance();
|
||||
|
79
src/de/steamwar/sql/BauweltMember.java
Normale Datei
79
src/de/steamwar/sql/BauweltMember.java
Normale Datei
@ -0,0 +1,79 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class BauweltMember {
|
||||
private static final Map<Integer, BauweltMember> memberCache = new HashMap<>();
|
||||
|
||||
public static void clear() {
|
||||
memberCache.clear();
|
||||
}
|
||||
|
||||
private static final Table<BauweltMember> table = new Table<>(BauweltMember.class);
|
||||
private static final SelectStatement<BauweltMember> getMember = table.select(Table.PRIMARY);
|
||||
private static final SelectStatement<BauweltMember> getMembers = table.selectFields("BauweltID");
|
||||
|
||||
public static BauweltMember getBauMember(UUID ownerID, UUID memberID){
|
||||
return getBauMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId());
|
||||
}
|
||||
|
||||
public static BauweltMember getBauMember(int ownerID, int memberID){
|
||||
BauweltMember member = memberCache.get(memberID);
|
||||
if(member != null)
|
||||
return member;
|
||||
return getMember.select(ownerID, memberID);
|
||||
}
|
||||
|
||||
public static List<BauweltMember> getMembers(UUID bauweltID){
|
||||
return getMembers(SteamwarUser.get(bauweltID).getId());
|
||||
}
|
||||
|
||||
public static List<BauweltMember> getMembers(int bauweltID){
|
||||
return getMembers.listSelect(bauweltID);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int bauweltID;
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int memberID;
|
||||
@Getter
|
||||
@Field
|
||||
private final boolean worldEdit;
|
||||
@Getter
|
||||
@Field
|
||||
private final boolean world;
|
||||
|
||||
public BauweltMember(int bauweltID, int memberID, boolean worldEdit, boolean world) {
|
||||
this.bauweltID = bauweltID;
|
||||
this.memberID = memberID;
|
||||
this.worldEdit = worldEdit;
|
||||
this.world = world;
|
||||
memberCache.put(memberID, this);
|
||||
}
|
||||
}
|
71
src/de/steamwar/sql/CheckedSchematic.java
Normale Datei
71
src/de/steamwar/sql/CheckedSchematic.java
Normale Datei
@ -0,0 +1,71 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class CheckedSchematic {
|
||||
|
||||
private static final Table<CheckedSchematic> table = new Table<>(CheckedSchematic.class);
|
||||
private static final SelectStatement<CheckedSchematic> statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC");
|
||||
|
||||
public static List<CheckedSchematic> getLastDeclinedOfNode(int node){
|
||||
return statusOfNode.listSelect(node);
|
||||
}
|
||||
|
||||
@Field(nullable = true)
|
||||
private final Integer nodeId;
|
||||
@Field
|
||||
private final int nodeOwner;
|
||||
@Field
|
||||
private final String nodeName;
|
||||
@Getter
|
||||
@Field
|
||||
private final int validator;
|
||||
@Getter
|
||||
@Field
|
||||
private final Timestamp startTime;
|
||||
@Getter
|
||||
@Field
|
||||
private final Timestamp endTime;
|
||||
@Getter
|
||||
@Field
|
||||
private final String declineReason;
|
||||
|
||||
public int getNode() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public String getSchemName() {
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
public int getSchemOwner() {
|
||||
return nodeOwner;
|
||||
}
|
||||
}
|
75
src/de/steamwar/sql/Event.java
Normale Datei
75
src/de/steamwar/sql/Event.java
Normale Datei
@ -0,0 +1,75 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Event {
|
||||
|
||||
private static final Table<Event> table = new Table<>(Event.class);
|
||||
private static final SelectStatement<Event> byId = table.select(Table.PRIMARY);
|
||||
|
||||
public static Event get(int eventID){
|
||||
return byId.select(eventID);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int eventID;
|
||||
@Getter
|
||||
@Field(keys = {"eventName"})
|
||||
private final String eventName;
|
||||
@Getter
|
||||
@Field
|
||||
private final Timestamp deadline;
|
||||
@Getter
|
||||
@Field
|
||||
private final Timestamp start;
|
||||
@Getter
|
||||
@Field
|
||||
private final Timestamp end;
|
||||
@Getter
|
||||
@Field
|
||||
private final int maximumTeamMembers;
|
||||
@Field(nullable = true)
|
||||
private final SchematicType schemType;
|
||||
@Field
|
||||
private final boolean publicSchemsOnly;
|
||||
@Field
|
||||
private final boolean spectateSystem;
|
||||
|
||||
public boolean publicSchemsOnly() {
|
||||
return publicSchemsOnly;
|
||||
}
|
||||
public boolean spectateSystem(){
|
||||
return spectateSystem;
|
||||
}
|
||||
|
||||
public SchematicType getSchematicType() {
|
||||
return schemType;
|
||||
}
|
||||
}
|
72
src/de/steamwar/sql/EventFight.java
Normale Datei
72
src/de/steamwar/sql/EventFight.java
Normale Datei
@ -0,0 +1,72 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class EventFight {
|
||||
|
||||
private static final Table<EventFight> table = new Table<>(EventFight.class);
|
||||
private static final SelectStatement<EventFight> byId = table.select(Table.PRIMARY);
|
||||
private static final Statement setResult = table.update(Table.PRIMARY, "Ergebnis");
|
||||
private static final Statement setFight = table.update(Table.PRIMARY, "Fight");
|
||||
|
||||
public static EventFight get(int fightID) {
|
||||
return byId.select(fightID);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field
|
||||
private final int eventID;
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int fightID;
|
||||
@Getter
|
||||
@Field
|
||||
private final int teamBlue;
|
||||
@Getter
|
||||
@Field
|
||||
private final int teamRed;
|
||||
@Getter
|
||||
@Field
|
||||
private final int kampfleiter;
|
||||
@Getter
|
||||
@Field(def = "0")
|
||||
private int ergebnis;
|
||||
@Field(nullable = true)
|
||||
private int fight;
|
||||
|
||||
public void setErgebnis(int winner) {
|
||||
this.ergebnis = winner;
|
||||
setResult.update(winner, fightID);
|
||||
}
|
||||
|
||||
public void setFight(int fight) {
|
||||
//Fight.FightID, not EventFight.FightID
|
||||
this.fight = fight;
|
||||
setFight.update(fight, fightID);
|
||||
}
|
||||
}
|
61
src/de/steamwar/sql/Fight.java
Normale Datei
61
src/de/steamwar/sql/Fight.java
Normale Datei
@ -0,0 +1,61 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Fight {
|
||||
|
||||
private static final Table<Fight> table = new Table<>(Fight.class);
|
||||
private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition");
|
||||
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int fightID;
|
||||
@Field
|
||||
private final String gameMode;
|
||||
@Field
|
||||
private final String server;
|
||||
@Field
|
||||
private final Timestamp startTime;
|
||||
@Field
|
||||
private final int duration;
|
||||
@Field
|
||||
private final int blueLeader;
|
||||
@Field
|
||||
private final int redLeader;
|
||||
@Field(nullable = true)
|
||||
private final Integer blueSchem;
|
||||
@Field(nullable = true)
|
||||
private final Integer redSchem;
|
||||
@Field
|
||||
private final int win;
|
||||
@Field
|
||||
private final String wincondition;
|
||||
|
||||
public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){
|
||||
return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition);
|
||||
}
|
||||
}
|
49
src/de/steamwar/sql/FightPlayer.java
Normale Datei
49
src/de/steamwar/sql/FightPlayer.java
Normale Datei
@ -0,0 +1,49 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class FightPlayer {
|
||||
|
||||
private static final Table<FightPlayer> table = new Table<>(FightPlayer.class);
|
||||
private static final Statement create = table.insertAll();
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int fightID;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int userID;
|
||||
@Field
|
||||
private final int team;
|
||||
@Field
|
||||
private final String kit;
|
||||
@Field
|
||||
private final int kills;
|
||||
@Field
|
||||
private final boolean isOut;
|
||||
|
||||
public static void create(int fightID, int userID, boolean blue, String kit, int kills, boolean isOut) {
|
||||
create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut);
|
||||
}
|
||||
}
|
23
src/de/steamwar/sql/NoClipboardException.java
Normale Datei
23
src/de/steamwar/sql/NoClipboardException.java
Normale Datei
@ -0,0 +1,23 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
public class NoClipboardException extends RuntimeException {
|
||||
}
|
69
src/de/steamwar/sql/NodeDownload.java
Normale Datei
69
src/de/steamwar/sql/NodeDownload.java
Normale Datei
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2021 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Timestamp;
|
||||
import java.time.Instant;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class NodeDownload {
|
||||
|
||||
private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
private static final String LINK_BASE = "https://steamwar.de/download.php?schem=";
|
||||
|
||||
private static final Table<NodeDownload> table = new Table<>(NodeDownload.class);
|
||||
private static final Statement insert = table.insertFields("NodeId", "Link");
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int nodeId;
|
||||
@Field
|
||||
private final String link;
|
||||
@Field(def = "CURRENT_TIMESTAMP")
|
||||
private final Timestamp timestamp;
|
||||
|
||||
public static String getLink(SchematicNode schem){
|
||||
if(schem.isDir())
|
||||
throw new SecurityException("Can not Download Directorys");
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
digest.reset();
|
||||
digest.update((Instant.now().toString() + schem.getOwner() + schem.getId()).getBytes());
|
||||
String hash = base16encode(digest.digest());
|
||||
insert.update(schem.getId(), hash);
|
||||
return LINK_BASE + hash;
|
||||
}
|
||||
public static String base16encode(byte[] byteArray) {
|
||||
StringBuilder hexBuffer = new StringBuilder(byteArray.length * 2);
|
||||
for (byte b : byteArray)
|
||||
hexBuffer.append(HEX[(b >>> 4) & 0xF]).append(HEX[b & 0xF]);
|
||||
return hexBuffer.toString();
|
||||
}
|
||||
}
|
91
src/de/steamwar/sql/NodeMember.java
Normale Datei
91
src/de/steamwar/sql/NodeMember.java
Normale Datei
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class NodeMember {
|
||||
|
||||
public static void init() {
|
||||
// enforce class initialization
|
||||
}
|
||||
|
||||
private static final Table<NodeMember> table = new Table<>(NodeMember.class);
|
||||
private static final SelectStatement<NodeMember> getNodeMember = table.select(Table.PRIMARY);
|
||||
private static final SelectStatement<NodeMember> getNodeMembers = table.selectFields("NodeId");
|
||||
private static final SelectStatement<NodeMember> getSchematics = table.selectFields("UserId");
|
||||
private static final Statement create = table.insert(Table.PRIMARY);
|
||||
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||
private static final Statement updateParent = table.update(Table.PRIMARY, "ParentId");
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int nodeId;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int userId;
|
||||
@Field(nullable = true, def = "null")
|
||||
private Integer parentId;
|
||||
|
||||
public int getNode() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public int getMember() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Optional<Integer> getParent() {
|
||||
return Optional.ofNullable(parentId);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
delete.update(nodeId, userId);
|
||||
}
|
||||
|
||||
public static NodeMember createNodeMember(int node, int member) {
|
||||
create.update(node, member);
|
||||
return new NodeMember(node, member, null);
|
||||
}
|
||||
|
||||
public static NodeMember getNodeMember(int node, int member) {
|
||||
return getNodeMember.select(node, member);
|
||||
}
|
||||
|
||||
public static Set<NodeMember> getNodeMembers(int node) {
|
||||
return new HashSet<>(getNodeMembers.listSelect(node));
|
||||
}
|
||||
|
||||
public static Set<NodeMember> getSchematics(int member) {
|
||||
return new HashSet<>(getSchematics.listSelect(member));
|
||||
}
|
||||
|
||||
public void setParentId(Integer parentId) {
|
||||
this.parentId = parentId;
|
||||
updateParent.update(this.parentId, nodeId, userId);
|
||||
}
|
||||
}
|
122
src/de/steamwar/sql/Punishment.java
Normale Datei
122
src/de/steamwar/sql/Punishment.java
Normale Datei
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.SqlTypeMapper;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Punishment {
|
||||
|
||||
static {
|
||||
SqlTypeMapper.nameEnumMapper(PunishmentType.class);
|
||||
}
|
||||
|
||||
private static final Table<Punishment> table = new Table<>(Punishment.class, "Punishments");
|
||||
private static final SelectStatement<Punishment> getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)");
|
||||
private static final SelectStatement<Punishment> getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1");
|
||||
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int punishmentId;
|
||||
@Field
|
||||
private final int userId;
|
||||
@Field
|
||||
@Getter
|
||||
private final int punisher;
|
||||
@Field
|
||||
@Getter
|
||||
private final PunishmentType type;
|
||||
@Field
|
||||
@Getter
|
||||
private final Timestamp startTime;
|
||||
@Field
|
||||
@Getter
|
||||
private final Timestamp endTime;
|
||||
@Field
|
||||
@Getter
|
||||
private final boolean perma;
|
||||
@Field
|
||||
@Getter
|
||||
private final String reason;
|
||||
|
||||
public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
|
||||
return getPunishment.select(user, type);
|
||||
}
|
||||
|
||||
public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
|
||||
return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
|
||||
}
|
||||
|
||||
public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer<Punishment> callback) {
|
||||
Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
|
||||
if(punishment == null || !punishment.isCurrent()) {
|
||||
return false;
|
||||
} else {
|
||||
callback.accept(punishment);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated // Not multiling, misleading title
|
||||
public String getBantime(Timestamp endTime, boolean perma) {
|
||||
if (perma) {
|
||||
return "permanent";
|
||||
} else {
|
||||
return endTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
|
||||
}
|
||||
}
|
||||
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return isPerma() || getEndTime().after(new Date());
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum PunishmentType {
|
||||
Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
|
||||
Mute( false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"),
|
||||
NoSchemReceiving(false, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"),
|
||||
NoSchemSharing(false, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"),
|
||||
NoSchemSubmitting(true, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"),
|
||||
NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER");
|
||||
|
||||
private final boolean needsAdmin;
|
||||
private final String teamMessage;
|
||||
private final String playerMessagePerma;
|
||||
private final String playerMessageUntil;
|
||||
private final String usageNotPunished;
|
||||
private final String unpunishmentMessage;
|
||||
}
|
||||
}
|
74
src/de/steamwar/sql/Replay.java
Normale Datei
74
src/de/steamwar/sql/Replay.java
Normale Datei
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Replay {
|
||||
|
||||
static {
|
||||
new SqlTypeMapper<>(File.class, "BLOB", (rs, identifier) -> {
|
||||
try {
|
||||
File file = File.createTempFile("replay", ".replay");
|
||||
file.deleteOnExit();
|
||||
Files.copy(rs.getBinaryStream(identifier), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
return file;
|
||||
} catch (IOException e) {
|
||||
throw new SQLException(e);
|
||||
}
|
||||
}, (st, index, value) -> {
|
||||
try {
|
||||
st.setBinaryStream(index, new FileInputStream(value));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new SQLException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final Table<Replay> table = new Table<>(Replay.class);
|
||||
private static final SelectStatement<Replay> get = table.select(Table.PRIMARY);
|
||||
|
||||
public static final Statement insert = table.insertAll();
|
||||
|
||||
public static Replay get(int fightID) {
|
||||
return get.select(fightID);
|
||||
}
|
||||
|
||||
public static void save(int fightID, File file) {
|
||||
insert.update(fightID, file);
|
||||
}
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int fightID;
|
||||
@Getter
|
||||
@Field
|
||||
private final File replay;
|
||||
}
|
33
src/de/steamwar/sql/SQLWrapper.java
Normale Datei
33
src/de/steamwar/sql/SQLWrapper.java
Normale Datei
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql;
|
||||
|
||||
import de.steamwar.ImplementationProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface SQLWrapper {
|
||||
SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl");
|
||||
|
||||
void loadSchemTypes(List<SchematicType> tmpTypes, Map<String, SchematicType> tmpFromDB);
|
||||
|
||||
void additionalExceptionMetadata(StringBuilder builder);
|
||||
}
|
61
src/de/steamwar/sql/SWException.java
Normale Datei
61
src/de/steamwar/sql/SWException.java
Normale Datei
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class SWException {
|
||||
|
||||
public static void init() {
|
||||
// force class initialialisation
|
||||
}
|
||||
|
||||
private static final String CWD = System.getProperty("user.dir");
|
||||
private static final String SERVER_NAME = new File(CWD).getName();
|
||||
|
||||
private static final Table<SWException> table = new Table<>(SWException.class, "Exception");
|
||||
private static final Statement insert = table.insertFields("server", "message", "stacktrace");
|
||||
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int id;
|
||||
@Field(def = "CURRENT_TIMESTAMP")
|
||||
private final Timestamp time;
|
||||
@Field
|
||||
private final String server;
|
||||
@Field
|
||||
private final String message;
|
||||
@Field
|
||||
private final String stacktrace;
|
||||
|
||||
public static void log(String message, String stacktrace){
|
||||
StringBuilder msgBuilder = new StringBuilder(message);
|
||||
SQLWrapper.impl.additionalExceptionMetadata(msgBuilder);
|
||||
msgBuilder.append("\nCWD: ").append(CWD);
|
||||
|
||||
insert.update(SERVER_NAME, msgBuilder.toString(), stacktrace);
|
||||
}
|
||||
}
|
44
src/de/steamwar/sql/SchemElo.java
Normale Datei
44
src/de/steamwar/sql/SchemElo.java
Normale Datei
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class SchemElo {
|
||||
|
||||
private static final Table<SchemElo> table = new Table<>(SchemElo.class);
|
||||
private static final SelectStatement<SchemElo> select = table.select(Table.PRIMARY);
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int schemId;
|
||||
@Field
|
||||
private final int elo;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int season;
|
||||
|
||||
public static int getElo(SchematicNode node, int season) {
|
||||
SchemElo elo = select.select(node, season);
|
||||
return elo != null ? elo.elo : 0;
|
||||
}
|
||||
}
|
564
src/de/steamwar/sql/SchematicNode.java
Normale Datei
564
src/de/steamwar/sql/SchematicNode.java
Normale Datei
@ -0,0 +1,564 @@
|
||||
/*
|
||||
* 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SchematicNode {
|
||||
|
||||
static {
|
||||
new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId));
|
||||
}
|
||||
|
||||
private static final Map<Integer, Map<String, List<String>>> TAB_CACHE = new HashMap<>();
|
||||
public static void clear() {
|
||||
TAB_CACHE.clear();
|
||||
}
|
||||
|
||||
private static final String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode ";
|
||||
|
||||
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
|
||||
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType");
|
||||
private static final Statement update = table.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat");
|
||||
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||
|
||||
private static final SelectStatement<SchematicNode> byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?");
|
||||
private static final SelectStatement<SchematicNode> byOwnerNameParent = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode " + Statement.NULL_SAFE_EQUALS + "?");
|
||||
private static final SelectStatement<SchematicNode> byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
|
||||
private static final SelectStatement<SchematicNode> dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName");
|
||||
private static final SelectStatement<SchematicNode> byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName");
|
||||
private static final SelectStatement<SchematicNode> byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName");
|
||||
private static final SelectStatement<SchematicNode> all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName");
|
||||
private static final SelectStatement<SchematicNode> list = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
|
||||
private static final SelectStatement<SchematicNode> byParentName = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
|
||||
private static final SelectStatement<SchematicNode> schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?");
|
||||
private static final SelectStatement<SchematicNode> accessibleByUserTypeInParent = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeType = ? AND EffectiveOwner = ? UNION SELECT SN.NodeId, SN.ParentNode FROM RSN, EffectiveSchematicNode SN WHERE SN.NodeId = RSN.ParentNode AND EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, RSN.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.ReplaceColor, SN.AllowReplay, SN.NodeFormat FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode" + Statement.NULL_SAFE_EQUALS + "?");
|
||||
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?");
|
||||
private static final SelectStatement<SchematicNode> byIdAndUser = new SelectStatement<>(table, "SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE NodeId = ?");
|
||||
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.ReplaceColor, SN.AllowReplay, SN.NodeFormat FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId");
|
||||
|
||||
static {
|
||||
NodeMember.init();
|
||||
}
|
||||
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int nodeId;
|
||||
@Field(keys = {"OwnerNameParent"})
|
||||
private final int nodeOwner;
|
||||
@Field(def = "0")
|
||||
@Getter
|
||||
private final int effectiveOwner;
|
||||
@Field(keys = {"OwnerNameParent"})
|
||||
private String nodeName;
|
||||
@Field(keys = {"OwnerNameParent"}, nullable = true)
|
||||
private Integer parentNode;
|
||||
@Field(def = "CURRENT_TIMESTAMP")
|
||||
private Timestamp lastUpdate;
|
||||
@Field(def = "''")
|
||||
private String nodeItem;
|
||||
@Field(def = "'normal'", nullable = true)
|
||||
private SchematicType nodeType;
|
||||
@Field(def = "0")
|
||||
private int nodeRank;
|
||||
@Field(def = "1")
|
||||
private boolean replaceColor;
|
||||
@Field(def = "1")
|
||||
private boolean allowReplay;
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
@Field(def = "1")
|
||||
private boolean nodeFormat;
|
||||
|
||||
private String brCache;
|
||||
|
||||
public SchematicNode(
|
||||
int nodeId,
|
||||
int nodeOwner,
|
||||
int effectiveOwner,
|
||||
String nodeName,
|
||||
Integer parentNode,
|
||||
Timestamp lastUpdate,
|
||||
String nodeItem,
|
||||
SchematicType nodeType,
|
||||
int nodeRank,
|
||||
boolean replaceColor,
|
||||
boolean allowReplay,
|
||||
boolean nodeFormat
|
||||
) {
|
||||
this.nodeId = nodeId;
|
||||
this.nodeOwner = nodeOwner;
|
||||
this.effectiveOwner = effectiveOwner;
|
||||
this.nodeName = nodeName;
|
||||
this.parentNode = parentNode;
|
||||
this.nodeItem = nodeItem;
|
||||
this.nodeType = nodeType;
|
||||
this.lastUpdate = lastUpdate;
|
||||
this.nodeRank = nodeRank;
|
||||
this.replaceColor = replaceColor;
|
||||
this.allowReplay = allowReplay;
|
||||
this.nodeFormat = nodeFormat;
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAll(SteamwarUser user) {
|
||||
return all.listSelect(user);
|
||||
}
|
||||
|
||||
public static Map<Integer, List<SchematicNode>> getAllMap(SteamwarUser user) {
|
||||
return map(getAll(user));
|
||||
}
|
||||
|
||||
public static List<SchematicNode> list(SteamwarUser user, Integer schematicId) {
|
||||
return list.listSelect(user, schematicId, user, user, schematicId, user, schematicId, schematicId);
|
||||
}
|
||||
|
||||
public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) {
|
||||
return byParentName.select(user, schematicId, user, name, user, schematicId, user, schematicId, schematicId, name);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> accessibleByUserType(SteamwarUser user, SchematicType type) {
|
||||
return accessibleByUserType.listSelect(user, type);
|
||||
}
|
||||
|
||||
public static Map<Integer, List<SchematicNode>> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) {
|
||||
return map(accessibleByUserType(user, type));
|
||||
}
|
||||
|
||||
public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) {
|
||||
return schematicAccessibleForUser.select(user, schematicId) != null;
|
||||
}
|
||||
|
||||
public static List<SchematicNode> accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) {
|
||||
return accessibleByUserTypeInParent.listSelect(type, user, user, user, parentId);
|
||||
}
|
||||
|
||||
public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) {
|
||||
return byIdAndUser.select(user, id);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> parentsOfNode(SteamwarUser user, Integer id) {
|
||||
return allParentsOfNode.listSelect(id, user, user, user);
|
||||
}
|
||||
|
||||
private static Map<Integer, List<SchematicNode>> map(List<SchematicNode> in) {
|
||||
Map<Integer, List<SchematicNode>> map = new HashMap<>();
|
||||
for (SchematicNode effectiveSchematicNode : in) {
|
||||
map.computeIfAbsent(effectiveSchematicNode.getOptionalParent().orElse(0), k -> new ArrayList<>()).add(effectiveSchematicNode);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static SchematicNode createSchematic(int owner, String name, Integer parent) {
|
||||
return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), "");
|
||||
}
|
||||
|
||||
public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) {
|
||||
return createSchematicNode(owner, name, parent, null, "");
|
||||
}
|
||||
|
||||
public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) {
|
||||
if (parent != null && parent == 0)
|
||||
parent = null;
|
||||
int nodeId = create.insertGetKey(owner, name, parent, item, type);
|
||||
return getSchematicNode(nodeId);
|
||||
}
|
||||
|
||||
public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) {
|
||||
return getSchematicNode(owner, name, parent.getId());
|
||||
}
|
||||
|
||||
public static SchematicNode getSchematicNode(int owner, String name, Integer parent) {
|
||||
return byOwnerNameParent.select(owner, name, parent);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) {
|
||||
return getSchematicNodeInNode(parent.getId());
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getSchematicNodeInNode(Integer parent) {
|
||||
return byParent.listSelect(parent);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getSchematicDirectoryInNode(Integer parent) {
|
||||
return dirsByParent.listSelect(parent);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static SchematicNode getSchematicDirectory(String name, SchematicNode parent) {
|
||||
return getSchematicNode(name, parent.getId());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static SchematicNode getSchematicDirectory(String name, Integer parent) {
|
||||
return getSchematicNode(name, parent);
|
||||
}
|
||||
|
||||
public static SchematicNode getSchematicNode(String name, Integer parent) {
|
||||
return byParentName.select(name, parent);
|
||||
}
|
||||
|
||||
public static SchematicNode getSchematicNode(int id) {
|
||||
return byId.select(id);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) {
|
||||
return accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) {
|
||||
return accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType));
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) {
|
||||
return byOwnerType.listSelect(owner, schemType);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static List<SchematicNode> getAllSchematicsOfType(String schemType) {
|
||||
return byType.listSelect(schemType);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) {
|
||||
return byType.listSelect(schemType);
|
||||
}
|
||||
|
||||
public static List<SchematicNode> deepGet(Integer parent, Predicate<SchematicNode> filter) {
|
||||
List<SchematicNode> finalList = new ArrayList<>();
|
||||
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(parent);
|
||||
nodes.forEach(node -> {
|
||||
if (node.isDir()) {
|
||||
finalList.addAll(deepGet(node.getId(), filter));
|
||||
} else {
|
||||
if (filter.test(node))
|
||||
finalList.add(node);
|
||||
}
|
||||
});
|
||||
return finalList;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) {
|
||||
return list(SteamwarUser.get(user), parent);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) {
|
||||
return getAll(SteamwarUser.get(user));
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
|
||||
return getAllParentsOfNode(node.getId());
|
||||
}
|
||||
|
||||
public static List<SchematicNode> getAllParentsOfNode(int node) {
|
||||
return allParentsOfNode.listSelect(node);
|
||||
}
|
||||
|
||||
public static SchematicNode getNodeFromPath(SteamwarUser user, String s) {
|
||||
if (s.startsWith("/")) {
|
||||
s = s.substring(1);
|
||||
}
|
||||
if (s.endsWith("/")) {
|
||||
s = s.substring(0, s.length() - 1);
|
||||
}
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (s.contains("/")) {
|
||||
String[] layers = s.split("/");
|
||||
Optional<SchematicNode> currentNode = Optional.ofNullable(SchematicNode.byParentName(user, null, layers[0]));
|
||||
for (int i = 1; i < layers.length; i++) {
|
||||
int finalI = i;
|
||||
Optional<SchematicNode> node = currentNode.map(effectiveSchematicNode -> SchematicNode.byParentName(user, effectiveSchematicNode.getId(), layers[finalI]));
|
||||
if (!node.isPresent()) {
|
||||
return null;
|
||||
} else {
|
||||
currentNode = node;
|
||||
if (!currentNode.map(SchematicNode::isDir).orElse(false) && i != layers.length - 1) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentNode.orElse(null);
|
||||
} else {
|
||||
return SchematicNode.byParentName(user, null, s);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<SchematicNode> filterSchems(int user, Predicate<SchematicNode> filter) {
|
||||
List<SchematicNode> finalList = new ArrayList<>();
|
||||
List<SchematicNode> nodes = getSchematicsAccessibleByUser(user, null);
|
||||
nodes.forEach(node -> {
|
||||
if (node.isDir()) {
|
||||
finalList.addAll(deepGet(node.getId(), filter));
|
||||
} else {
|
||||
if (filter.test(node))
|
||||
finalList.add(node);
|
||||
}
|
||||
});
|
||||
return finalList;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public int getOwner() {
|
||||
return nodeOwner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.nodeName = name;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public Integer getParent() {
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
public Optional<Integer> getOptionalParent() {
|
||||
return Optional.ofNullable(parentNode);
|
||||
}
|
||||
|
||||
public void setParent(Integer parent) {
|
||||
this.parentNode = parent;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public String getItem() {
|
||||
if (nodeItem.isEmpty()) {
|
||||
return isDir() ? "CHEST" : "CAULDRON_ITEM";
|
||||
}
|
||||
return nodeItem;
|
||||
}
|
||||
|
||||
public void setItem(String item) {
|
||||
this.nodeItem = item;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getType() {
|
||||
return nodeType.name();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setType(String type) {
|
||||
if(isDir())
|
||||
throw new SecurityException("Node is Directory");
|
||||
this.nodeType = SchematicType.fromDB(type);
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public boolean isDir() {
|
||||
return nodeType == null;
|
||||
}
|
||||
|
||||
public boolean getSchemFormat() {
|
||||
if(isDir())
|
||||
throw new SecurityException("Node is Directory");
|
||||
return nodeFormat;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
if(isDir())
|
||||
throw new SecurityException("Node is Directory");
|
||||
return nodeRank;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getRankUnsafe() {
|
||||
return nodeRank;
|
||||
}
|
||||
|
||||
public void setRank(int rank) {
|
||||
if(isDir())
|
||||
throw new SecurityException("Node is Directory");
|
||||
this.nodeRank = rank;
|
||||
}
|
||||
|
||||
public SchematicType getSchemtype() {
|
||||
if(isDir())
|
||||
throw new SecurityException("Is Directory");
|
||||
return nodeType;
|
||||
}
|
||||
|
||||
public void setSchemtype(SchematicType type) {
|
||||
if(isDir())
|
||||
throw new SecurityException("Is Directory");
|
||||
this.nodeType = type;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public boolean replaceColor() {
|
||||
return replaceColor;
|
||||
}
|
||||
|
||||
public void setReplaceColor(boolean replaceColor) {
|
||||
if(isDir())
|
||||
throw new SecurityException("Is Directory");
|
||||
this.replaceColor = replaceColor;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public boolean allowReplay() {
|
||||
return allowReplay;
|
||||
}
|
||||
|
||||
public void setAllowReplay(boolean allowReplay) {
|
||||
if(isDir())
|
||||
throw new SecurityException("Is Directory");
|
||||
this.allowReplay = allowReplay;
|
||||
updateDB();
|
||||
}
|
||||
|
||||
public SchematicNode getParentNode() {
|
||||
if(parentNode == null) return null;
|
||||
return SchematicNode.getSchematicNode(parentNode);
|
||||
}
|
||||
|
||||
public int getElo(int season) {
|
||||
return SchemElo.getElo(this, season);
|
||||
}
|
||||
|
||||
public boolean accessibleByUser(int user) {
|
||||
return NodeMember.getNodeMember(nodeId, user) != null;
|
||||
}
|
||||
|
||||
public Set<NodeMember> getMembers() {
|
||||
return NodeMember.getNodeMembers(nodeId);
|
||||
}
|
||||
|
||||
public Timestamp getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
private void updateDB() {
|
||||
this.lastUpdate = Timestamp.from(Instant.now());
|
||||
update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeFormat, nodeId);
|
||||
TAB_CACHE.clear();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
delete.update(nodeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof SchematicNode))
|
||||
return false;
|
||||
|
||||
return ((SchematicNode) obj).getId() == nodeId;
|
||||
}
|
||||
|
||||
public String generateBreadcrumbs(SteamwarUser user) {
|
||||
return byIdAndUser(user, nodeId).generateBreadcrumbs();
|
||||
}
|
||||
|
||||
public String generateBreadcrumbs(String split, SteamwarUser user) {
|
||||
return byIdAndUser(user, nodeId).generateBreadcrumbs(split);
|
||||
}
|
||||
|
||||
public String generateBreadcrumbs() {
|
||||
if(brCache == null) {
|
||||
brCache = generateBreadcrumbs("/");
|
||||
}
|
||||
return brCache;
|
||||
}
|
||||
|
||||
public String generateBreadcrumbs(String split) {
|
||||
StringBuilder builder = new StringBuilder(getName());
|
||||
Optional<SchematicNode> currentNode = Optional.of(this);
|
||||
if(currentNode.map(SchematicNode::isDir).orElse(false)) {
|
||||
builder.append(split);
|
||||
}
|
||||
while (currentNode.isPresent()) {
|
||||
currentNode = currentNode.flatMap(schematicNode -> Optional.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode);
|
||||
currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName()));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
|
||||
public static boolean invalidSchemName(String[] layers) {
|
||||
for (String layer : layers) {
|
||||
if (layer.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (layer.contains("/") ||
|
||||
layer.contains("\\") ||
|
||||
layer.contains("<") ||
|
||||
layer.contains(">") ||
|
||||
layer.contains("^") ||
|
||||
layer.contains("°") ||
|
||||
layer.contains("'") ||
|
||||
layer.contains("\"") ||
|
||||
layer.contains(" ")) {
|
||||
return true;
|
||||
}
|
||||
if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<String> getNodeTabcomplete(SteamwarUser user, String s) {
|
||||
boolean sws = s.startsWith("/");
|
||||
if (sws) {
|
||||
s = s.substring(1);
|
||||
}
|
||||
int index = s.lastIndexOf("/");
|
||||
String cacheKey = index == -1 ? "" : s.substring(0, index);
|
||||
if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) {
|
||||
return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey));
|
||||
}
|
||||
List<String> list = new ArrayList<>();
|
||||
if (s.contains("/")) {
|
||||
String preTab = s.substring(0, s.lastIndexOf("/") + 1);
|
||||
SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab);
|
||||
if (pa == null) return Collections.emptyList();
|
||||
List<SchematicNode> nodes = SchematicNode.list(user, pa.getId());
|
||||
String br = pa.generateBreadcrumbs();
|
||||
nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : "")));
|
||||
} else {
|
||||
List<SchematicNode> nodes = SchematicNode.list(user, null);
|
||||
nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : "")));
|
||||
}
|
||||
list.remove("//copy");
|
||||
TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list);
|
||||
return list;
|
||||
}
|
||||
}
|
116
src/de/steamwar/sql/SchematicType.java
Normale Datei
116
src/de/steamwar/sql/SchematicType.java
Normale Datei
@ -0,0 +1,116 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.SqlTypeMapper;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SchematicType {
|
||||
|
||||
public static final SchematicType Normal = new SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON");
|
||||
|
||||
private static final Map<String, SchematicType> fromDB;
|
||||
private static final List<SchematicType> types;
|
||||
|
||||
static {
|
||||
List<SchematicType> tmpTypes = new LinkedList<>();
|
||||
Map<String, SchematicType> tmpFromDB = new HashMap<>();
|
||||
|
||||
tmpTypes.add(Normal);
|
||||
tmpFromDB.put(Normal.name().toLowerCase(), Normal);
|
||||
|
||||
SQLWrapper.impl.loadSchemTypes(tmpTypes, tmpFromDB);
|
||||
|
||||
fromDB = Collections.unmodifiableMap(tmpFromDB);
|
||||
types = Collections.unmodifiableList(tmpTypes);
|
||||
}
|
||||
|
||||
static {
|
||||
new SqlTypeMapper<>(SchematicType.class, "VARCHAR(16)", (rs, identifier) -> {
|
||||
String t = rs.getString(identifier);
|
||||
return t != null ? fromDB.get(t) : null;
|
||||
}, (st, index, value) -> st.setString(index, value.toDB()));
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final String kuerzel;
|
||||
private final Type type;
|
||||
private final SchematicType checkType;
|
||||
private final String material;
|
||||
|
||||
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material){
|
||||
this.name = name;
|
||||
this.kuerzel = kuerzel;
|
||||
this.type = type;
|
||||
this.checkType = checkType;
|
||||
this.material = material;
|
||||
}
|
||||
|
||||
public boolean isAssignable(){
|
||||
return type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null);
|
||||
}
|
||||
|
||||
public SchematicType checkType(){
|
||||
return checkType;
|
||||
}
|
||||
|
||||
public boolean check(){
|
||||
return type == Type.CHECK_TYPE;
|
||||
}
|
||||
|
||||
public boolean fightType(){
|
||||
return type == Type.FIGHT_TYPE;
|
||||
}
|
||||
|
||||
public boolean writeable(){
|
||||
return type == Type.NORMAL;
|
||||
}
|
||||
|
||||
public String name(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getKuerzel() {
|
||||
return kuerzel;
|
||||
}
|
||||
|
||||
public String getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
public String toDB(){
|
||||
return name.toLowerCase();
|
||||
}
|
||||
|
||||
public static SchematicType fromDB(String input){
|
||||
return fromDB.get(input.toLowerCase());
|
||||
}
|
||||
|
||||
public static List<SchematicType> values(){
|
||||
return types;
|
||||
}
|
||||
|
||||
enum Type{
|
||||
NORMAL,
|
||||
CHECK_TYPE,
|
||||
FIGHT_TYPE
|
||||
}
|
||||
}
|
54
src/de/steamwar/sql/Season.java
Normale Datei
54
src/de/steamwar/sql/Season.java
Normale Datei
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.sql;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class Season {
|
||||
private Season() {}
|
||||
|
||||
public static int getSeason() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
int yearIndex = calendar.get(Calendar.MONTH) / 4;
|
||||
return (calendar.get(Calendar.YEAR) * 3 + yearIndex);
|
||||
}
|
||||
|
||||
public static String getSeasonStart() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
return calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) / 4 * 3 + 1) + "-1";
|
||||
}
|
||||
|
||||
public static String convertSeasonToString(int season){
|
||||
if (season == -1) return "";
|
||||
int yearSeason = season % 3;
|
||||
int year = (season - yearSeason) / 3;
|
||||
return String.format("%d-%d", year, yearSeason);
|
||||
}
|
||||
|
||||
public static int convertSeasonToNumber(String season){
|
||||
if (season.isEmpty()) return -1;
|
||||
String[] split = season.split("-");
|
||||
try {
|
||||
return Integer.parseInt(split[0]) * 3 + Integer.parseInt(split[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
171
src/de/steamwar/sql/SteamwarUser.java
Normale Datei
171
src/de/steamwar/sql/SteamwarUser.java
Normale Datei
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.*;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SteamwarUser {
|
||||
|
||||
static {
|
||||
new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString()));
|
||||
new SqlTypeMapper<>(Locale.class, "VARCHAR(32)", (rs, identifier) -> {
|
||||
String l = rs.getString(identifier);
|
||||
return l != null ? Locale.forLanguageTag(l) : null;
|
||||
}, (st, index, value) -> st.setString(index, value.toLanguageTag()));
|
||||
SqlTypeMapper.nameEnumMapper(UserGroup.class);
|
||||
new SqlTypeMapper<>(SteamwarUser.class, null, (rs, identifier) -> { throw new SecurityException("SteamwarUser cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.id));
|
||||
}
|
||||
|
||||
private static final Table<SteamwarUser> table = new Table<>(SteamwarUser.class, "UserData");
|
||||
private static final Statement insert = table.insertFields("UUID", "UserName");
|
||||
private static final SelectStatement<SteamwarUser> byID = table.selectFields("id");
|
||||
private static final SelectStatement<SteamwarUser> byUUID = table.selectFields("UUID");
|
||||
private static final SelectStatement<SteamwarUser> byName = table.selectFields("UserName");
|
||||
private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId");
|
||||
private static final SelectStatement<SteamwarUser> byTeam = table.selectFields("Team");
|
||||
private static final SelectStatement<SteamwarUser> getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
|
||||
private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
|
||||
private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale");
|
||||
private static final Statement updateTeam = table.update(Table.PRIMARY, "Team");
|
||||
private static final Statement updateLeader = table.update(Table.PRIMARY, "Leader");
|
||||
private static final Statement updateDiscord = table.update(Table.PRIMARY, "DiscordId");
|
||||
|
||||
private static final Map<Integer, SteamwarUser> usersById = new HashMap<>();
|
||||
private static final Map<UUID, SteamwarUser> usersByUUID = new HashMap<>();
|
||||
private static final Map<String, SteamwarUser> usersByName = new HashMap<>();
|
||||
private static final Map<Long, SteamwarUser> usersByDiscord = new HashMap<>();
|
||||
public static void clear() {
|
||||
usersById.clear();
|
||||
usersByName.clear();
|
||||
usersByUUID.clear();
|
||||
usersByDiscord.clear();
|
||||
}
|
||||
|
||||
public static void invalidate(int userId) {
|
||||
SteamwarUser user = usersById.remove(userId);
|
||||
if (user == null)
|
||||
return;
|
||||
usersByName.remove(user.getUserName());
|
||||
usersByUUID.remove(user.getUUID());
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Field(keys = {Table.PRIMARY}, autoincrement = true)
|
||||
private final int id;
|
||||
@Field(keys = {"uuid"})
|
||||
private final UUID uuid;
|
||||
@Getter
|
||||
@Field
|
||||
private String userName;
|
||||
@Getter
|
||||
@Field(def = "'Member'")
|
||||
private final UserGroup userGroup;
|
||||
@Getter
|
||||
@Field(def = "0")
|
||||
private int team;
|
||||
@Field(def = "0")
|
||||
private boolean leader;
|
||||
@Field(nullable = true)
|
||||
private Locale locale;
|
||||
@Field(def = "0")
|
||||
private boolean manualLocale;
|
||||
@Field(keys = {"discordId"}, nullable = true)
|
||||
private Long discordId;
|
||||
|
||||
public SteamwarUser(int id, UUID uuid, String userName, UserGroup userGroup, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) {
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.userName = userName;
|
||||
this.userGroup = userGroup;
|
||||
this.team = team;
|
||||
this.leader = leader;
|
||||
this.locale = locale;
|
||||
this.manualLocale = manualLocale;
|
||||
this.discordId = discordId != null && discordId != 0 ? discordId : null;
|
||||
|
||||
usersById.put(id, this);
|
||||
usersByName.put(userName.toLowerCase(), this);
|
||||
usersByUUID.put(uuid, this);
|
||||
if (this.discordId != null) {
|
||||
usersByDiscord.put(discordId, this);
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
if(locale != null)
|
||||
return locale;
|
||||
return Locale.getDefault();
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale, boolean manualLocale) {
|
||||
if (locale == null || (this.manualLocale && !manualLocale))
|
||||
return;
|
||||
|
||||
this.locale = locale;
|
||||
this.manualLocale = manualLocale;
|
||||
updateLocale.update(locale.toLanguageTag(), manualLocale, id);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(String userName){
|
||||
SteamwarUser user = usersByName.get(userName.toLowerCase());
|
||||
if(user != null)
|
||||
return user;
|
||||
return byName.select(userName);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(UUID uuid){
|
||||
SteamwarUser user = usersByUUID.get(uuid);
|
||||
if(user != null)
|
||||
return user;
|
||||
return byUUID.select(uuid);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(int id) {
|
||||
SteamwarUser user = usersById.get(id);
|
||||
if(user != null)
|
||||
return user;
|
||||
return byID.select(id);
|
||||
}
|
||||
|
||||
public static SteamwarUser get(Long discordId) {
|
||||
if(usersByDiscord.containsKey(discordId))
|
||||
return usersByDiscord.get(discordId);
|
||||
return byDiscord.select(discordId);
|
||||
}
|
||||
|
||||
public static void createOrUpdateUsername(UUID uuid, String userName) {
|
||||
insert.update(uuid, userName);
|
||||
}
|
||||
|
||||
public static List<SteamwarUser> getServerTeam() {
|
||||
return getServerTeam.listSelect();
|
||||
}
|
||||
|
||||
public static List<SteamwarUser> getTeam(int teamId) {
|
||||
return byTeam.listSelect(teamId);
|
||||
}
|
||||
}
|
61
src/de/steamwar/sql/Team.java
Normale Datei
61
src/de/steamwar/sql/Team.java
Normale Datei
@ -0,0 +1,61 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class Team {
|
||||
|
||||
private static final Table<Team> table = new Table<>(Team.class);
|
||||
private static final SelectStatement<Team> select = table.select(Table.PRIMARY);
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
@Getter
|
||||
private final int teamId;
|
||||
@Field
|
||||
@Getter
|
||||
private final String teamKuerzel;
|
||||
@Field
|
||||
@Getter
|
||||
private final String teamName;
|
||||
@Field(def = "'8'")
|
||||
@Getter
|
||||
private final String teamColor;
|
||||
|
||||
private static final Team pub = new Team(0, "PUB", "Öffentlich", "8");
|
||||
|
||||
public static Team get(int id) {
|
||||
if(id == 0)
|
||||
return pub;
|
||||
return select.select(id);
|
||||
}
|
||||
|
||||
public List<Integer> getMembers(){
|
||||
return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList());
|
||||
}
|
||||
}
|
54
src/de/steamwar/sql/TeamTeilnahme.java
Normale Datei
54
src/de/steamwar/sql/TeamTeilnahme.java
Normale Datei
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class TeamTeilnahme {
|
||||
|
||||
private static final Table<TeamTeilnahme> table = new Table<>(TeamTeilnahme.class);
|
||||
private static final SelectStatement<TeamTeilnahme> select = table.select(Table.PRIMARY);
|
||||
private static final SelectStatement<TeamTeilnahme> selectTeams = table.selectFields("EventID");
|
||||
private static final SelectStatement<TeamTeilnahme> selectEvents = table.selectFields("TeamID");
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int teamId;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int eventId;
|
||||
|
||||
public static boolean nimmtTeil(int teamID, int eventID){
|
||||
return select.select(teamID, eventID) != null;
|
||||
}
|
||||
|
||||
public static Set<Team> getTeams(int eventID){
|
||||
return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
|
||||
}
|
||||
|
||||
public static Set<Event> getEvents(int teamID){
|
||||
return selectEvents.listSelect(teamID).stream().map(tt -> Event.get(tt.eventId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
|
||||
}
|
||||
}
|
73
src/de/steamwar/sql/UserConfig.java
Normale Datei
73
src/de/steamwar/sql/UserConfig.java
Normale Datei
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.sql;
|
||||
|
||||
import de.steamwar.sql.internal.Field;
|
||||
import de.steamwar.sql.internal.SelectStatement;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.sql.internal.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class UserConfig {
|
||||
|
||||
private static final Table<UserConfig> table = new Table<>(UserConfig.class);
|
||||
private static final SelectStatement<UserConfig> select = table.select(Table.PRIMARY);
|
||||
private static final Statement insert = table.insertAll();
|
||||
private static final Statement delete = table.delete(Table.PRIMARY);
|
||||
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int user;
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final String config;
|
||||
@Field
|
||||
private final String value;
|
||||
|
||||
public static String getConfig(UUID player, String config) {
|
||||
return getConfig(SteamwarUser.get(player).getId(), config);
|
||||
}
|
||||
|
||||
public static String getConfig(int player, String config) {
|
||||
UserConfig value = select.select(player, config);
|
||||
return value != null ? value.value : null;
|
||||
}
|
||||
|
||||
public static void updatePlayerConfig(UUID uuid, String config, String value) {
|
||||
updatePlayerConfig(SteamwarUser.get(uuid).getId(), config, value);
|
||||
}
|
||||
|
||||
public static void updatePlayerConfig(int id, String config, String value) {
|
||||
if (value == null) {
|
||||
removePlayerConfig(id, config);
|
||||
return;
|
||||
}
|
||||
insert.update(id, config, value);
|
||||
}
|
||||
|
||||
public static void removePlayerConfig(UUID uuid, String config) {
|
||||
removePlayerConfig(SteamwarUser.get(uuid).getId(), config);
|
||||
}
|
||||
|
||||
public static void removePlayerConfig(int id, String config) {
|
||||
delete.update(id, config);
|
||||
}
|
||||
}
|
45
src/de/steamwar/sql/UserGroup.java
Normale Datei
45
src/de/steamwar/sql/UserGroup.java
Normale Datei
@ -0,0 +1,45 @@
|
||||
/*
|
||||
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.sql;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum UserGroup {
|
||||
Admin("§4", "§e", true, true, true),
|
||||
Developer("§3", "§f", true, true, true),
|
||||
Moderator("§c", "§f", true, true, true),
|
||||
Supporter("§9", "§f", false, true, true),
|
||||
Builder("§2", "§f", false, true, false),
|
||||
YouTuber("§5", "§f", false, false, false),
|
||||
Member("§7", "§7", false, false, false);
|
||||
|
||||
@Getter
|
||||
private final String colorCode;
|
||||
@Getter
|
||||
private final String chatColorCode;
|
||||
@Getter
|
||||
private final boolean adminGroup;
|
||||
@Getter
|
||||
private final boolean teamGroup;
|
||||
@Getter
|
||||
private final boolean checkSchematics;
|
||||
}
|
34
src/de/steamwar/sql/internal/Field.java
Normale Datei
34
src/de/steamwar/sql/internal/Field.java
Normale Datei
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql.internal;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Field {
|
||||
String[] keys() default {};
|
||||
String def() default "";
|
||||
boolean nullable() default false;
|
||||
boolean autoincrement() default false;
|
||||
}
|
34
src/de/steamwar/sql/internal/SQLConfig.java
Normale Datei
34
src/de/steamwar/sql/internal/SQLConfig.java
Normale Datei
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql.internal;
|
||||
|
||||
import de.steamwar.ImplementationProvider;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public interface SQLConfig {
|
||||
SQLConfig impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLConfigImpl");
|
||||
|
||||
Logger getLogger();
|
||||
|
||||
int maxConnections();
|
||||
|
||||
|
||||
}
|
72
src/de/steamwar/sql/internal/SelectStatement.java
Normale Datei
72
src/de/steamwar/sql/internal/SelectStatement.java
Normale Datei
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql.internal;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SelectStatement<T> extends Statement {
|
||||
private final Table<T> table;
|
||||
|
||||
SelectStatement(Table<T> table, String... kfields) {
|
||||
this(table, "SELECT " + Arrays.stream(table.fields).map(f -> f.identifier).collect(Collectors.joining(", ")) + " FROM " + table.name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
|
||||
}
|
||||
|
||||
public SelectStatement(Table<T> table, String sql) {
|
||||
super(sql);
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public T select(Object... values) {
|
||||
return select(rs -> {
|
||||
if (rs.next())
|
||||
return read(rs);
|
||||
return null;
|
||||
}, values);
|
||||
}
|
||||
|
||||
public List<T> listSelect(Object... values) {
|
||||
return select(rs -> {
|
||||
List<T> result = new ArrayList<>();
|
||||
while (rs.next())
|
||||
result.add(read(rs));
|
||||
|
||||
return result;
|
||||
}, values);
|
||||
}
|
||||
|
||||
private T read(ResultSet rs) throws SQLException {
|
||||
Object[] params = new Object[table.fields.length];
|
||||
for(int i = 0; i < params.length; i++) {
|
||||
params[i] = table.fields[i].read(rs);
|
||||
}
|
||||
|
||||
try {
|
||||
return table.constructor.newInstance(params);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
}
|
110
src/de/steamwar/sql/internal/SqlTypeMapper.java
Normale Datei
110
src/de/steamwar/sql/internal/SqlTypeMapper.java
Normale Datei
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql.internal;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Arrays;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class SqlTypeMapper<T> {
|
||||
private static final Map<Class<?>, SqlTypeMapper<?>> mappers = new IdentityHashMap<>();
|
||||
|
||||
public static <T> SqlTypeMapper<T> getMapper(Class<?> clazz) {
|
||||
return (SqlTypeMapper<T>) mappers.get(clazz);
|
||||
}
|
||||
|
||||
public static <T extends Enum<T>> void ordinalEnumMapper(Class<T> type) {
|
||||
T[] enumConstants = type.getEnumConstants();
|
||||
new SqlTypeMapper<>(
|
||||
type,
|
||||
"INTEGER(" + (int)Math.ceil(enumConstants.length/256.0) + ")",
|
||||
(rs, identifier) -> enumConstants[rs.getInt(identifier)],
|
||||
(st, index, value) -> st.setInt(index, value.ordinal())
|
||||
);
|
||||
}
|
||||
|
||||
public static <T extends Enum<T>> void nameEnumMapper(Class<T> type) {
|
||||
new SqlTypeMapper<>(
|
||||
type,
|
||||
"VARCHAR(" + Arrays.stream(type.getEnumConstants()).map(e -> e.name().length()).max(Integer::compareTo).orElse(0) + ")",
|
||||
(rs, identifier) -> Enum.valueOf(type, rs.getString(identifier)),
|
||||
(st, index, value) -> st.setString(index, value.name())
|
||||
);
|
||||
}
|
||||
|
||||
static {
|
||||
primitiveMapper(boolean.class, Boolean.class, "BOOLEAN", ResultSet::getBoolean, PreparedStatement::setBoolean);
|
||||
primitiveMapper(byte.class, Byte.class, "INTEGER(1)", ResultSet::getByte, PreparedStatement::setByte);
|
||||
primitiveMapper(short.class, Short.class, "INTEGER(2)", ResultSet::getShort, PreparedStatement::setShort);
|
||||
primitiveMapper(int.class, Integer.class, "INTEGER", ResultSet::getInt, PreparedStatement::setInt);
|
||||
primitiveMapper(long.class, Long.class, "INTEGER(8)", ResultSet::getLong, PreparedStatement::setLong);
|
||||
primitiveMapper(float.class, Float.class, "REAL", ResultSet::getFloat, PreparedStatement::setFloat);
|
||||
primitiveMapper(double.class, Double.class, "REAL", ResultSet::getDouble, PreparedStatement::setDouble);
|
||||
new SqlTypeMapper<>(String.class, "TEXT", ResultSet::getString, PreparedStatement::setString);
|
||||
new SqlTypeMapper<>(Timestamp.class, "TIMESTAMP", ResultSet::getTimestamp, PreparedStatement::setTimestamp);
|
||||
new SqlTypeMapper<>(InputStream.class, "BLOB", ResultSet::getBinaryStream, PreparedStatement::setBinaryStream);
|
||||
}
|
||||
|
||||
private static <T> void primitiveMapper(Class<T> primitive, Class<T> wrapped, String sqlType, SQLReader<T> reader, SQLWriter<T> writer) {
|
||||
new SqlTypeMapper<>(primitive, sqlType, reader, writer);
|
||||
new SqlTypeMapper<>(wrapped, sqlType, (rs, identifier) -> {
|
||||
T value = reader.read(rs, identifier);
|
||||
return rs.wasNull() ? null : value;
|
||||
}, writer);
|
||||
}
|
||||
|
||||
private final String sqlType;
|
||||
private final SQLReader<T> reader;
|
||||
private final SQLWriter<T> writer;
|
||||
|
||||
public SqlTypeMapper(Class<T> clazz, String sqlType, SQLReader<T> reader, SQLWriter<T> writer) {
|
||||
this.sqlType = sqlType;
|
||||
this.reader = reader;
|
||||
this.writer = writer;
|
||||
mappers.put(clazz, this);
|
||||
}
|
||||
|
||||
public T read(ResultSet rs, String identifier) throws SQLException {
|
||||
return reader.read(rs, identifier);
|
||||
}
|
||||
|
||||
public void write(PreparedStatement st, int index, Object value) throws SQLException {
|
||||
writer.write(st, index, (T) value);
|
||||
}
|
||||
|
||||
public String sqlType() {
|
||||
return sqlType;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SQLReader<T> {
|
||||
T read(ResultSet rs, String identifier) throws SQLException;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SQLWriter<T> {
|
||||
void write(PreparedStatement st, int index, T value) throws SQLException;
|
||||
}
|
||||
}
|
294
src/de/steamwar/sql/internal/Statement.java
Normale Datei
294
src/de/steamwar/sql/internal/Statement.java
Normale Datei
@ -0,0 +1,294 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Statement implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = SQLConfig.impl.getLogger();
|
||||
|
||||
private static final List<Statement> statements = new ArrayList<>();
|
||||
private static final Deque<Connection> connections = new ArrayDeque<>();
|
||||
private static final int MAX_CONNECTIONS;
|
||||
private static final Supplier<Connection> conProvider;
|
||||
static final Consumer<Table<?>> schemaCreator;
|
||||
static final String ON_DUPLICATE_KEY;
|
||||
static final UnaryOperator<String> upsertWrapper;
|
||||
public static final String NULL_SAFE_EQUALS;
|
||||
|
||||
private static final boolean MYSQL_MODE;
|
||||
private static final boolean PRODUCTION_DATABASE;
|
||||
|
||||
static {
|
||||
File file = new File(System.getProperty("user.home"), "mysql.properties");
|
||||
MYSQL_MODE = file.exists();
|
||||
|
||||
if(MYSQL_MODE) {
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
properties.load(new FileReader(file));
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException("Could not load SQL connection", e);
|
||||
}
|
||||
|
||||
String url = "jdbc:mysql://" + properties.getProperty("host") + ":" + properties.getProperty("port") + "/" + properties.getProperty("database") + "?useServerPrepStmts=true";
|
||||
String user = properties.getProperty("user");
|
||||
String password = properties.getProperty("password");
|
||||
|
||||
PRODUCTION_DATABASE = "core".equals(properties.getProperty("database"));
|
||||
MAX_CONNECTIONS = SQLConfig.impl.maxConnections();
|
||||
conProvider = () -> {
|
||||
try {
|
||||
return DriverManager.getConnection(url, user, password);
|
||||
} catch (SQLException e) {
|
||||
throw new SecurityException("Could not create MySQL connection", e);
|
||||
}
|
||||
};
|
||||
schemaCreator = table -> {};
|
||||
ON_DUPLICATE_KEY = " ON DUPLICATE KEY UPDATE ";
|
||||
upsertWrapper = f -> f + " = VALUES(" + f + ")";
|
||||
NULL_SAFE_EQUALS = " <=> ";
|
||||
} else {
|
||||
Connection connection;
|
||||
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + System.getProperty("user.home") + "/standalone.db");
|
||||
} catch (SQLException | ClassNotFoundException e) {
|
||||
throw new SecurityException("Could not create sqlite connection", e);
|
||||
}
|
||||
|
||||
PRODUCTION_DATABASE = false;
|
||||
MAX_CONNECTIONS = 1;
|
||||
conProvider = () -> connection;
|
||||
schemaCreator = Table::ensureExistanceInSqlite;
|
||||
ON_DUPLICATE_KEY = " ON CONFLICT DO UPDATE SET ";
|
||||
upsertWrapper = f -> f + " = " + f;
|
||||
NULL_SAFE_EQUALS = " IS ";
|
||||
}
|
||||
}
|
||||
|
||||
private static int connectionBudget = MAX_CONNECTIONS;
|
||||
|
||||
public static void closeAll() {
|
||||
synchronized (connections) {
|
||||
while(connectionBudget < MAX_CONNECTIONS) {
|
||||
if(connections.isEmpty())
|
||||
waitOnConnections();
|
||||
else
|
||||
closeConnection(aquireConnection());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean mysqlMode() {
|
||||
return MYSQL_MODE;
|
||||
}
|
||||
|
||||
public static boolean productionDatabase() {
|
||||
return PRODUCTION_DATABASE;
|
||||
}
|
||||
|
||||
private final boolean returnGeneratedKeys;
|
||||
private final String sql;
|
||||
private final Map<Connection, PreparedStatement> cachedStatements = new HashMap<>();
|
||||
|
||||
public Statement(String sql) {
|
||||
this(sql, false);
|
||||
}
|
||||
|
||||
public Statement(String sql, boolean returnGeneratedKeys) {
|
||||
this.sql = sql;
|
||||
this.returnGeneratedKeys = returnGeneratedKeys;
|
||||
synchronized (statements) {
|
||||
statements.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T select(ResultSetUser<T> user, Object... objects) {
|
||||
return withConnection(st -> {
|
||||
ResultSet rs = st.executeQuery();
|
||||
T result = user.use(rs);
|
||||
rs.close();
|
||||
return result;
|
||||
}, objects);
|
||||
}
|
||||
|
||||
public void update(Object... objects) {
|
||||
withConnection(PreparedStatement::executeUpdate, objects);
|
||||
}
|
||||
|
||||
public int insertGetKey(Object... objects) {
|
||||
return withConnection(st -> {
|
||||
st.executeUpdate();
|
||||
ResultSet rs = st.getGeneratedKeys();
|
||||
rs.next();
|
||||
return rs.getInt(1);
|
||||
}, objects);
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
private <T> T withConnection(SQLRunnable<T> runnable, Object... objects) {
|
||||
Connection connection = aquireConnection();
|
||||
T result;
|
||||
|
||||
try {
|
||||
result = tryWithConnection(connection, runnable, objects);
|
||||
} catch (Throwable e) {
|
||||
if(connectionInvalid(connection)) {
|
||||
closeConnection(connection);
|
||||
|
||||
return withConnection(runnable, objects);
|
||||
} else {
|
||||
synchronized (connections) {
|
||||
connections.push(connection);
|
||||
connections.notify();
|
||||
}
|
||||
|
||||
throw new SecurityException("Failing sql statement", e);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (connections) {
|
||||
connections.push(connection);
|
||||
connections.notify();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean connectionInvalid(Connection connection) {
|
||||
try {
|
||||
return connection.isClosed() || !connection.isValid(1);
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.INFO, "Could not check SQL connection status", e); // No database logging possible at this state
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T tryWithConnection(Connection connection, SQLRunnable<T> runnable, Object... objects) throws SQLException {
|
||||
PreparedStatement st = cachedStatements.get(connection);
|
||||
if(st == null) {
|
||||
if(returnGeneratedKeys)
|
||||
st = connection.prepareStatement(sql, java.sql.Statement.RETURN_GENERATED_KEYS);
|
||||
else
|
||||
st = connection.prepareStatement(sql);
|
||||
cachedStatements.put(connection, st);
|
||||
}
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
Object o = objects[i];
|
||||
if(o != null)
|
||||
SqlTypeMapper.getMapper(o.getClass()).write(st, i+1, o);
|
||||
else
|
||||
st.setNull(i+1, Types.NULL);
|
||||
}
|
||||
|
||||
return runnable.run(st);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
cachedStatements.values().forEach(st -> closeStatement(st, false));
|
||||
cachedStatements.clear();
|
||||
synchronized (statements) {
|
||||
statements.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void close(Connection connection) {
|
||||
PreparedStatement st = cachedStatements.remove(connection);
|
||||
if(st != null)
|
||||
closeStatement(st, true);
|
||||
}
|
||||
|
||||
private static Connection aquireConnection() {
|
||||
synchronized (connections) {
|
||||
while(connections.isEmpty() && connectionBudget == 0)
|
||||
waitOnConnections();
|
||||
|
||||
if(!connections.isEmpty()) {
|
||||
return connections.pop();
|
||||
} else {
|
||||
Connection connection = conProvider.get();
|
||||
connectionBudget--;
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void closeConnection(Connection connection) {
|
||||
synchronized (statements) {
|
||||
for (Statement statement : statements) {
|
||||
statement.close(connection);
|
||||
}
|
||||
}
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.INFO, "Could not close connection", e);
|
||||
}
|
||||
|
||||
synchronized (connections) {
|
||||
connectionBudget++;
|
||||
connections.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitOnConnections() {
|
||||
synchronized (connections) {
|
||||
try {
|
||||
connections.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void closeStatement(PreparedStatement st, boolean silent) {
|
||||
try {
|
||||
st.close();
|
||||
} catch (SQLException e) {
|
||||
if(!silent)
|
||||
logger.log(Level.INFO, "Could not close statement", e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ResultSetUser<T> {
|
||||
T use(ResultSet rs) throws SQLException;
|
||||
}
|
||||
|
||||
private interface SQLRunnable<T> {
|
||||
T run(PreparedStatement st) throws SQLException;
|
||||
}
|
||||
}
|
137
src/de/steamwar/sql/internal/Table.java
Normale Datei
137
src/de/steamwar/sql/internal/Table.java
Normale Datei
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.sql.internal;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Table<T> {
|
||||
public static final String PRIMARY = "primary";
|
||||
|
||||
final String name;
|
||||
final TableField<?>[] fields;
|
||||
private final Map<String, TableField<?>> fieldsByIdentifier = new HashMap<>();
|
||||
final Constructor<T> constructor;
|
||||
|
||||
private final Map<String, Table.TableField<?>[]> keys;
|
||||
|
||||
|
||||
public Table(Class<T> clazz) {
|
||||
this(clazz, clazz.getSimpleName());
|
||||
}
|
||||
|
||||
public Table(Class<T> clazz, String name) {
|
||||
this.name = name;
|
||||
this.fields = Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Field.class)).map(TableField::new).toArray(TableField[]::new);
|
||||
try {
|
||||
this.constructor = clazz.getDeclaredConstructor(Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Field.class)).map(java.lang.reflect.Field::getType).toArray(Class[]::new));
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
|
||||
keys = Arrays.stream(fields).flatMap(field -> Arrays.stream(field.field.keys())).distinct().collect(Collectors.toMap(Function.identity(), key -> Arrays.stream(fields).filter(field -> Arrays.asList(field.field.keys()).contains(key)).toArray(TableField[]::new)));
|
||||
|
||||
for (TableField<?> field : fields) {
|
||||
fieldsByIdentifier.put(field.identifier.toLowerCase(), field);
|
||||
}
|
||||
|
||||
Statement.schemaCreator.accept(this);
|
||||
}
|
||||
|
||||
public SelectStatement<T> select(String name) {
|
||||
return selectFields(keyFields(name));
|
||||
}
|
||||
public SelectStatement<T> selectFields(String... kfields) {
|
||||
return new SelectStatement<>(this, kfields);
|
||||
}
|
||||
|
||||
public Statement update(String name, String... fields) {
|
||||
return updateFields(fields, keyFields(name));
|
||||
}
|
||||
|
||||
public Statement updateField(String field, String... kfields) {
|
||||
return updateFields(new String[]{field}, kfields);
|
||||
}
|
||||
|
||||
public Statement updateFields(String[] fields, String... kfields) {
|
||||
return new Statement("UPDATE " + name + " SET " + Arrays.stream(fields).map(f -> f + " = ?").collect(Collectors.joining(", ")) + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
|
||||
}
|
||||
|
||||
public Statement insert(String name) {
|
||||
return insertFields(keyFields(name));
|
||||
}
|
||||
|
||||
public Statement insertAll() {
|
||||
return insertFields(false, Arrays.stream(fields).map(f -> f.identifier).toArray(String[]::new));
|
||||
}
|
||||
|
||||
public Statement insertFields(String... fields) {
|
||||
return insertFields(false, fields);
|
||||
}
|
||||
|
||||
public Statement insertFields(boolean returnGeneratedKeys, String... fields) {
|
||||
List<String> nonKeyFields = Arrays.stream(fields).filter(f -> fieldsByIdentifier.get(f.toLowerCase()).field.keys().length == 0).collect(Collectors.toList());
|
||||
return new Statement("INSERT INTO " + name + " (" + String.join(", ", fields) + ") VALUES (" + Arrays.stream(fields).map(f -> "?").collect(Collectors.joining(", ")) + ")" + (nonKeyFields.isEmpty() ? "" : Statement.ON_DUPLICATE_KEY + nonKeyFields.stream().map(Statement.upsertWrapper).collect(Collectors.joining(", "))), returnGeneratedKeys);
|
||||
}
|
||||
|
||||
public Statement delete(String name) {
|
||||
return deleteFields(keyFields(name));
|
||||
}
|
||||
|
||||
public Statement deleteFields(String... kfields) {
|
||||
return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
|
||||
}
|
||||
|
||||
void ensureExistanceInSqlite() {
|
||||
try (Statement statement = new Statement(
|
||||
"CREATE TABLE IF NOT EXISTS " + name + "(" +
|
||||
Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def())).collect(Collectors.joining(", ")) +
|
||||
keys.entrySet().stream().map(key -> (key.getKey().equals(PRIMARY) ? ", PRIMARY KEY(" : ", UNIQUE (") + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) +
|
||||
")")) {
|
||||
statement.update();
|
||||
}
|
||||
}
|
||||
|
||||
private String[] keyFields(String name) {
|
||||
return Arrays.stream(keys.get(name)).map(f -> f.identifier).toArray(String[]::new);
|
||||
}
|
||||
|
||||
static class TableField<T> {
|
||||
|
||||
final String identifier;
|
||||
|
||||
final SqlTypeMapper<T> mapper;
|
||||
private final Field field;
|
||||
|
||||
private TableField(java.lang.reflect.Field field) {
|
||||
this.identifier = field.getName();
|
||||
this.mapper = SqlTypeMapper.getMapper(field.getType());
|
||||
this.field = field.getAnnotation(Field.class);
|
||||
}
|
||||
|
||||
T read(ResultSet rs) throws SQLException {
|
||||
return mapper.read(rs, identifier);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
build:
|
||||
setup:
|
||||
- "ln -s /home/gitea/lib"
|
||||
- "cp ~/gradle.properties ."
|
||||
- "chmod u+x build.gradle"
|
||||
|
||||
build:
|
||||
- "./gradlew buildProject"
|
||||
- "./gradlew --stop"
|
||||
|
@ -57,4 +57,18 @@ public class ArgumentCommand extends TestSWCommand {
|
||||
public void argument(String sender, String arg) {
|
||||
throw new ExecutionIdentifier("RunArgument with String");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void minLengthArgument(String sender, @Length(min = 3) @StaticValue({"he", "hello"}) String arg) {
|
||||
throw new ExecutionIdentifier("RunLengthArgument with String");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void minAndMaxLengthArgument(String sender, @Length(min = 3, max = 3) @StaticValue({"wo", "world"}) String arg) {
|
||||
throw new ExecutionIdentifier("RunLengthArgument with String");
|
||||
}
|
||||
|
||||
public void arrayLengthArgument(String sender, @ArrayLength(max = 3) @StaticValue({"one", "two", "three"}) String... args) {
|
||||
throw new ExecutionIdentifier("RunArrayLengthArgument with String");
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ public class ArgumentCommandTest {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
try {
|
||||
cmd.execute("test", "", new String[]{"true", "false"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Boolean");
|
||||
}
|
||||
@ -54,6 +55,7 @@ public class ArgumentCommandTest {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
try {
|
||||
cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Float");
|
||||
}
|
||||
@ -64,6 +66,7 @@ public class ArgumentCommandTest {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
try {
|
||||
cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0", "0.0"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Double");
|
||||
}
|
||||
@ -74,6 +77,7 @@ public class ArgumentCommandTest {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
try {
|
||||
cmd.execute("test", "", new String[]{"0"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Integer");
|
||||
}
|
||||
@ -84,26 +88,17 @@ public class ArgumentCommandTest {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
try {
|
||||
cmd.execute("test", "", new String[]{"0", "0"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Long");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testString() {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
try {
|
||||
cmd.execute("test", "", new String[]{"Hello World"});
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTabComplete() {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
List<String> strings = cmd.tabComplete("test", "", new String[]{""});
|
||||
assertTabCompletes(strings, "true", "false");
|
||||
assertTabCompletes(strings, "true", "false", "hello", "wor");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -111,5 +106,23 @@ public class ArgumentCommandTest {
|
||||
ArgumentCommand cmd = new ArgumentCommand();
|
||||
List<String> strings = cmd.tabComplete("test", "", new String[]{"t"});
|
||||
assertTabCompletes(strings, "true", "t");
|
||||
|
||||
strings = cmd.tabComplete("test", "", new String[]{"h"});
|
||||
assertTabCompletes(strings, "h", "hello");
|
||||
|
||||
strings = cmd.tabComplete("test", "", new String[]{"hel"});
|
||||
assertTabCompletes(strings, "hel", "hello");
|
||||
|
||||
strings = cmd.tabComplete("test", "", new String[]{"w"});
|
||||
assertTabCompletes(strings, "w", "wor");
|
||||
|
||||
strings = cmd.tabComplete("test", "", new String[]{"wor"});
|
||||
assertTabCompletes(strings, "wor");
|
||||
|
||||
strings = cmd.tabComplete("test", "", new String[]{"worl"});
|
||||
assertTabCompletes(strings, "wor", "worl");
|
||||
|
||||
strings = cmd.tabComplete("test", "", new String[]{"one", "two", "three", "one"});
|
||||
assertTabCompletes(strings);
|
||||
}
|
||||
}
|
||||
|
@ -39,17 +39,18 @@ public class BetterExceptionCommand extends TestSWCommand {
|
||||
public TestTypeMapper<String> tabCompleteException() {
|
||||
return new TestTypeMapper<String>() {
|
||||
@Override
|
||||
public String map(String sender, String[] previousArguments, String s) {
|
||||
public String map(String sender, PreviousArguments previousArguments, String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(String sender, String value, MessageSender messageSender) {
|
||||
System.out.println("Validate: " + value);
|
||||
throw new SecurityException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
|
||||
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
|
||||
throw new SecurityException();
|
||||
}
|
||||
};
|
||||
|
@ -41,15 +41,14 @@ public class CacheCommand extends TestSWCommand {
|
||||
@Cached
|
||||
@Mapper(value = "int", local = true)
|
||||
public AbstractTypeMapper<String, Integer> typeMapper() {
|
||||
System.out.println("TypeMapper register");
|
||||
return new TestTypeMapper<Integer>() {
|
||||
@Override
|
||||
public Integer map(String sender, String[] previousArguments, String s) {
|
||||
public Integer map(String sender, PreviousArguments previousArguments, String s) {
|
||||
return Integer.parseInt(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
|
||||
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
|
||||
return Arrays.asList(count.getAndIncrement() + "");
|
||||
}
|
||||
};
|
||||
|
@ -36,6 +36,14 @@ public class CacheCommandTest {
|
||||
assertThat(tabCompletions1, is(equalTo(tabCompletions2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCachingWithDifferentMessages() {
|
||||
CacheCommand cmd = new CacheCommand();
|
||||
List<String> tabCompletions1 = cmd.tabComplete("test", "", new String[]{""});
|
||||
List<String> tabCompletions2 = cmd.tabComplete("test", "", new String[]{"0"});
|
||||
assertThat(tabCompletions1, is(equalTo(tabCompletions2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCachingWithDifferentSenders() {
|
||||
CacheCommand cmd = new CacheCommand();
|
||||
|
@ -43,7 +43,7 @@ public class NullMapperCommand extends TestSWCommand {
|
||||
public TestTypeMapper<String> typeMapper() {
|
||||
return new TestTypeMapper<String>() {
|
||||
@Override
|
||||
public String map(String sender, String[] previousArguments, String s) {
|
||||
public String map(String sender, PreviousArguments previousArguments, String s) {
|
||||
if (s.equals("Hello World")) {
|
||||
return null;
|
||||
}
|
||||
@ -51,7 +51,7 @@ public class NullMapperCommand extends TestSWCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
|
||||
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2020 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2022 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
|
||||
@ -19,7 +19,17 @@
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
class CommandNoHelpException extends RuntimeException {
|
||||
|
||||
CommandNoHelpException() {}
|
||||
import de.steamwar.command.dto.ExecutionIdentifier;
|
||||
import de.steamwar.command.dto.TestSWCommand;
|
||||
|
||||
public class NumberValidatorCommand extends TestSWCommand {
|
||||
|
||||
public NumberValidatorCommand() {
|
||||
super("numberValidator");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void test(String sender, @Min(intValue = 0) @Max(intValue = 10) int i) {
|
||||
throw new ExecutionIdentifier("RunNumberValidator with int");
|
||||
}
|
||||
}
|
51
testsrc/de/steamwar/command/NumberValidatorCommandTest.java
Normale Datei
51
testsrc/de/steamwar/command/NumberValidatorCommandTest.java
Normale Datei
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import static de.steamwar.AssertionUtils.assertCMDFramework;
|
||||
|
||||
public class NumberValidatorCommandTest {
|
||||
|
||||
@Test
|
||||
public void testMinValue() {
|
||||
NumberValidatorCommand command = new NumberValidatorCommand();
|
||||
command.execute("sender", "", new String[]{"-1"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxValue() {
|
||||
NumberValidatorCommand command = new NumberValidatorCommand();
|
||||
command.execute("sender", "", new String[]{"11"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidValue() {
|
||||
try {
|
||||
NumberValidatorCommand command = new NumberValidatorCommand();
|
||||
command.execute("sender", "", new String[]{"2"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunNumberValidator with int");
|
||||
}
|
||||
}
|
||||
}
|
70
testsrc/de/steamwar/command/PartOfCommand.java
Normale Datei
70
testsrc/de/steamwar/command/PartOfCommand.java
Normale Datei
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import de.steamwar.command.dto.TestSWCommand;
|
||||
import de.steamwar.command.dto.TestTypeMapper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PartOfCommand {
|
||||
|
||||
public static class ParentCommand extends TestSWCommand {
|
||||
|
||||
public ParentCommand() {
|
||||
super("parent");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void execute(String s, String... args) {
|
||||
throw new ExecutionIdentifier("ParentCommand with String...");
|
||||
}
|
||||
|
||||
@Mapper("test")
|
||||
public TestTypeMapper<Integer> typeMapper() {
|
||||
return new TestTypeMapper<Integer>() {
|
||||
@Override
|
||||
public Integer map(String sender, PreviousArguments previousArguments, String s) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
|
||||
return Arrays.asList(s);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@AbstractSWCommand.PartOf(ParentCommand.class)
|
||||
public static class SubCommand extends TestSWCommand {
|
||||
|
||||
public SubCommand() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Register
|
||||
public void execute(String s, @Mapper("test") int i) {
|
||||
throw new ExecutionIdentifier("SubCommand with int " + i);
|
||||
}
|
||||
}
|
||||
}
|
42
testsrc/de/steamwar/command/PartOfCommandTest.java
Normale Datei
42
testsrc/de/steamwar/command/PartOfCommandTest.java
Normale Datei
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import static de.steamwar.AssertionUtils.assertCMDFramework;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class PartOfCommandTest {
|
||||
|
||||
@Test
|
||||
public void testMerging() {
|
||||
PartOfCommand.ParentCommand command = new PartOfCommand.ParentCommand();
|
||||
new PartOfCommand.SubCommand();
|
||||
try {
|
||||
command.execute("test", "", new String[]{"0"});
|
||||
assertThat(true, is(false));
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "SubCommand with int -1");
|
||||
}
|
||||
}
|
||||
}
|
54
testsrc/de/steamwar/command/PreviousArgumentCommand.java
Normale Datei
54
testsrc/de/steamwar/command/PreviousArgumentCommand.java
Normale Datei
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import de.steamwar.command.dto.TestSWCommand;
|
||||
import de.steamwar.command.dto.TestTypeMapper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class PreviousArgumentCommand extends TestSWCommand {
|
||||
|
||||
public PreviousArgumentCommand() {
|
||||
super("previous");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void genericCommand(String s, int i, String l) {
|
||||
throw new ExecutionIdentifier(l);
|
||||
}
|
||||
|
||||
@ClassMapper(value = String.class, local = true)
|
||||
public TestTypeMapper<String> typeMapper() {
|
||||
return new TestTypeMapper<String>() {
|
||||
@Override
|
||||
public String map(String sender, PreviousArguments previousArguments, String s) {
|
||||
return "RunTypeMapper_" + previousArguments.getMappedArg(0) + "_" + previousArguments.getMappedArg(0).getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
|
||||
return Arrays.asList(previousArguments.getMappedArg(0) + "");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
56
testsrc/de/steamwar/command/PreviousArgumentCommandTest.java
Normale Datei
56
testsrc/de/steamwar/command/PreviousArgumentCommandTest.java
Normale Datei
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static de.steamwar.AssertionUtils.assertCMDFramework;
|
||||
import static de.steamwar.AssertionUtils.assertTabCompletes;
|
||||
|
||||
public class PreviousArgumentCommandTest {
|
||||
|
||||
@Test
|
||||
public void testPrevArg1() {
|
||||
PreviousArgumentCommand command = new PreviousArgumentCommand();
|
||||
List<String> strings = command.tabComplete("", "", new String[]{"1", ""});
|
||||
assertTabCompletes(strings, "1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrevArg2() {
|
||||
PreviousArgumentCommand command = new PreviousArgumentCommand();
|
||||
List<String> strings = command.tabComplete("", "", new String[]{"2", ""});
|
||||
assertTabCompletes(strings, "2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrevArgExecute() {
|
||||
PreviousArgumentCommand command = new PreviousArgumentCommand();
|
||||
try {
|
||||
command.execute("", "", new String[]{"2", "2"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunTypeMapper_2_Integer");
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@
|
||||
|
||||
package de.steamwar.command;
|
||||
|
||||
import de.steamwar.command.AbstractSWCommand.Register;
|
||||
import de.steamwar.command.dto.ExecutionIdentifier;
|
||||
import de.steamwar.command.dto.TestSWCommand;
|
||||
|
||||
@ -29,7 +28,7 @@ public class SimpleCommand extends TestSWCommand {
|
||||
super("simple");
|
||||
}
|
||||
|
||||
@Register(value = "a", help = true)
|
||||
@Register(value = "a", noTabComplete = true)
|
||||
public void test(String s, String... varargs) {
|
||||
throw new ExecutionIdentifier("RunSimple with Varargs");
|
||||
}
|
||||
|
@ -41,11 +41,13 @@ public class StaticValueCommandTest {
|
||||
StaticValueCommand cmd = new StaticValueCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"hello"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with hello");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"world"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with world");
|
||||
}
|
||||
@ -56,16 +58,19 @@ public class StaticValueCommandTest {
|
||||
StaticValueCommand cmd = new StaticValueCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-a"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-b"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-c"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
|
||||
}
|
||||
@ -76,16 +81,19 @@ public class StaticValueCommandTest {
|
||||
StaticValueCommand cmd = new StaticValueCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-d"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-e"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-f"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
|
||||
}
|
||||
@ -96,16 +104,19 @@ public class StaticValueCommandTest {
|
||||
StaticValueCommand cmd = new StaticValueCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-g"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 0");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-h"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 1");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-i"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 2");
|
||||
}
|
||||
@ -116,16 +127,19 @@ public class StaticValueCommandTest {
|
||||
StaticValueCommand cmd = new StaticValueCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-j"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 0");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-k"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 1");
|
||||
}
|
||||
try {
|
||||
cmd.execute("", "", new String[] {"-l"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 2");
|
||||
}
|
||||
|
50
testsrc/de/steamwar/command/SubCMDSortingCommand.java
Normale Datei
50
testsrc/de/steamwar/command/SubCMDSortingCommand.java
Normale Datei
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import de.steamwar.command.dto.TestSWCommand;
|
||||
|
||||
public class SubCMDSortingCommand extends TestSWCommand {
|
||||
|
||||
public SubCMDSortingCommand() {
|
||||
super("subcmdsorting");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void test(String s) {
|
||||
throw new ExecutionIdentifier("Command with 0 parameters");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void test(String s, String args) {
|
||||
throw new ExecutionIdentifier("Command with 1 parameter");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void test(String s, String i1, String i2, String i3, String... args) {
|
||||
throw new ExecutionIdentifier("Command with 3+n parameters");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void test(String s, String... args) {
|
||||
throw new ExecutionIdentifier("Command with n parameters");
|
||||
}
|
||||
}
|
83
testsrc/de/steamwar/command/SubCMDSortingCommandTest.java
Normale Datei
83
testsrc/de/steamwar/command/SubCMDSortingCommandTest.java
Normale Datei
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2022 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.dto.ExecutionIdentifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import static de.steamwar.AssertionUtils.assertCMDFramework;
|
||||
|
||||
public class SubCMDSortingCommandTest {
|
||||
|
||||
@Test
|
||||
public void testNoArgs() {
|
||||
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[]{});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 0 parameters");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneArgs() {
|
||||
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[]{"Hello"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 1 parameter");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneArgsVarArg() {
|
||||
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[]{"Hello", "World"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "Command with n parameters");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreeArgsVarArg() {
|
||||
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[]{"Hello", "World", "YoyoNow", "Hugo"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 3+n parameters");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThreeArgsVarArg2() {
|
||||
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
|
||||
try {
|
||||
cmd.execute("", "", new String[]{"Hello", "World", "YoyoNow"});
|
||||
assert false;
|
||||
} catch (Exception e) {
|
||||
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 3+n parameters");
|
||||
}
|
||||
}
|
||||
}
|
@ -51,9 +51,11 @@ public class TestSWCommand extends AbstractSWCommand<String> {
|
||||
|
||||
@Override
|
||||
protected void commandSystemError(String sender, CommandFrameworkException e) {
|
||||
System.out.println("CommandSystemError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void commandSystemWarning(Supplier<String> message) {
|
||||
System.out.println("CommandSystemWarning: " + message.get());
|
||||
}
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren