001/* 002 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License 003 * 004 * Permission is hereby granted, free of charge, to any person obtaining 005 * a copy of this software and associated documentation files (the 006 * "Software"), to deal in the Software without restriction, including 007 * without limitation the rights to use, copy, modify, merge, publish, 008 * distribute, sublicense, and/or sell copies of the Software, and to 009 * permit persons to whom the Software is furnished to do so, subject to 010 * the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be 013 * included in all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 016 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 017 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 018 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 019 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 020 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 021 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 022 */ 023 024package co.aikar.commands; 025 026import co.aikar.util.Table; 027import org.jetbrains.annotations.NotNull; 028 029import java.util.HashMap; 030import java.util.Locale; 031import java.util.Map; 032 033@SuppressWarnings("BooleanMethodIsAlwaysInverted") // No IDEA, you are wrong 034public class CommandConditions< 035 I extends CommandIssuer, 036 CEC extends CommandExecutionContext<CEC, I>, 037 CC extends ConditionContext<I> 038 > { 039 private CommandManager manager; 040 private Map<String, Condition<I>> conditions = new HashMap<>(); 041 private Table<Class<?>, String, ParameterCondition<?, ?, ?>> paramConditions = new Table<>(); 042 043 CommandConditions(CommandManager manager) { 044 this.manager = manager; 045 } 046 047 public Condition<I> addCondition(@NotNull String id, @NotNull Condition<I> handler) { 048 return this.conditions.put(id.toLowerCase(Locale.ENGLISH), handler); 049 } 050 051 public <P> ParameterCondition addCondition(Class<P> clazz, @NotNull String id, 052 @NotNull ParameterCondition<P, CEC, I> handler) { 053 return this.paramConditions.put(clazz, id.toLowerCase(Locale.ENGLISH), handler); 054 } 055 056 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument { 057 RegisteredCommand cmd = context.getRegisteredCommand(); 058 059 validateConditions(cmd.conditions, context); 060 validateConditions(cmd.scope, context); 061 } 062 063 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument { 064 validateConditions(scope.conditions, operationContext); 065 066 if (scope.parentCommand != null) { 067 validateConditions(scope.parentCommand, operationContext); 068 } 069 } 070 071 private void validateConditions(String conditions, CommandOperationContext context) throws InvalidCommandArgument { 072 if (conditions == null) { 073 return; 074 } 075 076 conditions = this.manager.getCommandReplacements().replace(conditions); 077 CommandIssuer issuer = context.getCommandIssuer(); 078 for (String cond : ACFPatterns.PIPE.split(conditions)) { 079 String[] split = ACFPatterns.COLON.split(cond, 2); 080 String id = split[0].toLowerCase(Locale.ENGLISH); 081 Condition<I> condition = this.conditions.get(id); 082 if (condition == null) { 083 RegisteredCommand cmd = context.getRegisteredCommand(); 084 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName()); 085 continue; 086 } 087 088 String config = split.length == 2 ? split[1] : null; 089 //noinspection unchecked 090 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config); 091 condition.validateCondition(conditionContext); 092 } 093 } 094 095 void validateConditions(CEC execContext, Object value) throws InvalidCommandArgument { 096 String conditions = execContext.getCommandParameter().getConditions(); 097 if (conditions == null) { 098 return; 099 } 100 conditions = this.manager.getCommandReplacements().replace(conditions); 101 I issuer = execContext.getIssuer(); 102 for (String cond : ACFPatterns.PIPE.split(conditions)) { 103 String[] split = ACFPatterns.COLON.split(cond, 2); 104 ParameterCondition condition; 105 Class<?> cls = execContext.getParam().getType(); 106 String id = split[0].toLowerCase(Locale.ENGLISH); 107 do { 108 condition = this.paramConditions.get(cls, id); 109 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) { 110 cls = cls.getSuperclass(); 111 } else { 112 break; 113 } 114 } while (cls != null); 115 116 117 if (condition == null) { 118 RegisteredCommand cmd = execContext.getCmd(); 119 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" + execContext.getParam().getName()); 120 continue; 121 } 122 String config = split.length == 2 ? split[1] : null; 123 //noinspection unchecked 124 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config); 125 126 //noinspection unchecked 127 condition.validateCondition(conditionContext, execContext, value); 128 } 129 } 130 131 public interface Condition<I extends CommandIssuer> { 132 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument; 133 } 134 135 public interface ParameterCondition<P, CEC extends CommandExecutionContext, I extends CommandIssuer> { 136 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument; 137 } 138}