001/* 002 * Copyright (c) 2016-2018 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.CommandPermission; 027import co.aikar.commands.annotation.Conditions; 028import co.aikar.commands.annotation.Default; 029import co.aikar.commands.annotation.Description; 030import co.aikar.commands.annotation.Flags; 031import co.aikar.commands.annotation.Optional; 032import co.aikar.commands.annotation.Single; 033import co.aikar.commands.annotation.Syntax; 034import co.aikar.commands.annotation.Values; 035import co.aikar.commands.contexts.ContextResolver; 036import co.aikar.commands.contexts.IssuerAwareContextResolver; 037import co.aikar.commands.contexts.IssuerOnlyContextResolver; 038import co.aikar.commands.contexts.OptionalContextResolver; 039 040import java.lang.reflect.Parameter; 041import java.util.Arrays; 042import java.util.HashMap; 043import java.util.HashSet; 044import java.util.Map; 045import java.util.Set; 046 047public class CommandParameter<CEC extends CommandExecutionContext<CEC, ? extends CommandIssuer>> { 048 private final Parameter parameter; 049 private final Class<?> type; 050 private final String name; 051 private final CommandManager manager; 052 private final int paramIndex; 053 054 private ContextResolver<?, CEC> resolver; 055 private boolean optional; 056 private Set<String> permissions = new HashSet<>(); 057 private String permission; 058 private String description; 059 private String defaultValue; 060 private String syntax; 061 private String conditions; 062 private boolean requiresInput; 063 private boolean commandIssuer; 064 private String[] values; 065 private Map<String, String> flags; 066 private boolean canConsumeInput; 067 private boolean optionalResolver; 068 boolean consumesRest; 069 070 public CommandParameter(RegisteredCommand<CEC> command, Parameter param, int paramIndex, boolean isLast) { 071 this.parameter = param; 072 this.type = param.getType(); 073 this.name = param.getName(); // do we care for an annotation to supply name? 074 this.manager = command.manager; 075 this.paramIndex = paramIndex; 076 Annotations annotations = manager.getAnnotations(); 077 078 this.defaultValue = annotations.getAnnotationValue(param, Default.class, Annotations.REPLACEMENTS | (type != String.class ? Annotations.NO_EMPTY : 0)); 079 this.description = annotations.getAnnotationValue(param, Description.class, Annotations.REPLACEMENTS | Annotations.DEFAULT_EMPTY); 080 this.conditions = annotations.getAnnotationValue(param, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); 081 082 //noinspection unchecked 083 this.resolver = manager.getCommandContexts().getResolver(type); 084 if (this.resolver == null) { 085 ACFUtil.sneaky(new InvalidCommandContextException( 086 "Parameter " + type.getSimpleName() + " of " + command + " has no applicable context resolver" 087 )); 088 } 089 090 this.optional = annotations.hasAnnotation(param, Optional.class) || this.defaultValue != null || (isLast && type == String[].class); 091 this.permission = annotations.getAnnotationValue(param, CommandPermission.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); 092 this.optionalResolver = isOptionalResolver(resolver); 093 this.requiresInput = !this.optional && !this.optionalResolver; 094 //noinspection unchecked 095 this.commandIssuer = paramIndex == 0 && manager.isCommandIssuer(type); 096 this.canConsumeInput = !this.commandIssuer && !(resolver instanceof IssuerOnlyContextResolver); 097 this.consumesRest = (type == String.class && !annotations.hasAnnotation(param, Single.class)) || (isLast && type == String[].class); 098 099 this.values = annotations.getAnnotationValues(param, Values.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); 100 101 this.syntax = null; 102 if (!commandIssuer) { 103 this.syntax = annotations.getAnnotationValue(param, Syntax.class); 104 if (syntax == null) { 105 if (!requiresInput && canConsumeInput) { 106 this.syntax = "[" + name + "]"; 107 } else if (requiresInput) { 108 this.syntax = "<" + name + ">"; 109 } 110 } 111 } 112 113 this.flags = new HashMap<>(); 114 String flags = annotations.getAnnotationValue(param, Flags.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); 115 if (flags != null) { 116 parseFlags(flags); 117 } 118 inheritContextFlags(command.scope); 119 this.computePermissions(); 120 } 121 122 private void inheritContextFlags(BaseCommand scope) { 123 if (!scope.contextFlags.isEmpty()) { 124 Class<?> pCls = this.type; 125 do { 126 parseFlags(scope.contextFlags.get(pCls)); 127 } while ((pCls = pCls.getSuperclass()) != null); 128 } 129 if (scope.parentCommand != null) { 130 inheritContextFlags(scope.parentCommand); 131 } 132 } 133 134 private void parseFlags(String flags) { 135 if (flags != null) { 136 for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) { 137 String[] v = ACFPatterns.EQUALS.split(s, 2); 138 if (!this.flags.containsKey(v[0])) { 139 this.flags.put(v[0], v.length > 1 ? v[1] : null); 140 } 141 } 142 } 143 } 144 145 private void computePermissions() { 146 this.permissions.clear(); 147 if (this.permission != null && !this.permission.isEmpty()) { 148 this.permissions.addAll(Arrays.asList(ACFPatterns.COMMA.split(this.permission))); 149 } 150 } 151 152 private boolean isOptionalResolver(ContextResolver<?, CEC> resolver) { 153 return resolver instanceof IssuerAwareContextResolver 154 || resolver instanceof IssuerOnlyContextResolver 155 || resolver instanceof OptionalContextResolver; 156 } 157 158 159 public Parameter getParameter() { 160 return parameter; 161 } 162 163 public Class<?> getType() { 164 return type; 165 } 166 167 public String getName() { 168 return name; 169 } 170 171 public CommandManager getManager() { 172 return manager; 173 } 174 175 public int getParamIndex() { 176 return paramIndex; 177 } 178 179 public ContextResolver<?, CEC> getResolver() { 180 return resolver; 181 } 182 183 public void setResolver(ContextResolver<?, CEC> resolver) { 184 this.resolver = resolver; 185 } 186 187 public boolean isOptional() { 188 return optional; 189 } 190 191 public void setOptional(boolean optional) { 192 this.optional = optional; 193 } 194 195 public String getDescription() { 196 return description; 197 } 198 199 public void setDescription(String description) { 200 this.description = description; 201 } 202 203 public String getDefaultValue() { 204 return defaultValue; 205 } 206 207 public void setDefaultValue(String defaultValue) { 208 this.defaultValue = defaultValue; 209 } 210 211 public boolean isCommandIssuer() { 212 return commandIssuer; 213 } 214 215 public void setCommandIssuer(boolean commandIssuer) { 216 this.commandIssuer = commandIssuer; 217 } 218 219 public String[] getValues() { 220 return values; 221 } 222 223 public void setValues(String[] values) { 224 this.values = values; 225 } 226 227 public Map<String, String> getFlags() { 228 return flags; 229 } 230 231 public void setFlags(Map<String, String> flags) { 232 this.flags = flags; 233 } 234 235 public boolean canConsumeInput() { 236 return canConsumeInput; 237 } 238 239 public void setCanConsumeInput(boolean canConsumeInput) { 240 this.canConsumeInput = canConsumeInput; 241 } 242 243 public void setOptionalResolver(boolean optionalResolver) { 244 this.optionalResolver = optionalResolver; 245 } 246 247 public boolean isOptionalResolver() { 248 return optionalResolver; 249 } 250 251 public boolean requiresInput() { 252 return requiresInput; 253 } 254 255 public void setRequiresInput(boolean requiresInput) { 256 this.requiresInput = requiresInput; 257 } 258 259 public String getSyntax() { 260 return syntax; 261 } 262 263 public void setSyntax(String syntax) { 264 this.syntax = syntax; 265 } 266 267 public String getConditions() { 268 return conditions; 269 } 270 271 public void setConditions(String conditions) { 272 this.conditions = conditions; 273 } 274 275 public Set<String> getRequiredPermissions() { 276 return permissions; 277 } 278}