From 860d0827703f634c8dfcb1c43ec93b544c37d25b Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 26 Sep 2022 15:58:06 +0200 Subject: [PATCH 01/84] Simplify LinkageProcessor --- src/de/steamwar/linkage/LinkageProcessor.java | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/de/steamwar/linkage/LinkageProcessor.java b/src/de/steamwar/linkage/LinkageProcessor.java index 5e34d44..70253df 100644 --- a/src/de/steamwar/linkage/LinkageProcessor.java +++ b/src/de/steamwar/linkage/LinkageProcessor.java @@ -28,7 +28,10 @@ import lombok.SneakyThrows; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; -import javax.lang.model.element.*; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; @@ -116,25 +119,23 @@ public class LinkageProcessor extends AbstractProcessor { BuildPlan buildPlan = new BuildPlan(packageName, className); - Set elements = roundEnv.getElementsAnnotatedWith(Linked.class); + Set elements = roundEnv.getElementsAnnotatedWith(Linked.class).stream() + .filter(element -> element.getKind() == ElementKind.CLASS) + .map(TypeElement.class::cast) + .collect(Collectors.toSet()); + Map neededFields = new HashMap<>(); Set fieldInjections = new HashSet<>(); - for (Element element : elements) { - if (element.getKind() != ElementKind.CLASS) { - continue; - } - TypeElement typeElement = (TypeElement) element; - Linked linked = element.getAnnotation(Linked.class); - if (linked == null) { - continue; - } + for (TypeElement typeElement : elements) { if (getLinkagesOfType(typeElement).size() > 1) { neededFields.put(typeElement.getQualifiedName().toString(), typeElement); } - List variableElements = typeElement.getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.FIELD).map(VariableElement.class::cast).filter(e -> { - return e.getAnnotation(LinkedInstance.class) != null; - }).collect(Collectors.toList()); + List variableElements = typeElement.getEnclosedElements().stream() + .filter(e -> e.getKind() == ElementKind.FIELD) + .map(VariableElement.class::cast) + .filter(e -> e.getAnnotation(LinkedInstance.class) != null) + .collect(Collectors.toList()); if (variableElements.isEmpty()) { continue; } @@ -177,12 +178,7 @@ public class LinkageProcessor extends AbstractProcessor { fieldInjections.forEach(Runnable::run); Map methods = new HashMap<>(); - for (Element element : elements) { - if (element.getKind() != ElementKind.CLASS) { - continue; - } - TypeElement typeElement = (TypeElement) element; - + for (TypeElement typeElement : elements) { System.out.println("Found element: " + typeElement.getQualifiedName().toString()); Map> linkages = getLinkagesOfType(typeElement); if (linkages.isEmpty()) { @@ -211,11 +207,11 @@ public class LinkageProcessor extends AbstractProcessor { } private String getElement(TypeElement typeElement, Map neededFields) { + String s = typeElement.getSimpleName().toString(); if (neededFields.containsKey(typeElement.getQualifiedName().toString())) { - String s = typeElement.getSimpleName().toString(); return s.substring(0, 1).toLowerCase() + s.substring(1); } - return "new " + typeElement.getSimpleName().toString() + "()"; + return "new " + s + "()"; } private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer stringConsumer, Runnable inner) { From 30299be974f9bc31097987020293fc432c661ae6 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 26 Sep 2022 16:40:42 +0200 Subject: [PATCH 02/84] Optimize some more stuff --- src/de/steamwar/linkage/LinkageProcessor.java | 99 +++++++++---------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/src/de/steamwar/linkage/LinkageProcessor.java b/src/de/steamwar/linkage/LinkageProcessor.java index 70253df..7965140 100644 --- a/src/de/steamwar/linkage/LinkageProcessor.java +++ b/src/de/steamwar/linkage/LinkageProcessor.java @@ -23,6 +23,7 @@ import de.steamwar.linkage.plan.BuildPlan; import de.steamwar.linkage.plan.FieldBuilder; import de.steamwar.linkage.plan.MethodBuilder; import de.steamwar.linkage.types.Plain_GENERIC; +import lombok.Cleanup; import lombok.Getter; import lombok.SneakyThrows; @@ -42,6 +43,7 @@ import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; @SupportedAnnotationTypes("de.steamwar.linkage.Linked") public class LinkageProcessor extends AbstractProcessor { @@ -52,7 +54,6 @@ public class LinkageProcessor extends AbstractProcessor { private static String pluginMain; private String name; - private String packageName; private String className; private Messager messager; @@ -72,7 +73,6 @@ public class LinkageProcessor extends AbstractProcessor { messager = processingEnv.getMessager(); - packageName = "de.steamwar." + name + ".linkage"; className = "LinkageUtils"; mainClass(); } @@ -90,22 +90,15 @@ public class LinkageProcessor extends AbstractProcessor { return; } context = pluginYMLFile.get().getName().equals("bungee.yml") ? Context.BUNGEE : Context.SPIGOT; - Optional mainName = getMainName(pluginYMLFile.get()); - if (!mainName.isPresent()) { + @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYMLFile.get()))); + Optional mainName = reader.lines() + .filter(line -> line.startsWith("main:")) + .map(line -> line.substring(line.indexOf(':') + 1).trim()) + .findFirst(); + if (mainName.isPresent()) { + pluginMain = mainName.get(); + } else { messager.printMessage(Diagnostic.Kind.ERROR, "Could not find main class in plugin.yml or bungee.yml"); - return; - } - pluginMain = mainName.get(); - } - - @SneakyThrows - private Optional getMainName(File pluginYML) { - if (!pluginYML.exists()) return Optional.empty(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYML)))) { - return reader.lines() - .filter(line -> line.startsWith("main:")) - .map(line -> line.substring(line.indexOf(':') + 1).trim()) - .findFirst(); } } @@ -116,14 +109,17 @@ public class LinkageProcessor extends AbstractProcessor { processed = true; Writer writer = processingEnv.getFiler().createSourceFile("de.steamwar." + name + ".linkage.LinkageUtils").openWriter(); - - BuildPlan buildPlan = new BuildPlan(packageName, className); + BuildPlan buildPlan = new BuildPlan("de.steamwar." + name + ".linkage", className); Set elements = roundEnv.getElementsAnnotatedWith(Linked.class).stream() .filter(element -> element.getKind() == ElementKind.CLASS) .map(TypeElement.class::cast) + .peek(typeElement -> System.out.println("Found element: " + typeElement.getQualifiedName().toString())) .collect(Collectors.toSet()); + Map, List> groupedByChecks = elements.stream() + .collect(Collectors.groupingBy(element -> checks(element, buildPlan))); + Map neededFields = new HashMap<>(); Set fieldInjections = new HashSet<>(); for (TypeElement typeElement : elements) { @@ -136,9 +132,7 @@ public class LinkageProcessor extends AbstractProcessor { .map(VariableElement.class::cast) .filter(e -> e.getAnnotation(LinkedInstance.class) != null) .collect(Collectors.toList()); - if (variableElements.isEmpty()) { - continue; - } + if (variableElements.isEmpty()) continue; for (VariableElement variableElement : variableElements) { if (!variableElement.getModifiers().contains(Modifier.PUBLIC)) { @@ -178,25 +172,33 @@ public class LinkageProcessor extends AbstractProcessor { fieldInjections.forEach(Runnable::run); Map methods = new HashMap<>(); - for (TypeElement typeElement : elements) { - System.out.println("Found element: " + typeElement.getQualifiedName().toString()); - Map> linkages = getLinkagesOfType(typeElement); - if (linkages.isEmpty()) { - continue; + for (Map.Entry, List> entry : groupedByChecks.entrySet()) { + Map>> groupedByMethod = new HashMap<>(); + for (TypeElement typeElement : entry.getValue()) { + for (Map.Entry> linkages : getLinkagesOfType(typeElement).entrySet()) { + Map> internalGroup = groupedByMethod.computeIfAbsent(linkages.getKey(), s -> new HashMap<>()); + for (LinkageType linkageType : linkages.getValue()) { + List list = internalGroup.computeIfAbsent(linkageType, s -> new ArrayList<>()); + list.add(typeElement); + } + } } - for (Map.Entry> entry : linkages.entrySet()) { - MethodBuilder method = methods.computeIfAbsent(entry.getKey(), s -> { + for (Map.Entry>> group : groupedByMethod.entrySet()) { + MethodBuilder method = methods.computeIfAbsent(group.getKey(), s -> { MethodBuilder methodBuilder = new MethodBuilder(s, "void"); buildPlan.addMethod(methodBuilder); return methodBuilder; }); - for (LinkageType type : entry.getValue()) { - specialElements(typeElement, buildPlan, method::addLine, () -> { + + if (!entry.getKey().isEmpty()) method.addLine("if (" + String.join(" && ", entry.getKey()) + ") {"); + for (Map.Entry> toGenerate : group.getValue().entrySet()) { + toGenerate.getValue().forEach(typeElement -> { buildPlan.addImport(typeElement.getQualifiedName().toString()); - type.generateCode(buildPlan, method, getElement(typeElement, neededFields), typeElement); + toGenerate.getKey().generateCode(buildPlan, method, getElement(typeElement, neededFields), typeElement); }); } + if (!entry.getKey().isEmpty()) method.addLine("}"); } } @@ -214,14 +216,14 @@ public class LinkageProcessor extends AbstractProcessor { return "new " + s + "()"; } - private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer stringConsumer, Runnable inner) { + private Set checks(TypeElement typeElement, BuildPlan buildPlan) { + Set checks = new HashSet<>(); MinVersion minVersion = typeElement.getAnnotation(MinVersion.class); MaxVersion maxVersion = typeElement.getAnnotation(MaxVersion.class); EventMode eventMode = typeElement.getAnnotation(EventMode.class); PluginCheck[] pluginChecks = typeElement.getAnnotationsByType(PluginCheck.class); if (context == Context.SPIGOT) { errorOnNonNull(typeElement, eventMode); - List checks = new ArrayList<>(); if (minVersion != null) { buildPlan.addImport("de.steamwar.core.Core"); checks.add("Core.getVersion() >= " + minVersion.value()); @@ -236,12 +238,8 @@ public class LinkageProcessor extends AbstractProcessor { return "Bukkit.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null"; }).forEach(checks::add); } - if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {"); - inner.run(); - if (!checks.isEmpty()) stringConsumer.accept("}"); } else { errorOnNonNull(typeElement, minVersion, maxVersion); - List checks = new ArrayList<>(); if (eventMode != null) { buildPlan.addImport("de.steamwar.bungeecore.BungeeCore"); checks.add(eventMode.value().getPrefix() + "BungeeCore.EVENT_MODE"); @@ -252,10 +250,15 @@ public class LinkageProcessor extends AbstractProcessor { return "BungeeCord.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null"; }).forEach(checks::add); } - if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {"); - inner.run(); - if (!checks.isEmpty()) stringConsumer.accept("}"); } + return checks; + } + + private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer stringConsumer, Runnable inner) { + Set checks = checks(typeElement, buildPlan); + if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {"); + inner.run(); + if (!checks.isEmpty()) stringConsumer.accept("}"); } private void errorOnNonNull(TypeElement typeElement, Annotation... annotations) { @@ -270,16 +273,10 @@ public class LinkageProcessor extends AbstractProcessor { private Map> getLinkagesOfType(TypeElement typeElement) { Map> linkages = new HashMap<>(); - LinkageType superClassType = resolveSingle(typeElement.getSuperclass()); - if (superClassType != null) { - linkages.computeIfAbsent(superClassType.method(), s -> new ArrayList<>()).add(superClassType); - } - for (TypeMirror typeMirror : typeElement.getInterfaces()) { - LinkageType interfaceType = resolveSingle(typeMirror); - if (interfaceType != null) { - linkages.computeIfAbsent(interfaceType.method(), s -> new ArrayList<>()).add(interfaceType); - } - } + Stream.concat(Stream.of(typeElement.getSuperclass()), typeElement.getInterfaces().stream()) + .map(this::resolveSingle) + .filter(Objects::nonNull) + .forEach(linkageType -> linkages.computeIfAbsent(linkageType.method(), s -> new ArrayList<>()).add(linkageType)); if (linkages.size() == 1 && linkages.containsKey("unlink")) { linkages.put(plain_GENERIC.method(), Collections.singletonList(plain_GENERIC)); } From 4b8310d4f855d90de26ab4630e9e26e371557b31 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 18 Dec 2022 17:14:01 +0100 Subject: [PATCH 03/84] Add ApplicableTypes for better type safety Add AbstractSWCommand.Min Add AbstractSWCommand.Max --- .../steamwar/command/AbstractSWCommand.java | 107 ++++++++++------- .../steamwar/command/AbstractValidator.java | 2 + ...elpException.java => ApplicableTypes.java} | 14 ++- src/de/steamwar/command/CommandPart.java | 40 +++---- src/de/steamwar/command/SWCommandUtils.java | 38 ------ src/de/steamwar/command/SubCommand.java | 112 +++++++++++++++++- .../command/BetterExceptionCommand.java | 1 + .../command/NumberValidatorCommand.java | 35 ++++++ .../command/NumberValidatorCommandTest.java | 51 ++++++++ .../de/steamwar/command/SimpleCommand.java | 3 +- .../steamwar/command/dto/TestSWCommand.java | 2 + 11 files changed, 294 insertions(+), 111 deletions(-) rename src/de/steamwar/command/{CommandNoHelpException.java => ApplicableTypes.java} (69%) create mode 100644 testsrc/de/steamwar/command/NumberValidatorCommand.java create mode 100644 testsrc/de/steamwar/command/NumberValidatorCommandTest.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 7e76d62..9ff2e28 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -35,7 +35,6 @@ public abstract class AbstractSWCommand { private boolean initialized = false; protected final List> commandList = new ArrayList<>(); - protected final List> commandHelpList = new ArrayList<>(); private final Map> localTypeMapper = new HashMap<>(); private final Map> localValidators = new HashMap<>(); @@ -109,15 +108,8 @@ public abstract class AbstractSWCommand { List errors = new ArrayList<>(); try { if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, finalArgs))) { - if (!errors.isEmpty()) { - errors.forEach(Runnable::run); - return; - } - commandHelpList.stream().anyMatch(s -> s.invoke((ignore) -> { - }, sender, alias, finalArgs)); + errors.forEach(Runnable::run); } - } catch (CommandNoHelpException e) { - // Ignored } catch (CommandFrameworkException e) { commandSystemError(sender, e); throw e; @@ -139,12 +131,8 @@ public abstract class AbstractSWCommand { .collect(Collectors.toList()); } - private void initialize() { + private synchronized void initialize() { if (initialized) return; - createMapping(); - } - - private synchronized void createMapping() { List methods = methods(); for (Method method : methods) { Cached cached = method.getAnnotation(Cached.class); @@ -181,32 +169,13 @@ public abstract class AbstractSWCommand { } 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; 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(); } + checkAnnotationApplicability(parameter, clazz); Mapper mapper = parameter.getAnnotation(Mapper.class); if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) { continue; @@ -217,7 +186,7 @@ public abstract class AbstractSWCommand { 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())); }); } @@ -229,18 +198,28 @@ public abstract class AbstractSWCommand { 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); - } - }); initialized = true; } + private void checkAnnotationApplicability(Parameter parameter, Class clazz) { + Annotation[] annotations = parameter.getAnnotations(); + for (Annotation annotation : annotations) { + ApplicableTypes applicableTypes = annotation.annotationType().getAnnotation(ApplicableTypes.class); + if (applicableTypes == null) continue; + Class[] types = applicableTypes.value(); + boolean applicable = false; + for (Class type : types) { + if (type.isAssignableFrom(clazz)) { + applicable = true; + break; + } + } + if (!applicable) { + commandSystemWarning(() -> "The parameter '" + parameter.toString() + "' is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'"); + } + } + } + private void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { T[] anno = SWCommandUtils.getAnnotation(method, annotation); if (anno == null || anno.length == 0) return; @@ -300,12 +279,22 @@ public abstract class AbstractSWCommand { return methods; } + // --- Annotation for the command --- + + /** + * Annotation for registering a method as a command + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) protected @interface Register { + + /** + * Identifier of subcommand + */ String[] value() default {}; + @Deprecated boolean help() default false; String[] description() default {}; @@ -359,8 +348,11 @@ public abstract class AbstractSWCommand { boolean local() default false; } + // --- Implicit TypeMapper --- + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) + @ApplicableTypes({String.class, int.class, Integer.class, boolean.class, Boolean.class}) protected @interface StaticValue { String[] value(); @@ -391,6 +383,8 @@ public abstract class AbstractSWCommand { boolean onlyUINIG() default false; } + // --- Implicit Validator --- + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) protected @interface ErrorMessage { @@ -415,6 +409,31 @@ public abstract class AbstractSWCommand { */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) + @ApplicableTypes({String.class}) protected @interface Quotable { } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + @ApplicableTypes({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + 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; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + @ApplicableTypes({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + 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; + } } diff --git a/src/de/steamwar/command/AbstractValidator.java b/src/de/steamwar/command/AbstractValidator.java index aab4876..7dc0b72 100644 --- a/src/de/steamwar/command/AbstractValidator.java +++ b/src/de/steamwar/command/AbstractValidator.java @@ -38,10 +38,12 @@ public interface AbstractValidator { */ boolean validate(K sender, T value, MessageSender messageSender); + @Deprecated default Validator validate(C value, MessageSender messageSender) { return new Validator<>(value, messageSender); } + @Deprecated @RequiredArgsConstructor class Validator { private final C value; diff --git a/src/de/steamwar/command/CommandNoHelpException.java b/src/de/steamwar/command/ApplicableTypes.java similarity index 69% rename from src/de/steamwar/command/CommandNoHelpException.java rename to src/de/steamwar/command/ApplicableTypes.java index e3d476a..2385b43 100644 --- a/src/de/steamwar/command/CommandNoHelpException.java +++ b/src/de/steamwar/command/ApplicableTypes.java @@ -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,13 @@ package de.steamwar.command; -class CommandNoHelpException extends RuntimeException { - - CommandNoHelpException() {} +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +@interface ApplicableTypes { + Class[] value(); } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index a81c646..bb39119 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -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; @@ -41,7 +42,7 @@ class CommandPart { private AbstractSWCommand command; private AbstractTypeMapper typeMapper; - private AbstractValidator validator; + private List> validators = new ArrayList<>(); private Class varArgType; private String optional; @@ -53,19 +54,12 @@ class CommandPart { @Setter private boolean onlyUseIfNoneIsGiven = false; - @Setter - private boolean allowNullValues = false; - - @Setter - private boolean quotable = false; - private Parameter parameter; private int parameterIndex; - public CommandPart(AbstractSWCommand command, AbstractTypeMapper typeMapper, AbstractValidator validator, Class varArgType, String optional, Parameter parameter, int parameterIndex) { + public CommandPart(AbstractSWCommand command, AbstractTypeMapper 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; @@ -74,6 +68,11 @@ class CommandPart { validatePart(); } + void addValidator(AbstractValidator validator) { + if (validator == null) return; + validators.add(validator); + } + public void setNext(CommandPart next) { if (varArgType != null) { throw new IllegalArgumentException("There can't be a next part if this is a vararg part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex); @@ -97,10 +96,12 @@ class CommandPart { } 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 validator : validators) { + if (!validator.validate(sender, array, (s, objects) -> { + errors.accept(() -> command.sendMessage(sender, s, objects)); + })) { + throw new CommandParseException(); + } } current.add(array); return; @@ -136,7 +137,8 @@ class CommandPart { public void generateTabComplete(List current, T sender, String[] args, int startIndex) { if (varArgType != null) { for (int i = startIndex; i < args.length - 1; i++) { - CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, i); + CheckArgumentResult validArgument = checkArgument((ignore) -> { + }, sender, args, i); if (!validArgument.success) { return; } @@ -149,7 +151,8 @@ class CommandPart { } if (args.length - 1 > startIndex) { - CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, startIndex); + CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> { + }, sender, args, startIndex); if (checkArgumentResult.success && next != null) { next.generateTabComplete(current, sender, args, startIndex + 1); return; @@ -186,10 +189,7 @@ class CommandPart { } catch (Exception e) { return new CheckArgumentResult(false, null); } - if (value instanceof String && !quotable && ((String) value).contains(" ")) { - return new CheckArgumentResult(false, null); - } - if (validator != null && errors != null) { + for (AbstractValidator validator : validators) { try { if (!validator.validate(sender, value, (s, objects) -> { errors.accept(() -> { @@ -202,6 +202,6 @@ class CommandPart { 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(true, value); } } diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 871439a..203bdd8 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -82,44 +82,6 @@ public class SWCommandUtils { MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper); } - static CommandPart generateCommandPart(AbstractSWCommand command, boolean help, String[] subCommand, Parameter[] parameters, Map> localTypeMapper, Map> localValidator) { - CommandPart first = null; - CommandPart 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 typeMapper = getTypeMapper(parameter, localTypeMapper); - AbstractValidator validator = (AbstractValidator) 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); - AbstractSWCommand.Quotable quotable = parameter.getAnnotation(AbstractSWCommand.Quotable.class); - - CommandPart commandPart = new CommandPart<>(command, typeMapper, validator, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i); - commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG()); - commandPart.setAllowNullValues(allowNull != null); - commandPart.setQuotable(quotable != null); - if (current != null) { - current.setNext(commandPart); - } - current = commandPart; - if (first == null) { - first = current; - } - } - return first; - } - public static AbstractTypeMapper getTypeMapper(Parameter parameter, Map> localTypeMapper) { Class clazz = parameter.getType(); if (parameter.isVarArgs()) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 9eb6e4f..bf11674 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -43,7 +43,7 @@ public class SubCommand { private CommandPart commandPart; - SubCommand(AbstractSWCommand abstractSWCommand, Method method, String[] subCommand, Map> localTypeMapper, Map> localValidator, boolean help, String[] description, boolean noTabComplete) { + SubCommand(AbstractSWCommand abstractSWCommand, Method method, String[] subCommand, Map> localTypeMapper, Map> localValidator, String[] description, boolean noTabComplete) { this.abstractSWCommand = abstractSWCommand; this.method = method; try { @@ -60,7 +60,7 @@ public class SubCommand { validator = (AbstractValidator) 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); @@ -97,7 +97,7 @@ public class SubCommand { 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; @@ -124,4 +124,110 @@ public class SubCommand { commandPart.generateTabComplete(list, sender, args, 0); return list; } + + private static CommandPart generateCommandPart(AbstractSWCommand command, String[] subCommand, Parameter[] parameters, Map> localTypeMapper, Map> localValidator) { + CommandPart first = null; + CommandPart 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 typeMapper = SWCommandUtils.getTypeMapper(parameter, localTypeMapper); + AbstractValidator validator = (AbstractValidator) SWCommandUtils.getValidator(parameter, localValidator); + Class varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null; + AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class); + AbstractSWCommand.Min min = parameter.getAnnotation(AbstractSWCommand.Min.class); + AbstractSWCommand.Max max = parameter.getAnnotation(AbstractSWCommand.Max.class); + + CommandPart commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i); + commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG()); + if (parameter.getAnnotation(AbstractSWCommand.Quotable.class) == null) { + commandPart.addValidator((AbstractValidator) STRING_SPACE_FILTER); + } + commandPart.addValidator(validator); + if (min != null) { + commandPart.addValidator((AbstractValidator) createMinValidator(varArgType != null ? varArgType : parameter.getType(), min)); + } + if (max != null) { + commandPart.addValidator((AbstractValidator) createMaxValidator(varArgType != null ? varArgType : parameter.getType(), max)); + } + if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) { + commandPart.addValidator((AbstractValidator) NULL_FILTER); + } + if (current != null) { + current.setNext(commandPart); + } + current = commandPart; + if (first == null) { + first = current; + } + } + return first; + } + + private static final AbstractValidator NULL_FILTER = (sender, value, messageSender) -> value != null; + + private static final AbstractValidator STRING_SPACE_FILTER = (sender, value, messageSender) -> { + if (!(value instanceof String)) return true; + String s = (String) value; + return !s.contains(" "); + }; + + private static AbstractValidator createMinValidator(Class clazz, AbstractSWCommand.Min min) { + Function comparator; + if (clazz == int.class || clazz == Integer.class) { + int minValue = min.intValue(); + comparator = number -> Integer.compare(number.intValue(), minValue); + } else if (clazz == long.class || clazz == Long.class) { + long minValue = min.longValue(); + comparator = number -> Long.compare(number.longValue(), minValue); + } else if (clazz == float.class || clazz == Float.class) { + float minValue = min.floatValue(); + comparator = number -> Float.compare(number.floatValue(), minValue); + } else if (clazz == double.class || clazz == Double.class) { + double minValue = min.doubleValue(); + comparator = number -> Double.compare(number.doubleValue(), minValue); + } else { + throw new IllegalArgumentException("Min annotation is not supported for " + clazz); + } + if (min.inclusive()) { + return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() >= 0; + } else { + return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() > 0; + } + } + + private static AbstractValidator createMaxValidator(Class clazz, AbstractSWCommand.Max max) { + Function comparator; + if (clazz == int.class || clazz == Integer.class) { + int minValue = max.intValue(); + comparator = number -> Integer.compare(number.intValue(), minValue); + } else if (clazz == long.class || clazz == Long.class) { + long minValue = max.longValue(); + comparator = number -> Long.compare(number.longValue(), minValue); + } else if (clazz == float.class || clazz == Float.class) { + float minValue = max.floatValue(); + comparator = number -> Float.compare(number.floatValue(), minValue); + } else if (clazz == double.class || clazz == Double.class) { + double minValue = max.doubleValue(); + comparator = number -> Double.compare(number.doubleValue(), minValue); + } else { + throw new IllegalArgumentException("Max annotation is not supported for " + clazz); + } + if (max.inclusive()) { + return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() <= 0; + } else { + return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() < 0; + } + } } diff --git a/testsrc/de/steamwar/command/BetterExceptionCommand.java b/testsrc/de/steamwar/command/BetterExceptionCommand.java index cd38179..d014186 100644 --- a/testsrc/de/steamwar/command/BetterExceptionCommand.java +++ b/testsrc/de/steamwar/command/BetterExceptionCommand.java @@ -45,6 +45,7 @@ public class BetterExceptionCommand extends TestSWCommand { @Override public boolean validate(String sender, String value, MessageSender messageSender) { + System.out.println("Validate: " + value); throw new SecurityException(); } diff --git a/testsrc/de/steamwar/command/NumberValidatorCommand.java b/testsrc/de/steamwar/command/NumberValidatorCommand.java new file mode 100644 index 0000000..9ba53a6 --- /dev/null +++ b/testsrc/de/steamwar/command/NumberValidatorCommand.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +package de.steamwar.command; + +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"); + } +} diff --git a/testsrc/de/steamwar/command/NumberValidatorCommandTest.java b/testsrc/de/steamwar/command/NumberValidatorCommandTest.java new file mode 100644 index 0000000..c45b4a8 --- /dev/null +++ b/testsrc/de/steamwar/command/NumberValidatorCommandTest.java @@ -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 . + */ + +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"); + } + } +} diff --git a/testsrc/de/steamwar/command/SimpleCommand.java b/testsrc/de/steamwar/command/SimpleCommand.java index 95aa43d..7388633 100644 --- a/testsrc/de/steamwar/command/SimpleCommand.java +++ b/testsrc/de/steamwar/command/SimpleCommand.java @@ -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"); } diff --git a/testsrc/de/steamwar/command/dto/TestSWCommand.java b/testsrc/de/steamwar/command/dto/TestSWCommand.java index 5fd3b3f..e0ac34b 100644 --- a/testsrc/de/steamwar/command/dto/TestSWCommand.java +++ b/testsrc/de/steamwar/command/dto/TestSWCommand.java @@ -51,9 +51,11 @@ public class TestSWCommand extends AbstractSWCommand { @Override protected void commandSystemError(String sender, CommandFrameworkException e) { + System.out.println("CommandSystemError: " + e.getMessage()); } @Override protected void commandSystemWarning(Supplier message) { + System.out.println("CommandSystemWarning: " + message.get()); } } From f2c0a2a16b98af98c90c6389dec656f137baf5d4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 18 Dec 2022 18:14:44 +0100 Subject: [PATCH 04/84] Add MethodMetaData and ParameterMetaData for better type safety --- .../steamwar/command/AbstractSWCommand.java | 121 ++++++++++-------- src/de/steamwar/command/MethodMetaData.java | 33 +++++ ...cableTypes.java => ParameterMetaData.java} | 4 +- 3 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 src/de/steamwar/command/MethodMetaData.java rename src/de/steamwar/command/{ApplicableTypes.java => ParameterMetaData.java} (94%) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 9ff2e28..30af847 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -25,6 +25,7 @@ import java.lang.reflect.Parameter; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.function.Function; import java.util.function.IntPredicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -133,49 +134,32 @@ public abstract class AbstractSWCommand { private synchronized void initialize() { if (initialized) return; - List methods = methods(); + List 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) -> { + addMapper(Mapper.class, method, (anno, typeMapper) -> { TabCompletionCache.add(typeMapper, cached); - if (anno.local()) { - localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper) 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) -> { + addMapper(ClassMapper.class, method, (anno, typeMapper) -> { TabCompletionCache.add(typeMapper, cached); - if (anno.local()) { - localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper) 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) validator); - } else { - SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value(), validator); - } + addValidator(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) validator); - } else { - SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), validator); - } + addValidator(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) -> { + 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(); - } - checkAnnotationApplicability(parameter, clazz); + if (parameter.isVarArgs()) clazz = clazz.getComponentType(); Mapper mapper = parameter.getAnnotation(Mapper.class); if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) { continue; @@ -201,12 +185,42 @@ public abstract class AbstractSWCommand { initialized = true; } - private void checkAnnotationApplicability(Parameter parameter, Class clazz) { - Annotation[] annotations = parameter.getAnnotations(); + private boolean validateMethod(Method method) { + if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { + MethodMetaData methodMetaData = annotation.annotationType().getAnnotation(MethodMetaData.class); + if (methodMetaData == null) return null; + if (method.getParameterCount() > methodMetaData.maxParameterCount()) { + return new Class[0]; + } + if (method.getParameterCount() < methodMetaData.minParameterCount()) { + return new Class[0]; + } + return methodMetaData.possibleReturnTypes(); + }, "The method '" + method + "'")) { + return false; + } + boolean valid = true; + for (Parameter parameter : method.getParameters()) { + Class type = parameter.getType(); + if (parameter.isVarArgs()) { + type = type.getComponentType(); + } + if (!checkType(parameter.getAnnotations(), type, annotation -> { + ParameterMetaData parameterMetaData = annotation.annotationType().getAnnotation(ParameterMetaData.class); + if (parameterMetaData == null) return null; + return parameterMetaData.possibleTypes(); + }, "The parameter '" + parameter + "'")) { + valid = false; + } + } + return valid; + } + + private boolean checkType(Annotation[] annotations, Class clazz, Function[]> toApplicableTypes, String warning) { + boolean valid = true; for (Annotation annotation : annotations) { - ApplicableTypes applicableTypes = annotation.annotationType().getAnnotation(ApplicableTypes.class); - if (applicableTypes == null) continue; - Class[] types = applicableTypes.value(); + Class[] types = toApplicableTypes.apply(annotation); + if (types == null) continue; boolean applicable = false; for (Class type : types) { if (type.isAssignableFrom(clazz)) { @@ -215,33 +229,27 @@ public abstract class AbstractSWCommand { } } if (!applicable) { - commandSystemWarning(() -> "The parameter '" + parameter.toString() + "' is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'"); + commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'"); + valid = false; } } + return valid; } - private void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { + private void add(Class annotation, Method method, boolean firstParameter, BiConsumer 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 void addMapper(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { - add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { + private void addMapper(Class annotation, Method method, BiConsumer> consumer) { + add(annotation, method, false, (anno, parameters) -> { try { method.setAccessible(true); consumer.accept(anno, (AbstractTypeMapper) method.invoke(this)); @@ -251,8 +259,8 @@ public abstract class AbstractSWCommand { }); } - private void addValidator(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { - add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { + private void addValidator(Class annotation, Method method, BiConsumer> consumer) { + add(annotation, method, false, (anno, parameters) -> { try { method.setAccessible(true); consumer.accept(anno, (AbstractValidator) method.invoke(this)); @@ -287,6 +295,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) + @MethodMetaData(possibleReturnTypes = void.class, minParameterCount = 1) protected @interface Register { /** @@ -303,6 +312,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) + @MethodMetaData(possibleReturnTypes = void.class, minParameterCount = 1) @interface Registeres { Register[] value(); } @@ -310,6 +320,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) + @MethodMetaData(possibleReturnTypes = AbstractTypeMapper.class, minParameterCount = 0, maxParameterCount = 0) protected @interface Mapper { String value(); @@ -318,6 +329,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) + @MethodMetaData(possibleReturnTypes = AbstractTypeMapper.class, minParameterCount = 0, maxParameterCount = 0) protected @interface ClassMapper { Class value(); @@ -326,6 +338,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) + @MethodMetaData(possibleReturnTypes = AbstractTypeMapper.class, minParameterCount = 0, maxParameterCount = 0) protected @interface Cached { long cacheDuration() default 5; TimeUnit timeUnit() default TimeUnit.SECONDS; @@ -334,6 +347,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) + @MethodMetaData(possibleReturnTypes = AbstractValidator.class, minParameterCount = 0, maxParameterCount = 0) protected @interface Validator { String value() default ""; @@ -342,6 +356,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) + @MethodMetaData(possibleReturnTypes = AbstractValidator.class, minParameterCount = 0, maxParameterCount = 0) protected @interface ClassValidator { Class value(); @@ -352,7 +367,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ApplicableTypes({String.class, int.class, Integer.class, boolean.class, Boolean.class}) + @ParameterMetaData(possibleTypes = {String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class}) protected @interface StaticValue { String[] value(); @@ -409,13 +424,13 @@ public abstract class AbstractSWCommand { */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ApplicableTypes({String.class}) + @ParameterMetaData(possibleTypes = {String.class}) protected @interface Quotable { } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ApplicableTypes({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @ParameterMetaData(possibleTypes = {int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) protected @interface Min { int intValue() default Integer.MIN_VALUE; long longValue() default Long.MIN_VALUE; @@ -427,7 +442,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ApplicableTypes({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @ParameterMetaData(possibleTypes = {int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) protected @interface Max { int intValue() default Integer.MAX_VALUE; long longValue() default Long.MAX_VALUE; diff --git a/src/de/steamwar/command/MethodMetaData.java b/src/de/steamwar/command/MethodMetaData.java new file mode 100644 index 0000000..4975ca8 --- /dev/null +++ b/src/de/steamwar/command/MethodMetaData.java @@ -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 . + */ + +package de.steamwar.command; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +@interface MethodMetaData { + Class[] possibleReturnTypes(); + int minParameterCount() default 0; + int maxParameterCount() default Integer.MAX_VALUE; +} diff --git a/src/de/steamwar/command/ApplicableTypes.java b/src/de/steamwar/command/ParameterMetaData.java similarity index 94% rename from src/de/steamwar/command/ApplicableTypes.java rename to src/de/steamwar/command/ParameterMetaData.java index 2385b43..c7c0467 100644 --- a/src/de/steamwar/command/ApplicableTypes.java +++ b/src/de/steamwar/command/ParameterMetaData.java @@ -26,6 +26,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) -@interface ApplicableTypes { - Class[] value(); +@interface ParameterMetaData { + Class[] possibleTypes(); } From 19e4949048ecaf7bb40159ca600ff6b3ab626eb7 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 18 Dec 2022 18:37:10 +0100 Subject: [PATCH 05/84] Add MetaData to bundle metadata annotations --- .../steamwar/command/AbstractSWCommand.java | 80 +++++++------------ .../{MethodMetaData.java => MetaData.java} | 21 +++-- .../steamwar/command/ParameterMetaData.java | 31 ------- 3 files changed, 42 insertions(+), 90 deletions(-) rename src/de/steamwar/command/{MethodMetaData.java => MetaData.java} (70%) delete mode 100644 src/de/steamwar/command/ParameterMetaData.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 30af847..8ab7cbd 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -26,7 +26,6 @@ import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.function.IntPredicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -139,18 +138,18 @@ public abstract class AbstractSWCommand { .collect(Collectors.toList()); for (Method method : methods) { Cached cached = method.getAnnotation(Cached.class); - addMapper(Mapper.class, method, (anno, typeMapper) -> { + this.>add(Mapper.class, method, (anno, typeMapper) -> { TabCompletionCache.add(typeMapper, cached); (anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value(), typeMapper); }); - addMapper(ClassMapper.class, method, (anno, typeMapper) -> { + this.>add(ClassMapper.class, method, (anno, typeMapper) -> { TabCompletionCache.add(typeMapper, cached); (anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value().getName(), typeMapper); }); - addValidator(Validator.class, method, (anno, validator) -> { + this.>add(Validator.class, method, (anno, validator) -> { (anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value(), validator); }); - addValidator(ClassValidator.class, method, (anno, validator) -> { + this.>add(ClassValidator.class, method, (anno, validator) -> { (anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value().getName(), validator); }); } @@ -176,42 +175,28 @@ public abstract class AbstractSWCommand { 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); - } + if (compare == 0) return Integer.compare(o1.comparableValue, o2.comparableValue); + return compare; }); initialized = true; } private boolean validateMethod(Method method) { if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { - MethodMetaData methodMetaData = annotation.annotationType().getAnnotation(MethodMetaData.class); + MetaData.Method methodMetaData = annotation.annotationType().getAnnotation(MetaData.Method.class); if (methodMetaData == null) return null; - if (method.getParameterCount() > methodMetaData.maxParameterCount()) { - return new Class[0]; - } - if (method.getParameterCount() < methodMetaData.minParameterCount()) { - return new Class[0]; - } - return methodMetaData.possibleReturnTypes(); - }, "The method '" + method + "'")) { - return false; - } + if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return new Class[0]; + return methodMetaData.value(); + }, "The method '" + method + "'")) return false; boolean valid = true; for (Parameter parameter : method.getParameters()) { Class type = parameter.getType(); - if (parameter.isVarArgs()) { - type = type.getComponentType(); - } + if (parameter.isVarArgs()) type = type.getComponentType(); if (!checkType(parameter.getAnnotations(), type, annotation -> { - ParameterMetaData parameterMetaData = annotation.annotationType().getAnnotation(ParameterMetaData.class); + MetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(MetaData.Parameter.class); if (parameterMetaData == null) return null; - return parameterMetaData.possibleTypes(); - }, "The parameter '" + parameter + "'")) { - valid = false; - } + return parameterMetaData.value(); + }, "The parameter '" + parameter + "'")) valid = false; } return valid; } @@ -248,22 +233,11 @@ public abstract class AbstractSWCommand { Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); } - private void addMapper(Class annotation, Method method, BiConsumer> consumer) { + private void add(Class annotation, Method method, BiConsumer consumer) { add(annotation, method, false, (anno, parameters) -> { try { method.setAccessible(true); - consumer.accept(anno, (AbstractTypeMapper) method.invoke(this)); - } catch (Exception e) { - throw new SecurityException(e.getMessage(), e); - } - }); - } - - private void addValidator(Class annotation, Method method, BiConsumer> consumer) { - add(annotation, method, false, (anno, parameters) -> { - try { - method.setAccessible(true); - consumer.accept(anno, (AbstractValidator) method.invoke(this)); + consumer.accept(anno, (K) method.invoke(this)); } catch (Exception e) { throw new SecurityException(e.getMessage(), e); } @@ -295,7 +269,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) - @MethodMetaData(possibleReturnTypes = void.class, minParameterCount = 1) + @MetaData.Method(value = void.class, minParameterCount = 1) protected @interface Register { /** @@ -312,7 +286,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MethodMetaData(possibleReturnTypes = void.class, minParameterCount = 1) + @MetaData.Method(value = void.class, minParameterCount = 1) @interface Registeres { Register[] value(); } @@ -320,7 +294,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) - @MethodMetaData(possibleReturnTypes = AbstractTypeMapper.class, minParameterCount = 0, maxParameterCount = 0) + @MetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface Mapper { String value(); @@ -329,7 +303,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MethodMetaData(possibleReturnTypes = AbstractTypeMapper.class, minParameterCount = 0, maxParameterCount = 0) + @MetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface ClassMapper { Class value(); @@ -338,7 +312,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MethodMetaData(possibleReturnTypes = AbstractTypeMapper.class, minParameterCount = 0, maxParameterCount = 0) + @MetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface Cached { long cacheDuration() default 5; TimeUnit timeUnit() default TimeUnit.SECONDS; @@ -347,7 +321,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) - @MethodMetaData(possibleReturnTypes = AbstractValidator.class, minParameterCount = 0, maxParameterCount = 0) + @MetaData.Method(value = AbstractValidator.class, maxParameterCount = 0) protected @interface Validator { String value() default ""; @@ -356,7 +330,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MethodMetaData(possibleReturnTypes = AbstractValidator.class, minParameterCount = 0, maxParameterCount = 0) + @MetaData.Method(value = AbstractValidator.class, maxParameterCount = 0) protected @interface ClassValidator { Class value(); @@ -367,7 +341,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ParameterMetaData(possibleTypes = {String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class}) + @MetaData.Parameter({String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class}) protected @interface StaticValue { String[] value(); @@ -424,13 +398,13 @@ public abstract class AbstractSWCommand { */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ParameterMetaData(possibleTypes = {String.class}) + @MetaData.Parameter({String.class}) protected @interface Quotable { } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ParameterMetaData(possibleTypes = {int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @MetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) protected @interface Min { int intValue() default Integer.MIN_VALUE; long longValue() default Long.MIN_VALUE; @@ -442,7 +416,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @ParameterMetaData(possibleTypes = {int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @MetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) protected @interface Max { int intValue() default Integer.MAX_VALUE; long longValue() default Long.MAX_VALUE; diff --git a/src/de/steamwar/command/MethodMetaData.java b/src/de/steamwar/command/MetaData.java similarity index 70% rename from src/de/steamwar/command/MethodMetaData.java rename to src/de/steamwar/command/MetaData.java index 4975ca8..5745fc4 100644 --- a/src/de/steamwar/command/MethodMetaData.java +++ b/src/de/steamwar/command/MetaData.java @@ -24,10 +24,19 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -@interface MethodMetaData { - Class[] possibleReturnTypes(); - int minParameterCount() default 0; - int maxParameterCount() default Integer.MAX_VALUE; +@interface MetaData { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.ANNOTATION_TYPE) + @interface Method { + Class[] value(); + int minParameterCount() default 0; + int maxParameterCount() default Integer.MAX_VALUE; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.ANNOTATION_TYPE) + @interface Parameter { + Class[] value(); + } } diff --git a/src/de/steamwar/command/ParameterMetaData.java b/src/de/steamwar/command/ParameterMetaData.java deleted file mode 100644 index c7c0467..0000000 --- a/src/de/steamwar/command/ParameterMetaData.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.command; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -@interface ParameterMetaData { - Class[] possibleTypes(); -} From 2e9e4e2e49645207f11da6332e687165a98b7c7a Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 18 Dec 2022 21:36:46 +0100 Subject: [PATCH 06/84] Fix ErrorMessage annotation and ClassValidator never being present on Parameter --- src/de/steamwar/command/SWCommandUtils.java | 11 +++-------- src/de/steamwar/command/SubCommand.java | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 203bdd8..2f16d7a 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -135,14 +135,6 @@ public class SWCommandUtils { public static AbstractValidator getValidator(Parameter parameter, Map> localValidator) { 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); - } - AbstractSWCommand.Validator validator = parameter.getAnnotation(AbstractSWCommand.Validator.class); if (validator != null) { if (validator.value() != null && !validator.value().isEmpty()) { @@ -150,7 +142,10 @@ public class SWCommandUtils { } return getValidator(clazz.getTypeName(), localValidator); } + return null; + } + public static AbstractValidator getErrorMessage(Parameter parameter) { AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class); if (errorMessage != null) { return (AbstractValidator) (sender, value, messageSender) -> { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index bf11674..0e75d7b 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -155,6 +155,7 @@ public class SubCommand { commandPart.addValidator((AbstractValidator) STRING_SPACE_FILTER); } commandPart.addValidator(validator); + commandPart.addValidator((AbstractValidator) SWCommandUtils.getErrorMessage(parameter)); if (min != null) { commandPart.addValidator((AbstractValidator) createMinValidator(varArgType != null ? varArgType : parameter.getType(), min)); } From 518207f343ae8fa054eaa6efe25bbd59a8d23b79 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 18 Dec 2022 21:49:36 +0100 Subject: [PATCH 07/84] Optimize some code --- src/de/steamwar/command/CommandPart.java | 28 ++++---------- src/de/steamwar/command/SubCommand.java | 47 +++++++----------------- 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index bb39119..dc8c6a9 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -65,7 +65,9 @@ class CommandPart { 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 validator) { @@ -80,28 +82,18 @@ class CommandPart { 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 errors, List 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(); - } + if (!validArgument.success) throw new CommandParseException(); Array.set(array, i - startIndex, validArgument.value); } for (AbstractValidator validator : validators) { if (!validator.validate(sender, array, (s, objects) -> { errors.accept(() -> command.sendMessage(sender, s, objects)); - })) { - throw new CommandParseException(); - } + })) throw new CommandParseException(); } current.add(array); return; @@ -137,11 +129,8 @@ class CommandPart { public void generateTabComplete(List current, T sender, String[] args, int startIndex) { if (varArgType != null) { 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, i); + if (!validArgument.success) return; } Collection strings = tabCompletes(sender, args, args.length - 1); if (strings != null) { @@ -151,8 +140,7 @@ class CommandPart { } if (args.length - 1 > startIndex) { - CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> { - }, sender, args, startIndex); + CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, startIndex); if (checkArgumentResult.success && next != null) { next.generateTabComplete(current, sender, args, startIndex + 1); return; diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 0e75d7b..9084fe7 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -110,12 +110,8 @@ public class SubCommand { } List 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; @@ -180,55 +176,38 @@ public class SubCommand { private static final AbstractValidator STRING_SPACE_FILTER = (sender, value, messageSender) -> { if (!(value instanceof String)) return true; - String s = (String) value; - return !s.contains(" "); + return !((String) value).contains(" "); }; private static AbstractValidator createMinValidator(Class clazz, AbstractSWCommand.Min min) { Function comparator; if (clazz == int.class || clazz == Integer.class) { - int minValue = min.intValue(); - comparator = number -> Integer.compare(number.intValue(), minValue); + comparator = number -> Integer.compare(number.intValue(), min.intValue()); } else if (clazz == long.class || clazz == Long.class) { - long minValue = min.longValue(); - comparator = number -> Long.compare(number.longValue(), minValue); + comparator = number -> Long.compare(number.longValue(), min.longValue()); } else if (clazz == float.class || clazz == Float.class) { - float minValue = min.floatValue(); - comparator = number -> Float.compare(number.floatValue(), minValue); + comparator = number -> Float.compare(number.floatValue(), min.floatValue()); } else if (clazz == double.class || clazz == Double.class) { - double minValue = min.doubleValue(); - comparator = number -> Double.compare(number.doubleValue(), minValue); + comparator = number -> Double.compare(number.doubleValue(), min.doubleValue()); } else { throw new IllegalArgumentException("Min annotation is not supported for " + clazz); } - if (min.inclusive()) { - return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() >= 0; - } else { - return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() > 0; - } + return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() >= (min.inclusive() ? 0 : 1); } private static AbstractValidator createMaxValidator(Class clazz, AbstractSWCommand.Max max) { Function comparator; if (clazz == int.class || clazz == Integer.class) { - int minValue = max.intValue(); - comparator = number -> Integer.compare(number.intValue(), minValue); + comparator = number -> Integer.compare(number.intValue(), max.intValue()); } else if (clazz == long.class || clazz == Long.class) { - long minValue = max.longValue(); - comparator = number -> Long.compare(number.longValue(), minValue); + comparator = number -> Long.compare(number.longValue(), max.longValue()); } else if (clazz == float.class || clazz == Float.class) { - float minValue = max.floatValue(); - comparator = number -> Float.compare(number.floatValue(), minValue); + comparator = number -> Float.compare(number.floatValue(), max.floatValue()); } else if (clazz == double.class || clazz == Double.class) { - double minValue = max.doubleValue(); - comparator = number -> Double.compare(number.doubleValue(), minValue); + comparator = number -> Double.compare(number.doubleValue(), max.doubleValue()); } else { throw new IllegalArgumentException("Max annotation is not supported for " + clazz); } - if (max.inclusive()) { - return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() <= 0; - } else { - return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() < 0; - } + return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() <= (max.inclusive() ? 0 : -1); } } From 8ce900db31e5c4cf0cc5a39dcb61bed297b9d392 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 12 Jan 2023 21:53:28 +0100 Subject: [PATCH 08/84] NodeMember: Move Member Schematics --- .../steamwar/sql/EffectiveSchematicNode.java | 248 ++++++++++++++++++ src/de/steamwar/sql/NodeMember.java | 17 +- src/de/steamwar/sql/SchematicNode.java | 108 ++------ 3 files changed, 278 insertions(+), 95 deletions(-) create mode 100644 src/de/steamwar/sql/EffectiveSchematicNode.java diff --git a/src/de/steamwar/sql/EffectiveSchematicNode.java b/src/de/steamwar/sql/EffectiveSchematicNode.java new file mode 100644 index 0000000..e62c7c9 --- /dev/null +++ b/src/de/steamwar/sql/EffectiveSchematicNode.java @@ -0,0 +1,248 @@ +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.*; +import java.util.stream.Collectors; + +import static de.steamwar.sql.SchematicNode.TAB_CACHE; + +public class EffectiveSchematicNode { + + private static final Table table = new Table<>(EffectiveSchematicNode.class, "EffectiveSchematicNode"); + + private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); + + private static final SelectStatement list_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null ORDER BY NodeName"); + private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? ORDER BY NodeName"); + private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement byParentName_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); + private static final SelectStatement accessibleByUserTypeParent = 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId"); + private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode = ?"); + private static final SelectStatement accessibleByUserTypeInParent_null = 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode is null"); + private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); + private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); + private static final SelectStatement 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 FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); + + public static List getAll(SteamwarUser user) { + return all.listSelect(user); + } + + public static Map> getAllMap(SteamwarUser user) { + return map(all.listSelect(user)); + } + + public static List list(SteamwarUser user, Integer schematicId) { + if(schematicId == null || schematicId == 0) { + return list_null.listSelect(user); + } else { + return list.listSelect(user, schematicId); + } + } + + public static Optional byParentName(SteamwarUser user, Integer schematicId, String name) { + if(schematicId == null || schematicId == 0) { + return Optional.ofNullable(byParentName_null.select(user, name)); + } else { + return Optional.ofNullable(byParentName.select(user, schematicId, name)); + } + } + + public static List accessibleByUserType(SteamwarUser user, SchematicType type) { + return accessibleByUserType.listSelect(type, user, user, user); + } + + public static Map> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) { + return map(accessibleByUserTypeParent.listSelect(type, user, user, user)); + } + + public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) { + return schematicAccessibleForUser.select(user, schematicId) != null; + } + + public static List accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) { + if(parentId == null || parentId == 0) { + return accessibleByUserTypeInParent_null.listSelect(type, user, user, user); + } else { + return accessibleByUserTypeInParent.listSelect(type, user, user, user, parentId); + } + } + + public static Optional byIdAndUser(SteamwarUser user, Integer id) { + return Optional.ofNullable(byIdAndUser.select(user, id)); + } + + public static List parentsOfNode(SteamwarUser user, Integer id) { + return allParentsOfNode.listSelect(id, user, user, user).stream().filter(n -> n.getNodeId() != id).collect(Collectors.toList()); + } + + private static Map> map(List in) { + Map> map = new HashMap<>(); + for (EffectiveSchematicNode effectiveSchematicNode : in) { + map.computeIfAbsent(effectiveSchematicNode.getNodeParent().orElse(0), k -> new ArrayList<>()).add(effectiveSchematicNode); + } + return map; + } + + @Getter + @Field + private final int nodeId; + @Getter + @Field + private final int nodeOwner; + @Getter + @Field + private final int effectiveOwner; + @Getter + @Field + private final String nodeName; + @Field(nullable = true) + private final Integer parentNode; + @Getter + @Field + private final Timestamp lastUpdate; + @Field + private final String nodeItem; + @Field(nullable = true) + private final SchematicType nodeType; + @Field + private final int nodeRank; + + private String brCache = null; + + public EffectiveSchematicNode(int nodeId, int nodeOwner, int effectiveOwner, String nodeName, Integer nodeParent, Timestamp lastUpdate, String nodeItem, SchematicType nodeType, int nodeRank) { + this.nodeId = nodeId; + this.nodeOwner = nodeOwner; + this.effectiveOwner = effectiveOwner; + this.nodeName = nodeName; + this.parentNode = nodeParent; + this.lastUpdate = lastUpdate; + this.nodeItem = nodeItem; + this.nodeType = nodeType; + this.nodeRank = nodeRank; + } + + public boolean isDir() { + return nodeType == null; + } + + public Optional getNodeParent() { + return Optional.ofNullable(parentNode); + } + + public Optional getParentNode() { + return byIdAndUser(SteamwarUser.get(effectiveOwner), parentNode); + } + + public SchematicType getNodeType() { + if(isDir()) { + throw new IllegalStateException("This node is a directory"); + } + return nodeType; + } + + public int getNodeRank() { + if(isDir()) { + throw new IllegalStateException("This node is a directory"); + } + return nodeRank; + } + + public SchematicNode toSchematicNode() { + return SchematicNode.getSchematicNode(nodeId); + } + + public boolean isWritable() { + return effectiveOwner == nodeOwner; + } + + public String getNodeItem() { + if (nodeItem.isEmpty()) { + return isDir() ? "CHEST" : "CAULDRON_ITEM"; + } + return nodeItem; + } + + public static List 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 list = new ArrayList<>(); + if (s.contains("/")) { + String preTab = s.substring(0, s.lastIndexOf("/") + 1); + Optional pa = EffectiveSchematicNode.getNodeFromPath(user, preTab); + if (!pa.isPresent()) return Collections.emptyList(); + List nodes = EffectiveSchematicNode.list(user, pa.get().getNodeId()); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs())); + } else { + List nodes = EffectiveSchematicNode.list(user, 0); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.getNodeName() + (node.isDir() ? "/" : ""))); + } + list.remove("//copy"); + TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list); + return list; + } + + public String generateBreadcrumbs() { + if(brCache == null) { + brCache = generateBreadcrumbs("/"); + } + return brCache; + } + + public String generateBreadcrumbs(String split) { + StringBuilder builder = new StringBuilder(getNodeName()); + Optional currentNode = Optional.of(this); + if(currentNode.map(EffectiveSchematicNode::isDir).orElse(false)) { + builder.append("/"); + } + while (currentNode.isPresent()) { + currentNode = currentNode.flatMap(EffectiveSchematicNode::getNodeParent).flatMap(integer -> byIdAndUser(SteamwarUser.get(effectiveOwner), integer)); + currentNode.ifPresent(node -> builder.insert(0, '/').insert(0, node.getNodeName())); + } + return builder.toString(); + } + + public static Optional 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 Optional.empty(); + } + if (s.contains("/")) { + String[] layers = s.split("/"); + Optional currentNode = EffectiveSchematicNode.byParentName(user, 0, layers[0]); + for (int i = 1; i < layers.length; i++) { + int finalI = i; + Optional node = currentNode.flatMap(effectiveSchematicNode -> EffectiveSchematicNode.byParentName(user, effectiveSchematicNode.getNodeId(), layers[finalI])); + if (!node.isPresent()) { + return Optional.empty(); + } else { + currentNode = node; + if (!currentNode.map(EffectiveSchematicNode::isDir).orElse(false) && i != layers.length - 1) { + return Optional.empty(); + } + } + } + return currentNode; + } else { + return EffectiveSchematicNode.byParentName(user, 0, s); + } + } +} diff --git a/src/de/steamwar/sql/NodeMember.java b/src/de/steamwar/sql/NodeMember.java index e704ad0..19417da 100644 --- a/src/de/steamwar/sql/NodeMember.java +++ b/src/de/steamwar/sql/NodeMember.java @@ -26,6 +26,7 @@ import de.steamwar.sql.internal.Table; import lombok.AllArgsConstructor; import java.util.HashSet; +import java.util.Optional; import java.util.Set; @AllArgsConstructor @@ -39,13 +40,16 @@ public class NodeMember { private static final SelectStatement getNodeMember = table.select(Table.PRIMARY); private static final SelectStatement getNodeMembers = table.selectFields("NodeId"); private static final SelectStatement getSchematics = table.selectFields("UserId"); - private static final Statement create = table.insertAll(); + 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; @@ -55,13 +59,17 @@ public class NodeMember { return userId; } + public Optional 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); + return new NodeMember(node, member, null); } public static NodeMember getNodeMember(int node, int member) { @@ -75,4 +83,9 @@ public class NodeMember { public static Set getSchematics(int member) { return new HashSet<>(getSchematics.listSelect(member)); } + + public void setParentId(Integer parentId) { + this.parentId = Optional.ofNullable(parentId).orElse(0) == 0 ? null : parentId; + updateParent.update(this.parentId, nodeId, userId); + } } diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 3e610a6..0299e77 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -38,7 +38,7 @@ public class SchematicNode { 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>> TAB_CACHE = new HashMap<>(); + protected static final Map>> TAB_CACHE = new HashMap<>(); public static void clear() { TAB_CACHE.clear(); } @@ -62,14 +62,8 @@ public class SchematicNode { private static final SelectStatement dirsByParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName"); private static final SelectStatement byParentName = table.selectFields("NodeName", "ParentNode"); private static final SelectStatement byParentName_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeName = ? AND ParentNode is NULL"); - private static final SelectStatement accessibleByUserTypeParent = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName"); - private static final SelectStatement accessibleByUserTypeParent_Null = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode is null ORDER BY NodeName"); - private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); - private static final SelectStatement accessibleByUser = new SelectStatement<>(table, nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName"); - private static final Statement schematicAccessibleForUser = new Statement("WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1"); - private static final SelectStatement allAccessibleByUser = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); static { @@ -100,8 +94,6 @@ public class SchematicNode { @Field(def = "1") private boolean nodeFormat; - private final Map brCache = new HashMap<>(); - public SchematicNode( int nodeId, int nodeOwner, @@ -196,14 +188,14 @@ public class SchematicNode { return byId.select(id); } + @Deprecated public static List getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) { - if(parent == null || parent == 0) - return accessibleByUserTypeParent_Null.listSelect(owner, owner, schemType); - return accessibleByUserTypeParent.listSelect(owner, owner, schemType, parent); + return EffectiveSchematicNode.accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); } + @Deprecated public static List getAllAccessibleSchematicsOfType(int user, String schemType) { - return accessibleByUserType.listSelect(user, user, schemType); + return EffectiveSchematicNode.accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType)).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); } public static List getAllSchematicsOfType(int owner, String schemType) { @@ -233,21 +225,14 @@ public class SchematicNode { return finalList; } + @Deprecated public static List getSchematicsAccessibleByUser(int user, Integer parent) { - if (parent == null || parent == 0) - return accessibleByUser.listSelect(user, user, user, user); - - if(schematicAccessibleForUser.select(rs -> { - rs.next(); - return rs.getInt("Accessible") > 0; - }, parent, user, user)) - return getSchematicNodeInNode(parent); - - return Collections.emptyList(); + return EffectiveSchematicNode.list(SteamwarUser.get(user), parent).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); } + @Deprecated public static List getAllSchematicsAccessibleByUser(int user) { - return allAccessibleByUser.listSelect(user, user); + return EffectiveSchematicNode.getAll(SteamwarUser.get(user)).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); } public static List getAllParentsOfNode(SchematicNode node) { @@ -258,38 +243,9 @@ public class SchematicNode { return allParentsOfNode.listSelect(node); } + @Deprecated public static SchematicNode getNodeFromPath(SteamwarUser user, String s) { - if (s.startsWith("/")) { - s = s.substring(1); - } - if (s.isEmpty()) { - return null; - } - if (s.contains("/")) { - String[] layers = s.split("/"); - SchematicNode currentNode = null; - for (int i = 0; i < layers.length; i++) { - int finalI = i; - Optional node; - if (currentNode == null) { - node = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny(); - } else { - node = Optional.ofNullable(SchematicNode.getSchematicNode(layers[i], currentNode.getId())); - } - if (!node.isPresent()) { - return null; - } else { - currentNode = node.get(); - if (!currentNode.isDir() && i != layers.length - 1) { - return null; - } - } - } - return currentNode; - } else { - String finalS = s; - return SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(finalS)).findAny().orElse(null); - } + return EffectiveSchematicNode.getNodeFromPath(user, s).map(EffectiveSchematicNode::toSchematicNode).orElse(null); } public static List filterSchems(int user, Predicate filter) { @@ -448,7 +404,6 @@ public class SchematicNode { private void updateDB() { this.lastUpdate = Timestamp.from(Instant.now()); update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeFormat, nodeId); - this.brCache.clear(); TAB_CACHE.clear(); } @@ -470,23 +425,11 @@ public class SchematicNode { } public String generateBreadcrumbs(SteamwarUser user) { - return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user)); + return EffectiveSchematicNode.byIdAndUser(user, nodeId).map(EffectiveSchematicNode::generateBreadcrumbs).orElse("/"); } public String generateBreadcrumbs(String split, SteamwarUser user) { - StringBuilder builder = new StringBuilder(getName()); - SchematicNode currentNode = this; - if (currentNode.isDir()) builder.append("/"); - final Set nodeMembers = NodeMember.getSchematics(user.getId()); - AtomicInteger i = new AtomicInteger(); - i.set(currentNode.getId()); - while (currentNode.getParentNode() != null && nodeMembers.stream().noneMatch(nodeMember -> nodeMember.getNode() == i.get())) { - currentNode = currentNode.getParentNode(); - i.set(currentNode.getId()); - builder.insert(0, split) - .insert(0, currentNode.getName()); - } - return builder.toString(); + return EffectiveSchematicNode.byIdAndUser(user, nodeId).map(node -> node.generateBreadcrumbs(split)).orElse("/"); } private static final List FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public")); @@ -513,30 +456,9 @@ public class SchematicNode { return false; } + @Deprecated public static List 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 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 nodes = SchematicNode.getSchematicNodeInNode(pa); - nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user))); - } else { - List nodes = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0); - 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; + return EffectiveSchematicNode.getNodeTabcomplete(user, s); } private static void rootWarning() { From 45bc5b05d71fb7bc909537b4c12522b175e62ffd Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sat, 14 Jan 2023 08:57:04 +0100 Subject: [PATCH 09/84] Add Copyright --- .../steamwar/sql/EffectiveSchematicNode.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/de/steamwar/sql/EffectiveSchematicNode.java b/src/de/steamwar/sql/EffectiveSchematicNode.java index e62c7c9..587034e 100644 --- a/src/de/steamwar/sql/EffectiveSchematicNode.java +++ b/src/de/steamwar/sql/EffectiveSchematicNode.java @@ -1,3 +1,22 @@ +/* + This file is a part of the SteamWar software. + + Copyright (C) 2023 SteamWar.de-Serverteam + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + package de.steamwar.sql; import de.steamwar.sql.internal.Field; From bd7635da0d686565fd45496e9966f14d336e3f1f Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 15 Jan 2023 11:23:58 +0100 Subject: [PATCH 10/84] Add CommandMetaData.ImplicitValidator for easier extension of implicit validators --- .../steamwar/command/AbstractSWCommand.java | 29 ++--- .../steamwar/command/AbstractTypeMapper.java | 2 + .../{MetaData.java => CommandMetaData.java} | 29 ++++- src/de/steamwar/command/CommandPart.java | 4 + .../command/ImplicitTypeValidators.java | 102 ++++++++++++++++++ src/de/steamwar/command/SubCommand.java | 48 +++++++-- 6 files changed, 192 insertions(+), 22 deletions(-) rename src/de/steamwar/command/{MetaData.java => CommandMetaData.java} (54%) create mode 100644 src/de/steamwar/command/ImplicitTypeValidators.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 8ab7cbd..2793c3c 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -183,7 +183,7 @@ public abstract class AbstractSWCommand { private boolean validateMethod(Method method) { if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { - MetaData.Method methodMetaData = annotation.annotationType().getAnnotation(MetaData.Method.class); + CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class); if (methodMetaData == null) return null; if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return new Class[0]; return methodMetaData.value(); @@ -193,7 +193,7 @@ public abstract class AbstractSWCommand { Class type = parameter.getType(); if (parameter.isVarArgs()) type = type.getComponentType(); if (!checkType(parameter.getAnnotations(), type, annotation -> { - MetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(MetaData.Parameter.class); + CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class); if (parameterMetaData == null) return null; return parameterMetaData.value(); }, "The parameter '" + parameter + "'")) valid = false; @@ -269,7 +269,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) - @MetaData.Method(value = void.class, minParameterCount = 1) + @CommandMetaData.Method(value = void.class, minParameterCount = 1) protected @interface Register { /** @@ -286,7 +286,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MetaData.Method(value = void.class, minParameterCount = 1) + @CommandMetaData.Method(value = void.class, minParameterCount = 1) @interface Registeres { Register[] value(); } @@ -294,7 +294,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) - @MetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) + @CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface Mapper { String value(); @@ -303,7 +303,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) + @CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface ClassMapper { Class value(); @@ -312,7 +312,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) + @CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface Cached { long cacheDuration() default 5; TimeUnit timeUnit() default TimeUnit.SECONDS; @@ -321,7 +321,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.METHOD}) - @MetaData.Method(value = AbstractValidator.class, maxParameterCount = 0) + @CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0) protected @interface Validator { String value() default ""; @@ -330,7 +330,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) - @MetaData.Method(value = AbstractValidator.class, maxParameterCount = 0) + @CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0) protected @interface ClassValidator { Class value(); @@ -341,7 +341,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @MetaData.Parameter({String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class}) + @CommandMetaData.Parameter({String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class}) protected @interface StaticValue { String[] value(); @@ -376,6 +376,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) + @CommandMetaData.ImplicitValidator(handler = ImplicitTypeValidators.ErrorMessageValidator.class, order = 1) protected @interface ErrorMessage { /** * Error message to be displayed when the parameter is invalid. @@ -398,13 +399,14 @@ public abstract class AbstractSWCommand { */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @MetaData.Parameter({String.class}) + @CommandMetaData.Parameter({String.class}) protected @interface Quotable { } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @MetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @CommandMetaData.ImplicitValidator(handler = ImplicitTypeValidators.MinValidator.class, order = 2) protected @interface Min { int intValue() default Integer.MIN_VALUE; long longValue() default Long.MIN_VALUE; @@ -416,7 +418,8 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @MetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) + @CommandMetaData.ImplicitValidator(handler = ImplicitTypeValidators.MaxValidator.class, order = 2) protected @interface Max { int intValue() default Integer.MAX_VALUE; long longValue() default Long.MAX_VALUE; diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index 4a340d8..1c8550b 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -25,6 +25,8 @@ public interface AbstractTypeMapper extends AbstractValidator { /** * The CommandSender can be null! */ + // TODO: Change the 'previousArguments' to List or something like that + // TODO: Change T return value to Pair or something like that. This would make OptionalValue easier to implement as the Boolean would indicate if s should be consumed or not T map(K sender, String[] previousArguments, String s); @Override diff --git a/src/de/steamwar/command/MetaData.java b/src/de/steamwar/command/CommandMetaData.java similarity index 54% rename from src/de/steamwar/command/MetaData.java rename to src/de/steamwar/command/CommandMetaData.java index 5745fc4..c7f79c7 100644 --- a/src/de/steamwar/command/MetaData.java +++ b/src/de/steamwar/command/CommandMetaData.java @@ -24,8 +24,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@interface MetaData { +public @interface CommandMetaData { + /** + * This annotation is only for internal use. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) @interface Method { @@ -34,9 +37,33 @@ import java.lang.annotation.Target; 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(); } + + /** + * This annotation can be used in conjunction with a class that implement {@link AbstractValidator} to + * create custom validator short hands for commands. The validator class itself should contain a constructor + * with two parameters the first is the annotation this annotation is used on and the second is of type {@link Class}. + */ + @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(); + } } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index dc8c6a9..2969872 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -192,4 +192,8 @@ class CommandPart { } return new CheckArgumentResult(true, value); } + + public Class getType() { + return varArgType != null ? varArgType : parameter.getType(); + } } diff --git a/src/de/steamwar/command/ImplicitTypeValidators.java b/src/de/steamwar/command/ImplicitTypeValidators.java new file mode 100644 index 0000000..7204ce5 --- /dev/null +++ b/src/de/steamwar/command/ImplicitTypeValidators.java @@ -0,0 +1,102 @@ +/* + * 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 . + */ + +package de.steamwar.command; + +import lombok.experimental.UtilityClass; + +import java.lang.reflect.Array; +import java.util.function.Function; + +@UtilityClass +public class ImplicitTypeValidators { + + public static class ErrorMessageValidator implements AbstractValidator { + + private AbstractSWCommand.ErrorMessage errorMessage; + + public ErrorMessageValidator(AbstractSWCommand.ErrorMessage errorMessage, Class type) { + 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; + } + } + + public static class MinValidator implements AbstractValidator { + + private int value; + private Function comparator; + + public MinValidator(AbstractSWCommand.Min min, Class clazz) { + this.value = min.inclusive() ? 0 : 1; + + if (clazz == int.class || clazz == Integer.class) { + comparator = number -> Integer.compare(number.intValue(), min.intValue()); + } else if (clazz == long.class || clazz == Long.class) { + comparator = number -> Long.compare(number.longValue(), min.longValue()); + } else if (clazz == float.class || clazz == Float.class) { + comparator = number -> Float.compare(number.floatValue(), min.floatValue()); + } else if (clazz == double.class || clazz == Double.class) { + comparator = number -> Double.compare(number.doubleValue(), min.doubleValue()); + } else { + throw new IllegalArgumentException("Min annotation is not supported for " + clazz); + } + } + + @Override + public boolean validate(T sender, Number value, MessageSender messageSender) { + return (comparator.apply(value).intValue()) >= this.value; + } + } + + public static class MaxValidator implements AbstractValidator { + + private int value; + private Function comparator; + + public MaxValidator(AbstractSWCommand.Max max, Class clazz) { + this.value = max.inclusive() ? 0 : -1; + + if (clazz == int.class || clazz == Integer.class) { + comparator = number -> Integer.compare(number.intValue(), max.intValue()); + } else if (clazz == long.class || clazz == Long.class) { + comparator = number -> Long.compare(number.longValue(), max.longValue()); + } else if (clazz == float.class || clazz == Float.class) { + comparator = number -> Float.compare(number.floatValue(), max.floatValue()); + } else if (clazz == double.class || clazz == Double.class) { + comparator = number -> Double.compare(number.doubleValue(), max.doubleValue()); + } else { + throw new IllegalArgumentException("Max annotation is not supported for " + clazz); + } + } + + @Override + public boolean validate(T sender, Number value, MessageSender messageSender) { + return (comparator.apply(value).intValue()) <= this.value; + } + } +} diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 9084fe7..da3576e 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -19,10 +19,13 @@ 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; @@ -142,22 +145,16 @@ public class SubCommand { AbstractValidator validator = (AbstractValidator) SWCommandUtils.getValidator(parameter, localValidator); Class varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null; AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class); - AbstractSWCommand.Min min = parameter.getAnnotation(AbstractSWCommand.Min.class); - AbstractSWCommand.Max max = parameter.getAnnotation(AbstractSWCommand.Max.class); CommandPart commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i); commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG()); if (parameter.getAnnotation(AbstractSWCommand.Quotable.class) == null) { commandPart.addValidator((AbstractValidator) STRING_SPACE_FILTER); } + handleImplicitTypeValidator(parameter, commandPart, true); commandPart.addValidator(validator); commandPart.addValidator((AbstractValidator) SWCommandUtils.getErrorMessage(parameter)); - if (min != null) { - commandPart.addValidator((AbstractValidator) createMinValidator(varArgType != null ? varArgType : parameter.getType(), min)); - } - if (max != null) { - commandPart.addValidator((AbstractValidator) createMaxValidator(varArgType != null ? varArgType : parameter.getType(), max)); - } + handleImplicitTypeValidator(parameter, commandPart, false); if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) { commandPart.addValidator((AbstractValidator) NULL_FILTER); } @@ -172,6 +169,41 @@ public class SubCommand { return first; } + private static void handleImplicitTypeValidator(Parameter parameter, CommandPart commandPart, boolean beforeValidatorAnnotation) { + Annotation[] annotations = parameter.getAnnotations(); + Map>> validators = new HashMap<>(); + for (Annotation annotation : annotations) { + CommandMetaData.ImplicitValidator implicitValidator = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitValidator.class); + if (implicitValidator == null) continue; + if (beforeValidatorAnnotation && implicitValidator.order() >= 0) continue; + if (!beforeValidatorAnnotation && implicitValidator.order() <= 0) continue; + Class clazz = implicitValidator.handler(); + if (!AbstractValidator.class.isAssignableFrom(clazz)) continue; + Constructor[] constructors = clazz.getConstructors(); + if (constructors.length != 1) continue; + Constructor constructor = constructors[0]; + Class[] parameterTypes = constructor.getParameterTypes(); + if (parameterTypes.length != 2) continue; + AbstractValidator validator; + if (!annotation.annotationType().isAssignableFrom(parameterTypes[0])) continue; + if (!Class.class.isAssignableFrom(parameterTypes[1])) continue; + try { + validator = (AbstractValidator) constructor.newInstance(annotation, commandPart.getType()); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + validators.computeIfAbsent(implicitValidator.order(), integer -> new ArrayList<>()).add(validator); + } + List keys = new ArrayList<>(validators.keySet()); + keys.sort(Integer::compareTo); + for (Integer key : keys) { + List> list = validators.get(key); + for (AbstractValidator validator : list) { + commandPart.addValidator(validator); + } + } + } + private static final AbstractValidator NULL_FILTER = (sender, value, messageSender) -> value != null; private static final AbstractValidator STRING_SPACE_FILTER = (sender, value, messageSender) -> { From 7e67f745718b38d3cfff2995656becc943d21ccd Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 15 Jan 2023 16:40:39 +0100 Subject: [PATCH 11/84] Add PreviousArguments for better handling in AbstractTypeMapper --- .../steamwar/command/AbstractSWCommand.java | 160 +++++++++++++++++- .../steamwar/command/AbstractTypeMapper.java | 23 ++- .../command/CommandFrameworkException.java | 6 - src/de/steamwar/command/CommandMetaData.java | 31 +++- src/de/steamwar/command/CommandPart.java | 38 +++-- .../command/ImplicitTypeValidators.java | 102 ----------- .../steamwar/command/PreviousArguments.java | 39 +++++ src/de/steamwar/command/SWCommandUtils.java | 84 ++------- src/de/steamwar/command/SubCommand.java | 141 +++++++++------ .../command/BetterExceptionCommand.java | 4 +- testsrc/de/steamwar/command/CacheCommand.java | 4 +- .../steamwar/command/NullMapperCommand.java | 4 +- 12 files changed, 370 insertions(+), 266 deletions(-) delete mode 100644 src/de/steamwar/command/ImplicitTypeValidators.java create mode 100644 src/de/steamwar/command/PreviousArguments.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 2793c3c..bf65ec8 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -20,6 +20,7 @@ package de.steamwar.command; import java.lang.annotation.*; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; @@ -295,10 +296,35 @@ public abstract class AbstractSWCommand { @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 implements AbstractTypeMapper { + + private AbstractTypeMapper inner; + + public Handler(AbstractSWCommand.Mapper mapper, Map> localTypeMapper) { + inner = (AbstractTypeMapper) 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 tabCompletes(T sender, PreviousArguments previousArguments, String s) { + return inner.tabCompletes(sender, previousArguments, s); + } + } } @Retention(RetentionPolicy.RUNTIME) @@ -322,10 +348,25 @@ public abstract class AbstractSWCommand { @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; + + class Handler implements AbstractValidator { + + private AbstractValidator inner; + + public Handler(AbstractSWCommand.Validator validator, Class clazz, Map> localValidator) { + inner = (AbstractValidator) SWCommandUtils.getValidator(validator, clazz, localValidator); + } + + @Override + public boolean validate(T sender, Object value, MessageSender messageSender) { + return inner.validate(sender, value, messageSender); + } + } } @Retention(RetentionPolicy.RUNTIME) @@ -342,6 +383,7 @@ public abstract class AbstractSWCommand { @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(); @@ -356,6 +398,53 @@ public abstract class AbstractSWCommand { boolean allowISE() default false; int[] falseValues() default { 0 }; + + class Handler implements AbstractTypeMapper { + + 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 tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value())); + Set 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 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 boolean validate(T sender, Object value, MessageSender messageSender) { + return inner.validate(sender, value, messageSender); + } + + @Override + public Collection tabCompletes(T sender, PreviousArguments previousArguments, String s) { + return inner.tabCompletes(sender, previousArguments, s); + } + } } @Retention(RetentionPolicy.RUNTIME) @@ -376,7 +465,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @CommandMetaData.ImplicitValidator(handler = ImplicitTypeValidators.ErrorMessageValidator.class, order = 1) + @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 1) protected @interface ErrorMessage { /** * Error message to be displayed when the parameter is invalid. @@ -387,6 +476,25 @@ public abstract class AbstractSWCommand { * This is the short form for 'allowEmptyArrays'. */ boolean allowEAs() default true; + + class Handler implements AbstractValidator { + + 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) @@ -406,7 +514,7 @@ public abstract class AbstractSWCommand { @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 = ImplicitTypeValidators.MinValidator.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2) protected @interface Min { int intValue() default Integer.MIN_VALUE; long longValue() default Long.MIN_VALUE; @@ -414,12 +522,28 @@ public abstract class AbstractSWCommand { double doubleValue() default Double.MIN_VALUE; boolean inclusive() default true; + + class Handler implements AbstractValidator { + + private int value; + private Function 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) { + 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 = ImplicitTypeValidators.MaxValidator.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2) protected @interface Max { int intValue() default Integer.MAX_VALUE; long longValue() default Long.MAX_VALUE; @@ -427,5 +551,35 @@ public abstract class AbstractSWCommand { double doubleValue() default Double.MAX_VALUE; boolean inclusive() default true; + + class Handler implements AbstractValidator { + + private int value; + private Function 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) { + return (comparator.apply(value).intValue()) <= this.value; + } + } + } + + private static Function 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); + } } } diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index 1c8550b..0363cd0 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -25,14 +25,29 @@ public interface AbstractTypeMapper extends AbstractValidator { /** * The CommandSender can be null! */ - // TODO: Change the 'previousArguments' to List or something like that - // TODO: Change T return value to Pair or something like that. This would make OptionalValue easier to implement as the Boolean would indicate if s should be consumed or not - 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 tabCompletes(K sender, String[] previousArguments, String s); + @Deprecated + default Collection tabCompletes(K sender, String[] previousArguments, String s) { + throw new IllegalArgumentException("This method is deprecated and should not be used anymore!"); + } + + default Collection tabCompletes(K sender, PreviousArguments previousArguments, String s) { + return tabCompletes(sender, previousArguments.userArgs, s); + } } diff --git a/src/de/steamwar/command/CommandFrameworkException.java b/src/de/steamwar/command/CommandFrameworkException.java index 53ee025..ea3d835 100644 --- a/src/de/steamwar/command/CommandFrameworkException.java +++ b/src/de/steamwar/command/CommandFrameworkException.java @@ -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; diff --git a/src/de/steamwar/command/CommandMetaData.java b/src/de/steamwar/command/CommandMetaData.java index c7f79c7..fdb1ea2 100644 --- a/src/de/steamwar/command/CommandMetaData.java +++ b/src/de/steamwar/command/CommandMetaData.java @@ -47,9 +47,34 @@ public @interface CommandMetaData { } /** - * This annotation can be used in conjunction with a class that implement {@link AbstractValidator} to - * create custom validator short hands for commands. The validator class itself should contain a constructor - * with two parameters the first is the annotation this annotation is used on and the second is of type {@link 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: + *
    + *
  • Annotation this annotation annotates
  • + *
  • {@link Class}
  • + *
  • {@link AbstractTypeMapper}, optional, if not present only one per parameter
  • + *
  • {@link java.util.Map} with types {@link String} and {@link AbstractValidator}
  • + *
+ */ + @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: + *
    + *
  • Annotation this annotation annotates
  • + *
  • {@link Class}
  • + *
  • {@link java.util.Map} with types {@link String} and {@link AbstractValidator}
  • + *
*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 2969872..5a056d2 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -32,7 +32,8 @@ import java.util.function.Consumer; class CommandPart { - 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 { @@ -86,7 +87,7 @@ class CommandPart { 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); + CheckArgumentResult validArgument = checkArgument(null, sender, args, current, i); if (!validArgument.success) throw new CommandParseException(); Array.set(array, i - startIndex, validArgument.value); } @@ -99,16 +100,16 @@ class CommandPart { 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(); } @@ -126,13 +127,13 @@ class CommandPart { } } - public void generateTabComplete(List current, T sender, String[] args, int startIndex) { + public void generateTabComplete(List current, T sender, String[] args, List mappedArgs, int startIndex) { if (varArgType != null) { for (int i = startIndex; i < args.length - 1; i++) { - CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, i); + CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, mappedArgs, i); if (!validArgument.success) return; } - Collection strings = tabCompletes(sender, args, args.length - 1); + Collection strings = tabCompletes(sender, args, mappedArgs, args.length - 1); if (strings != null) { current.addAll(strings); } @@ -140,40 +141,43 @@ class CommandPart { } 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 strings = tabCompletes(sender, args, startIndex); + Collection 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 tabCompletes(T sender, String[] args, int startIndex) { + private Collection tabCompletes(T sender, String[] args, List mappedArgs, int startIndex) { return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> { 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 errors, T sender, String[] args, int index) { + private CheckArgumentResult checkArgument(Consumer errors, T sender, String[] args, List 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); } diff --git a/src/de/steamwar/command/ImplicitTypeValidators.java b/src/de/steamwar/command/ImplicitTypeValidators.java deleted file mode 100644 index 7204ce5..0000000 --- a/src/de/steamwar/command/ImplicitTypeValidators.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 . - */ - -package de.steamwar.command; - -import lombok.experimental.UtilityClass; - -import java.lang.reflect.Array; -import java.util.function.Function; - -@UtilityClass -public class ImplicitTypeValidators { - - public static class ErrorMessageValidator implements AbstractValidator { - - private AbstractSWCommand.ErrorMessage errorMessage; - - public ErrorMessageValidator(AbstractSWCommand.ErrorMessage errorMessage, Class type) { - 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; - } - } - - public static class MinValidator implements AbstractValidator { - - private int value; - private Function comparator; - - public MinValidator(AbstractSWCommand.Min min, Class clazz) { - this.value = min.inclusive() ? 0 : 1; - - if (clazz == int.class || clazz == Integer.class) { - comparator = number -> Integer.compare(number.intValue(), min.intValue()); - } else if (clazz == long.class || clazz == Long.class) { - comparator = number -> Long.compare(number.longValue(), min.longValue()); - } else if (clazz == float.class || clazz == Float.class) { - comparator = number -> Float.compare(number.floatValue(), min.floatValue()); - } else if (clazz == double.class || clazz == Double.class) { - comparator = number -> Double.compare(number.doubleValue(), min.doubleValue()); - } else { - throw new IllegalArgumentException("Min annotation is not supported for " + clazz); - } - } - - @Override - public boolean validate(T sender, Number value, MessageSender messageSender) { - return (comparator.apply(value).intValue()) >= this.value; - } - } - - public static class MaxValidator implements AbstractValidator { - - private int value; - private Function comparator; - - public MaxValidator(AbstractSWCommand.Max max, Class clazz) { - this.value = max.inclusive() ? 0 : -1; - - if (clazz == int.class || clazz == Integer.class) { - comparator = number -> Integer.compare(number.intValue(), max.intValue()); - } else if (clazz == long.class || clazz == Long.class) { - comparator = number -> Long.compare(number.longValue(), max.longValue()); - } else if (clazz == float.class || clazz == Float.class) { - comparator = number -> Float.compare(number.floatValue(), max.floatValue()); - } else if (clazz == double.class || clazz == Double.class) { - comparator = number -> Double.compare(number.doubleValue(), max.doubleValue()); - } else { - throw new IllegalArgumentException("Max annotation is not supported for " + clazz); - } - } - - @Override - public boolean validate(T sender, Number value, MessageSender messageSender) { - return (comparator.apply(value).intValue()) <= this.value; - } - } -} diff --git a/src/de/steamwar/command/PreviousArguments.java b/src/de/steamwar/command/PreviousArguments.java new file mode 100644 index 0000000..24701c0 --- /dev/null +++ b/src/de/steamwar/command/PreviousArguments.java @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +package de.steamwar.command; + +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 getMappedArg(int index) { + return (T) mappedArgs[mappedArgs.length - index - 1]; + } +} diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 2f16d7a..5bedbe6 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -82,49 +82,7 @@ public class SWCommandUtils { MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper); } - public static AbstractTypeMapper getTypeMapper(Parameter parameter, Map> 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>) 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 tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value())); - Set 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 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 AbstractTypeMapper getTypeMapper(String name, Map> localTypeMapper) { AbstractTypeMapper typeMapper = localTypeMapper.getOrDefault(name, (AbstractTypeMapper) MAPPER_FUNCTIONS.getOrDefault(name, null)); if (typeMapper == null) { throw new IllegalArgumentException("No mapper found for " + name); @@ -132,40 +90,24 @@ public class SWCommandUtils { return typeMapper; } - public static AbstractValidator getValidator(Parameter parameter, Map> localValidator) { + public static AbstractTypeMapper getTypeMapper(Parameter parameter, Map> localTypeMapper) { Class clazz = parameter.getType(); - - 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 (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); } - return null; + if (clazz.isEnum() && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { + return createEnumMapper((Class>) clazz); + } + return getTypeMapper(clazz.getTypeName(), localTypeMapper); } - public static AbstractValidator getErrorMessage(Parameter parameter) { - AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class); - if (errorMessage != null) { - return (AbstractValidator) (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; - } - - private static AbstractValidator getValidator(String s, Map> localGuardChecker) { - AbstractValidator validator = localGuardChecker.getOrDefault(s, (AbstractValidator) VALIDATOR_FUNCTIONS.getOrDefault(s, null)); - if (validator == null) { + public static AbstractValidator getValidator(AbstractSWCommand.Validator validator, Class type, Map> localValidator) { + String s = validator.value() != null && !validator.value().isEmpty() ? validator.value() : type.getTypeName(); + AbstractValidator concreteValidator = localValidator.getOrDefault(s, (AbstractValidator) VALIDATOR_FUNCTIONS.getOrDefault(s, null)); + if (concreteValidator == null) { throw new IllegalArgumentException("No validator found for " + s); } - return validator; + return concreteValidator; } public static void addMapper(Class clazz, AbstractTypeMapper mapper) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index da3576e..961cf29 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -61,7 +61,10 @@ public class SubCommand { Parameter[] parameters = method.getParameters(); comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : -parameters.length; - validator = (AbstractValidator) SWCommandUtils.getValidator(parameters[0], localValidator); + AbstractSWCommand.Validator validator = parameters[0].getAnnotation(AbstractSWCommand.Validator.class); + if (validator != null) { + this.validator = (AbstractValidator) SWCommandUtils.getValidator(validator, parameters[0].getType(), localValidator); + } commandPart = generateCommandPart(abstractSWCommand, subCommand, parameters, localTypeMapper, localValidator); @@ -120,7 +123,7 @@ public class SubCommand { return null; } List list = new ArrayList<>(); - commandPart.generateTabComplete(list, sender, args, 0); + commandPart.generateTabComplete(list, sender, args, new ArrayList<>(), 0); return list; } @@ -141,8 +144,7 @@ public class SubCommand { } for (int i = 1; i < parameters.length; i++) { Parameter parameter = parameters[i]; - AbstractTypeMapper typeMapper = SWCommandUtils.getTypeMapper(parameter, localTypeMapper); - AbstractValidator validator = (AbstractValidator) SWCommandUtils.getValidator(parameter, localValidator); + AbstractTypeMapper typeMapper = handleImplicitTypeMapper(parameter, localTypeMapper); Class varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null; AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class); @@ -151,10 +153,7 @@ public class SubCommand { if (parameter.getAnnotation(AbstractSWCommand.Quotable.class) == null) { commandPart.addValidator((AbstractValidator) STRING_SPACE_FILTER); } - handleImplicitTypeValidator(parameter, commandPart, true); - commandPart.addValidator(validator); - commandPart.addValidator((AbstractValidator) SWCommandUtils.getErrorMessage(parameter)); - handleImplicitTypeValidator(parameter, commandPart, false); + handleImplicitTypeValidator(parameter, commandPart, localValidator); if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) { commandPart.addValidator((AbstractValidator) NULL_FILTER); } @@ -169,29 +168,72 @@ public class SubCommand { return first; } - private static void handleImplicitTypeValidator(Parameter parameter, CommandPart commandPart, boolean beforeValidatorAnnotation) { + private static AbstractTypeMapper handleImplicitTypeMapper(Parameter parameter, Map> localTypeMapper) { + Class type = parameter.getType(); + if (parameter.isVarArgs()) { + type = type.getComponentType(); + } + + Annotation[] annotations = parameter.getAnnotations(); + Constructor sourceConstructor = null; + Annotation sourceAnnotation = null; + List> parentConstructors = new ArrayList<>(); + List 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 current; + if (sourceAnnotation != null) { + current = createInstance(sourceConstructor, sourceAnnotation, type, localTypeMapper); + } else { + current = (AbstractTypeMapper) 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 void handleImplicitTypeValidator(Parameter parameter, CommandPart commandPart, Map> localValidator) { Annotation[] annotations = parameter.getAnnotations(); Map>> validators = new HashMap<>(); for (Annotation annotation : annotations) { CommandMetaData.ImplicitValidator implicitValidator = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitValidator.class); if (implicitValidator == null) continue; - if (beforeValidatorAnnotation && implicitValidator.order() >= 0) continue; - if (!beforeValidatorAnnotation && implicitValidator.order() <= 0) continue; Class clazz = implicitValidator.handler(); if (!AbstractValidator.class.isAssignableFrom(clazz)) continue; Constructor[] constructors = clazz.getConstructors(); if (constructors.length != 1) continue; - Constructor constructor = constructors[0]; - Class[] parameterTypes = constructor.getParameterTypes(); - if (parameterTypes.length != 2) continue; - AbstractValidator validator; - if (!annotation.annotationType().isAssignableFrom(parameterTypes[0])) continue; - if (!Class.class.isAssignableFrom(parameterTypes[1])) continue; - try { - validator = (AbstractValidator) constructor.newInstance(annotation, commandPart.getType()); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } + AbstractValidator validator = createInstance(constructors[0], annotation, commandPart.getType(), localValidator); validators.computeIfAbsent(implicitValidator.order(), integer -> new ArrayList<>()).add(validator); } List keys = new ArrayList<>(validators.keySet()); @@ -204,42 +246,33 @@ public class SubCommand { } } + private static T createInstance(Constructor constructor, Object... parameter) { + Class[] types = constructor.getParameterTypes(); + List 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 NULL_FILTER = (sender, value, messageSender) -> value != null; private static final AbstractValidator STRING_SPACE_FILTER = (sender, value, messageSender) -> { if (!(value instanceof String)) return true; return !((String) value).contains(" "); }; - - private static AbstractValidator createMinValidator(Class clazz, AbstractSWCommand.Min min) { - Function comparator; - if (clazz == int.class || clazz == Integer.class) { - comparator = number -> Integer.compare(number.intValue(), min.intValue()); - } else if (clazz == long.class || clazz == Long.class) { - comparator = number -> Long.compare(number.longValue(), min.longValue()); - } else if (clazz == float.class || clazz == Float.class) { - comparator = number -> Float.compare(number.floatValue(), min.floatValue()); - } else if (clazz == double.class || clazz == Double.class) { - comparator = number -> Double.compare(number.doubleValue(), min.doubleValue()); - } else { - throw new IllegalArgumentException("Min annotation is not supported for " + clazz); - } - return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() >= (min.inclusive() ? 0 : 1); - } - - private static AbstractValidator createMaxValidator(Class clazz, AbstractSWCommand.Max max) { - Function comparator; - if (clazz == int.class || clazz == Integer.class) { - comparator = number -> Integer.compare(number.intValue(), max.intValue()); - } else if (clazz == long.class || clazz == Long.class) { - comparator = number -> Long.compare(number.longValue(), max.longValue()); - } else if (clazz == float.class || clazz == Float.class) { - comparator = number -> Float.compare(number.floatValue(), max.floatValue()); - } else if (clazz == double.class || clazz == Double.class) { - comparator = number -> Double.compare(number.doubleValue(), max.doubleValue()); - } else { - throw new IllegalArgumentException("Max annotation is not supported for " + clazz); - } - return (sender, value, messageSender) -> comparator.apply((Number) value).intValue() <= (max.inclusive() ? 0 : -1); - } } diff --git a/testsrc/de/steamwar/command/BetterExceptionCommand.java b/testsrc/de/steamwar/command/BetterExceptionCommand.java index d014186..feb47fc 100644 --- a/testsrc/de/steamwar/command/BetterExceptionCommand.java +++ b/testsrc/de/steamwar/command/BetterExceptionCommand.java @@ -39,7 +39,7 @@ public class BetterExceptionCommand extends TestSWCommand { public TestTypeMapper tabCompleteException() { return new TestTypeMapper() { @Override - public String map(String sender, String[] previousArguments, String s) { + public String map(String sender, PreviousArguments previousArguments, String s) { return null; } @@ -50,7 +50,7 @@ public class BetterExceptionCommand extends TestSWCommand { } @Override - public Collection tabCompletes(String sender, String[] previousArguments, String s) { + public Collection tabCompletes(String sender, PreviousArguments previousArguments, String s) { throw new SecurityException(); } }; diff --git a/testsrc/de/steamwar/command/CacheCommand.java b/testsrc/de/steamwar/command/CacheCommand.java index 1771bca..5057ffd 100644 --- a/testsrc/de/steamwar/command/CacheCommand.java +++ b/testsrc/de/steamwar/command/CacheCommand.java @@ -44,12 +44,12 @@ public class CacheCommand extends TestSWCommand { System.out.println("TypeMapper register"); return new TestTypeMapper() { @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 tabCompletes(String sender, String[] previousArguments, String s) { + public Collection tabCompletes(String sender, PreviousArguments previousArguments, String s) { return Arrays.asList(count.getAndIncrement() + ""); } }; diff --git a/testsrc/de/steamwar/command/NullMapperCommand.java b/testsrc/de/steamwar/command/NullMapperCommand.java index e47eab7..30f4061 100644 --- a/testsrc/de/steamwar/command/NullMapperCommand.java +++ b/testsrc/de/steamwar/command/NullMapperCommand.java @@ -43,7 +43,7 @@ public class NullMapperCommand extends TestSWCommand { public TestTypeMapper typeMapper() { return new TestTypeMapper() { @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 tabCompletes(String sender, String[] previousArguments, String s) { + public Collection tabCompletes(String sender, PreviousArguments previousArguments, String s) { return null; } }; From c94ee5c55f25caa053eae3a8b56fda7b4d707f14 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sun, 15 Jan 2023 17:55:17 +0100 Subject: [PATCH 12/84] Add Copyright --- .../steamwar/sql/EffectiveSchematicNode.java | 267 ------------------ src/de/steamwar/sql/SchematicNode.java | 184 ++++++++++-- 2 files changed, 163 insertions(+), 288 deletions(-) delete mode 100644 src/de/steamwar/sql/EffectiveSchematicNode.java diff --git a/src/de/steamwar/sql/EffectiveSchematicNode.java b/src/de/steamwar/sql/EffectiveSchematicNode.java deleted file mode 100644 index 587034e..0000000 --- a/src/de/steamwar/sql/EffectiveSchematicNode.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - This file is a part of the SteamWar software. - - Copyright (C) 2023 SteamWar.de-Serverteam - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -package de.steamwar.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.*; -import java.util.stream.Collectors; - -import static de.steamwar.sql.SchematicNode.TAB_CACHE; - -public class EffectiveSchematicNode { - - private static final Table table = new Table<>(EffectiveSchematicNode.class, "EffectiveSchematicNode"); - - private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - - private static final SelectStatement list_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null ORDER BY NodeName"); - private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? ORDER BY NodeName"); - private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? AND NodeName = ? ORDER BY NodeName"); - private static final SelectStatement byParentName_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null AND NodeName = ? ORDER BY NodeName"); - private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); - private static final SelectStatement accessibleByUserTypeParent = 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId"); - private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode = ?"); - private static final SelectStatement accessibleByUserTypeInParent_null = 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode is null"); - private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); - private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); - private static final SelectStatement 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 FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); - - public static List getAll(SteamwarUser user) { - return all.listSelect(user); - } - - public static Map> getAllMap(SteamwarUser user) { - return map(all.listSelect(user)); - } - - public static List list(SteamwarUser user, Integer schematicId) { - if(schematicId == null || schematicId == 0) { - return list_null.listSelect(user); - } else { - return list.listSelect(user, schematicId); - } - } - - public static Optional byParentName(SteamwarUser user, Integer schematicId, String name) { - if(schematicId == null || schematicId == 0) { - return Optional.ofNullable(byParentName_null.select(user, name)); - } else { - return Optional.ofNullable(byParentName.select(user, schematicId, name)); - } - } - - public static List accessibleByUserType(SteamwarUser user, SchematicType type) { - return accessibleByUserType.listSelect(type, user, user, user); - } - - public static Map> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) { - return map(accessibleByUserTypeParent.listSelect(type, user, user, user)); - } - - public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) { - return schematicAccessibleForUser.select(user, schematicId) != null; - } - - public static List accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) { - if(parentId == null || parentId == 0) { - return accessibleByUserTypeInParent_null.listSelect(type, user, user, user); - } else { - return accessibleByUserTypeInParent.listSelect(type, user, user, user, parentId); - } - } - - public static Optional byIdAndUser(SteamwarUser user, Integer id) { - return Optional.ofNullable(byIdAndUser.select(user, id)); - } - - public static List parentsOfNode(SteamwarUser user, Integer id) { - return allParentsOfNode.listSelect(id, user, user, user).stream().filter(n -> n.getNodeId() != id).collect(Collectors.toList()); - } - - private static Map> map(List in) { - Map> map = new HashMap<>(); - for (EffectiveSchematicNode effectiveSchematicNode : in) { - map.computeIfAbsent(effectiveSchematicNode.getNodeParent().orElse(0), k -> new ArrayList<>()).add(effectiveSchematicNode); - } - return map; - } - - @Getter - @Field - private final int nodeId; - @Getter - @Field - private final int nodeOwner; - @Getter - @Field - private final int effectiveOwner; - @Getter - @Field - private final String nodeName; - @Field(nullable = true) - private final Integer parentNode; - @Getter - @Field - private final Timestamp lastUpdate; - @Field - private final String nodeItem; - @Field(nullable = true) - private final SchematicType nodeType; - @Field - private final int nodeRank; - - private String brCache = null; - - public EffectiveSchematicNode(int nodeId, int nodeOwner, int effectiveOwner, String nodeName, Integer nodeParent, Timestamp lastUpdate, String nodeItem, SchematicType nodeType, int nodeRank) { - this.nodeId = nodeId; - this.nodeOwner = nodeOwner; - this.effectiveOwner = effectiveOwner; - this.nodeName = nodeName; - this.parentNode = nodeParent; - this.lastUpdate = lastUpdate; - this.nodeItem = nodeItem; - this.nodeType = nodeType; - this.nodeRank = nodeRank; - } - - public boolean isDir() { - return nodeType == null; - } - - public Optional getNodeParent() { - return Optional.ofNullable(parentNode); - } - - public Optional getParentNode() { - return byIdAndUser(SteamwarUser.get(effectiveOwner), parentNode); - } - - public SchematicType getNodeType() { - if(isDir()) { - throw new IllegalStateException("This node is a directory"); - } - return nodeType; - } - - public int getNodeRank() { - if(isDir()) { - throw new IllegalStateException("This node is a directory"); - } - return nodeRank; - } - - public SchematicNode toSchematicNode() { - return SchematicNode.getSchematicNode(nodeId); - } - - public boolean isWritable() { - return effectiveOwner == nodeOwner; - } - - public String getNodeItem() { - if (nodeItem.isEmpty()) { - return isDir() ? "CHEST" : "CAULDRON_ITEM"; - } - return nodeItem; - } - - public static List 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 list = new ArrayList<>(); - if (s.contains("/")) { - String preTab = s.substring(0, s.lastIndexOf("/") + 1); - Optional pa = EffectiveSchematicNode.getNodeFromPath(user, preTab); - if (!pa.isPresent()) return Collections.emptyList(); - List nodes = EffectiveSchematicNode.list(user, pa.get().getNodeId()); - nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs())); - } else { - List nodes = EffectiveSchematicNode.list(user, 0); - nodes.forEach(node -> list.add((sws ? "/" : "") + node.getNodeName() + (node.isDir() ? "/" : ""))); - } - list.remove("//copy"); - TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list); - return list; - } - - public String generateBreadcrumbs() { - if(brCache == null) { - brCache = generateBreadcrumbs("/"); - } - return brCache; - } - - public String generateBreadcrumbs(String split) { - StringBuilder builder = new StringBuilder(getNodeName()); - Optional currentNode = Optional.of(this); - if(currentNode.map(EffectiveSchematicNode::isDir).orElse(false)) { - builder.append("/"); - } - while (currentNode.isPresent()) { - currentNode = currentNode.flatMap(EffectiveSchematicNode::getNodeParent).flatMap(integer -> byIdAndUser(SteamwarUser.get(effectiveOwner), integer)); - currentNode.ifPresent(node -> builder.insert(0, '/').insert(0, node.getNodeName())); - } - return builder.toString(); - } - - public static Optional 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 Optional.empty(); - } - if (s.contains("/")) { - String[] layers = s.split("/"); - Optional currentNode = EffectiveSchematicNode.byParentName(user, 0, layers[0]); - for (int i = 1; i < layers.length; i++) { - int finalI = i; - Optional node = currentNode.flatMap(effectiveSchematicNode -> EffectiveSchematicNode.byParentName(user, effectiveSchematicNode.getNodeId(), layers[finalI])); - if (!node.isPresent()) { - return Optional.empty(); - } else { - currentNode = node; - if (!currentNode.map(EffectiveSchematicNode::isDir).orElse(false) && i != layers.length - 1) { - return Optional.empty(); - } - } - } - return currentNode; - } else { - return EffectiveSchematicNode.byParentName(user, 0, s); - } - } -} diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 0299e77..4c6d826 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -21,6 +21,7 @@ package de.steamwar.sql; import de.steamwar.sql.internal.*; import lombok.AccessLevel; +import lombok.Getter; import lombok.Setter; import java.io.ByteArrayOutputStream; @@ -28,7 +29,6 @@ import java.io.PrintStream; import java.sql.Timestamp; import java.time.Instant; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -45,7 +45,7 @@ public class SchematicNode { private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"}; private static String nodeSelectCreator(String itemPrefix) { - return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + " FROM SchematicNode "; + return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + ", NodeOwner AS EffectiveOwner FROM SchematicNode "; } private static final Table table = new Table<>(SchematicNode.class); @@ -60,11 +60,19 @@ public class SchematicNode { private static final SelectStatement byParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL ORDER BY NodeName"); private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName"); private static final SelectStatement dirsByParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName"); - private static final SelectStatement byParentName = table.selectFields("NodeName", "ParentNode"); - private static final SelectStatement byParentName_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeName = ? AND ParentNode is NULL"); private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); - private static final SelectStatement allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); + private static final SelectStatement list_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null ORDER BY NodeName"); + private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? ORDER BY NodeName"); + private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement byParentName_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); + private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode = ?"); + private static final SelectStatement accessibleByUserTypeInParent_null = 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode is null"); + private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); + private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); + private static final SelectStatement 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 FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); static { NodeMember.init(); @@ -74,6 +82,9 @@ public class SchematicNode { private final int nodeId; @Field(keys = {"OwnerNameParent"}) private final int nodeOwner; + @Field + @Getter + private final int effectiveOwner; @Field(keys = {"OwnerNameParent"}) private String nodeName; @Field(keys = {"OwnerNameParent"}, nullable = true) @@ -94,9 +105,12 @@ public class SchematicNode { @Field(def = "1") private boolean nodeFormat; + private String brCache; + public SchematicNode( int nodeId, int nodeOwner, + int effectiveOwner, String nodeName, Integer parentNode, Timestamp lastUpdate, @@ -109,6 +123,7 @@ public class SchematicNode { ) { this.nodeId = nodeId; this.nodeOwner = nodeOwner; + this.effectiveOwner = effectiveOwner; this.nodeName = nodeName; this.parentNode = parentNode; this.nodeItem = nodeItem; @@ -120,6 +135,66 @@ public class SchematicNode { this.nodeFormat = nodeFormat; } + public static List getAll(SteamwarUser user) { + return all.listSelect(user); + } + + public static Map> getAllMap(SteamwarUser user) { + return map(getAll(user)); + } + + public static List list(SteamwarUser user, Integer schematicId) { + if(schematicId == null || schematicId == 0) { + return list_null.listSelect(user); + } else { + return list.listSelect(user, schematicId); + } + } + + public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { + if(schematicId == null || schematicId == 0) { + return byParentName_null.select(user, name); + } else { + return byParentName.select(user, schematicId, name); + } + } + + public static List accessibleByUserType(SteamwarUser user, SchematicType type) { + return accessibleByUserType.listSelect(type, user, user, user); + } + + public static Map> 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 accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) { + if(parentId == null || parentId == 0) { + return accessibleByUserTypeInParent_null.listSelect(type, user, user, user); + } else { + return accessibleByUserTypeInParent.listSelect(type, user, user, user, parentId); + } + } + + public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) { + return byIdAndUser.select(user, id); + } + + public static List parentsOfNode(SteamwarUser user, Integer id) { + return allParentsOfNode.listSelect(id, user, user, user).stream().filter(n -> n.getId() != id).collect(Collectors.toList()); + } + + private static Map> map(List in) { + Map> 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(), ""); } @@ -188,14 +263,12 @@ public class SchematicNode { return byId.select(id); } - @Deprecated public static List getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) { - return EffectiveSchematicNode.accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); + return accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent); } - @Deprecated public static List getAllAccessibleSchematicsOfType(int user, String schemType) { - return EffectiveSchematicNode.accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType)).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); + return accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType)); } public static List getAllSchematicsOfType(int owner, String schemType) { @@ -227,12 +300,12 @@ public class SchematicNode { @Deprecated public static List getSchematicsAccessibleByUser(int user, Integer parent) { - return EffectiveSchematicNode.list(SteamwarUser.get(user), parent).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); + return list(SteamwarUser.get(user), parent); } @Deprecated public static List getAllSchematicsAccessibleByUser(int user) { - return EffectiveSchematicNode.getAll(SteamwarUser.get(user)).stream().map(EffectiveSchematicNode::toSchematicNode).collect(Collectors.toList()); + return getAll(SteamwarUser.get(user)); } public static List getAllParentsOfNode(SchematicNode node) { @@ -243,9 +316,35 @@ public class SchematicNode { return allParentsOfNode.listSelect(node); } - @Deprecated public static SchematicNode getNodeFromPath(SteamwarUser user, String s) { - return EffectiveSchematicNode.getNodeFromPath(user, s).map(EffectiveSchematicNode::toSchematicNode).orElse(null); + 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 currentNode = Optional.ofNullable(SchematicNode.byParentName(user, 0, layers[0])); + for (int i = 1; i < layers.length; i++) { + int finalI = i; + Optional 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, 0, s); + } } public static List filterSchems(int user, Predicate filter) { @@ -262,11 +361,6 @@ public class SchematicNode { return finalList; } - @Deprecated - public static Integer countNodes() { - return -1; - } - public int getId() { return nodeId; } @@ -288,6 +382,10 @@ public class SchematicNode { return parentNode; } + public Optional getOptionalParent() { + return Optional.ofNullable(parentNode); + } + public void setParent(Integer parent) { this.parentNode = parent; updateDB(); @@ -424,12 +522,34 @@ public class SchematicNode { return ((SchematicNode) obj).getId() == nodeId; } + @Deprecated public String generateBreadcrumbs(SteamwarUser user) { - return EffectiveSchematicNode.byIdAndUser(user, nodeId).map(EffectiveSchematicNode::generateBreadcrumbs).orElse("/"); + return generateBreadcrumbs(); } + @Deprecated public String generateBreadcrumbs(String split, SteamwarUser user) { - return EffectiveSchematicNode.byIdAndUser(user, nodeId).map(node -> node.generateBreadcrumbs(split)).orElse("/"); + return generateBreadcrumbs(split); + } + + public String generateBreadcrumbs() { + if(brCache == null) { + brCache = generateBreadcrumbs("/"); + } + return brCache; + } + + public String generateBreadcrumbs(String split) { + StringBuilder builder = new StringBuilder(getName()); + Optional currentNode = Optional.of(this); + if(currentNode.map(SchematicNode::isDir).orElse(false)) { + builder.append(split); + } + while (currentNode.isPresent()) { + currentNode = currentNode.flatMap(SchematicNode::getOptionalParent).map(integer -> byIdAndUser(SteamwarUser.get(effectiveOwner), integer)); + currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName())); + } + return builder.toString(); } private static final List FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public")); @@ -458,7 +578,29 @@ public class SchematicNode { @Deprecated public static List getNodeTabcomplete(SteamwarUser user, String s) { - return EffectiveSchematicNode.getNodeTabcomplete(user, 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 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 nodes = SchematicNode.list(user, pa.getId()); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs())); + } else { + List nodes = SchematicNode.list(user, 0); + 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; } private static void rootWarning() { From d23f2563f69a973c35709a76c3b201704d6f6f39 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 15 Jan 2023 18:29:14 +0100 Subject: [PATCH 13/84] Add test for new system --- .../command/PreviousArgumentCommand.java | 54 ++++++++++++++++++ .../command/PreviousArgumentCommandTest.java | 56 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 testsrc/de/steamwar/command/PreviousArgumentCommand.java create mode 100644 testsrc/de/steamwar/command/PreviousArgumentCommandTest.java diff --git a/testsrc/de/steamwar/command/PreviousArgumentCommand.java b/testsrc/de/steamwar/command/PreviousArgumentCommand.java new file mode 100644 index 0000000..454283c --- /dev/null +++ b/testsrc/de/steamwar/command/PreviousArgumentCommand.java @@ -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 . + */ + +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 typeMapper() { + return new TestTypeMapper() { + @Override + public String map(String sender, PreviousArguments previousArguments, String s) { + return "RunTypeMapper_" + previousArguments.getMappedArg(0) + "_" + previousArguments.getMappedArg(0).getClass().getSimpleName(); + } + + @Override + public Collection tabCompletes(String sender, PreviousArguments previousArguments, String s) { + return Arrays.asList(previousArguments.getMappedArg(0) + ""); + } + }; + } +} diff --git a/testsrc/de/steamwar/command/PreviousArgumentCommandTest.java b/testsrc/de/steamwar/command/PreviousArgumentCommandTest.java new file mode 100644 index 0000000..941b1c3 --- /dev/null +++ b/testsrc/de/steamwar/command/PreviousArgumentCommandTest.java @@ -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 . + */ + +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 strings = command.tabComplete("", "", new String[]{"1", ""}); + assertTabCompletes(strings, "1"); + } + + @Test + public void testPrevArg2() { + PreviousArgumentCommand command = new PreviousArgumentCommand(); + List 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"); + } + } +} From 9276f79d93c1f3028871aa57e6c1c80d31d1d475 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sun, 15 Jan 2023 20:07:01 +0100 Subject: [PATCH 14/84] Utilize null safe equals --- src/de/steamwar/sql/internal/SelectStatement.java | 2 +- src/de/steamwar/sql/internal/Statement.java | 3 +++ src/de/steamwar/sql/internal/Table.java | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/sql/internal/SelectStatement.java b/src/de/steamwar/sql/internal/SelectStatement.java index e17dcb7..0b39160 100644 --- a/src/de/steamwar/sql/internal/SelectStatement.java +++ b/src/de/steamwar/sql/internal/SelectStatement.java @@ -31,7 +31,7 @@ public class SelectStatement extends Statement { private final Table table; SelectStatement(Table 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 "))); + this(table, "SELECT " + Arrays.stream(table.fields).map(f -> f.identifier).collect(Collectors.joining(", ")) + " FROM " + table.name + " WHERE " + Arrays.stream(kfields).map(f -> f + Statement.NULL_SAFE_EQUALS + "?").collect(Collectors.joining(" AND "))); } public SelectStatement(Table table, String sql) { diff --git a/src/de/steamwar/sql/internal/Statement.java b/src/de/steamwar/sql/internal/Statement.java index 6c44332..ff39a09 100644 --- a/src/de/steamwar/sql/internal/Statement.java +++ b/src/de/steamwar/sql/internal/Statement.java @@ -41,6 +41,7 @@ public class Statement implements AutoCloseable { static final Consumer> schemaCreator; static final String ON_DUPLICATE_KEY; static final UnaryOperator upsertWrapper; + public static final String NULL_SAFE_EQUALS; private static final boolean MYSQL_MODE; private static final boolean PRODUCTION_DATABASE; @@ -73,6 +74,7 @@ public class Statement implements AutoCloseable { schemaCreator = table -> {}; ON_DUPLICATE_KEY = " ON DUPLICATE KEY UPDATE "; upsertWrapper = f -> f + " = VALUES(" + f + ")"; + NULL_SAFE_EQUALS = " <=> "; } else { Connection connection; @@ -89,6 +91,7 @@ public class Statement implements AutoCloseable { schemaCreator = Table::ensureExistanceInSqlite; ON_DUPLICATE_KEY = " ON CONFLICT DO UPDATE SET "; upsertWrapper = f -> f + " = " + f; + NULL_SAFE_EQUALS = " IS "; } } diff --git a/src/de/steamwar/sql/internal/Table.java b/src/de/steamwar/sql/internal/Table.java index cbad10a..3040642 100644 --- a/src/de/steamwar/sql/internal/Table.java +++ b/src/de/steamwar/sql/internal/Table.java @@ -75,7 +75,7 @@ public class Table { } 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 "))); + return new Statement("UPDATE " + name + " SET " + Arrays.stream(fields).map(f -> f + " = ?").collect(Collectors.joining(", ")) + " WHERE " + Arrays.stream(kfields).map(f -> f + Statement.NULL_SAFE_EQUALS + "?").collect(Collectors.joining(" AND "))); } public Statement insert(String name) { @@ -100,7 +100,7 @@ public class Table { } public Statement deleteFields(String... kfields) { - return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND "))); + return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + Statement.NULL_SAFE_EQUALS + "?").collect(Collectors.joining(" AND "))); } void ensureExistanceInSqlite() { From 6a6483235c50c436a82d6471789dc2e5998cd4b1 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sun, 15 Jan 2023 20:11:41 +0100 Subject: [PATCH 15/84] Add Copyright --- src/de/steamwar/sql/SchematicNode.java | 50 ++++++++++---------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 4c6d826..0dadeb0 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -56,20 +56,15 @@ public class SchematicNode { private static final SelectStatement byId = table.select(Table.PRIMARY); private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); private static final SelectStatement byOwnerNameParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode is NULL"); - private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? ORDER BY NodeName"); - private static final SelectStatement byParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL ORDER BY NodeName"); - private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName"); - private static final SelectStatement dirsByParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName"); + private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode <=> ? ORDER BY NodeName"); + private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement list_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null ORDER BY NodeName"); - private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? ORDER BY NodeName"); - private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode = ? AND NodeName = ? ORDER BY NodeName"); - private static final SelectStatement byParentName_null = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode is null AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); + private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); - private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode = ?"); - private static final SelectStatement accessibleByUserTypeInParent_null = 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode is null"); + private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode" + Statement.NULL_SAFE_EQUALS + "?"); private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); @@ -135,6 +130,13 @@ public class SchematicNode { this.nodeFormat = nodeFormat; } + private static Integer nullOrNotNul(Integer i) { + if(i == null || i == 0) { + return null; + } + return i; + } + public static List getAll(SteamwarUser user) { return all.listSelect(user); } @@ -144,19 +146,11 @@ public class SchematicNode { } public static List list(SteamwarUser user, Integer schematicId) { - if(schematicId == null || schematicId == 0) { - return list_null.listSelect(user); - } else { - return list.listSelect(user, schematicId); - } + return list.listSelect(user, nullOrNotNul(schematicId)); } public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { - if(schematicId == null || schematicId == 0) { - return byParentName_null.select(user, name); - } else { - return byParentName.select(user, schematicId, name); - } + return byParentName.select(user, nullOrNotNul(schematicId), name); } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { @@ -172,11 +166,7 @@ public class SchematicNode { } public static List accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) { - if(parentId == null || parentId == 0) { - return accessibleByUserTypeInParent_null.listSelect(type, user, user, user); - } else { - return accessibleByUserTypeInParent.listSelect(type, user, user, user, parentId); - } + return accessibleByUserTypeInParent.listSelect(type, user, user, user, nullOrNotNul(parentId)); } public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) { @@ -227,18 +217,16 @@ public class SchematicNode { public static List getSchematicNodeInNode(Integer parent) { if(parent == null || parent == 0) { rootWarning(); - return byParent_null.listSelect(); } - return byParent.listSelect(parent); + return byParent.listSelect(nullOrNotNul(parent)); } public static List getSchematicDirectoryInNode(Integer parent) { if(parent == null || parent == 0) { rootWarning(); - return dirsByParent_null.listSelect(); } - return dirsByParent.listSelect(parent); + return dirsByParent.listSelect(nullOrNotNul(parent)); } @Deprecated @@ -254,9 +242,8 @@ public class SchematicNode { public static SchematicNode getSchematicNode(String name, Integer parent) { if(parent == null || parent == 0) { rootWarning(); - return byParentName_null.select(name); } - return byParentName.select(name, parent); + return byParentName.select(name, nullOrNotNul(parent)); } public static SchematicNode getSchematicNode(int id) { @@ -576,7 +563,6 @@ public class SchematicNode { return false; } - @Deprecated public static List getNodeTabcomplete(SteamwarUser user, String s) { boolean sws = s.startsWith("/"); if (sws) { From ad43512d30efa05f9905296ff739df61b80431a2 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sun, 15 Jan 2023 20:18:53 +0100 Subject: [PATCH 16/84] Add Copyright --- src/de/steamwar/sql/SchematicNode.java | 38 ++++---------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 0dadeb0..594b20a 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -55,7 +55,6 @@ public class SchematicNode { private static final SelectStatement byId = table.select(Table.PRIMARY); private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); - private static final SelectStatement byOwnerNameParent_null = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode is NULL"); private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode <=> ? ORDER BY NodeName"); private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); @@ -130,13 +129,6 @@ public class SchematicNode { this.nodeFormat = nodeFormat; } - private static Integer nullOrNotNul(Integer i) { - if(i == null || i == 0) { - return null; - } - return i; - } - public static List getAll(SteamwarUser user) { return all.listSelect(user); } @@ -146,11 +138,11 @@ public class SchematicNode { } public static List list(SteamwarUser user, Integer schematicId) { - return list.listSelect(user, nullOrNotNul(schematicId)); + return list.listSelect(user, schematicId); } public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { - return byParentName.select(user, nullOrNotNul(schematicId), name); + return byParentName.select(user, schematicId, name); } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { @@ -166,7 +158,7 @@ public class SchematicNode { } public static List accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) { - return accessibleByUserTypeInParent.listSelect(type, user, user, user, nullOrNotNul(parentId)); + return accessibleByUserTypeInParent.listSelect(type, user, user, user, parentId); } public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) { @@ -205,8 +197,6 @@ public class SchematicNode { } public static SchematicNode getSchematicNode(int owner, String name, Integer parent) { - if (parent == null || parent == 0) - return byOwnerNameParent_null.select(owner, name); return byOwnerNameParent.select(owner, name, parent); } @@ -215,18 +205,11 @@ public class SchematicNode { } public static List getSchematicNodeInNode(Integer parent) { - if(parent == null || parent == 0) { - rootWarning(); - } - - return byParent.listSelect(nullOrNotNul(parent)); + return byParent.listSelect(parent); } public static List getSchematicDirectoryInNode(Integer parent) { - if(parent == null || parent == 0) { - rootWarning(); - } - return dirsByParent.listSelect(nullOrNotNul(parent)); + return dirsByParent.listSelect(parent); } @Deprecated @@ -240,10 +223,7 @@ public class SchematicNode { } public static SchematicNode getSchematicNode(String name, Integer parent) { - if(parent == null || parent == 0) { - rootWarning(); - } - return byParentName.select(name, nullOrNotNul(parent)); + return byParentName.select(name, parent); } public static SchematicNode getSchematicNode(int id) { @@ -588,10 +568,4 @@ public class SchematicNode { TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list); return list; } - - private static void rootWarning() { - ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream(); - new Throwable().printStackTrace(new PrintStream(stacktraceOutput)); - SWException.log("PERFORMANCE!!! Getting all/weird subset of schematic nodes with parent NULL", stacktraceOutput.toString()); - } } From d56150a8b105f5ad63a8c3a21b6833f388a83225 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 15 Jan 2023 20:45:29 +0100 Subject: [PATCH 17/84] Fix one weird behaviour --- .../steamwar/command/PreviousArguments.java | 23 +++++++++++++++++++ src/de/steamwar/command/SWCommandUtils.java | 5 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/command/PreviousArguments.java b/src/de/steamwar/command/PreviousArguments.java index 24701c0..e9851c0 100644 --- a/src/de/steamwar/command/PreviousArguments.java +++ b/src/de/steamwar/command/PreviousArguments.java @@ -19,6 +19,10 @@ package de.steamwar.command; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + public class PreviousArguments { public final String[] userArgs; @@ -36,4 +40,23 @@ public class PreviousArguments { public T getMappedArg(int index) { return (T) mappedArgs[mappedArgs.length - index - 1]; } + + public Optional getFirst(Class clazz) { + for (Object o : mappedArgs) { + if (clazz.isInstance(o)) { + return Optional.of((T) o); + } + } + return Optional.empty(); + } + + public List getAll(Class clazz) { + List list = new ArrayList<>(); + for (Object o : mappedArgs) { + if (clazz.isInstance(o)) { + list.add((T) o); + } + } + return list; + } } diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 5bedbe6..32b7fcc 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -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() { @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 tabCompletes(Object sender, String[] previousArguments, String s) { + public Collection tabCompletes(Object sender, PreviousArguments previousArguments, String s) { return ((BiFunction>) tabCompleter).apply(sender, s); } }; From a3f13737e4031628b5f29ddc43250dbbc233f13e Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sun, 15 Jan 2023 20:46:11 +0100 Subject: [PATCH 18/84] Add Copyright --- src/de/steamwar/sql/NodeMember.java | 2 +- src/de/steamwar/sql/SchematicNode.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/de/steamwar/sql/NodeMember.java b/src/de/steamwar/sql/NodeMember.java index 19417da..a952028 100644 --- a/src/de/steamwar/sql/NodeMember.java +++ b/src/de/steamwar/sql/NodeMember.java @@ -85,7 +85,7 @@ public class NodeMember { } public void setParentId(Integer parentId) { - this.parentId = Optional.ofNullable(parentId).orElse(0) == 0 ? null : parentId; + this.parentId = parentId == null || parentId == 0 ? null : parentId; updateParent.update(this.parentId, nodeId, userId); } } diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 594b20a..9142154 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -24,8 +24,6 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; import java.sql.Timestamp; import java.time.Instant; import java.util.*; @@ -38,7 +36,7 @@ public class SchematicNode { 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)); } - protected static final Map>> TAB_CACHE = new HashMap<>(); + private static final Map>> TAB_CACHE = new HashMap<>(); public static void clear() { TAB_CACHE.clear(); } From 2e39d677d2c9b6518886db620e571f564009ca14 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sun, 15 Jan 2023 21:10:35 +0100 Subject: [PATCH 19/84] Add Copyright --- src/de/steamwar/sql/SchematicNode.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 9142154..b0064c6 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -41,10 +41,7 @@ public class SchematicNode { TAB_CACHE.clear(); } - private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"}; - private static String nodeSelectCreator(String itemPrefix) { - return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + ", NodeOwner AS EffectiveOwner FROM SchematicNode "; - } + 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 table = new Table<>(SchematicNode.class); private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType"); @@ -53,10 +50,10 @@ public class SchematicNode { private static final SelectStatement byId = table.select(Table.PRIMARY); private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); - private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode <=> ? ORDER BY NodeName"); - private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); - private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); - private static final SelectStatement byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName"); + private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); + private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); + private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); + private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); From d30650bad30d53b5fb6c4e5c9499b71c577cf849 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 16 Jan 2023 16:33:17 +0100 Subject: [PATCH 20/84] Fixes "0 wird nicht benutzt" --- src/de/steamwar/sql/NodeMember.java | 2 +- src/de/steamwar/sql/SchematicNode.java | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/de/steamwar/sql/NodeMember.java b/src/de/steamwar/sql/NodeMember.java index a952028..e25764e 100644 --- a/src/de/steamwar/sql/NodeMember.java +++ b/src/de/steamwar/sql/NodeMember.java @@ -85,7 +85,7 @@ public class NodeMember { } public void setParentId(Integer parentId) { - this.parentId = parentId == null || parentId == 0 ? null : parentId; + this.parentId = parentId; updateParent.update(this.parentId, nodeId, userId); } } diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index b0064c6..ca03c0a 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -48,7 +48,7 @@ public class SchematicNode { 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 byId = table.select(Table.PRIMARY); + private static final SelectStatement byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?"); private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); @@ -58,10 +58,10 @@ public class SchematicNode { private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); - private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode" + Statement.NULL_SAFE_EQUALS + "?"); + private static final SelectStatement 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 accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); - private static final SelectStatement 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 FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); + private static final SelectStatement 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(); @@ -71,7 +71,7 @@ public class SchematicNode { private final int nodeId; @Field(keys = {"OwnerNameParent"}) private final int nodeOwner; - @Field + @Field(def = "0") @Getter private final int effectiveOwner; @Field(keys = {"OwnerNameParent"}) @@ -161,7 +161,7 @@ public class SchematicNode { } public static List parentsOfNode(SteamwarUser user, Integer id) { - return allParentsOfNode.listSelect(id, user, user, user).stream().filter(n -> n.getId() != id).collect(Collectors.toList()); + return allParentsOfNode.listSelect(id, user, user, user); } private static Map> map(List in) { @@ -290,7 +290,7 @@ public class SchematicNode { } if (s.contains("/")) { String[] layers = s.split("/"); - Optional currentNode = Optional.ofNullable(SchematicNode.byParentName(user, 0, layers[0])); + Optional currentNode = Optional.ofNullable(SchematicNode.byParentName(user, null, layers[0])); for (int i = 1; i < layers.length; i++) { int finalI = i; Optional node = currentNode.map(effectiveSchematicNode -> SchematicNode.byParentName(user, effectiveSchematicNode.getId(), layers[finalI])); @@ -305,7 +305,7 @@ public class SchematicNode { } return currentNode.orElse(null); } else { - return SchematicNode.byParentName(user, 0, s); + return SchematicNode.byParentName(user, null, s); } } @@ -484,14 +484,12 @@ public class SchematicNode { return ((SchematicNode) obj).getId() == nodeId; } - @Deprecated public String generateBreadcrumbs(SteamwarUser user) { - return generateBreadcrumbs(); + return byIdAndUser(user, nodeId).generateBreadcrumbs(); } - @Deprecated public String generateBreadcrumbs(String split, SteamwarUser user) { - return generateBreadcrumbs(split); + return byIdAndUser(user, nodeId).generateBreadcrumbs(split); } public String generateBreadcrumbs() { @@ -556,7 +554,7 @@ public class SchematicNode { List nodes = SchematicNode.list(user, pa.getId()); nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs())); } else { - List nodes = SchematicNode.list(user, 0); + List nodes = SchematicNode.list(user, null); nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); } list.remove("//copy"); From 785f31a728726265d7075fb6d92e9c794cc66b54 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 16 Jan 2023 16:40:01 +0100 Subject: [PATCH 21/84] Fixes "0 wird nicht benutzt" --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index ca03c0a..5a8258f 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -49,7 +49,7 @@ public class SchematicNode { private static final Statement delete = table.delete(Table.PRIMARY); private static final SelectStatement byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?"); - private static final SelectStatement byOwnerNameParent = table.select("OwnerNameParent"); + private static final SelectStatement byOwnerNameParent = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode = ?"); private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); From 4422c7c0ba55eb2a25c142531b5c59a09cf3f4c1 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 17 Jan 2023 18:20:24 +0100 Subject: [PATCH 22/84] Hotfix statement --- src/de/steamwar/sql/SchematicNode.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 5a8258f..2709399 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -28,7 +28,6 @@ import java.sql.Timestamp; import java.time.Instant; import java.util.*; import java.util.function.Predicate; -import java.util.stream.Collectors; public class SchematicNode { @@ -141,7 +140,7 @@ public class SchematicNode { } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { - return accessibleByUserType.listSelect(type, user, user, user); + return accessibleByUserType.listSelect(type, user); } public static Map> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) { From 1cf75f37422f695822a5e5aa5fd9ad85eaee92f9 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 17 Jan 2023 18:27:29 +0100 Subject: [PATCH 23/84] Hotfix AbstractSWCommand --- src/de/steamwar/command/AbstractSWCommand.java | 2 +- src/de/steamwar/sql/SchematicNode.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index bf65ec8..ef0ccf1 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -95,7 +95,7 @@ public abstract class AbstractSWCommand { list.add(arg); } if (quote) { - commandSystemWarning(() -> "Missing closing quote in command"); + builder.append("\""); } if (builder.length() > 0) { list.add(builder.toString()); diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 2709399..ed9ca39 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -140,7 +140,7 @@ public class SchematicNode { } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { - return accessibleByUserType.listSelect(type, user); + return accessibleByUserType.listSelect(user, type); } public static Map> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) { From ee35d659924c67c042eb3b9a04c2bc79f20cca1a Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 17 Jan 2023 19:26:19 +0100 Subject: [PATCH 24/84] Revert to null unsafe default --- src/de/steamwar/sql/internal/SelectStatement.java | 2 +- src/de/steamwar/sql/internal/Table.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/sql/internal/SelectStatement.java b/src/de/steamwar/sql/internal/SelectStatement.java index 0b39160..e17dcb7 100644 --- a/src/de/steamwar/sql/internal/SelectStatement.java +++ b/src/de/steamwar/sql/internal/SelectStatement.java @@ -31,7 +31,7 @@ public class SelectStatement extends Statement { private final Table table; SelectStatement(Table 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 + Statement.NULL_SAFE_EQUALS + "?").collect(Collectors.joining(" AND "))); + 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 table, String sql) { diff --git a/src/de/steamwar/sql/internal/Table.java b/src/de/steamwar/sql/internal/Table.java index 3040642..cbad10a 100644 --- a/src/de/steamwar/sql/internal/Table.java +++ b/src/de/steamwar/sql/internal/Table.java @@ -75,7 +75,7 @@ public class Table { } 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 + Statement.NULL_SAFE_EQUALS + "?").collect(Collectors.joining(" AND "))); + 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) { @@ -100,7 +100,7 @@ public class Table { } public Statement deleteFields(String... kfields) { - return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + Statement.NULL_SAFE_EQUALS + "?").collect(Collectors.joining(" AND "))); + return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND "))); } void ensureExistanceInSqlite() { From d6b3dbc446895dad8706405f306d1f68ec2128d6 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 19:38:50 +0100 Subject: [PATCH 25/84] NodeMember Fix #1 --- src/de/steamwar/sql/SchematicNode.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index ed9ca39..e57290f 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,7 +54,7 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement list = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); + private static final SelectStatement 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 <=> ? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE ParentNode <=> ? AND NodeOwner = ?"); private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 + "?"); @@ -132,7 +132,7 @@ public class SchematicNode { } public static List list(SteamwarUser user, Integer schematicId) { - return list.listSelect(user, schematicId); + return list.listSelect(user, schematicId, user, user, schematicId, user); } public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { @@ -501,11 +501,12 @@ public class SchematicNode { public String generateBreadcrumbs(String split) { StringBuilder builder = new StringBuilder(getName()); Optional currentNode = Optional.of(this); + List parents = SchematicNode.parentsOfNode(SteamwarUser.get(effectiveOwner), nodeId); if(currentNode.map(SchematicNode::isDir).orElse(false)) { builder.append(split); } while (currentNode.isPresent()) { - currentNode = currentNode.flatMap(SchematicNode::getOptionalParent).map(integer -> byIdAndUser(SteamwarUser.get(effectiveOwner), integer)); + currentNode = currentNode.flatMap(SchematicNode::getOptionalParent).flatMap(integer -> parents.stream().filter(schematicNode -> schematicNode.getId() == integer).findFirst()); currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName())); } return builder.toString(); From ef26175fc3186064910c740ebf35604e39389542 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 19:47:47 +0100 Subject: [PATCH 26/84] NodeMember Fix #1 --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index e57290f..df7d4b4 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,7 +54,7 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 <=> ? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE ParentNode <=> ? AND NodeOwner = ?"); + private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ?"); private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 + "?"); From 4e65bfe0985e8f538ab3e2d66bf92dae918787ae Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 17 Jan 2023 19:59:49 +0100 Subject: [PATCH 27/84] Fix ordering --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index df7d4b4..2220146 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,7 +54,7 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ?"); + private static final SelectStatement 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 = ? ORDER BY NodeName UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? ORDER BY NodeName"); private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 + "?"); From 1a5455af93af567807f5c321594b61e2668cfb86 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 17 Jan 2023 20:00:37 +0100 Subject: [PATCH 28/84] HOTFIX --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 2220146..d909baa 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,7 +54,7 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 = ? ORDER BY NodeName UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay, NodeFormat FROM SchematicNode WHERE ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? ORDER BY NodeName"); + private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? ORDER BY NodeName"); private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 + "?"); From b6be12db92643d1053488fbf711221078368a768 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 20:49:46 +0100 Subject: [PATCH 29/84] Fix: Optimize generateBreadcrumbs --- src/de/steamwar/sql/SchematicNode.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index df7d4b4..06f1eb0 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -501,12 +501,20 @@ public class SchematicNode { public String generateBreadcrumbs(String split) { StringBuilder builder = new StringBuilder(getName()); Optional currentNode = Optional.of(this); - List parents = SchematicNode.parentsOfNode(SteamwarUser.get(effectiveOwner), nodeId); if(currentNode.map(SchematicNode::isDir).orElse(false)) { builder.append(split); } while (currentNode.isPresent()) { - currentNode = currentNode.flatMap(SchematicNode::getOptionalParent).flatMap(integer -> parents.stream().filter(schematicNode -> schematicNode.getId() == integer).findFirst()); + currentNode = currentNode.flatMap(SchematicNode::getOptionalParent).map(integer -> { + NodeMember member = NodeMember.getNodeMember(integer, nodeOwner); + if (member != null) { + integer = member.getParent().orElse(null); + if(integer == null) { + return null; + } + } + return SchematicNode.getSchematicNode(integer); + }); currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName())); } return builder.toString(); @@ -552,7 +560,8 @@ public class SchematicNode { SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab); if (pa == null) return Collections.emptyList(); List nodes = SchematicNode.list(user, pa.getId()); - nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs())); + String br = pa.generateBreadcrumbs(); + nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName())); } else { List nodes = SchematicNode.list(user, null); nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); From d959d8426b62497952ac691ab0649930abd5c0c4 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 21:06:51 +0100 Subject: [PATCH 30/84] Fix: Optimize getPath and fix generateBreadCrumbs --- src/de/steamwar/sql/SchematicNode.java | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index a284f17..9110b18 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -55,7 +55,7 @@ public class SchematicNode { private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? ORDER BY NodeName"); - private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? AND NodeName = ? ORDER BY NodeName"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); @@ -136,7 +136,7 @@ public class SchematicNode { } public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { - return byParentName.select(user, schematicId, name); + return byParentName.select(user, schematicId, user, name, user, schematicId, user, name); } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { @@ -505,16 +505,7 @@ public class SchematicNode { builder.append(split); } while (currentNode.isPresent()) { - currentNode = currentNode.flatMap(SchematicNode::getOptionalParent).map(integer -> { - NodeMember member = NodeMember.getNodeMember(integer, nodeOwner); - if (member != null) { - integer = member.getParent().orElse(null); - if(integer == null) { - return null; - } - } - return SchematicNode.getSchematicNode(integer); - }); + currentNode = currentNode.flatMap( schematicNode -> Optional.of(NodeMember.getNodeMember(schematicNode.getId(), nodeOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode); currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName())); } return builder.toString(); From e880c2944bdec7519497cfc0e50cc40e9da219bc Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 21:12:11 +0100 Subject: [PATCH 31/84] Fix: Optimize getPath and fix generateBreadCrumbs --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 9110b18..ded3a17 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -505,7 +505,7 @@ public class SchematicNode { builder.append(split); } while (currentNode.isPresent()) { - currentNode = currentNode.flatMap( schematicNode -> Optional.of(NodeMember.getNodeMember(schematicNode.getId(), nodeOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode); + currentNode = currentNode.flatMap(schematicNode -> Optional.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), nodeOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode); currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName())); } return builder.toString(); From 47cf5d9e91fb659864e1288a043b96b14ab56170 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 21:42:57 +0100 Subject: [PATCH 32/84] Fix: Not Own Schematics not schowing --- src/de/steamwar/sql/SchematicNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index ded3a17..8fbcf6b 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,8 +54,8 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 ParentNode " + Statement.NULL_SAFE_EQUALS + "? AND NodeOwner = ? AND NodeName = ? ORDER BY NodeName"); + private static final SelectStatement 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 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 schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); @@ -132,11 +132,11 @@ public class SchematicNode { } public static List list(SteamwarUser user, Integer schematicId) { - return list.listSelect(user, schematicId, user, user, schematicId, user); + 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, name); + return byParentName.select(user, schematicId, user, user, schematicId, user, schematicId, schematicId, name); } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { @@ -552,7 +552,7 @@ public class SchematicNode { if (pa == null) return Collections.emptyList(); List nodes = SchematicNode.list(user, pa.getId()); String br = pa.generateBreadcrumbs(); - nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName())); + nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : ""))); } else { List nodes = SchematicNode.list(user, null); nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); From 1a8808fecdefbb875161ec8bb00e24143336ae2c Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 17 Jan 2023 21:48:59 +0100 Subject: [PATCH 33/84] HOTFIX --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 8fbcf6b..d65af22 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -136,7 +136,7 @@ public class SchematicNode { } public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) { - return byParentName.select(user, schematicId, user, user, schematicId, user, schematicId, schematicId, name); + return byParentName.select(user, schematicId, user, name, user, schematicId, user, schematicId, schematicId, name); } public static List accessibleByUserType(SteamwarUser user, SchematicType type) { From ee6412c34e2b7671e024875e5ccea4296a5fe3bc Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 17 Jan 2023 21:59:04 +0100 Subject: [PATCH 34/84] HOTFIX --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index d65af22..c348460 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -55,7 +55,7 @@ public class SchematicNode { private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); private static final SelectStatement 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 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 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 schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); From 04577cc076ee5d53dc1eea4fc338df8fbe9eb722 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 22:08:51 +0100 Subject: [PATCH 35/84] Fix: Performance of byIdAndUser --- src/de/steamwar/sql/SchematicNode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index d65af22..e465105 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,12 +54,12 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 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 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 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 schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); - private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); + private static final SelectStatement 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 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 { From 58d6506a44e4988955a4784ae8174200d0973883 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 17 Jan 2023 22:52:47 +0100 Subject: [PATCH 36/84] Fix: Wrong Owner in Breadcrumbs --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 88dbaa4..110d3d9 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -505,7 +505,7 @@ public class SchematicNode { builder.append(split); } while (currentNode.isPresent()) { - currentNode = currentNode.flatMap(schematicNode -> Optional.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), nodeOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode); + 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(); From d12fbbd339f6f34108301aa08d1377eceea250a5 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 20 Jan 2023 11:08:31 +0100 Subject: [PATCH 37/84] Potential SQL connection churn fix Signed-off-by: Lixfel --- src/de/steamwar/sql/internal/Statement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/internal/Statement.java b/src/de/steamwar/sql/internal/Statement.java index f78f0d4..e842d5f 100644 --- a/src/de/steamwar/sql/internal/Statement.java +++ b/src/de/steamwar/sql/internal/Statement.java @@ -234,7 +234,7 @@ public class Statement implements AutoCloseable { private static Connection aquireConnection() { synchronized (connections) { - if(connections.isEmpty() && connectionBudget == 0) + while(connections.isEmpty() && connectionBudget == 0) waitOnConnections(); if(!connections.isEmpty()) { From a738dfa1c5540afa8ef8db04f81cd7e6d06a19ce Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Fri, 20 Jan 2023 19:38:51 +0100 Subject: [PATCH 38/84] Fix: Multiple //copy Schematics --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 110d3d9..0e64522 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -48,7 +48,7 @@ public class SchematicNode { private static final Statement delete = table.delete(Table.PRIMARY); private static final SelectStatement byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?"); - private static final SelectStatement byOwnerNameParent = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode = ?"); + private static final SelectStatement byOwnerNameParent = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode " + Statement.NULL_SAFE_EQUALS + "?"); private static final SelectStatement byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName"); private static final SelectStatement dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName"); private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); From 9992dfaf181b77526472616d6e7124bd3960509d Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 21 Jan 2023 17:06:56 +0100 Subject: [PATCH 39/84] Add better caching key normalization --- src/de/steamwar/command/AbstractTypeMapper.java | 9 +++++++++ src/de/steamwar/command/CommandPart.java | 2 +- src/de/steamwar/command/TabCompletionCache.java | 7 +++++-- testsrc/de/steamwar/command/CacheCommand.java | 6 +++++- testsrc/de/steamwar/command/CacheCommandTest.java | 8 ++++++++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index 0363cd0..ab9d116 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -50,4 +50,13 @@ public interface AbstractTypeMapper extends AbstractValidator { default Collection tabCompletes(K sender, PreviousArguments previousArguments, String s) { return tabCompletes(sender, previousArguments.userArgs, s); } + + /** + * Normalize the cache key by sender and user provided argument.
+ * Note: The CommandSender can be null!
+ * Returning null and the empty string are equivalent. + */ + default String normalize(K sender, String s) { + return s; + } } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 5a056d2..926ad2a 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -165,7 +165,7 @@ class CommandPart { } private Collection tabCompletes(T sender, String[] args, List mappedArgs, int startIndex) { - return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> { + return TabCompletionCache.tabComplete(sender, args[startIndex], (AbstractTypeMapper) typeMapper, command, () -> { try { return typeMapper.tabCompletes(sender, new PreviousArguments(Arrays.copyOf(args, startIndex), mappedArgs.toArray()), args[startIndex]); } catch (Throwable e) { diff --git a/src/de/steamwar/command/TabCompletionCache.java b/src/de/steamwar/command/TabCompletionCache.java index 6f2d0ff..4e50a18 100644 --- a/src/de/steamwar/command/TabCompletionCache.java +++ b/src/de/steamwar/command/TabCompletionCache.java @@ -51,6 +51,7 @@ public class TabCompletionCache { @AllArgsConstructor private static class Key { private Object sender; + private String arg; private AbstractTypeMapper typeMapper; } @@ -61,9 +62,11 @@ public class TabCompletionCache { private Collection tabCompletions; } - Collection tabComplete(Object sender, AbstractTypeMapper typeMapper, AbstractSWCommand command, Supplier> tabCompleteSupplier) { + Collection tabComplete(Object sender, String arg, AbstractTypeMapper typeMapper, AbstractSWCommand command, Supplier> 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 = global.contains(typeMapper) ? new Key(null, normalizedArg, typeMapper) : new Key(sender, normalizedArg, typeMapper); TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> { return new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get()); }); diff --git a/testsrc/de/steamwar/command/CacheCommand.java b/testsrc/de/steamwar/command/CacheCommand.java index 5057ffd..ee7a4cc 100644 --- a/testsrc/de/steamwar/command/CacheCommand.java +++ b/testsrc/de/steamwar/command/CacheCommand.java @@ -41,7 +41,6 @@ public class CacheCommand extends TestSWCommand { @Cached @Mapper(value = "int", local = true) public AbstractTypeMapper typeMapper() { - System.out.println("TypeMapper register"); return new TestTypeMapper() { @Override public Integer map(String sender, PreviousArguments previousArguments, String s) { @@ -52,6 +51,11 @@ public class CacheCommand extends TestSWCommand { public Collection tabCompletes(String sender, PreviousArguments previousArguments, String s) { return Arrays.asList(count.getAndIncrement() + ""); } + + @Override + public String normalize(String sender, String s) { + return ""; + } }; } } diff --git a/testsrc/de/steamwar/command/CacheCommandTest.java b/testsrc/de/steamwar/command/CacheCommandTest.java index 122528f..4a6e183 100644 --- a/testsrc/de/steamwar/command/CacheCommandTest.java +++ b/testsrc/de/steamwar/command/CacheCommandTest.java @@ -36,6 +36,14 @@ public class CacheCommandTest { assertThat(tabCompletions1, is(equalTo(tabCompletions2))); } + @Test + public void testCachingWithDifferentMessages() { + CacheCommand cmd = new CacheCommand(); + List tabCompletions1 = cmd.tabComplete("test", "", new String[]{""}); + List tabCompletions2 = cmd.tabComplete("test", "", new String[]{"0"}); + assertThat(tabCompletions1, is(equalTo(tabCompletions2))); + } + @Test public void testCachingWithDifferentSenders() { CacheCommand cmd = new CacheCommand(); From 8100f9c044c8d067380f3d51479032974c00f565 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 21 Jan 2023 17:07:48 +0100 Subject: [PATCH 40/84] Retain current behaviour --- src/de/steamwar/command/AbstractTypeMapper.java | 2 +- testsrc/de/steamwar/command/CacheCommand.java | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/de/steamwar/command/AbstractTypeMapper.java b/src/de/steamwar/command/AbstractTypeMapper.java index ab9d116..040f303 100644 --- a/src/de/steamwar/command/AbstractTypeMapper.java +++ b/src/de/steamwar/command/AbstractTypeMapper.java @@ -57,6 +57,6 @@ public interface AbstractTypeMapper extends AbstractValidator { * Returning null and the empty string are equivalent. */ default String normalize(K sender, String s) { - return s; + return null; } } diff --git a/testsrc/de/steamwar/command/CacheCommand.java b/testsrc/de/steamwar/command/CacheCommand.java index ee7a4cc..7cfdb72 100644 --- a/testsrc/de/steamwar/command/CacheCommand.java +++ b/testsrc/de/steamwar/command/CacheCommand.java @@ -51,11 +51,6 @@ public class CacheCommand extends TestSWCommand { public Collection tabCompletes(String sender, PreviousArguments previousArguments, String s) { return Arrays.asList(count.getAndIncrement() + ""); } - - @Override - public String normalize(String sender, String s) { - return ""; - } }; } } From b4df616bc462540fd669e313b32facc42be9f4cb Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 21 Jan 2023 21:46:48 +0100 Subject: [PATCH 41/84] Fix some tab completion cache stuff --- src/de/steamwar/command/CommandPart.java | 2 +- src/de/steamwar/command/TabCompletionCache.java | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 926ad2a..b902892 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -165,7 +165,7 @@ class CommandPart { } private Collection tabCompletes(T sender, String[] args, List mappedArgs, int startIndex) { - return TabCompletionCache.tabComplete(sender, args[startIndex], (AbstractTypeMapper) typeMapper, command, () -> { + return TabCompletionCache.tabComplete(sender, args[startIndex], (AbstractTypeMapper) typeMapper, () -> { try { return typeMapper.tabCompletes(sender, new PreviousArguments(Arrays.copyOf(args, startIndex), mappedArgs.toArray()), args[startIndex]); } catch (Throwable e) { diff --git a/src/de/steamwar/command/TabCompletionCache.java b/src/de/steamwar/command/TabCompletionCache.java index 4e50a18..551ce19 100644 --- a/src/de/steamwar/command/TabCompletionCache.java +++ b/src/de/steamwar/command/TabCompletionCache.java @@ -57,22 +57,23 @@ public class TabCompletionCache { @AllArgsConstructor private static class TabCompletions { - private AbstractSWCommand command; private long timestamp; private Collection tabCompletions; } - Collection tabComplete(Object sender, String arg, AbstractTypeMapper typeMapper, AbstractSWCommand command, Supplier> tabCompleteSupplier) { + Collection tabComplete(Object sender, String arg, AbstractTypeMapper typeMapper, Supplier> tabCompleteSupplier) { if (!cached.contains(typeMapper)) return tabCompleteSupplier.get(); + String normalizedArg = typeMapper.normalize(sender, arg); if (normalizedArg == null) normalizedArg = ""; - Key key = global.contains(typeMapper) ? new Key(null, normalizedArg, typeMapper) : new Key(sender, normalizedArg, typeMapper); + 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; From 2cbd7b8117216dd9306c71edcfe900c762e7643e Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 21 Jan 2023 22:12:56 +0100 Subject: [PATCH 42/84] Add inverting of validator, removes more boilerplate --- src/de/steamwar/command/AbstractSWCommand.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index ef0ccf1..86a00f5 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -354,17 +354,21 @@ public abstract class AbstractSWCommand { boolean local() default false; + boolean invert() default false; + class Handler implements AbstractValidator { private AbstractValidator inner; + private boolean invert; public Handler(AbstractSWCommand.Validator validator, Class clazz, Map> localValidator) { inner = (AbstractValidator) SWCommandUtils.getValidator(validator, clazz, localValidator); + invert = validator.invert(); } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { - return inner.validate(sender, value, messageSender); + return inner.validate(sender, value, messageSender) ^ invert; } } } From 6cbee16c19bd60e3fa11c5bc24c83d5c27d156b0 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 13:45:22 +0100 Subject: [PATCH 43/84] Remove Quotable because it was a dumb idea to add Add AbstractSWCommand.Length and AbstractSWCommand.ArrayLength --- .../steamwar/command/AbstractSWCommand.java | 177 +++++++++++------- src/de/steamwar/command/CommandMetaData.java | 8 +- src/de/steamwar/command/SWCommandUtils.java | 10 +- src/de/steamwar/command/SubCommand.java | 8 - .../de/steamwar/command/ArgumentCommand.java | 2 +- .../steamwar/command/ArgumentCommandTest.java | 23 --- 6 files changed, 117 insertions(+), 111 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 86a00f5..9b0264f 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -21,12 +21,14 @@ 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.Function; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -67,48 +69,11 @@ public abstract class AbstractSWCommand { protected void sendMessage(T sender, String message, Object[] args) {} - private String[] quote(String[] args) { - List list = new ArrayList<>(); - StringBuilder builder = new StringBuilder(); - boolean quote = false; - for (String arg : args) { - if (arg.startsWith("\"") && arg.endsWith("\"")) { - list.add(arg); - continue; - } - if (arg.startsWith("\"")) { - quote = true; - builder.append(arg); - continue; - } - if (arg.endsWith("\"")) { - quote = false; - builder.append(" ").append(arg); - list.add(builder.toString()); - builder = new StringBuilder(); - continue; - } - if (quote) { - builder.append(" ").append(arg); - continue; - } - list.add(arg); - } - if (quote) { - builder.append("\""); - } - if (builder.length() > 0) { - list.add(builder.toString()); - } - return list.toArray(new String[0]); - } - protected final void execute(T sender, String alias, String[] args) { initialize(); - String[] finalArgs = quote(args); List errors = new ArrayList<>(); try { - if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, finalArgs))) { + if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) { errors.forEach(Runnable::run); } } catch (CommandFrameworkException e) { @@ -119,11 +84,10 @@ public abstract class AbstractSWCommand { protected final List tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException { initialize(); - String[] finalArgs = quote(args); String string = args[args.length - 1].toLowerCase(); return commandList.stream() .filter(s -> !s.noTabComplete) - .map(s -> s.tabComplete(sender, finalArgs)) + .map(s -> s.tabComplete(sender, args)) .filter(Objects::nonNull) .flatMap(Collection::stream) .filter(s -> !s.isEmpty()) @@ -185,9 +149,16 @@ public abstract class AbstractSWCommand { private boolean validateMethod(Method method) { if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class); - if (methodMetaData == null) return null; - if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return new Class[0]; - return methodMetaData.value(); + if (methodMetaData == null) return aClass -> true; + if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return aClass -> false; + return aClass -> { + 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()) { @@ -195,26 +166,32 @@ public abstract class AbstractSWCommand { if (parameter.isVarArgs()) type = type.getComponentType(); if (!checkType(parameter.getAnnotations(), type, annotation -> { CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class); - if (parameterMetaData == null) return null; - return parameterMetaData.value(); + if (parameterMetaData == null) return aClass -> true; + Class handler = parameterMetaData.handler(); + if (Predicate.class.isAssignableFrom(handler)) { + try { + return (Predicate>) handler.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } + } + return aClass -> { + 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, Function[]> toApplicableTypes, String warning) { + private boolean checkType(Annotation[] annotations, Class clazz, Function>> toApplicableTypes, String warning) { boolean valid = true; for (Annotation annotation : annotations) { - Class[] types = toApplicableTypes.apply(annotation); - if (types == null) continue; - boolean applicable = false; - for (Class type : types) { - if (type.isAssignableFrom(clazz)) { - applicable = true; - break; - } - } - if (!applicable) { + Predicate> predicate = toApplicableTypes.apply(annotation); + if (!predicate.test(clazz)) { commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'"); valid = false; } @@ -506,15 +483,6 @@ public abstract class AbstractSWCommand { protected @interface AllowNull { } - /** - * This annotation is used to mark a String to be quotable with multiple words. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.PARAMETER}) - @CommandMetaData.Parameter({String.class}) - protected @interface Quotable { - } - @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class}) @@ -586,4 +554,83 @@ public abstract class AbstractSWCommand { 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 implements AbstractTypeMapper { + + private int min; + private int max; + private AbstractTypeMapper inner; + + public Handler(Length length, AbstractTypeMapper 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 tabCompletes(T sender, PreviousArguments previousArguments, String s) { + List 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.ImplicitValidator(handler = ArrayLength.Handler.class, order = 2) + protected @interface ArrayLength { + int min() default 0; + int max() default Integer.MAX_VALUE; + + class Type implements Predicate> { + @Override + public boolean test(Class clazz) { + return clazz.isArray(); + } + } + + class Handler implements AbstractValidator { + + private int min; + private int max; + + public Handler(ArrayLength arrayLength) { + this.min = arrayLength.min(); + this.max = arrayLength.max(); + } + + @Override + public boolean validate(T sender, Object value, MessageSender messageSender) { + if (value == null) return false; + int length = Array.getLength(value); + return length >= min && length <= max; + } + } + } } diff --git a/src/de/steamwar/command/CommandMetaData.java b/src/de/steamwar/command/CommandMetaData.java index fdb1ea2..33909ac 100644 --- a/src/de/steamwar/command/CommandMetaData.java +++ b/src/de/steamwar/command/CommandMetaData.java @@ -19,10 +19,7 @@ package de.steamwar.command; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; public @interface CommandMetaData { @@ -43,7 +40,8 @@ public @interface CommandMetaData { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) @interface Parameter { - Class[] value(); + Class[] value() default {}; + Class handler() default void.class; } /** diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index 32b7fcc..c06d584 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -61,15 +61,7 @@ public class SWCommandUtils { 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 -> { - if (s.startsWith("\"") && s.endsWith("\"")) { - return s.substring(1, s.length() - 1); - } - if (s.startsWith("'") && s.endsWith("'")) { - return s.substring(1, s.length() - 1); - } - return s; - }, Collections::singletonList)); + MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList)); } public static , K, V> void init(SWTypeMapperCreator swTypeMapperCreator) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 961cf29..d74423b 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -150,9 +150,6 @@ public class SubCommand { CommandPart commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i); commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG()); - if (parameter.getAnnotation(AbstractSWCommand.Quotable.class) == null) { - commandPart.addValidator((AbstractValidator) STRING_SPACE_FILTER); - } handleImplicitTypeValidator(parameter, commandPart, localValidator); if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) { commandPart.addValidator((AbstractValidator) NULL_FILTER); @@ -270,9 +267,4 @@ public class SubCommand { } private static final AbstractValidator NULL_FILTER = (sender, value, messageSender) -> value != null; - - private static final AbstractValidator STRING_SPACE_FILTER = (sender, value, messageSender) -> { - if (!(value instanceof String)) return true; - return !((String) value).contains(" "); - }; } diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index ad1c1ec..a107270 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -54,7 +54,7 @@ public class ArgumentCommand extends TestSWCommand { } @Register - public void argument(String sender, @Quotable String arg) { + public void argument(String sender, String arg) { throw new ExecutionIdentifier("RunArgument with String"); } } diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index fd4fb5f..0af9327 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -94,29 +94,6 @@ public class ArgumentCommandTest { } } - @Test - public void testString() { - ArgumentCommand cmd = new ArgumentCommand(); - try { - cmd.execute("test", "", new String[]{"Hello World"}); - assert false; - } catch (Exception e) { - assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String"); - } - try { - cmd.execute("test", "", new String[]{"\"Hello World\""}); - assert false; - } catch (Exception e) { - assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String"); - } - try { - cmd.execute("test", "", new String[]{"\"Hello", "World\""}); - assert false; - } catch (Exception e) { - assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String"); - } - } - @Test public void testTabComplete() { ArgumentCommand cmd = new ArgumentCommand(); From c9d915b01e7ab4d065cc08c6e5e297d1c6181d0b Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 15:41:05 +0100 Subject: [PATCH 44/84] Fix stuff and update tests --- .../steamwar/command/AbstractSWCommand.java | 53 +++++++++++++------ src/de/steamwar/command/CommandPart.java | 6 ++- .../de/steamwar/command/ArgumentCommand.java | 15 ++++++ .../steamwar/command/ArgumentCommandTest.java | 17 +++++- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 9b0264f..892d804 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -67,7 +67,8 @@ public abstract class AbstractSWCommand { 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(); @@ -91,7 +92,7 @@ public abstract class AbstractSWCommand { .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()); } @@ -150,7 +151,8 @@ public abstract class AbstractSWCommand { if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class); if (methodMetaData == null) return aClass -> true; - if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) return aClass -> false; + if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) + return aClass -> false; return aClass -> { Class[] types = methodMetaData.value(); if (types == null) return true; @@ -171,7 +173,8 @@ public abstract class AbstractSWCommand { if (Predicate.class.isAssignableFrom(handler)) { try { return (Predicate>) handler.getConstructor().newInstance(); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { } } return aClass -> { @@ -318,7 +321,9 @@ public abstract class AbstractSWCommand { @CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0) protected @interface Cached { long cacheDuration() default 5; + TimeUnit timeUnit() default TimeUnit.SECONDS; + boolean global() default false; } @@ -378,7 +383,7 @@ public abstract class AbstractSWCommand { */ boolean allowISE() default false; - int[] falseValues() default { 0 }; + int[] falseValues() default {0}; class Handler implements AbstractTypeMapper { @@ -416,11 +421,6 @@ public abstract class AbstractSWCommand { 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 tabCompletes(T sender, PreviousArguments previousArguments, String s) { return inner.tabCompletes(sender, previousArguments, s); @@ -446,7 +446,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 1) + @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 2) protected @interface ErrorMessage { /** * Error message to be displayed when the parameter is invalid. @@ -486,11 +486,14 @@ public abstract class AbstractSWCommand { @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) + @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 3) 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; @@ -507,6 +510,7 @@ public abstract class AbstractSWCommand { @Override public boolean validate(T sender, Number value, MessageSender messageSender) { + if (value == null) return true; return (comparator.apply(value).intValue()) >= this.value; } } @@ -515,11 +519,14 @@ public abstract class AbstractSWCommand { @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) + @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 3) 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; @@ -536,6 +543,7 @@ public abstract class AbstractSWCommand { @Override public boolean validate(T sender, Number value, MessageSender messageSender) { + if (value == null) return true; return (comparator.apply(value).intValue()) <= this.value; } } @@ -560,6 +568,7 @@ public abstract class AbstractSWCommand { @CommandMetaData.ImplicitTypeMapper(handler = Length.Handler.class) protected @interface Length { int min() default 0; + int max() default Integer.MAX_VALUE; class Handler implements AbstractTypeMapper { @@ -603,11 +612,14 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter(handler = ArrayLength.Type.class) - @CommandMetaData.ImplicitValidator(handler = ArrayLength.Handler.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = ArrayLength.Handler.class, order = 1) protected @interface ArrayLength { int min() default 0; + int max() default Integer.MAX_VALUE; + String errorMessage() default ""; + class Type implements Predicate> { @Override public boolean test(Class clazz) { @@ -619,17 +631,26 @@ public abstract class AbstractSWCommand { private int min; private int max; + private String errorMessage; public Handler(ArrayLength arrayLength) { this.min = arrayLength.min(); this.max = arrayLength.max(); + this.errorMessage = arrayLength.errorMessage(); + if (this.errorMessage.isEmpty()) { + this.errorMessage = null; + } } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { - if (value == null) return false; + if (value == null) return true; int length = Array.getLength(value); - return length >= min && length <= max; + boolean valid = length >= min && length <= max; + if (errorMessage != null) { + messageSender.send(!valid, errorMessage); + } + return valid; } } } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index b902892..8a45791 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -181,6 +181,7 @@ class CommandPart { } catch (Exception e) { return new CheckArgumentResult(false, null); } + boolean success = true; for (AbstractValidator validator : validators) { try { if (!validator.validate(sender, value, (s, objects) -> { @@ -188,13 +189,14 @@ class CommandPart { 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(true, value); + return new CheckArgumentResult(success, value); } public Class getType() { diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index a107270..48f9b40 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -57,4 +57,19 @@ 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"); + } + + @Register + public void arrayLengthArgument(String sender, @ArrayLength(min = 2) int... args) { + throw new ExecutionIdentifier("RunArrayLengthArgument with Integer"); + } } diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index 0af9327..5450c6e 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -98,7 +98,7 @@ public class ArgumentCommandTest { public void testTabComplete() { ArgumentCommand cmd = new ArgumentCommand(); List strings = cmd.tabComplete("test", "", new String[]{""}); - assertTabCompletes(strings, "true", "false"); + assertTabCompletes(strings, "true", "false", "hello", "wor"); } @Test @@ -106,5 +106,20 @@ public class ArgumentCommandTest { ArgumentCommand cmd = new ArgumentCommand(); List 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"); } } From e31534632e1d6b9278cc03de2691d21d9342cccd Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 16:04:35 +0100 Subject: [PATCH 45/84] Fix stuff and update tests --- .../steamwar/command/AbstractSWCommand.java | 55 +++++++------------ .../de/steamwar/command/ArgumentCommand.java | 5 -- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 892d804..5cdb213 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -26,10 +26,7 @@ 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.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; +import java.util.function.*; import java.util.stream.Collectors; public abstract class AbstractSWCommand { @@ -148,12 +145,12 @@ public abstract class AbstractSWCommand { } private boolean validateMethod(Method method) { - if (!checkType(method.getAnnotations(), method.getReturnType(), annotation -> { + if (!checkType(method.getAnnotations(), method.getReturnType(), false, annotation -> { CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class); - if (methodMetaData == null) return aClass -> true; + if (methodMetaData == null) return (aClass, varArg) -> true; if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount()) - return aClass -> false; - return aClass -> { + return (aClass, varArg) -> false; + return (aClass, varArg) -> { Class[] types = methodMetaData.value(); if (types == null) return true; for (Class type : types) { @@ -164,20 +161,19 @@ public abstract class AbstractSWCommand { }, "The method '" + method + "'")) return false; boolean valid = true; for (Parameter parameter : method.getParameters()) { - Class type = parameter.getType(); - if (parameter.isVarArgs()) type = type.getComponentType(); - if (!checkType(parameter.getAnnotations(), type, annotation -> { + if (!checkType(parameter.getAnnotations(), parameter.getType(), parameter.isVarArgs(), annotation -> { CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class); - if (parameterMetaData == null) return aClass -> true; + if (parameterMetaData == null) return (aClass, varArg) -> true; Class handler = parameterMetaData.handler(); - if (Predicate.class.isAssignableFrom(handler)) { + if (BiPredicate.class.isAssignableFrom(handler)) { try { - return (Predicate>) handler.getConstructor().newInstance(); + return (BiPredicate, Boolean>) handler.getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { } } - return aClass -> { + return (aClass, varArg) -> { + if (varArg) aClass = aClass.getComponentType(); Class[] types = parameterMetaData.value(); if (types == null) return true; for (Class current : types) { @@ -190,11 +186,11 @@ public abstract class AbstractSWCommand { return valid; } - private boolean checkType(Annotation[] annotations, Class clazz, Function>> toApplicableTypes, String warning) { + private boolean checkType(Annotation[] annotations, Class clazz, boolean varArg, Function, Boolean>> toApplicableTypes, String warning) { boolean valid = true; for (Annotation annotation : annotations) { - Predicate> predicate = toApplicableTypes.apply(annotation); - if (!predicate.test(clazz)) { + BiPredicate, Boolean> predicate = toApplicableTypes.apply(annotation); + if (!predicate.test(clazz, varArg)) { commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'"); valid = false; } @@ -446,7 +442,7 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) - @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = 2) + @CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = Integer.MAX_VALUE) protected @interface ErrorMessage { /** * Error message to be displayed when the parameter is invalid. @@ -486,7 +482,7 @@ public abstract class AbstractSWCommand { @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 = 3) + @CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2) protected @interface Min { int intValue() default Integer.MIN_VALUE; @@ -519,7 +515,7 @@ public abstract class AbstractSWCommand { @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 = 3) + @CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2) protected @interface Max { int intValue() default Integer.MAX_VALUE; @@ -618,11 +614,9 @@ public abstract class AbstractSWCommand { int max() default Integer.MAX_VALUE; - String errorMessage() default ""; - - class Type implements Predicate> { + class Type implements BiPredicate, Boolean> { @Override - public boolean test(Class clazz) { + public boolean test(Class clazz, Boolean isVarArgs) { return clazz.isArray(); } } @@ -631,26 +625,17 @@ public abstract class AbstractSWCommand { private int min; private int max; - private String errorMessage; public Handler(ArrayLength arrayLength) { this.min = arrayLength.min(); this.max = arrayLength.max(); - this.errorMessage = arrayLength.errorMessage(); - if (this.errorMessage.isEmpty()) { - this.errorMessage = null; - } } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { if (value == null) return true; int length = Array.getLength(value); - boolean valid = length >= min && length <= max; - if (errorMessage != null) { - messageSender.send(!valid, errorMessage); - } - return valid; + return length >= min && length <= max; } } } diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index 48f9b40..451d6e9 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -67,9 +67,4 @@ public class ArgumentCommand extends TestSWCommand { public void minAndMaxLengthArgument(String sender, @Length(min = 3, max = 3) @StaticValue({"wo", "world"}) String arg) { throw new ExecutionIdentifier("RunLengthArgument with String"); } - - @Register - public void arrayLengthArgument(String sender, @ArrayLength(min = 2) int... args) { - throw new ExecutionIdentifier("RunArrayLengthArgument with Integer"); - } } From 4e6f73c63728ca5ef6bcb68db88dadc29feb46b0 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 22 Jan 2023 22:04:00 +0100 Subject: [PATCH 46/84] Add ArrayLength visual update for tabcompletions --- .../steamwar/command/AbstractSWCommand.java | 40 +++++++++++++++++-- src/de/steamwar/command/CommandPart.java | 7 +++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 5cdb213..6bc3177 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -26,7 +26,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.function.*; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; public abstract class AbstractSWCommand { @@ -608,7 +611,8 @@ public abstract class AbstractSWCommand { @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER}) @CommandMetaData.Parameter(handler = ArrayLength.Type.class) - @CommandMetaData.ImplicitValidator(handler = ArrayLength.Handler.class, order = 1) + @CommandMetaData.ImplicitTypeMapper(handler = ArrayLength.HandlerTypeMapper.class) + @CommandMetaData.ImplicitValidator(handler = ArrayLength.HandlerValidator.class, order = 1) protected @interface ArrayLength { int min() default 0; @@ -621,12 +625,40 @@ public abstract class AbstractSWCommand { } } - class Handler implements AbstractValidator { + class HandlerTypeMapper implements AbstractTypeMapper { + + private int max; + private AbstractTypeMapper inner; + + public HandlerTypeMapper(ArrayLength arrayLength, AbstractTypeMapper 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 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 implements AbstractValidator { private int min; private int max; - public Handler(ArrayLength arrayLength) { + public HandlerValidator(ArrayLength arrayLength) { this.min = arrayLength.min(); this.max = arrayLength.max(); } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 8a45791..971775a 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -129,11 +129,16 @@ class CommandPart { public void generateTabComplete(List current, T sender, String[] args, List mappedArgs, int startIndex) { if (varArgType != null) { + List currentArgs = new ArrayList<>(mappedArgs); + List varArgs = new ArrayList<>(); for (int i = startIndex; i < args.length - 1; i++) { CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, mappedArgs, i); if (!validArgument.success) return; + varArgs.add(validArgument.value); } - Collection strings = tabCompletes(sender, args, mappedArgs, args.length - 1); + + currentArgs.add(varArgs.toArray()); + Collection strings = tabCompletes(sender, args, currentArgs, args.length - 1); if (strings != null) { current.addAll(strings); } From 49e4bf64b7ba0d7c412919ac1573b07c7263a6b0 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Mon, 23 Jan 2023 14:06:40 +0100 Subject: [PATCH 47/84] Add ArrayLength test --- testsrc/de/steamwar/command/ArgumentCommand.java | 4 ++++ testsrc/de/steamwar/command/ArgumentCommandTest.java | 3 +++ 2 files changed, 7 insertions(+) diff --git a/testsrc/de/steamwar/command/ArgumentCommand.java b/testsrc/de/steamwar/command/ArgumentCommand.java index 451d6e9..41f59c7 100644 --- a/testsrc/de/steamwar/command/ArgumentCommand.java +++ b/testsrc/de/steamwar/command/ArgumentCommand.java @@ -67,4 +67,8 @@ public class ArgumentCommand extends TestSWCommand { 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"); + } } diff --git a/testsrc/de/steamwar/command/ArgumentCommandTest.java b/testsrc/de/steamwar/command/ArgumentCommandTest.java index 5450c6e..b46c184 100644 --- a/testsrc/de/steamwar/command/ArgumentCommandTest.java +++ b/testsrc/de/steamwar/command/ArgumentCommandTest.java @@ -121,5 +121,8 @@ public class ArgumentCommandTest { strings = cmd.tabComplete("test", "", new String[]{"worl"}); assertTabCompletes(strings, "wor", "worl"); + + strings = cmd.tabComplete("test", "", new String[]{"one", "two", "three", "one"}); + assertTabCompletes(strings); } } From e2654e72bf99334682d2e09f3e9b204595c40e8a Mon Sep 17 00:00:00 2001 From: Lixfel Date: Mon, 23 Jan 2023 16:30:35 +0100 Subject: [PATCH 48/84] WIP CommonDB 2 Signed-off-by: Lixfel --- src/de/steamwar/sql/BannedUserIPs.java | 61 ++++++++++++++ src/de/steamwar/sql/Event.java | 34 ++++++++ src/de/steamwar/sql/Fight.java | 5 ++ src/de/steamwar/sql/FightPlayer.java | 16 ++++ src/de/steamwar/sql/PollAnswer.java | 77 +++++++++++++++++ src/de/steamwar/sql/Session.java | 45 ++++++++++ src/de/steamwar/sql/Team.java | 30 +++++-- src/de/steamwar/sql/TeamTeilnahme.java | 16 ++++ src/de/steamwar/sql/UserGroup.java | 83 +++++++++++-------- .../steamwar/sql/internal/SqlTypeMapper.java | 1 + 10 files changed, 327 insertions(+), 41 deletions(-) create mode 100644 src/de/steamwar/sql/BannedUserIPs.java create mode 100644 src/de/steamwar/sql/PollAnswer.java create mode 100644 src/de/steamwar/sql/Session.java diff --git a/src/de/steamwar/sql/BannedUserIPs.java b/src/de/steamwar/sql/BannedUserIPs.java new file mode 100644 index 0000000..f765b76 --- /dev/null +++ b/src/de/steamwar/sql/BannedUserIPs.java @@ -0,0 +1,61 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.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; + +import java.sql.Timestamp; +import java.util.List; + +@AllArgsConstructor +public class BannedUserIPs { + + private static final Table table = new Table<>(BannedUserIPs.class); + + private static final SelectStatement getByID = table.selectFields("UserID"); + private static final SelectStatement getByIP = new SelectStatement<>(table, "SELECT * FROM BannedUserIPs WHERE IP = ? ORDER BY Timestamp DESC"); + private static final Statement banIP = table.insert(Table.PRIMARY); + + @Getter + @Field(keys = {Table.PRIMARY}) + private final int userID; + @Getter + @Field(def = "CURRENT_TIMESTAMP") + private final Timestamp timestamp; + @Field(keys = {Table.PRIMARY}) + private final String ip; + + public static List get(int userID) { + return getByID.listSelect(userID); + } + + public static List get(String ip) { + return getByIP.listSelect(ip); + } + + public static void banIP(int userID, String ip){ + banIP.update(userID, ip); + } +} diff --git a/src/de/steamwar/sql/Event.java b/src/de/steamwar/sql/Event.java index a4c30e6..90dcba2 100644 --- a/src/de/steamwar/sql/Event.java +++ b/src/de/steamwar/sql/Event.java @@ -26,17 +26,41 @@ import lombok.AllArgsConstructor; import lombok.Getter; import java.sql.Timestamp; +import java.time.Instant; +import java.util.List; @AllArgsConstructor public class Event { private static final Table table = new Table<>(Event.class); + + private static final SelectStatement byCurrent = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start < now() AND End > now()"); private static final SelectStatement byId = table.select(Table.PRIMARY); + private static final SelectStatement byName = table.select("eventName"); + private static final SelectStatement byComing = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start > now()"); + + private static Event current = null; + + public static Event get(){ + if(current != null && current.now()) + return current; + + current = byCurrent.select(); + return current; + } public static Event get(int eventID){ return byId.select(eventID); } + public static Event get(String eventName) { + return byName.select(eventName); + } + + public static List getComing() { + return byComing.listSelect(); + } + @Getter @Field(keys = {Table.PRIMARY}, autoincrement = true) private final int eventID; @@ -72,4 +96,14 @@ public class Event { public SchematicType getSchematicType() { return schemType; } + + @Deprecated + public String getSchemType() { + return schemType.toDB(); + } + + private boolean now() { + Instant now = Instant.now(); + return now.isAfter(start.toInstant()) && now.isBefore(end.toInstant()); + } } diff --git a/src/de/steamwar/sql/Fight.java b/src/de/steamwar/sql/Fight.java index 0f9eae6..0a26a88 100644 --- a/src/de/steamwar/sql/Fight.java +++ b/src/de/steamwar/sql/Fight.java @@ -25,6 +25,8 @@ import de.steamwar.sql.internal.Table; import lombok.AllArgsConstructor; import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.List; @AllArgsConstructor public class Fight { @@ -55,6 +57,9 @@ public class Fight { @Field private final String wincondition; + private final List bluePlayers = new ArrayList<>(); + private final List redPlayers = new ArrayList<>(); + 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); } diff --git a/src/de/steamwar/sql/FightPlayer.java b/src/de/steamwar/sql/FightPlayer.java index cc529b8..73b5a05 100644 --- a/src/de/steamwar/sql/FightPlayer.java +++ b/src/de/steamwar/sql/FightPlayer.java @@ -20,20 +20,29 @@ 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; + +import java.util.Collections; +import java.util.List; @AllArgsConstructor public class FightPlayer { private static final Table table = new Table<>(FightPlayer.class); private static final Statement create = table.insertAll(); + private static final SelectStatement batchGet = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN ?"); + @Getter @Field(keys = {Table.PRIMARY}) private final int fightID; + @Getter @Field(keys = {Table.PRIMARY}) private final int userID; + @Getter @Field private final int team; @Field @@ -46,4 +55,11 @@ public class FightPlayer { 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); } + + public static List batchGet(Integer[] fightIds) { + if(fightIds.length == 0) + return Collections.emptyList(); + + return batchGet.listSelect((Object) fightIds); + } } diff --git a/src/de/steamwar/sql/PollAnswer.java b/src/de/steamwar/sql/PollAnswer.java new file mode 100644 index 0000000..933f253 --- /dev/null +++ b/src/de/steamwar/sql/PollAnswer.java @@ -0,0 +1,77 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.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; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@AllArgsConstructor +public class PollAnswer { + + @Getter + @Setter + private static String currentPoll; + + private static final Table table = new Table<>(PollAnswer.class); + + private static final SelectStatement get = table.select(Table.PRIMARY); + private static final Statement getResults = new Statement("SELECT Count(UserID) AS Times, Answer FROM PollAnswer WHERE Question = ? GROUP BY Answer ORDER BY Times ASC"); + private static final Statement insert = table.insertAll(); + + @Field(keys = {Table.PRIMARY}) + private final int userID; + @Field(keys = {Table.PRIMARY}) + private final String question; + @Field(def = "0") + private int answer; + + public static PollAnswer get(int userID) { + PollAnswer answer = get.select(userID, currentPoll); + if(answer == null) + return new PollAnswer(userID, currentPoll, 0); + return answer; + } + + public static Map getCurrentResults() { + return getResults.select(rs -> { + Map retMap = new HashMap<>(); + while (rs.next()) + retMap.put(rs.getInt("Answer")-1, rs.getInt("Times")); + return retMap; + }, currentPoll); + } + + public boolean hasAnswered(){ + return answer != 0; + } + + public void setAnswer(int answer){ + this.answer = answer; + insert.update(userID, question, answer); + } +} diff --git a/src/de/steamwar/sql/Session.java b/src/de/steamwar/sql/Session.java new file mode 100644 index 0000000..18ad2c8 --- /dev/null +++ b/src/de/steamwar/sql/Session.java @@ -0,0 +1,45 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.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 Session { + + private static final Table table = new Table<>(Session.class); + private static final Statement insert = table.insert(Table.PRIMARY); + + public static void insertSession(int userID, Timestamp startTime){ + insert.update(userID, startTime); + } + + @Field(keys = {Table.PRIMARY}) + private int userId; + @Field(keys = {Table.PRIMARY}) + private Timestamp startTime; + @Field(def = "CURRENT_TIMESTAMP") + private Timestamp endTime; +} diff --git a/src/de/steamwar/sql/Team.java b/src/de/steamwar/sql/Team.java index 10e6892..977c4fe 100644 --- a/src/de/steamwar/sql/Team.java +++ b/src/de/steamwar/sql/Team.java @@ -25,14 +25,23 @@ import de.steamwar.sql.internal.Table; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @AllArgsConstructor public class Team { + private static final Map teamCache = new HashMap<>(); + + public static void clear() { + teamCache.clear(); + } + private static final Table table = new Table<>(Team.class); - private static final SelectStatement select = table.select(Table.PRIMARY); + private static final SelectStatement byId = table.select(Table.PRIMARY); + private static final SelectStatement byName = new SelectStatement<>(table, "SELECT * FROM Team WHERE (lower(TeamName) = ? OR lower(TeamKuerzel) = ?) AND NOT TeamDeleted"); @Field(keys = {Table.PRIMARY}) @Getter @@ -46,13 +55,22 @@ public class Team { @Field(def = "'8'") @Getter private final String teamColor; - - private static final Team pub = new Team(0, "PUB", "Öffentlich", "8"); + @Field(nullable = true) + private String address; + @Field(def = "'25565'") + private int port; + @Field(def = "0") + private boolean teamDeleted; public static Team get(int id) { - if(id == 0) - return pub; - return select.select(id); + return teamCache.computeIfAbsent(id, byId::select); + //TODOs: BungeeCore flush caches + //TODOs: Team public removal test + //TODOs: ArraySelect test + } + + public static Team get(String name){ + return byName.select(name, name); } public List getMembers(){ diff --git a/src/de/steamwar/sql/TeamTeilnahme.java b/src/de/steamwar/sql/TeamTeilnahme.java index 533b830..f1484da 100644 --- a/src/de/steamwar/sql/TeamTeilnahme.java +++ b/src/de/steamwar/sql/TeamTeilnahme.java @@ -21,6 +21,7 @@ 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; @@ -34,6 +35,9 @@ public class TeamTeilnahme { private static final SelectStatement select = table.select(Table.PRIMARY); private static final SelectStatement selectTeams = table.selectFields("EventID"); private static final SelectStatement selectEvents = table.selectFields("TeamID"); + private static final Statement insert = table.insert(Table.PRIMARY); + private static final Statement delete = table.delete(Table.PRIMARY); + private static final Statement deleteFuture = new Statement("DELETE t FROM TeamTeilnahme t INNER JOIN Event e ON t.EventID = e.EventID WHERE t.TeamID = ? AND e.Start > NOW()"); @Field(keys = {Table.PRIMARY}) private final int teamId; @@ -44,6 +48,18 @@ public class TeamTeilnahme { return select.select(teamID, eventID) != null; } + public static void teilnehmen(int teamID, int eventID){ + insert.update(teamID, eventID); + } + + public static void notTeilnehmen(int teamID, int eventID){ + delete.update(teamID, eventID); + } + + public static void deleteFuture(int teamID) { + deleteFuture.update(teamID); + } + public static Set getTeams(int eventID){ return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries) } diff --git a/src/de/steamwar/sql/UserGroup.java b/src/de/steamwar/sql/UserGroup.java index cef4fee..aea2dba 100644 --- a/src/de/steamwar/sql/UserGroup.java +++ b/src/de/steamwar/sql/UserGroup.java @@ -1,21 +1,21 @@ -/* - 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 . -*/ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ package de.steamwar.sql; @@ -24,22 +24,35 @@ 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); + Admin("§4", "§e", "Admin", true, true, true, true), + Developer("§3", "§f", "Dev", true, true, true, true), + Moderator("§c", "§f", "Mod", true, true, true, true), + Supporter("§9", "§f", "Sup", false, true, true, true), + Builder("§2", "§f", "Arch", false, true, false, true), + YouTuber("§5", "§f", "YT", false, false, false, true), + Member("§7", "§7", "", false, 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; + @Getter + private final String colorCode; + @Getter + private final String chatColorCode; + @Getter + private final String chatPrefix; + @Getter + private final boolean adminGroup; + @Getter + private final boolean teamGroup; + @Getter + private final boolean checkSchematics; + @Getter + private final boolean privilegedMods; + + public static UserGroup getUsergroup(String name) { + for(UserGroup group : values()) { + if(group.name().equalsIgnoreCase(name)) + return group; + } + + throw new IllegalArgumentException(name); + } } \ No newline at end of file diff --git a/src/de/steamwar/sql/internal/SqlTypeMapper.java b/src/de/steamwar/sql/internal/SqlTypeMapper.java index 34c6173..b9ff93e 100644 --- a/src/de/steamwar/sql/internal/SqlTypeMapper.java +++ b/src/de/steamwar/sql/internal/SqlTypeMapper.java @@ -65,6 +65,7 @@ public final class SqlTypeMapper { 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); + new SqlTypeMapper<>(Integer[].class, null, (rs, identifier) -> { throw new SecurityException("Arrays cannot be used as type (recursive select)"); }, (st, index, value) -> st.setArray(index, st.getConnection().createArrayOf("INTEGER", value))); } private static void primitiveMapper(Class primitive, Class wrapped, String sqlType, SQLReader reader, SQLWriter writer) { From ef79a2e7db64e3694d1f913b7e1b5a256dd25c38 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 25 Jan 2023 20:41:16 +0100 Subject: [PATCH 49/84] Add some unfinished new sorting --- .../steamwar/command/AbstractSWCommand.java | 7 +- src/de/steamwar/command/SubCommand.java | 20 ++++-- .../command/StaticValueCommandTest.java | 14 ++++ .../command/SubCMDSortingCommand.java | 54 ++++++++++++++ .../command/SubCMDSortingCommandTest.java | 72 +++++++++++++++++++ 5 files changed, 157 insertions(+), 10 deletions(-) create mode 100644 testsrc/de/steamwar/command/SubCMDSortingCommand.java create mode 100644 testsrc/de/steamwar/command/SubCMDSortingCommandTest.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 6bc3177..1ad1969 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -139,11 +139,8 @@ public abstract class AbstractSWCommand { }); } - this.commandList.sort((o1, o2) -> { - int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length); - if (compare == 0) return Integer.compare(o1.comparableValue, o2.comparableValue); - return compare; - }); + Collections.sort(commandList); + System.out.println(commandList.stream().map(o -> o.method).collect(Collectors.toList())); initialized = true; } diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index d74423b..86543d5 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -32,7 +32,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -public class SubCommand { +public class SubCommand implements Comparable> { private AbstractSWCommand abstractSWCommand; Method method; @@ -42,7 +42,8 @@ public class SubCommand { private Function senderFunction; AbstractValidator validator; boolean noTabComplete; - int comparableValue; + + private Parameter[] parameters; private CommandPart commandPart; @@ -58,9 +59,7 @@ public class SubCommand { 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) SWCommandUtils.getValidator(validator, parameters[0].getType(), localValidator); @@ -72,6 +71,17 @@ public class SubCommand { senderFunction = t -> parameters[0].getType().cast(t); } + @Override + public int compareTo(SubCommand o) { + int tLength = parameters.length + subCommand.length; + int oLength = o.parameters.length + o.subCommand.length; + + if (parameters[parameters.length - 1].isVarArgs()) tLength *= -1; + if (o.parameters[o.parameters.length - 1].isVarArgs()) oLength *= -1; + + return -Integer.compare(tLength, oLength); + } + boolean invoke(Consumer errors, T sender, String alias, String[] args) { try { if (!senderPredicate.test(sender)) { diff --git a/testsrc/de/steamwar/command/StaticValueCommandTest.java b/testsrc/de/steamwar/command/StaticValueCommandTest.java index 665250a..75a7dfd 100644 --- a/testsrc/de/steamwar/command/StaticValueCommandTest.java +++ b/testsrc/de/steamwar/command/StaticValueCommandTest.java @@ -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"); } diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommand.java b/testsrc/de/steamwar/command/SubCMDSortingCommand.java new file mode 100644 index 0000000..35781e5 --- /dev/null +++ b/testsrc/de/steamwar/command/SubCMDSortingCommand.java @@ -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 . + */ + +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) { + System.out.println("HERE 1"); + throw new ExecutionIdentifier("Command with 0 parameters"); + } + + @Register + public void test(String s, String... args) { + System.out.println("HERE 2"); + throw new ExecutionIdentifier("Command with 1 parameters"); + } + + @Register + public void test(String s, String p, String... args) { + System.out.println("HERE 3"); + throw new ExecutionIdentifier("Command with 2 parameters"); + } + + @Register + public void test(String s, String p, String p2, String... args) { + System.out.println("HERE 4"); + throw new ExecutionIdentifier("Command with 3 parameters"); + } +} diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java b/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java new file mode 100644 index 0000000..8f22085 --- /dev/null +++ b/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java @@ -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 . + */ + +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 parameters"); + } + } + + @Test + public void testTwoArgs() { + SubCMDSortingCommand cmd = new SubCMDSortingCommand(); + try { + cmd.execute("", "", new String[]{"Hello", "World"}); + assert false; + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "Command with 2 parameters"); + } + } + + @Test + public void testThreeArgs() { + SubCMDSortingCommand cmd = new SubCMDSortingCommand(); + try { + cmd.execute("", "", new String[]{"Hello", "World", "!"}); + assert false; + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "Command with 3 parameters"); + } + } +} From 65df8ddab08419d7daa2af625c1f87435601e483 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 2 Feb 2023 17:53:56 +0100 Subject: [PATCH 50/84] Fix command sorting with varargs --- src/de/steamwar/command/CommandPart.java | 2 ++ src/de/steamwar/command/SubCommand.java | 7 +++++-- .../command/SubCMDSortingCommand.java | 21 ++++++------------- .../command/SubCMDSortingCommandTest.java | 17 +++------------ 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 971775a..5eb264c 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -124,6 +124,8 @@ class CommandPart { } if (next != null) { next.generateArgumentArray(errors, current, sender, args, startIndex + 1); + } else if (startIndex + 1 < args.length) { + throw new CommandParseException(); } } diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 86543d5..6c076b1 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -76,8 +76,11 @@ public class SubCommand implements Comparable> { int tLength = parameters.length + subCommand.length; int oLength = o.parameters.length + o.subCommand.length; - if (parameters[parameters.length - 1].isVarArgs()) tLength *= -1; - if (o.parameters[o.parameters.length - 1].isVarArgs()) oLength *= -1; + boolean tVarArgs = parameters[parameters.length - 1].isVarArgs(); + boolean oVarArgs = o.parameters[o.parameters.length - 1].isVarArgs(); + + if (tVarArgs) tLength *= -1; + if (oVarArgs) oLength *= -1; return -Integer.compare(tLength, oLength); } diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommand.java b/testsrc/de/steamwar/command/SubCMDSortingCommand.java index 35781e5..d2a67dd 100644 --- a/testsrc/de/steamwar/command/SubCMDSortingCommand.java +++ b/testsrc/de/steamwar/command/SubCMDSortingCommand.java @@ -30,25 +30,16 @@ public class SubCMDSortingCommand extends TestSWCommand { @Register public void test(String s) { - System.out.println("HERE 1"); 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... args) { - System.out.println("HERE 2"); - throw new ExecutionIdentifier("Command with 1 parameters"); - } - - @Register - public void test(String s, String p, String... args) { - System.out.println("HERE 3"); - throw new ExecutionIdentifier("Command with 2 parameters"); - } - - @Register - public void test(String s, String p, String p2, String... args) { - System.out.println("HERE 4"); - throw new ExecutionIdentifier("Command with 3 parameters"); + throw new ExecutionIdentifier("Command with n parameters"); } } diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java b/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java index 8f22085..6981e29 100644 --- a/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java +++ b/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java @@ -44,29 +44,18 @@ public class SubCMDSortingCommandTest { cmd.execute("", "", new String[]{"Hello"}); assert false; } catch (Exception e) { - assertCMDFramework(e, ExecutionIdentifier.class, "Command with 1 parameters"); + assertCMDFramework(e, ExecutionIdentifier.class, "Command with 1 parameter"); } } @Test - public void testTwoArgs() { + 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 2 parameters"); - } - } - - @Test - public void testThreeArgs() { - SubCMDSortingCommand cmd = new SubCMDSortingCommand(); - try { - cmd.execute("", "", new String[]{"Hello", "World", "!"}); - assert false; - } catch (Exception e) { - assertCMDFramework(e, ExecutionIdentifier.class, "Command with 3 parameters"); + assertCMDFramework(e, ExecutionIdentifier.class, "Command with n parameters"); } } } From 45e9698634f9c6c4e8ff4de315d6133056931da5 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 2 Feb 2023 18:07:29 +0100 Subject: [PATCH 51/84] Fix command sorting with varargs --- .../steamwar/command/AbstractSWCommand.java | 1 - src/de/steamwar/command/SubCommand.java | 2 ++ .../command/SubCMDSortingCommand.java | 5 +++++ .../command/SubCMDSortingCommandTest.java | 22 +++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 1ad1969..84ae620 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -140,7 +140,6 @@ public abstract class AbstractSWCommand { } Collections.sort(commandList); - System.out.println(commandList.stream().map(o -> o.method).collect(Collectors.toList())); initialized = true; } diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 6c076b1..58fa919 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -82,6 +82,8 @@ public class SubCommand implements Comparable> { if (tVarArgs) tLength *= -1; if (oVarArgs) oLength *= -1; + if (tVarArgs && oVarArgs) return Integer.compare(tLength, oLength); + return -Integer.compare(tLength, oLength); } diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommand.java b/testsrc/de/steamwar/command/SubCMDSortingCommand.java index d2a67dd..0f4e151 100644 --- a/testsrc/de/steamwar/command/SubCMDSortingCommand.java +++ b/testsrc/de/steamwar/command/SubCMDSortingCommand.java @@ -38,6 +38,11 @@ public class SubCMDSortingCommand extends TestSWCommand { 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"); diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java b/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java index 6981e29..1f3203c 100644 --- a/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java +++ b/testsrc/de/steamwar/command/SubCMDSortingCommandTest.java @@ -58,4 +58,26 @@ public class SubCMDSortingCommandTest { 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"); + } + } } From d439ecb8d01d576310766e497c970bce7eb1db2f Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 4 Feb 2023 12:54:50 +0100 Subject: [PATCH 52/84] WIP CommonDB Signed-off-by: Lixfel --- src/de/steamwar/sql/BauweltMember.java | 35 ++++++++++++++++++--- src/de/steamwar/sql/SchematicType.java | 18 ++++++----- src/de/steamwar/sql/internal/Statement.java | 4 +-- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/de/steamwar/sql/BauweltMember.java b/src/de/steamwar/sql/BauweltMember.java index aa3ed93..87393e8 100644 --- a/src/de/steamwar/sql/BauweltMember.java +++ b/src/de/steamwar/sql/BauweltMember.java @@ -21,6 +21,7 @@ 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.Getter; @@ -36,6 +37,12 @@ public class BauweltMember { private static final Table table = new Table<>(BauweltMember.class); private static final SelectStatement getMember = table.select(Table.PRIMARY); private static final SelectStatement getMembers = table.selectFields("BauweltID"); + private static final Statement update = table.insertAll(); + private static final Statement delete = table.delete(Table.PRIMARY); + + public static void addMember(UUID ownerID, UUID memberID) { + new BauweltMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId(), false, false).updateDB(); + } public static BauweltMember getBauMember(UUID ownerID, UUID memberID){ return getBauMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId()); @@ -43,7 +50,7 @@ public class BauweltMember { public static BauweltMember getBauMember(int ownerID, int memberID){ BauweltMember member = memberCache.get(memberID); - if(member != null) + if(member != null && member.bauweltID == ownerID) return member; return getMember.select(ownerID, memberID); } @@ -63,11 +70,11 @@ public class BauweltMember { @Field(keys = {Table.PRIMARY}) private final int memberID; @Getter - @Field - private final boolean worldEdit; + @Field(def = "0") + private boolean worldEdit; @Getter - @Field - private final boolean world; + @Field(def = "0") + private boolean world; public BauweltMember(int bauweltID, int memberID, boolean worldEdit, boolean world) { this.bauweltID = bauweltID; @@ -76,4 +83,22 @@ public class BauweltMember { this.world = world; memberCache.put(memberID, this); } + + public void setWorldEdit(boolean worldEdit) { + this.worldEdit = worldEdit; + updateDB(); + } + + public void setWorld(boolean world) { + this.world = world; + updateDB(); + } + + private void updateDB(){ + update.update(bauweltID, memberID, worldEdit, world); + } + + public void remove(){ + delete.update(bauweltID, memberID); + } } diff --git a/src/de/steamwar/sql/SchematicType.java b/src/de/steamwar/sql/SchematicType.java index d87065c..43cfb44 100644 --- a/src/de/steamwar/sql/SchematicType.java +++ b/src/de/steamwar/sql/SchematicType.java @@ -20,6 +20,7 @@ package de.steamwar.sql; import de.steamwar.sql.internal.SqlTypeMapper; +import lombok.Getter; import java.util.*; @@ -51,17 +52,26 @@ public class SchematicType { } private final String name; + @Getter private final String kuerzel; private final Type type; private final SchematicType checkType; + @Getter private final String material; + @Getter + private final Date deadline; SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material){ + this(name, kuerzel, type, checkType, material, null); + } + + SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, Date deadline){ this.name = name; this.kuerzel = kuerzel; this.type = type; this.checkType = checkType; this.material = material; + this.deadline = deadline; } public boolean isAssignable(){ @@ -88,14 +98,6 @@ public class SchematicType { return name; } - public String getKuerzel() { - return kuerzel; - } - - public String getMaterial() { - return material; - } - public String toDB(){ return name.toLowerCase(); } diff --git a/src/de/steamwar/sql/internal/Statement.java b/src/de/steamwar/sql/internal/Statement.java index e842d5f..c29f711 100644 --- a/src/de/steamwar/sql/internal/Statement.java +++ b/src/de/steamwar/sql/internal/Statement.java @@ -95,7 +95,7 @@ public class Statement implements AutoCloseable { } } - private static int connectionBudget = MAX_CONNECTIONS; + private static volatile int connectionBudget = MAX_CONNECTIONS; public static void closeAll() { synchronized (connections) { @@ -234,7 +234,7 @@ public class Statement implements AutoCloseable { private static Connection aquireConnection() { synchronized (connections) { - while(connections.isEmpty() && connectionBudget == 0) + while(connections.isEmpty() && connectionBudget <= 0) waitOnConnections(); if(!connections.isEmpty()) { From 3e1c4e9059ef2400c79aaa5089eaafca075640c6 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 17 Feb 2023 11:47:39 +0100 Subject: [PATCH 53/84] WIP CommonDB Signed-off-by: Lixfel --- src/de/steamwar/sql/Team.java | 74 +++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/src/de/steamwar/sql/Team.java b/src/de/steamwar/sql/Team.java index 977c4fe..8765ad9 100644 --- a/src/de/steamwar/sql/Team.java +++ b/src/de/steamwar/sql/Team.java @@ -21,6 +21,7 @@ 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; @@ -42,38 +43,97 @@ public class Team { private static final Table table = new Table<>(Team.class); private static final SelectStatement byId = table.select(Table.PRIMARY); private static final SelectStatement byName = new SelectStatement<>(table, "SELECT * FROM Team WHERE (lower(TeamName) = ? OR lower(TeamKuerzel) = ?) AND NOT TeamDeleted"); + private static final SelectStatement all = table.selectFields("TeamDeleted"); + private static final Statement insert = table.insertFields("TeamKuerzel", "TeamName"); + private static final Statement update = table.update(Table.PRIMARY, "TeamKuerzel", "TeamName", "TeamColor", "Address", "Port"); + private static final Statement delete = table.update(Table.PRIMARY, "TeamDeleted"); + private static final Statement getSize = new Statement("SELECT COUNT(id) FROM UserData WHERE Team = ?"); - @Field(keys = {Table.PRIMARY}) + @Field(keys = {Table.PRIMARY}, autoincrement = true) @Getter private final int teamId; @Field @Getter - private final String teamKuerzel; + private String teamKuerzel; @Field @Getter - private final String teamName; + private String teamName; @Field(def = "'8'") @Getter - private final String teamColor; + private String teamColor; @Field(nullable = true) + @Getter private String address; @Field(def = "'25565'") + @Getter private int port; @Field(def = "0") private boolean teamDeleted; + public static void create(String kuerzel, String name){ + insert.update(kuerzel, name); + } + public static Team get(int id) { return teamCache.computeIfAbsent(id, byId::select); - //TODOs: BungeeCore flush caches - //TODOs: Team public removal test - //TODOs: ArraySelect test } public static Team get(String name){ + // No cache lookup due to low frequency use return byName.select(name, name); } + public static List getAll(){ + clear(); + List teams = all.listSelect(false); + teams.forEach(team -> teamCache.put(team.getTeamId(), team)); + return teams; + } + public List getMembers(){ return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList()); } + + public int size(){ + return getSize.select(rs -> { + rs.next(); + return rs.getInt("COUNT(id)"); + }, teamId); + } + + public void disband(SteamwarUser user){ + user.setLeader(false); + delete.update(true, teamId); + teamCache.remove(teamId); + TeamTeilnahme.deleteFuture(teamId); + } + + public void setTeamKuerzel(String teamKuerzel) { + this.teamKuerzel = teamKuerzel; + updateDB(); + } + + public void setTeamName(String teamName) { + this.teamName = teamName; + updateDB(); + } + + public void setTeamColor(String teamColor) { + this.teamColor = teamColor; + updateDB(); + } + + public void setAddress(String address) { + this.address = address; + updateDB(); + } + + public void setPort(int port) { + this.port = port; + updateDB(); + } + + private void updateDB(){ + update.update(teamKuerzel, teamName, teamColor, address, port, teamId); + } } From 5d53ede5e8e7b707f10144f86d419fe4d6cf3366 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 18 Feb 2023 12:05:03 +0100 Subject: [PATCH 54/84] Add PartOf annotation for splitting big commands into multiple files --- .../steamwar/command/AbstractSWCommand.java | 24 +++++++ .../de/steamwar/command/PartOfCommand.java | 70 +++++++++++++++++++ .../steamwar/command/PartOfCommandTest.java | 43 ++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 testsrc/de/steamwar/command/PartOfCommand.java create mode 100644 testsrc/de/steamwar/command/PartOfCommandTest.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 84ae620..7ba5e62 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -34,6 +34,8 @@ import java.util.stream.Collectors; public abstract class AbstractSWCommand { + private static Map>, List>> dependencyMap = new HashMap<>(); + private Class clazz; // This is used in createMappings() private boolean initialized = false; @@ -48,6 +50,13 @@ public abstract class AbstractSWCommand { protected AbstractSWCommand(Class clazz, String command, String... aliases) { this.clazz = clazz; + + PartOf partOf = this.getClass().getAnnotation(PartOf.class); + if (partOf != null) { + dependencyMap.computeIfAbsent((Class>) partOf.value(), k -> new ArrayList<>()).add(this); + return; + } + createAndSafeCommand(command, aliases); unregister(); register(); @@ -139,6 +148,15 @@ public abstract class AbstractSWCommand { }); } + 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; } @@ -237,6 +255,12 @@ public abstract class AbstractSWCommand { return methods; } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE}) + public @interface PartOf { + Class value(); + } + // --- Annotation for the command --- /** diff --git a/testsrc/de/steamwar/command/PartOfCommand.java b/testsrc/de/steamwar/command/PartOfCommand.java new file mode 100644 index 0000000..4f2fe9a --- /dev/null +++ b/testsrc/de/steamwar/command/PartOfCommand.java @@ -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 . + */ + +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 typeMapper() { + return new TestTypeMapper() { + @Override + public Integer map(String sender, PreviousArguments previousArguments, String s) { + return Integer.parseInt(s); + } + + @Override + public Collection 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"); + } + } +} diff --git a/testsrc/de/steamwar/command/PartOfCommandTest.java b/testsrc/de/steamwar/command/PartOfCommandTest.java new file mode 100644 index 0000000..b02614a --- /dev/null +++ b/testsrc/de/steamwar/command/PartOfCommandTest.java @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +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) { + e.printStackTrace(); + assertCMDFramework(e, ExecutionIdentifier.class, "SubCommand with int"); + } + } +} From 241117a6a65b95bfcee9bd4b784d344f635a78f9 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 18 Feb 2023 12:13:20 +0100 Subject: [PATCH 55/84] Fix test --- testsrc/de/steamwar/command/PartOfCommand.java | 4 ++-- testsrc/de/steamwar/command/PartOfCommandTest.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/testsrc/de/steamwar/command/PartOfCommand.java b/testsrc/de/steamwar/command/PartOfCommand.java index 4f2fe9a..9c66a4b 100644 --- a/testsrc/de/steamwar/command/PartOfCommand.java +++ b/testsrc/de/steamwar/command/PartOfCommand.java @@ -44,7 +44,7 @@ public class PartOfCommand { return new TestTypeMapper() { @Override public Integer map(String sender, PreviousArguments previousArguments, String s) { - return Integer.parseInt(s); + return -1; } @Override @@ -64,7 +64,7 @@ public class PartOfCommand { @Register public void execute(String s, @Mapper("test") int i) { - throw new ExecutionIdentifier("SubCommand with int"); + throw new ExecutionIdentifier("SubCommand with int " + i); } } } diff --git a/testsrc/de/steamwar/command/PartOfCommandTest.java b/testsrc/de/steamwar/command/PartOfCommandTest.java index b02614a..726384e 100644 --- a/testsrc/de/steamwar/command/PartOfCommandTest.java +++ b/testsrc/de/steamwar/command/PartOfCommandTest.java @@ -36,8 +36,7 @@ public class PartOfCommandTest { command.execute("test", "", new String[]{"0"}); assertThat(true, is(false)); } catch (Exception e) { - e.printStackTrace(); - assertCMDFramework(e, ExecutionIdentifier.class, "SubCommand with int"); + assertCMDFramework(e, ExecutionIdentifier.class, "SubCommand with int -1"); } } } From 69cff99801238984fd463de390b581f91f18a5d1 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 18 Feb 2023 14:01:14 +0100 Subject: [PATCH 56/84] Make Map final --- src/de/steamwar/command/AbstractSWCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 7ba5e62..b8dc078 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -34,7 +34,7 @@ import java.util.stream.Collectors; public abstract class AbstractSWCommand { - private static Map>, List>> dependencyMap = new HashMap<>(); + private static final Map>, List>> dependencyMap = new HashMap<>(); private Class clazz; // This is used in createMappings() From e52e9c5ccd8ef9b87ce06d4eeeaaa4044afa89b5 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Tue, 21 Feb 2023 13:24:32 +0100 Subject: [PATCH 57/84] Seperate SchematicData --- src/de/steamwar/sql/NodeData.java | 69 ++++++++++++++++++++++++++ src/de/steamwar/sql/SchematicNode.java | 14 ++---- 2 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 src/de/steamwar/sql/NodeData.java diff --git a/src/de/steamwar/sql/NodeData.java b/src/de/steamwar/sql/NodeData.java new file mode 100644 index 0000000..6550458 --- /dev/null +++ b/src/de/steamwar/sql/NodeData.java @@ -0,0 +1,69 @@ +package de.steamwar.sql; + +import de.steamwar.sql.internal.*; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.sql.PreparedStatement; +import java.util.zip.GZIPInputStream; + +@AllArgsConstructor +public class NodeData { + + static { + new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); + new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); + } + + private static final Table table = new Table<>(NodeData.class); + + private static final Statement updateDatabase = new Statement("UPDATE NodeData SET SchemData = ?, NodeFormat = ? WHERE NodeId = ?"); + private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ?"); + + private static final SelectStatement get = table.select(Table.PRIMARY); + + public static NodeData get(SchematicNode node) { + if(node.isDir()) + throw new IllegalArgumentException("Node is a directory"); + return get.select(node); + } + + @Getter + @Field(keys = {Table.PRIMARY}) + private final int nodeId; + + @Field + private boolean nodeFormat; + + public InputStream schemData() throws IOException { + try { + return selSchemData.select(rs -> { + rs.next(); + InputStream schemData = rs.getBinaryStream("SchemData"); + try { + if(rs.wasNull() || schemData.available() == 0) { + throw new SecurityException("SchemData is null"); + } + return new GZIPInputStream(schemData); + } catch (IOException e) { + throw new SecurityException("SchemData is wrong", e); + } + }, nodeId); + } catch (Exception e) { + throw new IOException(e); + } + } + + public void saveFromStream(InputStream blob, boolean newFormat) { + updateDatabase.update(blob, newFormat, nodeId); + nodeFormat = newFormat; + } + + public boolean getNodeFormat() { + return nodeFormat; + } +} diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 0e64522..ec7b72f 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -44,7 +44,7 @@ public class SchematicNode { private static final Table 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 update = table.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay"); private static final Statement delete = table.delete(Table.PRIMARY); private static final SelectStatement byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?"); @@ -89,9 +89,6 @@ public class SchematicNode { private boolean replaceColor; @Field(def = "1") private boolean allowReplay; - @Setter(AccessLevel.PACKAGE) - @Field(def = "1") - private boolean nodeFormat; private String brCache; @@ -106,8 +103,7 @@ public class SchematicNode { SchematicType nodeType, int nodeRank, boolean replaceColor, - boolean allowReplay, - boolean nodeFormat + boolean allowReplay ) { this.nodeId = nodeId; this.nodeOwner = nodeOwner; @@ -120,7 +116,6 @@ public class SchematicNode { this.nodeRank = nodeRank; this.replaceColor = replaceColor; this.allowReplay = allowReplay; - this.nodeFormat = nodeFormat; } public static List getAll(SteamwarUser user) { @@ -381,10 +376,11 @@ public class SchematicNode { return nodeType == null; } + @Deprecated public boolean getSchemFormat() { if(isDir()) throw new SecurityException("Node is Directory"); - return nodeFormat; + return NodeData.get(this).getNodeFormat(); } public int getRank() { @@ -462,7 +458,7 @@ public class SchematicNode { private void updateDB() { this.lastUpdate = Timestamp.from(Instant.now()); - update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeFormat, nodeId); + update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeId); TAB_CACHE.clear(); } From 8dbf62e560de95615d93057481e8202ec11c85c4 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 21 Feb 2023 18:22:06 +0100 Subject: [PATCH 58/84] WIP CommonDB: Milestone SWUser Signed-off-by: Lixfel --- src/de/steamwar/sql/BannedUserIPs.java | 5 + src/de/steamwar/sql/EventFight.java | 62 ++++++- src/de/steamwar/sql/Fight.java | 62 ++++++- src/de/steamwar/sql/Mod.java | 101 ++++++++++++ src/de/steamwar/sql/Punishment.java | 72 +++++--- src/de/steamwar/sql/SchemElo.java | 23 ++- src/de/steamwar/sql/SteamwarUser.java | 220 ++++++++++++++++++------- src/de/steamwar/sql/Tutorial.java | 94 +++++++++++ 8 files changed, 541 insertions(+), 98 deletions(-) create mode 100644 src/de/steamwar/sql/Mod.java create mode 100644 src/de/steamwar/sql/Tutorial.java diff --git a/src/de/steamwar/sql/BannedUserIPs.java b/src/de/steamwar/sql/BannedUserIPs.java index f765b76..9a31099 100644 --- a/src/de/steamwar/sql/BannedUserIPs.java +++ b/src/de/steamwar/sql/BannedUserIPs.java @@ -37,6 +37,7 @@ public class BannedUserIPs { private static final SelectStatement getByID = table.selectFields("UserID"); private static final SelectStatement getByIP = new SelectStatement<>(table, "SELECT * FROM BannedUserIPs WHERE IP = ? ORDER BY Timestamp DESC"); private static final Statement banIP = table.insert(Table.PRIMARY); + private static final Statement unbanIPs = table.deleteFields("UserID"); @Getter @Field(keys = {Table.PRIMARY}) @@ -58,4 +59,8 @@ public class BannedUserIPs { public static void banIP(int userID, String ip){ banIP.update(userID, ip); } + + public static void unbanIPs(int userID) { + unbanIPs.update(userID); + } } diff --git a/src/de/steamwar/sql/EventFight.java b/src/de/steamwar/sql/EventFight.java index 60e4831..eed7f51 100644 --- a/src/de/steamwar/sql/EventFight.java +++ b/src/de/steamwar/sql/EventFight.java @@ -26,18 +26,41 @@ import de.steamwar.sql.internal.Table; import lombok.AllArgsConstructor; import lombok.Getter; +import java.sql.Timestamp; +import java.util.*; + +import static java.time.temporal.ChronoUnit.SECONDS; + @AllArgsConstructor -public class EventFight { +public class EventFight implements Comparable { private static final Table table = new Table<>(EventFight.class); private static final SelectStatement byId = table.select(Table.PRIMARY); + private static final SelectStatement allComing = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE StartTime > now() ORDER BY StartTime ASC"); + private static final SelectStatement event = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE EventID = ? ORDER BY StartTime ASC"); + private static final Statement reschedule = table.update(Table.PRIMARY, "StartTime"); private static final Statement setResult = table.update(Table.PRIMARY, "Ergebnis"); private static final Statement setFight = table.update(Table.PRIMARY, "Fight"); + private static final Queue fights = new PriorityQueue<>(); + public static EventFight get(int fightID) { return byId.select(fightID); } + public static void loadAllComingFights() { + fights.clear(); + fights.addAll(allComing.listSelect()); + } + + public static List getEvent(int eventID) { + return event.listSelect(eventID); + } + + public static Queue getFights() { + return fights; + } + @Getter @Field private final int eventID; @@ -46,6 +69,15 @@ public class EventFight { private final int fightID; @Getter @Field + private Timestamp startTime; + @Getter + @Field + private final String spielmodus; + @Getter + @Field + private final String map; + @Getter + @Field private final int teamBlue; @Getter @Field @@ -69,4 +101,32 @@ public class EventFight { this.fight = fight; setFight.update(fight, fightID); } + + public boolean hasFinished() { + return fight != 0; + } + + public void reschedule() { + startTime = Timestamp.from(new Date().toInstant().plus(30, SECONDS)); + reschedule.update(startTime, fightID); + } + + @Override + public int hashCode(){ + return fightID; + } + + @Override + public boolean equals(Object o){ + if(o == null) + return false; + if(!(o instanceof EventFight)) + return false; + return fightID == ((EventFight) o).fightID; + } + + @Override + public int compareTo(EventFight o) { + return startTime.compareTo(o.startTime); + } } diff --git a/src/de/steamwar/sql/Fight.java b/src/de/steamwar/sql/Fight.java index 0a26a88..8576184 100644 --- a/src/de/steamwar/sql/Fight.java +++ b/src/de/steamwar/sql/Fight.java @@ -20,26 +20,49 @@ 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; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @AllArgsConstructor public class Fight { private static final Table table = new Table<>(Fight.class); + private static final SelectStatement getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.AllowReplay) AND (r.NodeId IS NULL OR r.AllowReplay) AS ReplayAllowed, (SELECT COUNT(1) FROM Replay WHERE Replay.FightID = f.FightID) as ReplayAvailable FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?"); private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition"); + public static List getPage(int page, int elementsPerPage) { + List fights = getPage.listSelect(page * elementsPerPage, elementsPerPage); + + List fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID).toArray(Integer[]::new)); + for(Fight fight : fights) { + fight.initPlayers(fightPlayers); + } + + SteamwarUser.batchCache(fightPlayers.stream().map(FightPlayer::getUserID).collect(Collectors.toSet())); + return fights; + } + + 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); + } + + @Getter @Field(keys = {Table.PRIMARY}, autoincrement = true) private final int fightID; @Field private final String gameMode; + @Getter @Field private final String server; + @Getter @Field private final Timestamp startTime; @Field @@ -52,15 +75,50 @@ public class Fight { private final Integer blueSchem; @Field(nullable = true) private final Integer redSchem; + @Getter @Field private final int win; @Field private final String wincondition; + @Field // Virtual field for easy select + private final boolean replayAllowed; + @Field // Virtual field for easy select + private final boolean replayAvailable; + @Getter private final List bluePlayers = new ArrayList<>(); + @Getter private final List redPlayers = new ArrayList<>(); - 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); + public SchematicType getSchemType() { + return SchematicType.fromDB(gameMode); + } + + public SteamwarUser getBlueLeader() { + return SteamwarUser.get(blueLeader); + } + + public SteamwarUser getRedLeader() { + return SteamwarUser.get(redLeader); + } + + public boolean replayAllowed() { + return replayExists() && replayAllowed; + } + + public boolean replayExists() { + return getSchemType() != null && replayAvailable; + } + + private void initPlayers(List fightPlayers) { + for(FightPlayer fp : fightPlayers) { + if(fp.getFightID() != fightID) + continue; + + if(fp.getTeam() == 1) + bluePlayers.add(fp); + else + redPlayers.add(fp); + } } } diff --git a/src/de/steamwar/sql/Mod.java b/src/de/steamwar/sql/Mod.java new file mode 100644 index 0000000..2a4e698 --- /dev/null +++ b/src/de/steamwar/sql/Mod.java @@ -0,0 +1,101 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.sql; + +import de.steamwar.sql.internal.*; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@AllArgsConstructor +public class Mod { + + static { + SqlTypeMapper.ordinalEnumMapper(Platform.class); + SqlTypeMapper.ordinalEnumMapper(ModType.class); + } + + private static final Table table = new Table<>(Mod.class, "Mods"); + private static final SelectStatement get = table.select(Table.PRIMARY); + private static final SelectStatement findFirst = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = 0 LIMIT 1"); + private static final SelectStatement getPageOfType = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = ? ORDER BY ModName DESC LIMIT ?, ?"); + private static final Statement insert = table.insert(Table.PRIMARY); + private static final Statement set = table.update(Table.PRIMARY, "ModType"); + + public static Mod get(String name, Platform platform) { + return get.select(platform, name); + } + + public static Mod getOrCreate(String name, Platform platform) { + Mod mod = get(name, platform); + if(mod != null) + return mod; + + insert.update(platform, name); + return new Mod(platform, name, ModType.UNKLASSIFIED); + } + + public static List getAllModsFiltered(int page, int elementsPerPage, Mod.ModType filter) { + return Mod.getPageOfType.listSelect(filter, page * elementsPerPage, elementsPerPage); + } + + public static Mod findFirstMod() { + return findFirst.select(); + } + + @Getter + @Field(keys = {Table.PRIMARY}) + private final Platform platform; + @Getter + @Field(keys = {Table.PRIMARY}) + private final String modName; + @Getter + @Field(def = "0") + private ModType modType; + + public void setModType(Mod.ModType modType) { + set.update(modType, platform, modName); + this.modType = modType; + } + + public enum Platform { + FORGE, + LABYMOD, + FABRIC + } + + public enum ModType { + UNKLASSIFIED("7"), + GREEN("a"), + YELLOW("e"), + RED("c"), + YOUTUBER_ONLY("6"); + + ModType(String colorcode) { + this.colorcode = colorcode; + } + private final String colorcode; + + public String getColorCode() { + return colorcode; + } + } +} diff --git a/src/de/steamwar/sql/Punishment.java b/src/de/steamwar/sql/Punishment.java index 086f445..5960dce 100644 --- a/src/de/steamwar/sql/Punishment.java +++ b/src/de/steamwar/sql/Punishment.java @@ -19,16 +19,16 @@ 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 de.steamwar.sql.internal.*; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.sql.Timestamp; +import java.time.Instant; import java.time.format.DateTimeFormatter; import java.util.Date; +import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -40,13 +40,48 @@ public class Punishment { SqlTypeMapper.nameEnumMapper(PunishmentType.class); } + public static final Timestamp PERMA_TIME = Timestamp.from(Instant.ofEpochSecond(946674800)); + private static final Table table = new Table<>(Punishment.class, "Punishments"); private static final SelectStatement getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)"); private static final SelectStatement getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1"); + private static final SelectStatement getAllPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? ORDER BY `PunishmentId` DESC"); + private static final Statement insert = table.insertFields(true, "UserId", "Punisher", "Type", "EndTime", "Perma", "Reason"); + + public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) { + return getPunishment.select(user, type); + } + + public static Map getPunishmentsOfPlayer(int user) { + return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment)); + } + + public static List getAllPunishmentsOfPlayer(int user) { + return getAllPunishments.listSelect(user); + } + + public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer callback) { + Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type); + if(punishment == null || !punishment.isCurrent()) { + return false; + } else { + callback.accept(punishment); + return true; + } + } + + public static Punishment createPunishment(int user, int executor, PunishmentType type, String reason, Timestamp endTime, boolean perma) { + if(perma && !endTime.equals(PERMA_TIME)) { + throw new IllegalArgumentException("Permanent punishments must have an end time of `Punishment.PERMA_TIME`"); + } + int punishmentId = insert.insertGetKey(user, executor, type.name(), endTime, perma, reason); + return new Punishment(punishmentId, user, executor, type, Timestamp.from(Instant.now()), endTime, perma, reason); + } @Field(keys = {Table.PRIMARY}, autoincrement = true) private final int punishmentId; @Field + @Getter private final int userId; @Field @Getter @@ -67,24 +102,6 @@ public class Punishment { @Getter private final String reason; - public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) { - return getPunishment.select(user, type); - } - - public static Map 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 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) { @@ -94,15 +111,12 @@ public class Punishment { } } - public int getUserId() { - return userId; - } - public boolean isCurrent() { return isPerma() || getEndTime().after(new Date()); } @AllArgsConstructor + @RequiredArgsConstructor @Getter public enum PunishmentType { Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"), @@ -110,7 +124,10 @@ public class Punishment { 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"); + NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"), + NoFightServer(false, "NOFIGHTSERVER_TEAM", "NOFIGHTSERVER_PERMA", "NOFIGHTSERVER_UNTIL", "UNNOFIGHTSERVER_ERROR", "UNNOFIGHTSERVER"), + NoTeamServer(true, "NOTEAMSERVER_TEAM", "NOTEAMSERVER_PERMA", "NOTEAMSERVER_UNTIL", "UNNOTEAMSERVER_ERROR", "UNNOTEAMSERVER"), + Note(false, "NOTE_TEAM", null, null, null, null, true); private final boolean needsAdmin; private final String teamMessage; @@ -118,5 +135,6 @@ public class Punishment { private final String playerMessageUntil; private final String usageNotPunished; private final String unpunishmentMessage; + private boolean multi = false; } } diff --git a/src/de/steamwar/sql/SchemElo.java b/src/de/steamwar/sql/SchemElo.java index 64090ce..1f795d6 100644 --- a/src/de/steamwar/sql/SchemElo.java +++ b/src/de/steamwar/sql/SchemElo.java @@ -21,14 +21,32 @@ 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; @AllArgsConstructor public class SchemElo { + public static final int ELO_DEFAULT = 1000; + private static final Table table = new Table<>(SchemElo.class); private static final SelectStatement select = table.select(Table.PRIMARY); + private static final Statement setElo = table.insertAll(); + + public static int getElo(SchematicNode node, int season) { + SchemElo elo = select.select(node, season); + return elo != null ? elo.elo : 0; + } + + public static int getCurrentElo(int schemID) { + SchemElo elo = select.select(schemID, Season.getSeason()); + return elo != null ? elo.elo : ELO_DEFAULT; + } + + public static void setElo(int schemID, int elo) { + setElo.update(schemID, elo, Season.getSeason()); + } @Field(keys = {Table.PRIMARY}) private final int schemId; @@ -36,9 +54,4 @@ public class SchemElo { 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; - } } diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index 874643f..dfbbcd0 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -22,7 +22,10 @@ package de.steamwar.sql; import de.steamwar.sql.internal.*; import lombok.Getter; +import java.sql.Timestamp; import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public class SteamwarUser { @@ -44,12 +47,17 @@ public class SteamwarUser { private static final SelectStatement byDiscord = table.selectFields("DiscordId"); private static final SelectStatement byTeam = table.selectFields("Team"); private static final SelectStatement getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'"); + private static final SelectStatement batchGet = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN ?"); + 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 Statement getPlaytime = new Statement("SELECT SUM(UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime)) as Playtime FROM Session WHERE UserID = ?"); + private static final Statement getFirstjoin = new Statement("SELECT MIN(StartTime) AS FirstJoin FROM Session WHERE UserID = ?"); + private static final Map usersById = new HashMap<>(); private static final Map usersByUUID = new HashMap<>(); private static final Map usersByName = new HashMap<>(); @@ -69,67 +77,6 @@ public class SteamwarUser { 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) @@ -157,8 +104,22 @@ public class SteamwarUser { return byDiscord.select(discordId); } - public static void createOrUpdateUsername(UUID uuid, String userName) { - insert.update(uuid, userName); + public static SteamwarUser getOrCreate(UUID uuid, String name, Consumer newPlayer, BiConsumer nameUpdate) { + SteamwarUser user = SteamwarUser.get(uuid); + + if (user != null) { + if (!user.userName.equals(name)) { + updateName.update(name, user.id); + nameUpdate.accept(user.userName, name); + user.userName = name; + } + } else { + insert.update(uuid, name); + newPlayer.accept(uuid); + return get(uuid); + } + + return user; } public static List getServerTeam() { @@ -168,4 +129,137 @@ public class SteamwarUser { public static List getTeam(int teamId) { return byTeam.listSelect(teamId); } + + public static void batchCache(Set ids) { + ids.removeIf(usersById::containsKey); + if(ids.isEmpty()) + return; + + batchGet.listSelect((Object) ids.toArray()); + } + + @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; + @Getter + @Field(def = "0") + private boolean leader; + @Field(nullable = true) + private Locale locale; + @Field(def = "0") + private boolean manualLocale; + @Getter + @Field(keys = {"discordId"}, nullable = true) + private Long discordId; + + private final Map punishments; + + 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); + } + punishments = Punishment.getPunishmentsOfPlayer(id); //TODO load always on Subservers? + } + + public UUID getUUID() { + return uuid; + } + + public Locale getLocale() { + if(locale != null) + return locale; + return Locale.getDefault(); + } + + public Punishment getPunishment(Punishment.PunishmentType type) { + return punishments.getOrDefault(type, null); + } + + public boolean isPunished(Punishment.PunishmentType punishment) { + if (!punishments.containsKey(punishment)) { + return false; + } + if (!punishments.get(punishment).isCurrent()) { + if (punishment == Punishment.PunishmentType.Ban) { + BannedUserIPs.unbanIPs(id); + } + punishments.remove(punishment); + return false; + } + return true; + } + + public double getOnlinetime() { + return getPlaytime.select(rs -> { + if (rs.next() && rs.getBigDecimal("Playtime") != null) + return rs.getBigDecimal("Playtime").doubleValue(); + return 0.0; + }, id); + } + + public Timestamp getFirstjoin() { + return getFirstjoin.select(rs -> { + if (rs.next()) + return rs.getTimestamp("FirstJoin"); + return null; + }, id); + } + + public void punish(Punishment.PunishmentType punishment, Timestamp time, String banReason, int from, boolean perma) { + punishments.remove(punishment); + punishments.put(punishment, Punishment.createPunishment(id, from, punishment, banReason, time, perma)); + } + + public void setTeam(int team) { + this.team = team; + updateTeam.update(team, id); + setLeader(false); + } + + public void setLeader(boolean leader) { + this.leader = leader; + updateLeader.update(leader, id); + } + + 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 void setDiscordId(Long discordId) { + usersByDiscord.remove(this.discordId); + this.discordId = discordId; + updateDiscord.update(discordId, id); + if (discordId != null) { + usersByDiscord.put(discordId, this); + } + } } diff --git a/src/de/steamwar/sql/Tutorial.java b/src/de/steamwar/sql/Tutorial.java new file mode 100644 index 0000000..3249bfe --- /dev/null +++ b/src/de/steamwar/sql/Tutorial.java @@ -0,0 +1,94 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.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; + +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class Tutorial { + + private static final Table table = new Table<>(Tutorial.class); + private static final SelectStatement by_popularity = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Released = ? GROUP BY t.TutorialID ORDER BY SUM(r.Stars) DESC LIMIT ?, ?"); + private static final SelectStatement own = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? GROUP BY t.TutorialID ORDER BY t.TutorialID ASC LIMIT ?, ?"); + private static final SelectStatement by_creator_name = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? AND t.Name = ? GROUP BY t.TutorialID"); + private static final SelectStatement by_id = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.TutorialID = ? GROUP BY t.TutorialID"); + private static final Statement rate = new Statement("INSERT INTO TutorialRating (TutorialID, UserID, Stars) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Stars = VALUES(Stars)"); + private static final Statement create = new Statement("INSERT INTO Tutorial (Creator, Name, Item) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Item = VALUES(Item), Released = 0"); + private static final Statement release = table.update(Table.PRIMARY, "released"); + private static final Statement delete = table.delete(Table.PRIMARY); + + public static List getPage(int page, int elementsPerPage, boolean released) { + List tutorials = by_popularity.listSelect(released, page * elementsPerPage, elementsPerPage); + SteamwarUser.batchCache(tutorials.stream().map(tutorial -> tutorial.creator).collect(Collectors.toSet())); + return tutorials; + } + + public static List getOwn(int user, int page, int elementsPerPage) { + return own.listSelect(user, page * elementsPerPage, elementsPerPage); + } + + public static Tutorial create(int creator, String name, String item) { + create.update(creator, name, item); + return by_creator_name.select(creator, name); + } + + public static Tutorial get(int id) { + return by_id.select(id); + } + + @Getter + @Field(keys = {Table.PRIMARY}, autoincrement = true) + private final int id; + @Getter + @Field(keys = {"CreatorName"}) + private final int creator; + @Getter + @Field(keys = {"CreatorName"}) + private final String name; + @Getter + @Field(def = "'BOOK'") + private final String item; + @Getter + @Field(def = "0") + private final boolean released; + @Getter + @Field(def = "0") // Not really a field, but necessary for select generation + private final double stars; + + public void release() { + release.update(1, id); + } + + public void delete() { + delete.update(id); + } + + public void rate(int user, int rating) { + rate.update(id, user, rating); + } +} From 9b53ad748b32cc38ade1ccc606b116c0d6fbf1fb Mon Sep 17 00:00:00 2001 From: Lixfel Date: Tue, 21 Feb 2023 20:03:17 +0100 Subject: [PATCH 59/84] Es ist vollbracht Signed-off-by: Lixfel --- src/de/steamwar/sql/CheckedSchematic.java | 15 ++ src/de/steamwar/sql/IgnoreSystem.java | 59 +++++++ src/de/steamwar/sql/SchemElo.java | 3 +- src/de/steamwar/sql/UserElo.java | 178 ++++++++++++++++++++++ 4 files changed, 253 insertions(+), 2 deletions(-) create mode 100644 src/de/steamwar/sql/IgnoreSystem.java create mode 100644 src/de/steamwar/sql/UserElo.java diff --git a/src/de/steamwar/sql/CheckedSchematic.java b/src/de/steamwar/sql/CheckedSchematic.java index 71fe24a..e173d41 100644 --- a/src/de/steamwar/sql/CheckedSchematic.java +++ b/src/de/steamwar/sql/CheckedSchematic.java @@ -21,6 +21,7 @@ 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; @@ -33,11 +34,25 @@ public class CheckedSchematic { private static final Table table = new Table<>(CheckedSchematic.class); private static final SelectStatement statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); + private static final SelectStatement nodeHistory = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); + private static final Statement insert = table.insertAll(); + + public static void create(int nodeId, String name, int owner, int validator, Timestamp startTime, Timestamp endTime, String reason){ + insert.update(nodeId, owner, name, validator, startTime, endTime, reason); + } + + public static void create(SchematicNode node, int validator, Timestamp startTime, Timestamp endTime, String reason){ + create(node.getId(), node.getName(), node.getOwner(), validator, startTime, endTime, reason); + } public static List getLastDeclinedOfNode(int node){ return statusOfNode.listSelect(node); } + public static List previousChecks(SchematicNode node) { + return nodeHistory.listSelect(node.getId()); + } + @Field(nullable = true) private final Integer nodeId; @Field diff --git a/src/de/steamwar/sql/IgnoreSystem.java b/src/de/steamwar/sql/IgnoreSystem.java new file mode 100644 index 0000000..d6ce26a --- /dev/null +++ b/src/de/steamwar/sql/IgnoreSystem.java @@ -0,0 +1,59 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.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.sql.ResultSet; +import java.util.UUID; + +@AllArgsConstructor +public class IgnoreSystem { + + private static final Table table = new Table<>(IgnoreSystem.class, "IgnoredPlayers"); + private static final SelectStatement 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 ignorer; + @Field(keys = {Table.PRIMARY}) + private final int ignored; + + public static boolean isIgnored(UUID ignorer, UUID ignored){ + return isIgnored(SteamwarUser.get(ignorer), SteamwarUser.get(ignored)); + } + + public static boolean isIgnored(SteamwarUser ignorer, SteamwarUser ignored) { + return select.select(ResultSet::next, ignorer.getId(), ignored.getId()); + } + + public static void ignore(SteamwarUser ignorer, SteamwarUser ignored) { + insert.update(ignorer.getId(), ignored.getId()); + } + + public static void unIgnore(SteamwarUser ignorer, SteamwarUser ignored) { + delete.update(ignorer.getId(), ignored.getId()); + } +} diff --git a/src/de/steamwar/sql/SchemElo.java b/src/de/steamwar/sql/SchemElo.java index 1f795d6..0e92cfd 100644 --- a/src/de/steamwar/sql/SchemElo.java +++ b/src/de/steamwar/sql/SchemElo.java @@ -27,8 +27,7 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class SchemElo { - - public static final int ELO_DEFAULT = 1000; + private static final int ELO_DEFAULT = 1000; private static final Table table = new Table<>(SchemElo.class); private static final SelectStatement select = table.select(Table.PRIMARY); diff --git a/src/de/steamwar/sql/UserElo.java b/src/de/steamwar/sql/UserElo.java new file mode 100644 index 0000000..5de692c --- /dev/null +++ b/src/de/steamwar/sql/UserElo.java @@ -0,0 +1,178 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +@AllArgsConstructor +public class UserElo { + private static final int ELO_DEFAULT = 1000; + + private static final Map>> gameModeUserEloCache = new ConcurrentHashMap<>(); + private static final Map maxEloCache = new ConcurrentHashMap<>(); + private static final Map emblemCache = new ConcurrentHashMap<>(); + + public static void clear() { + gameModeUserEloCache.clear(); + maxEloCache.clear(); + emblemCache.clear(); + } + + private static final Table table = new Table<>(UserElo.class); + private static final SelectStatement getElo = table.select(Table.PRIMARY); + private static final Statement setElo = table.insertAll(); + private static final Statement maxElo = new Statement("SELECT MAX(Elo) AS MaxElo FROM UserElo WHERE Season = ? AND GameMode = ?"); + + private static final Statement place = new Statement("SELECT COUNT(*) AS Place FROM UserElo WHERE GameMode = ? AND Elo > ? AND Season = ?"); + private static final Statement fightsOfSeason = new Statement("SELECT COUNT(*) AS Fights FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode = ? AND UNIX_TIMESTAMP(StartTime) + Duration >= UNIX_TIMESTAMP(?)"); + + @Field(keys = {Table.PRIMARY}) + private final int season; + @Field(keys = {Table.PRIMARY}) + private final String gameMode; + @Field(keys = {Table.PRIMARY}) + private final int userId; + @Field + private final int elo; + + public static int getEloOrDefault(int userID, String gameMode) { + return getElo(userID, gameMode).orElse(ELO_DEFAULT); + } + + public static Optional getElo(int userID, String gameMode) { + return gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).computeIfAbsent(userID, uid -> Optional.ofNullable(getElo.select(Season.getSeason(), gameMode, userID)).map(userElo -> userElo.elo)); + } + + public static int getFightsOfSeason(int userID, String gameMode) { + return fightsOfSeason.select(rs -> { + if (rs.next()) + return rs.getInt("Fights"); + return 0; + }, userID, gameMode, Season.getSeasonStart()); + } + + private static int getMaxElo(String gameMode) { + return maxEloCache.computeIfAbsent(gameMode, gm -> maxElo.select(rs -> { + if (rs.next()) + return rs.getInt("MaxElo"); + return 0; + }, Season.getSeason(), gameMode)); + } + + public static void setElo(int userId, String gameMode, int elo) { + emblemCache.remove(userId); + + Optional oldElo = Optional.ofNullable(gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).put(userId, Optional.of(elo))).orElse(Optional.empty()); + int maxElo = getMaxElo(gameMode); + if (elo > maxElo || (oldElo.isPresent() && oldElo.get() == maxElo)) { + maxEloCache.remove(gameMode); + emblemCache.clear(); + } + + setElo.update(Season.getSeason(), gameMode, userId, elo); + } + + public static int getPlacement(int elo, String gameMode) { + return place.select(rs -> { + if (rs.next()) + return rs.getInt("Place") + 1; + return -1; + }, gameMode, elo, Season.getSeason()); + } + + public static String getEmblem(SteamwarUser user, List rankedModes) { + return emblemCache.computeIfAbsent(user.getId(), userId -> { + switch( + rankedModes.stream().filter( + mode -> UserElo.getFightsOfSeason(user.getId(), mode) >= 10 + ).map( + mode -> getProgression(user.getId(), mode) + ).max(Integer::compareTo).orElse(0) + ) { + case 0: + return ""; + case 1: + return "§7✧ "; + case 2: + return "§f✦ "; + case 3: + return "§e✶ "; + case 4: + return "§a✷ "; + case 5: + return "§b✸ "; + case 6: + return "§c✹ "; + case 7: + return "§5❂ "; + default: + throw new SecurityException("Progression out of range"); + } + }); + } + + public static String getEmblemProgression(String gameMode, int userId) { + switch (getProgression(userId, gameMode)) { + case 0: + return "§8✧ ✦ ✶ ✷ ✸ ✹ ❂"; + case 1: + return "§7✧ §8✦ ✶ ✷ ✸ ✹ ❂"; + case 2: + return "§8✧ §f✦ §8✶ ✷ ✸ ✹ ❂"; + case 3: + return "§8✧ ✦ §e✶ §8✷ ✸ ✹ ❂"; + case 4: + return "§8✧ ✦ ✶ §a✷ §8✸ ✹ ❂"; + case 5: + return "§8✧ ✦ ✶ ✷ §b✸ §8✹ ❂"; + case 6: + return "§8✧ ✦ ✶ ✷ ✸ §c✹ §8❂"; + case 7: + return "§8✧ ✦ ✶ ✷ ✸ ✹ §5❂"; + default: + throw new SecurityException("Progression is not in range"); + } + } + + private static int getProgression(int userId, String gameMode) { + int elo = getElo(userId, gameMode).orElse(0); + if(elo == 0) + return 0; + int maxElo = getMaxElo(gameMode); + + if (elo > maxElo * 0.99) return 7; + if (elo > maxElo * 0.97) return 6; + if (elo > maxElo * 0.94) return 5; + if (elo > maxElo * 0.88) return 4; + if (elo > maxElo * 0.76) return 3; + if (elo > maxElo * 0.51) return 2; + return 1; + } +} From 588d3c9dda47a8e61e4e01eea9c915f9d853eb3b Mon Sep 17 00:00:00 2001 From: Lixfel Date: Wed, 22 Feb 2023 16:18:03 +0100 Subject: [PATCH 60/84] Fix Bugs Signed-off-by: Lixfel --- src/de/steamwar/sql/Event.java | 5 ----- src/de/steamwar/sql/Fight.java | 2 +- src/de/steamwar/sql/FightPlayer.java | 12 ++++++------ src/de/steamwar/sql/SteamwarUser.java | 6 ++++-- src/de/steamwar/sql/Tutorial.java | 8 ++++---- src/de/steamwar/sql/internal/SqlTypeMapper.java | 1 - 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/de/steamwar/sql/Event.java b/src/de/steamwar/sql/Event.java index 90dcba2..7985232 100644 --- a/src/de/steamwar/sql/Event.java +++ b/src/de/steamwar/sql/Event.java @@ -97,11 +97,6 @@ public class Event { return schemType; } - @Deprecated - public String getSchemType() { - return schemType.toDB(); - } - private boolean now() { Instant now = Instant.now(); return now.isAfter(start.toInstant()) && now.isBefore(end.toInstant()); diff --git a/src/de/steamwar/sql/Fight.java b/src/de/steamwar/sql/Fight.java index 8576184..efa230d 100644 --- a/src/de/steamwar/sql/Fight.java +++ b/src/de/steamwar/sql/Fight.java @@ -41,7 +41,7 @@ public class Fight { public static List getPage(int page, int elementsPerPage) { List fights = getPage.listSelect(page * elementsPerPage, elementsPerPage); - List fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID).toArray(Integer[]::new)); + List fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID)); for(Fight fight : fights) { fight.initPlayers(fightPlayers); } diff --git a/src/de/steamwar/sql/FightPlayer.java b/src/de/steamwar/sql/FightPlayer.java index 73b5a05..57b977c 100644 --- a/src/de/steamwar/sql/FightPlayer.java +++ b/src/de/steamwar/sql/FightPlayer.java @@ -26,8 +26,9 @@ import de.steamwar.sql.internal.Table; import lombok.AllArgsConstructor; import lombok.Getter; -import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; @AllArgsConstructor public class FightPlayer { @@ -56,10 +57,9 @@ public class FightPlayer { create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut); } - public static List batchGet(Integer[] fightIds) { - if(fightIds.length == 0) - return Collections.emptyList(); - - return batchGet.listSelect((Object) fightIds); + public static List batchGet(Stream fightIds) { + try (SelectStatement batch = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN (" + fightIds.map(Object::toString).collect(Collectors.joining(", ")) + ")")) { + return batch.listSelect(); + } } } diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index dfbbcd0..7ce882d 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -26,6 +26,7 @@ import java.sql.Timestamp; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.stream.Collectors; public class SteamwarUser { @@ -47,7 +48,6 @@ public class SteamwarUser { private static final SelectStatement byDiscord = table.selectFields("DiscordId"); private static final SelectStatement byTeam = table.selectFields("Team"); private static final SelectStatement getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'"); - private static final SelectStatement batchGet = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN ?"); private static final Statement updateName = table.update(Table.PRIMARY, "UserName"); private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale"); @@ -135,7 +135,9 @@ public class SteamwarUser { if(ids.isEmpty()) return; - batchGet.listSelect((Object) ids.toArray()); + try (SelectStatement batch = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN (" + ids.stream().map(Object::toString).collect(Collectors.joining(", ")) + ")")) { + batch.listSelect(); + } } @Getter diff --git a/src/de/steamwar/sql/Tutorial.java b/src/de/steamwar/sql/Tutorial.java index 3249bfe..9febcba 100644 --- a/src/de/steamwar/sql/Tutorial.java +++ b/src/de/steamwar/sql/Tutorial.java @@ -63,7 +63,7 @@ public class Tutorial { @Getter @Field(keys = {Table.PRIMARY}, autoincrement = true) - private final int id; + private final int tutorialId; @Getter @Field(keys = {"CreatorName"}) private final int creator; @@ -81,14 +81,14 @@ public class Tutorial { private final double stars; public void release() { - release.update(1, id); + release.update(1, tutorialId); } public void delete() { - delete.update(id); + delete.update(tutorialId); } public void rate(int user, int rating) { - rate.update(id, user, rating); + rate.update(tutorialId, user, rating); } } diff --git a/src/de/steamwar/sql/internal/SqlTypeMapper.java b/src/de/steamwar/sql/internal/SqlTypeMapper.java index b9ff93e..34c6173 100644 --- a/src/de/steamwar/sql/internal/SqlTypeMapper.java +++ b/src/de/steamwar/sql/internal/SqlTypeMapper.java @@ -65,7 +65,6 @@ public final class SqlTypeMapper { 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); - new SqlTypeMapper<>(Integer[].class, null, (rs, identifier) -> { throw new SecurityException("Arrays cannot be used as type (recursive select)"); }, (st, index, value) -> st.setArray(index, st.getConnection().createArrayOf("INTEGER", value))); } private static void primitiveMapper(Class primitive, Class wrapped, String sqlType, SQLReader reader, SQLWriter writer) { From 109cc3ec7e0124650185ddb55bad9a9ec611e612 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 23 Feb 2023 09:55:59 +0100 Subject: [PATCH 61/84] Fix SchematicNode Tabcomplete --- src/de/steamwar/sql/SchematicNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 0e64522..9702781 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -549,7 +549,7 @@ public class SchematicNode { if (s.contains("/")) { String preTab = s.substring(0, s.lastIndexOf("/") + 1); SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab); - if (pa == null) return Collections.emptyList(); + if (pa == null) return new ArrayList<>(); List nodes = SchematicNode.list(user, pa.getId()); String br = pa.generateBreadcrumbs(); nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : ""))); From 95b46f3a9a83eb63f5770552134967a1921ae159 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Thu, 23 Feb 2023 14:03:51 +0100 Subject: [PATCH 62/84] Fix Team lookup Signed-off-by: Lixfel --- src/de/steamwar/sql/Team.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/de/steamwar/sql/Team.java b/src/de/steamwar/sql/Team.java index 8765ad9..b1a650b 100644 --- a/src/de/steamwar/sql/Team.java +++ b/src/de/steamwar/sql/Team.java @@ -80,6 +80,7 @@ public class Team { public static Team get(String name){ // No cache lookup due to low frequency use + name = name.toLowerCase(); return byName.select(name, name); } From 3778d6c1f943bb87025e4a3ab23f76c25e6325b6 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Thu, 23 Feb 2023 17:20:21 +0100 Subject: [PATCH 63/84] Hotfix SWUser Init --- src/de/steamwar/sql/SteamwarUser.java | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index 7ce882d..9e75bdd 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -81,27 +81,27 @@ public class SteamwarUser { SteamwarUser user = usersByName.get(userName.toLowerCase()); if(user != null) return user; - return byName.select(userName); + return loadPunishments(byName.select(userName)); } public static SteamwarUser get(UUID uuid){ SteamwarUser user = usersByUUID.get(uuid); if(user != null) return user; - return byUUID.select(uuid); + return loadPunishments(byUUID.select(uuid)); } public static SteamwarUser get(int id) { SteamwarUser user = usersById.get(id); if(user != null) return user; - return byID.select(id); + return loadPunishments(byID.select(id)); } public static SteamwarUser get(Long discordId) { if(usersByDiscord.containsKey(discordId)) return usersByDiscord.get(discordId); - return byDiscord.select(discordId); + return loadPunishments(byDiscord.select(discordId)); } public static SteamwarUser getOrCreate(UUID uuid, String name, Consumer newPlayer, BiConsumer nameUpdate) { @@ -123,11 +123,11 @@ public class SteamwarUser { } public static List getServerTeam() { - return getServerTeam.listSelect(); + return loadPunishments(getServerTeam.listSelect()); } public static List getTeam(int teamId) { - return byTeam.listSelect(teamId); + return loadPunishments(byTeam.listSelect(teamId)); } public static void batchCache(Set ids) { @@ -140,6 +140,16 @@ public class SteamwarUser { } } + private static List loadPunishments(List users) { + users.forEach(user -> user.punishments = Punishment.getPunishmentsOfPlayer(user.getId())); + return users; + } + + private static SteamwarUser loadPunishments(SteamwarUser user) { + user.punishments = Punishment.getPunishmentsOfPlayer(user.getId()); + return user; + } + @Getter @Field(keys = {Table.PRIMARY}, autoincrement = true) private final int id; @@ -165,7 +175,7 @@ public class SteamwarUser { @Field(keys = {"discordId"}, nullable = true) private Long discordId; - private final Map punishments; + private Map punishments; public SteamwarUser(int id, UUID uuid, String userName, UserGroup userGroup, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) { this.id = id; @@ -184,7 +194,6 @@ public class SteamwarUser { if (this.discordId != null) { usersByDiscord.put(discordId, this); } - punishments = Punishment.getPunishmentsOfPlayer(id); //TODO load always on Subservers? } public UUID getUUID() { From 9c6a7cf07dd25d1a4f12c4451373927d413dd9f4 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 23 Feb 2023 18:19:18 +0100 Subject: [PATCH 64/84] Add License Header --- src/de/steamwar/sql/NodeData.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/de/steamwar/sql/NodeData.java b/src/de/steamwar/sql/NodeData.java index 6550458..466c7e7 100644 --- a/src/de/steamwar/sql/NodeData.java +++ b/src/de/steamwar/sql/NodeData.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package de.steamwar.sql; import de.steamwar.sql.internal.*; From 2ff1ebea25753c49a948217c5a96492eac1cd807 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Fri, 24 Feb 2023 07:30:55 +0100 Subject: [PATCH 65/84] Fix Queries --- src/de/steamwar/sql/SchematicNode.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index 127f728..dd022cc 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -40,7 +40,7 @@ public class SchematicNode { 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 String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode "; private static final Table table = new Table<>(SchematicNode.class); private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType"); @@ -54,13 +54,13 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 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 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 FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName"); + private static final SelectStatement 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 FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); - private static final SelectStatement 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 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode" + Statement.NULL_SAFE_EQUALS + "?"); private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); - private static final SelectStatement 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 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"); + private static final SelectStatement byIdAndUser = new SelectStatement<>(table, "SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE NodeId = ?"); + private static final SelectStatement 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 FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId"); static { NodeMember.init(); From f2f2a901e52dadd1afbf41e436245cd78c1acd2b Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Fri, 24 Feb 2023 09:19:28 +0100 Subject: [PATCH 66/84] Fix Queries --- src/de/steamwar/sql/NodeData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/NodeData.java b/src/de/steamwar/sql/NodeData.java index 466c7e7..df3e094 100644 --- a/src/de/steamwar/sql/NodeData.java +++ b/src/de/steamwar/sql/NodeData.java @@ -40,7 +40,7 @@ public class NodeData { private static final Table table = new Table<>(NodeData.class); - private static final Statement updateDatabase = new Statement("UPDATE NodeData SET SchemData = ?, NodeFormat = ? WHERE NodeId = ?"); + private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE NodeFormat = VALUES(NodeFormat), SchemData = VALUES(SchemData)"); private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ?"); private static final SelectStatement get = table.select(Table.PRIMARY); From 9c15c0ccfeb40032bc48e89cd2af434b7e83b53d Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Fri, 24 Feb 2023 09:21:50 +0100 Subject: [PATCH 67/84] Fix Queries --- src/de/steamwar/sql/NodeData.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/sql/NodeData.java b/src/de/steamwar/sql/NodeData.java index df3e094..f8814b0 100644 --- a/src/de/steamwar/sql/NodeData.java +++ b/src/de/steamwar/sql/NodeData.java @@ -48,7 +48,13 @@ public class NodeData { public static NodeData get(SchematicNode node) { if(node.isDir()) throw new IllegalArgumentException("Node is a directory"); - return get.select(node); + return get.select(rs -> { + if(rs.next()) { + return new NodeData(node.getId(), rs.getBoolean("NodeFormat")); + } else { + return new NodeData(node.getId(), false); + } + }, node); } @Getter From 178c96e7c6e4fe7e7833194c976d2289340c31f3 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Fri, 24 Feb 2023 09:31:03 +0100 Subject: [PATCH 68/84] Fix Queries --- src/de/steamwar/sql/NodeData.java | 2 +- src/de/steamwar/sql/SchematicNode.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/de/steamwar/sql/NodeData.java b/src/de/steamwar/sql/NodeData.java index f8814b0..4998267 100644 --- a/src/de/steamwar/sql/NodeData.java +++ b/src/de/steamwar/sql/NodeData.java @@ -84,7 +84,7 @@ public class NodeData { } public void saveFromStream(InputStream blob, boolean newFormat) { - updateDatabase.update(blob, newFormat, nodeId); + updateDatabase.update(nodeId, newFormat, blob); nodeFormat = newFormat; } diff --git a/src/de/steamwar/sql/SchematicNode.java b/src/de/steamwar/sql/SchematicNode.java index dd022cc..7bedbdc 100644 --- a/src/de/steamwar/sql/SchematicNode.java +++ b/src/de/steamwar/sql/SchematicNode.java @@ -54,8 +54,8 @@ public class SchematicNode { private static final SelectStatement byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); private static final SelectStatement byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName"); private static final SelectStatement all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName"); - private static final SelectStatement 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 FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName"); - private static final SelectStatement 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 FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?"); + private static final SelectStatement list = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay 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 FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName"); + private static final SelectStatement byParentName = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay 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 FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?"); private static final SelectStatement schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?"); private static final SelectStatement 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 FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE RSN.ParentNode" + Statement.NULL_SAFE_EQUALS + "?"); private static final SelectStatement accessibleByUserType = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeType = ?"); From aa91584a1e361f5b466f4e359462051d5e065da3 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Fri, 24 Feb 2023 10:24:29 +0100 Subject: [PATCH 69/84] Add BIS --- src/de/steamwar/sql/NodeData.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/de/steamwar/sql/NodeData.java b/src/de/steamwar/sql/NodeData.java index 4998267..5274381 100644 --- a/src/de/steamwar/sql/NodeData.java +++ b/src/de/steamwar/sql/NodeData.java @@ -23,10 +23,7 @@ import de.steamwar.sql.internal.*; import lombok.AllArgsConstructor; import lombok.Getter; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PipedInputStream; +import java.io.*; import java.sql.PreparedStatement; import java.util.zip.GZIPInputStream; @@ -36,6 +33,7 @@ public class NodeData { static { new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); + new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream); } private static final Table table = new Table<>(NodeData.class); From 38457db0141d34e6f9f133e70bc3061b8a3d3e5a Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 24 Feb 2023 13:36:19 +0100 Subject: [PATCH 70/84] Hotfix NPE Signed-off-by: Lixfel --- src/de/steamwar/sql/SteamwarUser.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index 9e75bdd..13aecc9 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -146,6 +146,9 @@ public class SteamwarUser { } private static SteamwarUser loadPunishments(SteamwarUser user) { + if(user == null) + return null; + user.punishments = Punishment.getPunishmentsOfPlayer(user.getId()); return user; } From ec9d56dab9091ddf514b5d8066c8313f5ad18c75 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Fri, 24 Feb 2023 17:08:18 +0100 Subject: [PATCH 71/84] Hotfix NPE Signed-off-by: Lixfel --- src/de/steamwar/sql/SteamwarUser.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/de/steamwar/sql/SteamwarUser.java b/src/de/steamwar/sql/SteamwarUser.java index 13aecc9..c2fcc29 100644 --- a/src/de/steamwar/sql/SteamwarUser.java +++ b/src/de/steamwar/sql/SteamwarUser.java @@ -75,6 +75,7 @@ public class SteamwarUser { return; usersByName.remove(user.getUserName()); usersByUUID.remove(user.getUUID()); + usersByDiscord.remove(user.getDiscordId()); } public static SteamwarUser get(String userName){ @@ -105,7 +106,7 @@ public class SteamwarUser { } public static SteamwarUser getOrCreate(UUID uuid, String name, Consumer newPlayer, BiConsumer nameUpdate) { - SteamwarUser user = SteamwarUser.get(uuid); + SteamwarUser user = get(uuid); if (user != null) { if (!user.userName.equals(name)) { @@ -113,13 +114,13 @@ public class SteamwarUser { nameUpdate.accept(user.userName, name); user.userName = name; } + + return user; } else { insert.update(uuid, name); newPlayer.accept(uuid); return get(uuid); } - - return user; } public static List getServerTeam() { @@ -136,7 +137,7 @@ public class SteamwarUser { return; try (SelectStatement batch = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN (" + ids.stream().map(Object::toString).collect(Collectors.joining(", ")) + ")")) { - batch.listSelect(); + loadPunishments(batch.listSelect()); } } From 3a8df67f197748e24921f69a1c1c77ac6528ce05 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sat, 25 Feb 2023 07:35:32 +0100 Subject: [PATCH 72/84] Fix banneduserips Signed-off-by: Lixfel --- src/de/steamwar/sql/BannedUserIPs.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/sql/BannedUserIPs.java b/src/de/steamwar/sql/BannedUserIPs.java index 9a31099..f582817 100644 --- a/src/de/steamwar/sql/BannedUserIPs.java +++ b/src/de/steamwar/sql/BannedUserIPs.java @@ -27,6 +27,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import java.sql.Timestamp; +import java.time.Instant; import java.util.List; @AllArgsConstructor @@ -36,7 +37,7 @@ public class BannedUserIPs { private static final SelectStatement getByID = table.selectFields("UserID"); private static final SelectStatement getByIP = new SelectStatement<>(table, "SELECT * FROM BannedUserIPs WHERE IP = ? ORDER BY Timestamp DESC"); - private static final Statement banIP = table.insert(Table.PRIMARY); + private static final Statement banIP = table.insertAll(); private static final Statement unbanIPs = table.deleteFields("UserID"); @Getter @@ -57,7 +58,7 @@ public class BannedUserIPs { } public static void banIP(int userID, String ip){ - banIP.update(userID, ip); + banIP.update(userID, Timestamp.from(Instant.now()), ip); } public static void unbanIPs(int userID) { From 4f4caac67f26f68370ab3f4b07f9e199d1ec99c2 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 25 Feb 2023 15:04:30 +0100 Subject: [PATCH 73/84] Fix LinkageProcessor --- src/de/steamwar/linkage/LinkageProcessor.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/de/steamwar/linkage/LinkageProcessor.java b/src/de/steamwar/linkage/LinkageProcessor.java index f547817..488f9bf 100644 --- a/src/de/steamwar/linkage/LinkageProcessor.java +++ b/src/de/steamwar/linkage/LinkageProcessor.java @@ -173,18 +173,15 @@ public class LinkageProcessor extends AbstractProcessor { Map methods = new HashMap<>(); for (Map.Entry, List> entry : groupedByChecks.entrySet()) { - Map>> groupedByMethod = new HashMap<>(); + Map>> groupedByMethod = new HashMap<>(); for (TypeElement typeElement : entry.getValue()) { for (Map.Entry> linkages : getLinkagesOfType(typeElement).entrySet()) { - Map> internalGroup = groupedByMethod.computeIfAbsent(linkages.getKey(), s -> new HashMap<>()); - for (LinkageType linkageType : linkages.getValue()) { - List list = internalGroup.computeIfAbsent(linkageType, s -> new ArrayList<>()); - list.add(typeElement); - } + groupedByMethod.computeIfAbsent(linkages.getKey(), ignored -> new HashMap<>()) + .put(typeElement, linkages.getValue()); } } - for (Map.Entry>> group : groupedByMethod.entrySet()) { + for (Map.Entry>> group : groupedByMethod.entrySet()) { MethodBuilder method = methods.computeIfAbsent(group.getKey(), s -> { MethodBuilder methodBuilder = new MethodBuilder(s, "void"); buildPlan.addMethod(methodBuilder); @@ -192,10 +189,17 @@ public class LinkageProcessor extends AbstractProcessor { }); if (!entry.getKey().isEmpty()) method.addLine("if (" + String.join(" && ", entry.getKey()) + ") {"); - for (Map.Entry> toGenerate : group.getValue().entrySet()) { - toGenerate.getValue().forEach(typeElement -> { + for (Map.Entry> toGenerate : group.getValue().entrySet()) { + TypeElement typeElement = toGenerate.getKey(); + String instance = getElement(typeElement, neededFields); + if (toGenerate.getValue().size() > 1 && instance.startsWith("new ")) { + method.addLine(typeElement.getSimpleName() + " local" + typeElement.getSimpleName().toString() + " = " + instance + ";"); + instance = "local" + typeElement.getSimpleName().toString(); + } + String finalInstance = instance; + toGenerate.getValue().forEach(linkageType -> { buildPlan.addImport(typeElement.getQualifiedName().toString()); - toGenerate.getKey().generateCode(buildPlan, method, getElement(typeElement, neededFields), typeElement); + linkageType.generateCode(buildPlan, method, finalInstance, typeElement); }); } if (!entry.getKey().isEmpty()) method.addLine("}"); From e36022e64fdb6d3ec58d85ae734766a141674c0a Mon Sep 17 00:00:00 2001 From: Lixfel Date: Sun, 26 Feb 2023 15:50:26 +0100 Subject: [PATCH 74/84] Fix MemberCache invalidation Signed-off-by: Lixfel --- src/de/steamwar/sql/BauweltMember.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/de/steamwar/sql/BauweltMember.java b/src/de/steamwar/sql/BauweltMember.java index 87393e8..37be705 100644 --- a/src/de/steamwar/sql/BauweltMember.java +++ b/src/de/steamwar/sql/BauweltMember.java @@ -100,5 +100,6 @@ public class BauweltMember { public void remove(){ delete.update(bauweltID, memberID); + memberCache.remove(memberID); } } From 32de0b912d823d90de274885ab2beaa6aa6d190a Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 4 Mar 2023 12:08:20 +0100 Subject: [PATCH 75/84] Hotfix invert from Validator --- src/de/steamwar/command/AbstractSWCommand.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index b8dc078..dd87f30 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -369,7 +369,11 @@ public abstract class AbstractSWCommand { @Override public boolean validate(T sender, Object value, MessageSender messageSender) { - return inner.validate(sender, value, messageSender) ^ invert; + if (invert) { + return !inner.validate(sender, value, messageSender); + } else { + return inner.validate(sender, value, messageSender); + } } } } From 53efec2d0f8f5cfc314b25600aca540b1e4402ed Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 4 Mar 2023 12:24:46 +0100 Subject: [PATCH 76/84] Hotfix invert on first parameter --- .../steamwar/command/AbstractSWCommand.java | 2 + src/de/steamwar/command/SubCommand.java | 4 ++ .../command/InvertValidatorCommand.java | 45 +++++++++++++++++++ .../command/ValidatorCommandTest.java | 11 +++++ 4 files changed, 62 insertions(+) create mode 100644 testsrc/de/steamwar/command/InvertValidatorCommand.java diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index dd87f30..bda6e90 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -363,12 +363,14 @@ public abstract class AbstractSWCommand { private boolean invert; public Handler(AbstractSWCommand.Validator validator, Class clazz, Map> localValidator) { + System.out.println("Validator: " + validator + " " + clazz); inner = (AbstractValidator) SWCommandUtils.getValidator(validator, clazz, localValidator); invert = validator.invert(); } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { + System.out.println("Validator: " + inner + " " + invert); if (invert) { return !inner.validate(sender, value, messageSender); } else { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 58fa919..6b8828e 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -63,6 +63,10 @@ public class SubCommand implements Comparable> { AbstractSWCommand.Validator validator = parameters[0].getAnnotation(AbstractSWCommand.Validator.class); if (validator != null) { this.validator = (AbstractValidator) SWCommandUtils.getValidator(validator, parameters[0].getType(), localValidator); + if (validator.invert()) { + AbstractValidator current = this.validator; + this.validator = (sender, value, messageSender) -> !current.validate(sender, value, messageSender); + } } commandPart = generateCommandPart(abstractSWCommand, subCommand, parameters, localTypeMapper, localValidator); diff --git a/testsrc/de/steamwar/command/InvertValidatorCommand.java b/testsrc/de/steamwar/command/InvertValidatorCommand.java new file mode 100644 index 0000000..505d09b --- /dev/null +++ b/testsrc/de/steamwar/command/InvertValidatorCommand.java @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +package de.steamwar.command; + +import de.steamwar.command.dto.ExecutionIdentifier; +import de.steamwar.command.dto.TestSWCommand; +import de.steamwar.command.dto.TestValidator; + +public class InvertValidatorCommand extends TestSWCommand { + + public InvertValidatorCommand() { + super("testvalidator"); + } + + @Register + public void test(@Validator(value = "Text", invert = true) String sender, boolean b, boolean b2) { + System.out.println("test: " + sender + " " + b + " " + b2); + throw new ExecutionIdentifier("RunTestInvert"); + } + + @Validator(value = "Text", local = true) + public TestValidator testValidator() { + return (sender, value, messageSender) -> { + System.out.println("testValidator: " + sender + " " + value + " " + messageSender); + return false; + }; + } +} diff --git a/testsrc/de/steamwar/command/ValidatorCommandTest.java b/testsrc/de/steamwar/command/ValidatorCommandTest.java index d9712e3..084f53e 100644 --- a/testsrc/de/steamwar/command/ValidatorCommandTest.java +++ b/testsrc/de/steamwar/command/ValidatorCommandTest.java @@ -60,4 +60,15 @@ public class ValidatorCommandTest { assertCMDFramework(e, ExecutionIdentifier.class, "RunOnErrorDouble"); } } + + @Test + public void testInvert() { + InvertValidatorCommand cmd = new InvertValidatorCommand(); + try { + cmd.execute("test", "", new String[]{"false", "true"}); + assertThat(true, is(false)); + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "RunTestInvert"); + } + } } From b48a25453d89218f33987af532c83ad6b074b3af Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 4 Mar 2023 12:35:44 +0100 Subject: [PATCH 77/84] Hotfix the hotfix for Validator --- src/de/steamwar/command/AbstractSWCommand.java | 8 +------- .../de/steamwar/command/InvertValidatorCommand.java | 6 ++++++ testsrc/de/steamwar/command/ValidatorCommandTest.java | 11 +++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index bda6e90..b8dc078 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -363,19 +363,13 @@ public abstract class AbstractSWCommand { private boolean invert; public Handler(AbstractSWCommand.Validator validator, Class clazz, Map> localValidator) { - System.out.println("Validator: " + validator + " " + clazz); inner = (AbstractValidator) SWCommandUtils.getValidator(validator, clazz, localValidator); invert = validator.invert(); } @Override public boolean validate(T sender, Object value, MessageSender messageSender) { - System.out.println("Validator: " + inner + " " + invert); - if (invert) { - return !inner.validate(sender, value, messageSender); - } else { - return inner.validate(sender, value, messageSender); - } + return inner.validate(sender, value, messageSender) ^ invert; } } } diff --git a/testsrc/de/steamwar/command/InvertValidatorCommand.java b/testsrc/de/steamwar/command/InvertValidatorCommand.java index 505d09b..076d77a 100644 --- a/testsrc/de/steamwar/command/InvertValidatorCommand.java +++ b/testsrc/de/steamwar/command/InvertValidatorCommand.java @@ -35,6 +35,12 @@ public class InvertValidatorCommand extends TestSWCommand { throw new ExecutionIdentifier("RunTestInvert"); } + @Register + public void test(String sender, @Validator(value = "Text", invert = true) String h, int i) { + System.out.println("test: " + sender + " " + h + " " + i); + throw new ExecutionIdentifier("RunTestInvert2"); + } + @Validator(value = "Text", local = true) public TestValidator testValidator() { return (sender, value, messageSender) -> { diff --git a/testsrc/de/steamwar/command/ValidatorCommandTest.java b/testsrc/de/steamwar/command/ValidatorCommandTest.java index 084f53e..9f55f97 100644 --- a/testsrc/de/steamwar/command/ValidatorCommandTest.java +++ b/testsrc/de/steamwar/command/ValidatorCommandTest.java @@ -71,4 +71,15 @@ public class ValidatorCommandTest { assertCMDFramework(e, ExecutionIdentifier.class, "RunTestInvert"); } } + + @Test + public void testInvertOther() { + InvertValidatorCommand cmd = new InvertValidatorCommand(); + try { + cmd.execute("test", "", new String[]{"Hello", "0"}); + assertThat(true, is(false)); + } catch (Exception e) { + assertCMDFramework(e, ExecutionIdentifier.class, "RunTestInvert2"); + } + } } From 92cddbada179e32061b6497e7bdc81b7e0efa8b5 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 4 Mar 2023 12:36:06 +0100 Subject: [PATCH 78/84] Hotfix the hotfix for Validator --- testsrc/de/steamwar/command/ValidatorCommandTest.java | 4 ++-- ...nvertValidatorCommand.java => ValidatorInvertCommand.java} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename testsrc/de/steamwar/command/{InvertValidatorCommand.java => ValidatorInvertCommand.java} (94%) diff --git a/testsrc/de/steamwar/command/ValidatorCommandTest.java b/testsrc/de/steamwar/command/ValidatorCommandTest.java index 9f55f97..142d518 100644 --- a/testsrc/de/steamwar/command/ValidatorCommandTest.java +++ b/testsrc/de/steamwar/command/ValidatorCommandTest.java @@ -63,7 +63,7 @@ public class ValidatorCommandTest { @Test public void testInvert() { - InvertValidatorCommand cmd = new InvertValidatorCommand(); + ValidatorInvertCommand cmd = new ValidatorInvertCommand(); try { cmd.execute("test", "", new String[]{"false", "true"}); assertThat(true, is(false)); @@ -74,7 +74,7 @@ public class ValidatorCommandTest { @Test public void testInvertOther() { - InvertValidatorCommand cmd = new InvertValidatorCommand(); + ValidatorInvertCommand cmd = new ValidatorInvertCommand(); try { cmd.execute("test", "", new String[]{"Hello", "0"}); assertThat(true, is(false)); diff --git a/testsrc/de/steamwar/command/InvertValidatorCommand.java b/testsrc/de/steamwar/command/ValidatorInvertCommand.java similarity index 94% rename from testsrc/de/steamwar/command/InvertValidatorCommand.java rename to testsrc/de/steamwar/command/ValidatorInvertCommand.java index 076d77a..9007697 100644 --- a/testsrc/de/steamwar/command/InvertValidatorCommand.java +++ b/testsrc/de/steamwar/command/ValidatorInvertCommand.java @@ -23,9 +23,9 @@ import de.steamwar.command.dto.ExecutionIdentifier; import de.steamwar.command.dto.TestSWCommand; import de.steamwar.command.dto.TestValidator; -public class InvertValidatorCommand extends TestSWCommand { +public class ValidatorInvertCommand extends TestSWCommand { - public InvertValidatorCommand() { + public ValidatorInvertCommand() { super("testvalidator"); } From 6f7846956e41e1528e5e0d092db3affdb94a6a0c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 5 Mar 2023 21:54:35 +0100 Subject: [PATCH 79/84] Hotfix help system --- src/de/steamwar/command/AbstractSWCommand.java | 13 ++++++++++++- src/de/steamwar/command/CommandPart.java | 14 ++++++++++++++ src/de/steamwar/command/SWCommandUtils.java | 4 +++- src/de/steamwar/command/SubCommand.java | 3 +++ .../de/steamwar/command/SubCMDSortingCommand.java | 8 ++++++++ 5 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index b8dc078..06bb9f2 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -40,6 +40,7 @@ public abstract class AbstractSWCommand { private boolean initialized = false; protected final List> commandList = new ArrayList<>(); + protected final List> helpCommandList = new ArrayList<>(); private final Map> localTypeMapper = new HashMap<>(); private final Map> localValidators = new HashMap<>(); @@ -83,7 +84,9 @@ public abstract class AbstractSWCommand { initialize(); List errors = new ArrayList<>(); try { - if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) { + if (commandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) { + errors.forEach(Runnable::run); + } else if (helpCommandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) { errors.forEach(Runnable::run); } } catch (CommandFrameworkException e) { @@ -158,6 +161,14 @@ public abstract class AbstractSWCommand { } Collections.sort(commandList); + commandList.removeIf(subCommand -> { + if (subCommand.isHelp) { + helpCommandList.add(subCommand); + return true; + } else { + return false; + } + }); initialized = true; } diff --git a/src/de/steamwar/command/CommandPart.java b/src/de/steamwar/command/CommandPart.java index 5eb264c..02991b4 100644 --- a/src/de/steamwar/command/CommandPart.java +++ b/src/de/steamwar/command/CommandPart.java @@ -83,6 +83,20 @@ class CommandPart { this.next = next; } + public boolean isHelp() { + if (next == null) { + if (varArgType == null) { + return false; + } + if (varArgType != String.class) { + return false; + } + return typeMapper == SWCommandUtils.STRING_MAPPER; + } else { + return next.isHelp(); + } + } + public void generateArgumentArray(Consumer errors, List current, T sender, String[] args, int startIndex) { if (varArgType != null) { Object array = Array.newInstance(varArgType, args.length - startIndex); diff --git a/src/de/steamwar/command/SWCommandUtils.java b/src/de/steamwar/command/SWCommandUtils.java index c06d584..ed16eab 100644 --- a/src/de/steamwar/command/SWCommandUtils.java +++ b/src/de/steamwar/command/SWCommandUtils.java @@ -51,6 +51,8 @@ public class SWCommandUtils { } }; + static final AbstractTypeMapper STRING_MAPPER = createMapper(s -> s, Collections::singletonList); + static { addMapper(boolean.class, Boolean.class, createMapper(s -> { if (s.equalsIgnoreCase("true")) return true; @@ -61,7 +63,7 @@ public class SWCommandUtils { 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)); + MAPPER_FUNCTIONS.put(String.class.getTypeName(), STRING_MAPPER); } public static , K, V> void init(SWTypeMapperCreator swTypeMapperCreator) { diff --git a/src/de/steamwar/command/SubCommand.java b/src/de/steamwar/command/SubCommand.java index 6b8828e..368a817 100644 --- a/src/de/steamwar/command/SubCommand.java +++ b/src/de/steamwar/command/SubCommand.java @@ -47,6 +47,8 @@ public class SubCommand implements Comparable> { private CommandPart commandPart; + boolean isHelp = false; + SubCommand(AbstractSWCommand abstractSWCommand, Method method, String[] subCommand, Map> localTypeMapper, Map> localValidator, String[] description, boolean noTabComplete) { this.abstractSWCommand = abstractSWCommand; this.method = method; @@ -70,6 +72,7 @@ public class SubCommand implements Comparable> { } commandPart = generateCommandPart(abstractSWCommand, subCommand, parameters, localTypeMapper, localValidator); + if (commandPart != null) isHelp = commandPart.isHelp(); senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass()); senderFunction = t -> parameters[0].getType().cast(t); diff --git a/testsrc/de/steamwar/command/SubCMDSortingCommand.java b/testsrc/de/steamwar/command/SubCMDSortingCommand.java index 0f4e151..8b28760 100644 --- a/testsrc/de/steamwar/command/SubCMDSortingCommand.java +++ b/testsrc/de/steamwar/command/SubCMDSortingCommand.java @@ -21,6 +21,9 @@ 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.Collections; public class SubCMDSortingCommand extends TestSWCommand { @@ -47,4 +50,9 @@ public class SubCMDSortingCommand extends TestSWCommand { public void test(String s, String... args) { throw new ExecutionIdentifier("Command with n parameters"); } + + @ClassMapper(value = String.class, local = true) + public AbstractTypeMapper stringMapper() { + return SWCommandUtils.createMapper(s -> s, Collections::singletonList); + } } From 7474478489da42735a32e84b3f91144c64847e30 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sun, 5 Mar 2023 21:58:52 +0100 Subject: [PATCH 80/84] Hotfix help system --- src/de/steamwar/command/AbstractSWCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 06bb9f2..0562dcc 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -86,7 +86,8 @@ public abstract class AbstractSWCommand { try { if (commandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) { errors.forEach(Runnable::run); - } else if (helpCommandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) { + } else return; + if (errors.isEmpty() && helpCommandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) { errors.forEach(Runnable::run); } } catch (CommandFrameworkException e) { From d06970f494f3b644a46e9f20ee4eccbf8f716f9c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Mar 2023 16:49:51 +0100 Subject: [PATCH 81/84] Add initialize call with parent for part of --- src/de/steamwar/command/AbstractSWCommand.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 0562dcc..143ce1b 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -80,6 +80,9 @@ public abstract class AbstractSWCommand { protected void sendMessage(T sender, String message, Object[] args) { } + protected void initialisePartOf(AbstractSWCommand parent) { + } + protected final void execute(T sender, String alias, String[] args) { initialize(); List errors = new ArrayList<>(); @@ -156,6 +159,7 @@ public abstract class AbstractSWCommand { dependencyMap.get(this.getClass()).forEach(abstractSWCommand -> { abstractSWCommand.localTypeMapper.putAll((Map) localTypeMapper); abstractSWCommand.localValidators.putAll((Map) localValidators); + abstractSWCommand.initialisePartOf(this); abstractSWCommand.initialize(); commandList.addAll((Collection) abstractSWCommand.commandList); }); From 780caae80dc936ccdfcb43fb0bcc93e49e525953 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Mar 2023 16:53:17 +0100 Subject: [PATCH 82/84] Add initialize call with parent for part of --- src/de/steamwar/command/AbstractSWCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 143ce1b..7f2a383 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -159,7 +159,7 @@ public abstract class AbstractSWCommand { dependencyMap.get(this.getClass()).forEach(abstractSWCommand -> { abstractSWCommand.localTypeMapper.putAll((Map) localTypeMapper); abstractSWCommand.localValidators.putAll((Map) localValidators); - abstractSWCommand.initialisePartOf(this); + abstractSWCommand.initialisePartOf((AbstractSWCommand) this); abstractSWCommand.initialize(); commandList.addAll((Collection) abstractSWCommand.commandList); }); From 41b46eb6f57318ebe5fcbbb7ca48baa481a66b04 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Tue, 14 Mar 2023 16:57:11 +0100 Subject: [PATCH 83/84] Add initialize call with parent for part of --- src/de/steamwar/command/AbstractSWCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 7f2a383..6441d65 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -80,7 +80,7 @@ public abstract class AbstractSWCommand { protected void sendMessage(T sender, String message, Object[] args) { } - protected void initialisePartOf(AbstractSWCommand parent) { + protected void initialisePartOf(AbstractSWCommand parent) { } protected final void execute(T sender, String alias, String[] args) { @@ -159,7 +159,7 @@ public abstract class AbstractSWCommand { dependencyMap.get(this.getClass()).forEach(abstractSWCommand -> { abstractSWCommand.localTypeMapper.putAll((Map) localTypeMapper); abstractSWCommand.localValidators.putAll((Map) localValidators); - abstractSWCommand.initialisePartOf((AbstractSWCommand) this); + abstractSWCommand.initialisePartOf(this); abstractSWCommand.initialize(); commandList.addAll((Collection) abstractSWCommand.commandList); }); From 595df40e1e9078c310cbbb4bbded07744c361f15 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 17 Mar 2023 21:09:08 +0100 Subject: [PATCH 84/84] Hotfix tab completions for help string varargs commands --- src/de/steamwar/command/AbstractSWCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/de/steamwar/command/AbstractSWCommand.java b/src/de/steamwar/command/AbstractSWCommand.java index 6441d65..65f6643 100644 --- a/src/de/steamwar/command/AbstractSWCommand.java +++ b/src/de/steamwar/command/AbstractSWCommand.java @@ -31,6 +31,7 @@ import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; public abstract class AbstractSWCommand { @@ -102,7 +103,7 @@ public abstract class AbstractSWCommand { protected final List tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException { initialize(); String string = args[args.length - 1].toLowerCase(); - return commandList.stream() + return Stream.concat(commandList.stream(), helpCommandList.stream()) .filter(s -> !s.noTabComplete) .map(s -> s.tabComplete(sender, args)) .filter(Objects::nonNull)