/* * This file is a part of the SteamWar software. * * Copyright (C) 2020 SteamWar.de-Serverteam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package de.steamwar.command; import lombok.AllArgsConstructor; import lombok.Setter; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.function.Consumer; class CommandPart { private static final String[] EMPTY_ARRAY = new String[0]; @AllArgsConstructor private static class CheckArgumentResult { private final boolean success; private final Object value; } private AbstractSWCommand command; private AbstractTypeMapper typeMapper; private AbstractValidator validator; private AbstractGuardChecker guardChecker; private Class varArgType; private String optional; private GuardCheckType guardCheckType; private CommandPart next = null; @Setter private boolean ignoreAsArgument = false; public CommandPart(AbstractSWCommand command, AbstractTypeMapper typeMapper, AbstractValidator validator, AbstractGuardChecker guardChecker, Class varArgType, String optional, GuardCheckType guardCheckType) { this.command = command; this.typeMapper = typeMapper; this.validator = validator; this.guardChecker = guardChecker; this.varArgType = varArgType; this.optional = optional; this.guardCheckType = guardCheckType; validatePart(); } public void setNext(CommandPart next) { if (varArgType != null) { throw new IllegalArgumentException("There can't be a next part if this is a vararg part!"); } this.next = next; } private void validatePart() { if (guardCheckType == GuardCheckType.TAB_COMPLETE) { throw new IllegalArgumentException("Tab complete is not allowed as a guard check type!"); } if (optional != null && varArgType != null) { throw new IllegalArgumentException("A vararg part can't have an optional part!"); } if (optional != null) { try { typeMapper.map(null, EMPTY_ARRAY, optional); } catch (Exception e) { throw new IllegalArgumentException("The optional part is not valid!"); } } } 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, null, sender, args, i); if (!validArgument.success) { throw new CommandParseException(); } Array.set(array, i - startIndex, validArgument.value); } if (validator != null && !validator.validate(sender, array, (s, objects) -> { errors.accept(() -> command.sendMessage(sender, s, objects)); })) { throw new CommandParseException(); } current.add(array); return; } CheckArgumentResult validArgument = checkArgument(errors, null, sender, args, startIndex); if (!validArgument.success && optional == null) { throw new CommandParseException(); } if (!validArgument.success) { if (!ignoreAsArgument) { current.add(typeMapper.map(sender, EMPTY_ARRAY, optional)); } if (next != null) { next.generateArgumentArray(errors, current, sender, args, startIndex); } return; } if (!ignoreAsArgument) { current.add(validArgument.value); } if (next != null) { next.generateArgumentArray(errors, current, sender, args, startIndex + 1); } } public boolean guardCheck(T sender, String[] args, int startIndex) { if (varArgType != null) { for (int i = startIndex; i < args.length; i++) { GuardResult guardResult = checkGuard(guardCheckType, sender, args, i); if (guardResult == GuardResult.DENIED) { throw new CommandNoHelpException(); } if (guardResult == GuardResult.DENIED_WITH_HELP) { return false; } } return true; } GuardResult guardResult = checkGuard(guardCheckType, sender, args, startIndex); if (guardResult == GuardResult.DENIED) { if (optional != null && next != null) { return next.guardCheck(sender, args, startIndex); } throw new CommandNoHelpException(); } if (guardResult == GuardResult.DENIED_WITH_HELP) { if (optional != null && next != null) { return next.guardCheck(sender, args, startIndex); } return false; } if (next != null) { return next.guardCheck(sender, args, startIndex + 1); } return true; } 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) -> {}, GuardCheckType.TAB_COMPLETE, sender, args, i); if (!validArgument.success) { return; } } Collection strings = tabCompletes(sender, args, args.length - 1); if (strings != null) { current.addAll(strings); } return; } if (args.length - 1 > startIndex) { CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, GuardCheckType.TAB_COMPLETE, sender, args, startIndex); if (checkArgumentResult.success && next != null) { next.generateTabComplete(current, sender, args, startIndex + 1); return; } if (optional != null && next != null) { next.generateTabComplete(current, sender, args, startIndex); } return; } Collection strings = tabCompletes(sender, args, startIndex); if (strings != null) { current.addAll(strings); } if (optional != null && next != null) { next.generateTabComplete(current, sender, args, startIndex); } } private Collection tabCompletes(T sender, String[] args, int startIndex) { return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> { return typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]); }); } private CheckArgumentResult checkArgument(Consumer errors, GuardCheckType guardCheckType, T sender, String[] args, int index) { try { Object value = typeMapper.map(sender, Arrays.copyOf(args, index), args[index]); if (validator != null && errors != null) { if (!validator.validate(sender, value, (s, objects) -> { errors.accept(() -> { command.sendMessage(sender, s, objects); }); })) { return new CheckArgumentResult(false, null); } return new CheckArgumentResult(value != null, value); } if (value == null) { return new CheckArgumentResult(false, null); } GuardResult guardResult = checkGuard(guardCheckType, sender, args, index); switch (guardResult) { case ALLOWED: return new CheckArgumentResult(true, value); case DENIED: throw new CommandNoHelpException(); case DENIED_WITH_HELP: default: return new CheckArgumentResult(false, null); } } catch (Exception e) { return new CheckArgumentResult(false, null); } } private GuardResult checkGuard(GuardCheckType guardCheckType, T sender, String[] args, int index) { if (guardChecker != null && guardCheckType != null) { return guardChecker.guard(sender, guardCheckType, Arrays.copyOf(args, index), args[index]); } return GuardResult.ALLOWED; } }