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.CatchAll; 027import co.aikar.commands.annotation.CatchUnknown; 028import co.aikar.commands.annotation.CommandAlias; 029import co.aikar.commands.annotation.CommandPermission; 030import co.aikar.commands.annotation.Conditions; 031import co.aikar.commands.annotation.Default; 032import co.aikar.commands.annotation.HelpCommand; 033import co.aikar.commands.annotation.PreCommand; 034import co.aikar.commands.annotation.Subcommand; 035import co.aikar.commands.annotation.UnknownHandler; 036import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; 037import com.google.common.collect.HashMultimap; 038import com.google.common.collect.ImmutableList; 039import com.google.common.collect.ImmutableSet; 040import com.google.common.collect.Iterables; 041import com.google.common.collect.Lists; 042import com.google.common.collect.Maps; 043import com.google.common.collect.SetMultimap; 044import com.google.common.collect.Sets; 045 046import java.lang.reflect.Constructor; 047import java.lang.reflect.InvocationTargetException; 048import java.lang.reflect.Method; 049import java.lang.reflect.Parameter; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.Collections; 053import java.util.HashMap; 054import java.util.HashSet; 055import java.util.List; 056import java.util.Map; 057import java.util.Objects; 058import java.util.Optional; 059import java.util.Set; 060import java.util.Stack; 061import java.util.stream.Collectors; 062import java.util.stream.Stream; 063 064@SuppressWarnings("unused") 065public abstract class BaseCommand { 066 067 public static final String CATCHUNKNOWN = "__catchunknown"; 068 public static final String DEFAULT = "__default"; 069 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create(); 070 final Map<Class<?>, String> contextFlags = Maps.newHashMap(); 071 private Method preCommandHandler; 072 073 @SuppressWarnings("WeakerAccess") 074 private String execLabel; 075 @SuppressWarnings("WeakerAccess") 076 private String execSubcommand; 077 @SuppressWarnings("WeakerAccess") 078 private String[] origArgs; 079 CommandManager<?, ?, ?, ?, ?, ?> manager = null; 080 BaseCommand parentCommand; 081 Map<String, RootCommand> registeredCommands = new HashMap<>(); 082 String description; 083 String commandName; 084 String permission; 085 String conditions; 086 087 private ExceptionHandler exceptionHandler = null; 088 CommandOperationContext lastCommandOperationContext; 089 private String parentSubcommand; 090 091 public BaseCommand() {} 092 public BaseCommand(String cmd) { 093 this.commandName = cmd; 094 } 095 096 /** 097 * Gets the root command name that the user actually typed 098 * @return Name 099 */ 100 public String getExecCommandLabel() { 101 return execLabel; 102 } 103 104 /** 105 * Gets the actual sub command name the user typed 106 * @return Name 107 */ 108 public String getExecSubcommand() { 109 return execSubcommand; 110 } 111 112 /** 113 * Gets the actual args in string form the user typed 114 * @return Args 115 */ 116 public String[] getOrigArgs() { 117 return origArgs; 118 } 119 120 void setParentCommand(BaseCommand command) { 121 this.parentCommand = command; 122 } 123 void onRegister(CommandManager manager) { 124 onRegister(manager, this.commandName); 125 } 126 void onRegister(CommandManager manager, String cmd) { 127 manager.injectDependencies(this); 128 this.manager = manager; 129 130 final Annotations annotations = manager.getAnnotations(); 131 final Class<? extends BaseCommand> self = this.getClass(); 132 133 String[] cmdAliases = annotations.getAnnotationValues(self, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE | Annotations.NO_EMPTY); 134 135 if (cmd == null && cmdAliases != null) { 136 cmd = cmdAliases[0]; 137 } 138 139 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase(); 140 this.permission = annotations.getAnnotationValue(self, CommandPermission.class, Annotations.REPLACEMENTS); 141 this.description = this.commandName + " commands"; 142 this.parentSubcommand = getParentSubcommand(self); 143 this.conditions = annotations.getAnnotationValue(self, Conditions.class, Annotations.REPLACEMENTS | Annotations.NO_EMPTY); 144 145 registerSubcommands(); 146 147 if (cmdAliases != null) { 148 Set<String> cmdList = new HashSet<>(); 149 Collections.addAll(cmdList, cmdAliases); 150 cmdList.remove(cmd); 151 for (String cmdAlias : cmdList) { 152 register(cmdAlias, this); 153 } 154 } 155 156 if (cmd != null) { 157 register(cmd, this); 158 } 159 registerSubclasses(cmd); 160 161 } 162 163 private void registerSubclasses(String cmd) { 164 for (Class<?> clazz : this.getClass().getDeclaredClasses()) { 165 if (BaseCommand.class.isAssignableFrom(clazz)) { 166 try { 167 BaseCommand subCommand = null; 168 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); 169 for (Constructor<?> declaredConstructor : declaredConstructors) { 170 171 declaredConstructor.setAccessible(true); 172 Parameter[] parameters = declaredConstructor.getParameters(); 173 if (parameters.length == 1) { 174 subCommand = (BaseCommand) declaredConstructor.newInstance(this); 175 } else { 176 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")"); 177 } 178 } 179 if (subCommand != null) { 180 subCommand.setParentCommand(this); 181 subCommand.onRegister(manager, cmd); 182 this.subCommands.putAll(subCommand.subCommands); 183 this.registeredCommands.putAll(subCommand.registeredCommands); 184 } else { 185 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName()); 186 } 187 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { 188 this.manager.log(LogLevel.ERROR, "Error registering subclass", e); 189 } 190 } 191 } 192 } 193 194 private void registerSubcommands() { 195 final Annotations annotations = manager.getAnnotations(); 196 boolean foundDefault = false; 197 boolean foundCatchUnknown = false; 198 boolean isParentEmpty = parentSubcommand.isEmpty(); 199 200 for (Method method : this.getClass().getMethods()) { 201 method.setAccessible(true); 202 String sublist = null; 203 String sub = getSubcommandValue(method); 204 final boolean def = annotations.hasAnnotation(method, Default.class); 205 final String helpCommand = annotations.getAnnotationValue(method, HelpCommand.class, Annotations.NOTHING); 206 final String commandAliases = annotations.getAnnotationValue(method, CommandAlias.class, Annotations.NOTHING); 207 208 if (!isParentEmpty && def) { 209 sub = parentSubcommand; 210 } 211 if (isParentEmpty && (def || (!foundDefault && helpCommand != null))) { 212 if (!foundDefault) { 213 if (def) { 214 this.subCommands.get(DEFAULT).clear(); 215 foundDefault = true; 216 } 217 registerSubcommand(method, DEFAULT); 218 } else { 219 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); 220 } 221 } 222 223 if (sub != null) { 224 sublist = sub; 225 } else if (commandAliases != null) { 226 sublist = commandAliases; 227 } else if (helpCommand != null) { 228 sublist = helpCommand; 229 } 230 231 boolean preCommand = annotations.hasAnnotation(method, PreCommand.class); 232 boolean hasCatchUnknown = annotations.hasAnnotation(method, CatchUnknown.class) || 233 annotations.hasAnnotation(method, CatchAll.class) || 234 annotations.hasAnnotation(method, UnknownHandler.class); 235 236 if (hasCatchUnknown || (!foundCatchUnknown && helpCommand != null)) { 237 if (!foundCatchUnknown) { 238 if (hasCatchUnknown) { 239 this.subCommands.get(CATCHUNKNOWN).clear(); 240 foundCatchUnknown = true; 241 } 242 registerSubcommand(method, CATCHUNKNOWN); 243 } else { 244 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); 245 } 246 } else if (preCommand) { 247 if (this.preCommandHandler == null) { 248 this.preCommandHandler = method; 249 } else { 250 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); 251 } 252 } 253 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) { 254 registerSubcommand(method, sublist); 255 } 256 } 257 } 258 259 private String getSubcommandValue(Method method) { 260 final String sub = manager.getAnnotations().getAnnotationValue(method, Subcommand.class, Annotations.NOTHING); 261 if (sub == null) { 262 return null; 263 } 264 Class<?> clazz = method.getDeclaringClass(); 265 String parent = getParentSubcommand(clazz); 266 return parent == null || parent.isEmpty() ? sub : parent + " " + sub; 267 } 268 269 private String getParentSubcommand(Class<?> clazz) { 270 List<String> subList = new ArrayList<>(); 271 while (clazz != null) { 272 String sub = manager.getAnnotations().getAnnotationValue(clazz, Subcommand.class, Annotations.NOTHING); 273 if (sub != null) { 274 subList.add(sub); 275 } 276 clazz = clazz.getEnclosingClass(); 277 } 278 Collections.reverse(subList); 279 return ACFUtil.join(subList, " "); 280 } 281 282 private void register(String name, BaseCommand cmd) { 283 String nameLower = name.toLowerCase(); 284 RootCommand rootCommand = manager.obtainRootCommand(nameLower); 285 rootCommand.addChild(cmd); 286 287 this.registeredCommands.put(nameLower, rootCommand); 288 } 289 290 private void registerSubcommand(Method method, String subCommand) { 291 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase()); 292 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand); 293 // Must run getSubcommandPossibility BEFORE we rewrite it just after this. 294 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts); 295 296 // Strip pipes off for auto complete addition 297 for (int i = 0; i < subCommandParts.length; i++) { 298 String[] split = ACFPatterns.PIPE.split(subCommandParts[i]); 299 if (split.length == 0 || split[0].isEmpty()) { 300 throw new IllegalArgumentException("Invalid @Subcommand configuration for " + method.getName() + " - parts can not start with | or be empty"); 301 } 302 subCommandParts[i] = split[0]; 303 } 304 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " "); 305 final String[] aliasNames = manager.getAnnotations().getAnnotationValues(method, CommandAlias.class, Annotations.REPLACEMENTS | Annotations.LOWERCASE); 306 307 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; 308 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); 309 310 for (String subcmd : cmdList) { 311 subCommands.put(subcmd, cmd); 312 } 313 cmd.addSubcommands(cmdList); 314 315 if (aliasNames != null) { 316 for (String name : aliasNames) { 317 register(name, new ForwardingCommand(this, subCommandParts)); 318 } 319 } 320 } 321 322 /** 323 * Takes a string like "foo|bar baz|qux" and generates a list of 324 * - foo baz 325 * - foo qux 326 * - bar baz 327 * - bar qux 328 * 329 * For every possible sub command combination 330 * 331 * @param subCommandParts 332 * @return List of all sub command possibilities 333 */ 334 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) { 335 int i = 0; 336 Set<String> current = null; 337 while (true) { 338 Set<String> newList = new HashSet<>(); 339 340 if (i < subCommandParts.length) { 341 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) { 342 if (current != null) { 343 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); 344 } else { 345 newList.add(s1); 346 } 347 } 348 } 349 350 if (i + 1 < subCommandParts.length) { 351 current = newList; 352 i = i + 1; 353 continue; 354 } 355 356 return newList; 357 } 358 } 359 360 public void execute(CommandIssuer issuer, String commandLabel, String[] args) { 361 commandLabel = commandLabel.toLowerCase(); 362 try { 363 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false); 364 365 if (args.length > 0) { 366 CommandSearch cmd = findSubCommand(args); 367 if (cmd != null) { 368 execSubcommand = cmd.getCheckSub(); 369 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); 370 executeCommand(commandContext, issuer, execargs, cmd.cmd); 371 return; 372 } 373 } 374 375 if (subCommands.get(DEFAULT) != null && args.length == 0) { 376 executeSubcommand(commandContext, DEFAULT, issuer, args); 377 } else if (subCommands.get(CATCHUNKNOWN) != null) { 378 if (!executeSubcommand(commandContext, CATCHUNKNOWN, issuer, args)) { 379 help(issuer, args); 380 } 381 } else if (subCommands.get(DEFAULT) != null) { 382 executeSubcommand(commandContext, DEFAULT, issuer, args); 383 } 384 385 } finally { 386 postCommandOperation(); 387 } 388 } 389 390 RegisteredCommand<?> getRegisteredCommand(String[] args) { 391 final CommandSearch cmd = findSubCommand(args); 392 return cmd != null ? cmd.cmd : null; 393 } 394 395 private void postCommandOperation() { 396 CommandManager.commandOperationContext.get().pop(); 397 execSubcommand = null; 398 execLabel = null; 399 origArgs = new String[]{}; 400 } 401 402 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) { 403 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get(); 404 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync); 405 contexts.push(context); 406 lastCommandOperationContext = context; 407 execSubcommand = null; 408 execLabel = commandLabel; 409 origArgs = args; 410 return context; 411 } 412 413 public CommandIssuer getCurrentCommandIssuer() { 414 return CommandManager.getCurrentCommandIssuer(); 415 } 416 public CommandManager getCurrentCommandManager() { 417 return CommandManager.getCurrentCommandManager(); 418 } 419 420 private CommandSearch findSubCommand(String[] args) { 421 return findSubCommand(args, false); 422 } 423 private CommandSearch findSubCommand(String[] args, boolean completion) { 424 for (int i = args.length; i >= 0; i--) { 425 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); 426 Set<RegisteredCommand> cmds = subCommands.get(checkSub); 427 428 final int extraArgs = args.length - i; 429 if (!cmds.isEmpty()) { 430 RegisteredCommand cmd = null; 431 if (cmds.size() == 1) { 432 cmd = Iterables.getOnlyElement(cmds); 433 } else { 434 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> { 435 int required = c.requiredResolvers; 436 int optional = c.optionalResolvers; 437 return extraArgs <= required + optional && (completion || extraArgs >= required); 438 }).sorted((c1, c2) -> { 439 int a = c1.consumeInputResolvers; 440 int b = c2.consumeInputResolvers; 441 442 if (a == b) { 443 return 0; 444 } 445 return a < b ? 1 : -1; 446 }).findFirst(); 447 if (optCmd.isPresent()) { 448 cmd = optCmd.get(); 449 } 450 } 451 if (cmd != null) { 452 return new CommandSearch(cmd, i, checkSub); 453 } 454 } 455 } 456 return null; 457 } 458 459 private void executeCommand(CommandOperationContext commandOperationContext, 460 CommandIssuer issuer, String[] args, RegisteredCommand cmd) { 461 if (cmd.hasPermission(issuer)) { 462 commandOperationContext.setRegisteredCommand(cmd); 463 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) { 464 return; 465 } 466 List<String> sargs = Lists.newArrayList(args); 467 cmd.invoke(issuer, sargs, commandOperationContext); 468 } else { 469 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED); 470 } 471 } 472 473 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) { 474 return true; 475 } 476 477 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) { 478 return tabComplete(issuer, commandLabel, args, false); 479 } 480 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) 481 throws IllegalArgumentException { 482 483 commandLabel = commandLabel.toLowerCase(); 484 if (args.length == 0) { 485 args = new String[]{""}; 486 } 487 try { 488 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync); 489 490 final CommandSearch search = findSubCommand(args, true); 491 492 493 final List<String> cmds = new ArrayList<>(); 494 495 if (search != null) { 496 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync)); 497 } else if (subCommands.get(CATCHUNKNOWN).size() == 1) { 498 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHUNKNOWN)), args, commandLabel, isAsync)); 499 } else if (subCommands.get(DEFAULT).size() == 1) { 500 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync)); 501 } 502 503 return filterTabComplete(args[args.length - 1], cmds); 504 } finally { 505 postCommandOperation(); 506 } 507 } 508 509 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) { 510 final Set<String> cmds = new HashSet<>(); 511 final int cmdIndex = Math.max(0, args.length - 1); 512 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase(); 513 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) { 514 final String key = entry.getKey(); 515 if (key.startsWith(argString) && !CATCHUNKNOWN.equals(key) && !DEFAULT.equals(key)) { 516 final RegisteredCommand value = entry.getValue(); 517 if (!value.hasPermission(issuer)) { 518 continue; 519 } 520 521 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand); 522 cmds.add(split[cmdIndex]); 523 } 524 } 525 return new ArrayList<>(cmds); 526 } 527 528 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) { 529 if (!cmd.hasPermission(issuer) || args.length > cmd.consumeInputResolvers || args.length == 0 || cmd.complete == null) { 530 return ImmutableList.of(); 531 } 532 533 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, args, isAsync); 534 return filterTabComplete(args[args.length-1], cmds); 535 } 536 537 private static List<String> filterTabComplete(String arg, List<String> cmds) { 538 return cmds.stream() 539 .distinct() 540 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) 541 .collect(Collectors.toList()); 542 } 543 544 RegisteredCommand getSubcommand(String subcommand) { 545 return getSubcommand(subcommand, false); 546 } 547 548 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) { 549 final Set<RegisteredCommand> commands = subCommands.get(subcommand); 550 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) { 551 return commands.iterator().next(); 552 } 553 return null; 554 } 555 556 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) { 557 final RegisteredCommand cmd = this.getSubcommand(subcommand); 558 if (cmd != null) { 559 executeCommand(commandContext, issuer, args, cmd); 560 return true; 561 } 562 563 return false; 564 } 565 566 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { 567 Method pre = this.preCommandHandler; 568 if (pre != null) { 569 try { 570 Class<?>[] types = pre.getParameterTypes(); 571 Object[] parameters = new Object[pre.getParameterCount()]; 572 for (int i = 0; i < parameters.length; i++) { 573 Class<?> type = types[i]; 574 Object issuerObject = issuer.getIssuer(); 575 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { 576 parameters[i] = issuerObject; 577 } else if (CommandIssuer.class.isAssignableFrom(type)) { 578 parameters[i] = issuer; 579 } else if (RegisteredCommand.class.isAssignableFrom(type)) { 580 parameters[i] = cmd; 581 } else if (String[].class.isAssignableFrom((type))) { 582 parameters[i] = args; 583 } else { 584 parameters[i] = null; 585 } 586 } 587 588 return (boolean) pre.invoke(this, parameters); 589 } catch (IllegalAccessException | InvocationTargetException e) { 590 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); 591 } 592 } 593 return false; 594 } 595 596 /** @deprecated Unstable API */ @Deprecated @UnstableAPI 597 public CommandHelp getCommandHelp() { 598 return manager.generateCommandHelp(); 599 } 600 601 /** @deprecated Unstable API */ @Deprecated @UnstableAPI 602 public void showCommandHelp() { 603 getCommandHelp().showHelp(); 604 } 605 606 public void help(Object issuer, String[] args) { 607 help(manager.getCommandIssuer(issuer), args); 608 } 609 public void help(CommandIssuer issuer, String[] args) { 610 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); 611 } 612 public void doHelp(Object issuer, String... args) { 613 doHelp(manager.getCommandIssuer(issuer), args); 614 } 615 public void doHelp(CommandIssuer issuer, String... args) { 616 help(issuer, args); 617 } 618 619 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { 620 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, 621 "{command}", manager.getCommandPrefix(issuer) + cmd.command, 622 "{syntax}", cmd.syntaxText 623 ); 624 } 625 626 public boolean hasPermission(Object issuer) { 627 return hasPermission(manager.getCommandIssuer(issuer)); 628 } 629 630 public boolean hasPermission(CommandIssuer issuer) { 631 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer))); 632 } 633 634 635 public Set<String> getRequiredPermissions() { 636 if (this.permission == null || this.permission.isEmpty()) { 637 return ImmutableSet.of(); 638 } 639 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission)); 640 } 641 642 public boolean requiresPermission(String permission) { 643 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission); 644 } 645 646 public String getName() { 647 return commandName; 648 } 649 650 public ExceptionHandler getExceptionHandler() { 651 return exceptionHandler; 652 } 653 654 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { 655 this.exceptionHandler = exceptionHandler; 656 return this; 657 } 658 659 public RegisteredCommand getDefaultRegisteredCommand() { 660 return this.getSubcommand(DEFAULT); 661 } 662 663 public String setContextFlags(Class<?> cls, String flags) { 664 return this.contextFlags.put(cls, flags); 665 } 666 667 public String getContextFlags(Class<?> cls) { 668 return this.contextFlags.get(cls); 669 } 670 671 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub; 672 673 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) { 674 this.cmd = cmd; 675 this.argIndex = argIndex; 676 this.checkSub = checkSub; 677 } 678 679 String getCheckSub() { 680 return this.checkSub; 681 } 682 683 @Override 684 public boolean equals(Object o) { 685 if (this == o) return true; 686 if (o == null || getClass() != o.getClass()) return false; 687 CommandSearch that = (CommandSearch) o; 688 return argIndex == that.argIndex && 689 Objects.equals(cmd, that.cmd) && 690 Objects.equals(checkSub, that.checkSub); 691 } 692 693 @Override 694 public int hashCode() { 695 return Objects.hash(cmd, argIndex, checkSub); 696 } 697 } 698}