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.commands.annotation.Conditions; 027import com.google.common.collect.HashBasedTable; 028import com.google.common.collect.Maps; 029import com.google.common.collect.Table; 030import org.jetbrains.annotations.NotNull; 031 032import java.util.Map; 033 034@SuppressWarnings("BooleanMethodIsAlwaysInverted") // No IDEA, you are wrong 035public class CommandConditions < 036 I extends CommandIssuer, 037 CEC extends CommandExecutionContext<CEC, I>, 038 CC extends ConditionContext<I> 039 > { 040 private CommandManager manager; 041 private Map<String, Condition<I>> conditions = Maps.newHashMap(); 042 private Table<Class<?>, String, ParameterCondition<?, ?, ?>> paramConditions = HashBasedTable.create(); 043 044 CommandConditions(CommandManager manager) { 045 this.manager = manager; 046 } 047 048 public Condition<I> addCondition(@NotNull String id, @NotNull Condition<I> handler) { 049 return this.conditions.put(id.toLowerCase(), handler); 050 } 051 052 public <P> ParameterCondition addCondition(Class<P> clazz, @NotNull String id, 053 @NotNull ParameterCondition<P, CEC, I> handler) { 054 return this.paramConditions.put(clazz, id.toLowerCase(), handler); 055 } 056 057 void validateConditions(CommandOperationContext context) throws InvalidCommandArgument { 058 RegisteredCommand cmd = context.getRegisteredCommand(); 059 060 validateConditions(cmd.conditions, context); 061 validateConditions(cmd.scope, context); 062 } 063 064 private void validateConditions(BaseCommand scope, CommandOperationContext operationContext) throws InvalidCommandArgument { 065 validateConditions(scope.conditions, operationContext); 066 067 if (scope.parentCommand != null) { 068 validateConditions(scope.parentCommand, operationContext); 069 } 070 } 071 072 private void validateConditions(String conditions, CommandOperationContext context) throws InvalidCommandArgument { 073 if (conditions == null) { 074 return; 075 } 076 077 conditions = this.manager.getCommandReplacements().replace(conditions); 078 CommandIssuer issuer = context.getCommandIssuer(); 079 for (String cond : ACFPatterns.PIPE.split(conditions)) { 080 String[] split = ACFPatterns.COLON.split(cond, 2); 081 String id = split[0].toLowerCase(); 082 Condition<I> condition = this.conditions.get(id); 083 if (condition == null) { 084 RegisteredCommand cmd = context.getRegisteredCommand(); 085 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName()); 086 continue; 087 } 088 089 String config = split.length == 2 ? split[1] : null; 090 //noinspection unchecked 091 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config); 092 //noinspection unchecked 093 condition.validateCondition(conditionContext); 094 } 095 } 096 097 void validateConditions(CEC execContext, Object value) throws InvalidCommandArgument { 098 String conditions = execContext.getCommandParameter().getConditions(); 099 if (conditions == null) { 100 return; 101 } 102 conditions = this.manager.getCommandReplacements().replace(conditions); 103 I issuer = execContext.getIssuer(); 104 for (String cond : ACFPatterns.PIPE.split(conditions)) { 105 String[] split = ACFPatterns.COLON.split(cond, 2); 106 ParameterCondition condition; 107 Class<?> cls = execContext.getParam().getType(); 108 String id = split[0].toLowerCase(); 109 do { 110 condition = this.paramConditions.get(cls, id); 111 if (condition == null && cls.getSuperclass() != null && cls.getSuperclass() != Object.class) { 112 cls = cls.getSuperclass(); 113 } else { 114 break; 115 } 116 } while (cls != null); 117 118 119 if (condition == null) { 120 RegisteredCommand cmd = execContext.getCmd(); 121 this.manager.log(LogLevel.ERROR, "Could not find command condition " + id + " for " + cmd.method.getName() + "::" +execContext.getParam().getName()); 122 continue; 123 } 124 String config = split.length == 2 ? split[1] : null; 125 //noinspection unchecked 126 CC conditionContext = (CC) this.manager.createConditionContext(issuer, config); 127 128 //noinspection unchecked 129 condition.validateCondition(conditionContext, execContext, value); 130 } 131 } 132 133 public interface Condition <I extends CommandIssuer> { 134 void validateCondition(ConditionContext<I> context) throws InvalidCommandArgument; 135 } 136 137 public interface ParameterCondition <P, CEC extends CommandExecutionContext, I extends CommandIssuer> { 138 void validateCondition(ConditionContext<I> context, CEC execContext, P value) throws InvalidCommandArgument; 139 } 140}