diff --git a/deploy.sh b/deploy.sh index a405572c..6490674f 100755 --- a/deploy.sh +++ b/deploy.sh @@ -7,4 +7,4 @@ if [ ! -z "$1" ]; then cd - || exit 1 fi git add docs -git commit docs -m "Updated JavaDocs" +git commit docs -m "(DEPLOYED ACF) Updated JavaDocs" diff --git a/docs/acf-core/co/aikar/commands/BaseCommand.html b/docs/acf-core/co/aikar/commands/BaseCommand.html index a8b06777..26b175b9 100644 --- a/docs/acf-core/co/aikar/commands/BaseCommand.html +++ b/docs/acf-core/co/aikar/commands/BaseCommand.html @@ -18,7 +18,7 @@ catch(err) { } //--> -var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":42,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":42,"i21":10,"i22":10,"i23":10}; +var methods = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":42,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":42,"i23":10,"i24":10,"i25":10}; var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]}; var altColor = "altColor"; var rowColor = "rowColor"; @@ -112,7 +112,7 @@ var activeTableTab = "activeTableTab";
public abstract class BaseCommand +public abstract class BaseCommand extends Object@@ -205,74 +205,83 @@ extends ++ String+ + getContextFlags(Class<?> cls)- CommandIssuergetCurrentCommandIssuer()+ - CommandManagergetCurrentCommandManager()+ - RegisteredCommandgetDefaultRegisteredCommand()+ - ExceptionHandlergetExceptionHandler()+ - StringgetExecCommandLabel()Gets the root command name that the user actually typed+ - StringgetExecSubcommand()Gets the actual sub command name the user typed+ - StringgetName()+ - String[]getOrigArgs()Gets the actual args in string form the user typed+ - Set<String>getRequiredPermissions()+ - booleanhasPermission(CommandIssuer issuer)+ - booleanhasPermission(Object issuer)+ - voidhelp(CommandIssuer issuer, String[] args)+ - voidhelp(Object issuer, String[] args)+ - booleanrequiresPermission(String permission)+ + ++ String+ setContextFlags(Class<?> cls, + String flags)- BaseCommandsetExceptionHandler(ExceptionHandler exceptionHandler)+ voidshowCommandHelp()Deprecated. @@ -280,18 +289,18 @@ extends +- voidshowSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd)+ - List<String>tabComplete(CommandIssuer issuer, String commandLabel, String[] args)+ List<String>tabComplete(CommandIssuer issuer, String commandLabel, @@ -326,7 +335,7 @@ extendsdiff --git a/docs/acf-core/co/aikar/commands/ForwardingCommand.html b/docs/acf-core/co/aikar/commands/ForwardingCommand.html index 151fc8e9..2868530b 100644 --- a/docs/acf-core/co/aikar/commands/ForwardingCommand.html +++ b/docs/acf-core/co/aikar/commands/ForwardingCommand.html @@ -170,7 +170,7 @@ extends BaseCommand - CATCHALL
-public static final String CATCHALL+public static final String CATCHALL
- See Also:
- Constant Field Values
@@ -339,7 +348,7 @@ extends- @@ -386,7 +395,7 @@ extends
DEFAULT
-public static final String DEFAULT+public static final String DEFAULT- @@ -295,7 +295,7 @@ extends
getExecCommandLabel
-public String getExecCommandLabel()+public String getExecCommandLabel()Gets the root command name that the user actually typed
- Returns:
@@ -400,7 +409,7 @@ extends- diff --git a/docs/acf-core/co/aikar/commands/CommandExecutionContext.html b/docs/acf-core/co/aikar/commands/CommandExecutionContext.html index 0ce93c3f..a3912dbc 100644 --- a/docs/acf-core/co/aikar/commands/CommandExecutionContext.html +++ b/docs/acf-core/co/aikar/commands/CommandExecutionContext.html @@ -108,7 +108,7 @@ var activeTableTab = "activeTableTab";
getExecSubcommand
-public String getExecSubcommand()+public String getExecSubcommand()Gets the actual sub command name the user typed
- Returns:
@@ -414,7 +423,7 @@ extends- @@ -517,7 +526,7 @@ public void
getOrigArgs
-public String[] getOrigArgs()+public String[] getOrigArgs()Gets the actual args in string form the user typed
- Returns:
@@ -428,7 +437,7 @@ extends- @@ -439,7 +448,7 @@ extends
execute
-public void execute(CommandIssuer issuer, +public void execute(CommandIssuer issuer, String commandLabel, String[] args)- @@ -448,7 +457,7 @@ extends
getCurrentCommandIssuer
-public CommandIssuer getCurrentCommandIssuer()+public CommandIssuer getCurrentCommandIssuer()- @@ -457,7 +466,7 @@ extends
getCurrentCommandManager
-public CommandManager getCurrentCommandManager()+public CommandManager getCurrentCommandManager()- @@ -467,7 +476,7 @@ extends
canExecute
-public boolean canExecute(CommandIssuer issuer, +public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd)- @@ -478,7 +487,7 @@ extends
tabComplete
-public List<String> tabComplete(CommandIssuer issuer, +public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args)- @@ -507,7 +516,7 @@ public
tabComplete
-public List<String> tabComplete(CommandIssuer issuer, +public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) @@ -496,7 +505,7 @@ extendsgetCommandHelp
@Deprecated -public CommandHelp getCommandHelp()+public CommandHelp getCommandHelp()Deprecated. Unstable APIshowCommandHelp
@Deprecated -public void showCommandHelp()+public void showCommandHelp()Deprecated. Unstable API- @@ -527,7 +536,7 @@ public void
help
-public void help(Object issuer, +public void help(Object issuer, String[] args)- @@ -537,7 +546,7 @@ public void
help
-public void help(CommandIssuer issuer, +public void help(CommandIssuer issuer, String[] args)- @@ -547,7 +556,7 @@ public void
doHelp
-public void doHelp(Object issuer, +public void doHelp(Object issuer, String... args)- @@ -557,7 +566,7 @@ public void
doHelp
-public void doHelp(CommandIssuer issuer, +public void doHelp(CommandIssuer issuer, String... args)- @@ -567,7 +576,7 @@ public void
showSyntax
-public void showSyntax(CommandIssuer issuer, +public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd)- @@ -576,7 +585,7 @@ public void
hasPermission
-public boolean hasPermission(Object issuer)+public boolean hasPermission(Object issuer)- @@ -585,7 +594,7 @@ public void
hasPermission
-public boolean hasPermission(CommandIssuer issuer)+public boolean hasPermission(CommandIssuer issuer)- @@ -594,7 +603,7 @@ public void
getRequiredPermissions
-public Set<String> getRequiredPermissions()+public Set<String> getRequiredPermissions()- @@ -603,7 +612,7 @@ public void
requiresPermission
-public boolean requiresPermission(String permission)+public boolean requiresPermission(String permission)- @@ -612,7 +621,7 @@ public void
getName
-public String getName()+public String getName()- @@ -621,16 +630,35 @@ public void
getExceptionHandler
-public ExceptionHandler getExceptionHandler()+public ExceptionHandler getExceptionHandler()- -
setExceptionHandler
-public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)+public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)+
+ + + +
- +
getDefaultRegisteredCommand
-public RegisteredCommand getDefaultRegisteredCommand()+public RegisteredCommand getDefaultRegisteredCommand()++
+ + + +- +
+setContextFlags
+public String setContextFlags(Class<?> cls, + String flags)++
- +
getContextFlags
+public String getContextFlags(Class<?> cls)- @@ -278,7 +278,7 @@ extends
-public class CommandExecutionContext<CEC extends CommandExecutionContext,I extends CommandIssuer> +public class CommandExecutionContext<CEC extends CommandExecutionContext,I extends CommandIssuer> extends Objectissuer
-protected final I extends CommandIssuer issuer+protected final I extends CommandIssuer issuer- @@ -304,7 +304,7 @@ extends
popFirstArg
-public String popFirstArg()+public String popFirstArg()- @@ -313,7 +313,7 @@ extends
popLastArg
-public String popLastArg()+public String popLastArg()- @@ -322,7 +322,7 @@ extends
getFirstArg
-public String getFirstArg()+public String getFirstArg()- @@ -331,7 +331,7 @@ extends
getLastArg
-public String getLastArg()+public String getLastArg()- @@ -340,7 +340,7 @@ extends
isLastArg
-public boolean isLastArg()+public boolean isLastArg()- @@ -349,7 +349,7 @@ extends
getNumParams
-public int getNumParams()+public int getNumParams()- @@ -358,7 +358,7 @@ extends
canOverridePlayerContext
-public boolean canOverridePlayerContext()+public boolean canOverridePlayerContext()- @@ -367,7 +367,7 @@ extends
getResolvedArg
-public Object getResolvedArg(String arg)+public Object getResolvedArg(String arg)- @@ -376,7 +376,7 @@ extends
getResolvedArg
-public Object getResolvedArg(Class<?>... classes)+public Object getResolvedArg(Class<?>... classes)- @@ -386,7 +386,7 @@ extends
getResolvedArg
-public <T> T getResolvedArg(String key, +public <T> T getResolvedArg(String key, Class<?>... classes)- @@ -395,7 +395,7 @@ extends
isOptional
-public boolean isOptional()+public boolean isOptional()- @@ -404,7 +404,7 @@ extends
hasFlag
-public boolean hasFlag(String flag)+public boolean hasFlag(String flag)- @@ -414,7 +414,7 @@ extends
getFlagValue
-public String getFlagValue(String flag, +public String getFlagValue(String flag, String def)- @@ -424,7 +424,7 @@ extends
getFlagValue
-public Integer getFlagValue(String flag, +public Integer getFlagValue(String flag, Integer def)- @@ -433,7 +433,7 @@ extends
getAnnotation
-public <T extends Annotation> T getAnnotation(Class<T> cls)+public <T extends Annotation> T getAnnotation(Class<T> cls)- @@ -442,7 +442,7 @@ extends
hasAnnotation
-public <T extends Annotation> boolean hasAnnotation(Class<T> cls)+public <T extends Annotation> boolean hasAnnotation(Class<T> cls)- @@ -451,7 +451,7 @@ extends
getCmd
-public RegisteredCommand getCmd()+public RegisteredCommand getCmd()- @@ -460,7 +460,7 @@ extends
getParam
-public Parameter getParam()+public Parameter getParam()- @@ -469,7 +469,7 @@ extends
getIssuer
-public I getIssuer()+public I getIssuer()- @@ -478,7 +478,7 @@ extends
getArgs
-public List<String> getArgs()+public List<String> getArgs()- @@ -487,7 +487,7 @@ extends
getIndex
-public int getIndex()+public int getIndex()- @@ -496,7 +496,7 @@ extends
getPassedArgs
-public Map<String,Object> getPassedArgs()+public Map<String,Object> getPassedArgs()- @@ -505,7 +505,7 @@ extends
getFlags
-public Map<String,String> getFlags()+public Map<String,String> getFlags()- @@ -514,7 +514,7 @@ extends
joinArgs
-public String joinArgs()+public String joinArgs()joinArgs
-public String joinArgs(String sep)+public String joinArgs(String sep)canExecute, doHelp, doHelp, getCommandHelp, getCurrentCommandIssuer, getCurrentCommandManager, getDefaultRegisteredCommand, getExceptionHandler, getExecCommandLabel, getExecSubcommand, getName, getOrigArgs, getRequiredPermissions, hasPermission, help, help, requiresPermission, setExceptionHandler, showCommandHelp, showSyntax, tabComplete+canExecute, doHelp, doHelp, getCommandHelp, getContextFlags, getCurrentCommandIssuer, getCurrentCommandManager, getDefaultRegisteredCommand, getExceptionHandler, getExecCommandLabel, getExecSubcommand, getName, getOrigArgs, getRequiredPermissions, hasPermission, help, help, requiresPermission, setContextFlags, setExceptionHandler, showCommandHelp, showSyntax, tabComplete
- diff --git a/docs/acf-core/index-all.html b/docs/acf-core/index-all.html index 09c8fb68..7cc7adc2 100644 --- a/docs/acf-core/index-all.html +++ b/docs/acf-core/index-all.html @@ -490,6 +490,8 @@
- getContext(C) - Method in interface co.aikar.commands.contexts.ContextResolver
- +
- getContextFlags(Class<?>) - Method in class co.aikar.commands.BaseCommand
+- getContextValue(Class<? extends T>) - Method in class co.aikar.commands.CommandCompletionContext
- getContextValue(Class<? extends T>, Integer) - Method in class co.aikar.commands.CommandCompletionContext
@@ -1341,6 +1343,8 @@- setColor(int, FT) - Method in class co.aikar.commands.MessageFormatter
- +
- setContextFlags(Class<?>, String) - Method in class co.aikar.commands.BaseCommand
+- setDefaultExceptionHandler(ExceptionHandler) - Method in class co.aikar.commands.CommandManager
Sets the defaultdiff --git a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html index 209fb632..14b4dfa8 100644 --- a/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html +++ b/docs/acf-core/src-html/co/aikar/commands/BaseCommand.html @@ -45,641 +45,651 @@ 037import com.google.common.collect.ImmutableSet; 038import com.google.common.collect.Iterables; 039import com.google.common.collect.Lists; -040import com.google.common.collect.SetMultimap; -041import com.google.common.collect.Sets; -042 -043import java.lang.reflect.Constructor; -044import java.lang.reflect.InvocationTargetException; -045import java.lang.reflect.Method; -046import java.lang.reflect.Parameter; -047import java.util.ArrayList; -048import java.util.Arrays; -049import java.util.Collections; -050import java.util.HashMap; -051import java.util.HashSet; -052import java.util.List; -053import java.util.Map; -054import java.util.Objects; -055import java.util.Optional; -056import java.util.Set; -057import java.util.Stack; -058import java.util.stream.Collectors; -059import java.util.stream.Stream; -060 -061@SuppressWarnings("unused") -062public abstract class BaseCommand { -063 -064 public static final String CATCHALL = "__catchall"; -065 public static final String DEFAULT = "__default"; -066 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create(); -067 private Method preCommandHandler; -068 -069 @SuppressWarnings("WeakerAccess") -070 private String execLabel; +040import com.google.common.collect.Maps; +041import com.google.common.collect.SetMultimap; +042import com.google.common.collect.Sets; +043 +044import java.lang.reflect.Constructor; +045import java.lang.reflect.InvocationTargetException; +046import java.lang.reflect.Method; +047import java.lang.reflect.Parameter; +048import java.util.ArrayList; +049import java.util.Arrays; +050import java.util.Collections; +051import java.util.HashMap; +052import java.util.HashSet; +053import java.util.List; +054import java.util.Map; +055import java.util.Objects; +056import java.util.Optional; +057import java.util.Set; +058import java.util.Stack; +059import java.util.stream.Collectors; +060import java.util.stream.Stream; +061 +062@SuppressWarnings("unused") +063public abstract class BaseCommand { +064 +065 public static final String CATCHALL = "__catchall"; +066 public static final String DEFAULT = "__default"; +067 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create(); +068 final Map<Class<?>, String> contextFlags = Maps.newHashMap(); +069 private Method preCommandHandler; +070 071 @SuppressWarnings("WeakerAccess") -072 private String execSubcommand; +072 private String execLabel; 073 @SuppressWarnings("WeakerAccess") -074 private String[] origArgs; -075 CommandManager<?, ?, ?, ?, ?, ?> manager = null; -076 BaseCommand parentCommand; -077 Map<String, RootCommand> registeredCommands = new HashMap<>(); -078 String description; -079 String commandName; -080 String usageMessage; -081 String permission; -082 -083 private ExceptionHandler exceptionHandler = null; -084 CommandOperationContext lastCommandOperationContext; -085 private String parentSubcommand; -086 -087 public BaseCommand() {} -088 public BaseCommand(String cmd) { -089 this.commandName = cmd; -090 } -091 -092 /** -093 * Gets the root command name that the user actually typed -094 * @return Name -095 */ -096 public String getExecCommandLabel() { -097 return execLabel; -098 } -099 -100 /** -101 * Gets the actual sub command name the user typed -102 * @return Name -103 */ -104 public String getExecSubcommand() { -105 return execSubcommand; -106 } -107 -108 /** -109 * Gets the actual args in string form the user typed -110 * @return Args -111 */ -112 public String[] getOrigArgs() { -113 return origArgs; -114 } -115 -116 void setParentCommand(BaseCommand command) { -117 this.parentCommand = command; -118 } -119 void onRegister(CommandManager manager) { -120 onRegister(manager, this.commandName); -121 } -122 void onRegister(CommandManager manager, String cmd) { -123 this.manager = manager; -124 final Class<? extends BaseCommand> self = this.getClass(); -125 CommandAlias rootCmdAliasAnno = self.getAnnotation(CommandAlias.class); -126 String rootCmdAlias = rootCmdAliasAnno != null ? manager.getCommandReplacements().replace(rootCmdAliasAnno.value()).toLowerCase() : null; -127 if (cmd == null && rootCmdAlias != null) { -128 cmd = ACFPatterns.PIPE.split(rootCmdAlias)[0]; -129 } -130 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase(); -131 -132 this.description = this.commandName + " commands"; -133 this.usageMessage = "/" + this.commandName; -134 this.parentSubcommand = getParentSubcommand(this.getClass()); -135 -136 final CommandPermission perm = self.getAnnotation(CommandPermission.class); -137 if (perm != null) { -138 this.permission = manager.getCommandReplacements().replace(perm.value()); -139 } -140 -141 boolean foundDefault = false; -142 boolean foundCatchAll = false; -143 boolean isParentEmpty = parentSubcommand.isEmpty(); -144 for (Method method : self.getMethods()) { -145 method.setAccessible(true); -146 String sublist = null; -147 String sub = getSubcommandValue(method); -148 final Default def = method.getAnnotation(Default.class); -149 final HelpCommand helpCommand = method.getAnnotation(HelpCommand.class); -150 final CommandAlias commandAliases = method.getAnnotation(CommandAlias.class); -151 -152 if (!isParentEmpty && def != null) { -153 sub = parentSubcommand; -154 } -155 if (isParentEmpty && (def != null || (!foundDefault && helpCommand != null))) { -156 if (!foundDefault) { -157 if (def != null) { -158 this.subCommands.get(DEFAULT).clear(); -159 foundDefault = true; -160 } -161 registerSubcommand(method, DEFAULT); -162 } else { -163 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); -164 } -165 } -166 -167 if (sub != null) { -168 sublist = sub; -169 } else if (commandAliases != null) { -170 sublist = commandAliases.value(); -171 } else if (helpCommand != null) { -172 sublist = helpCommand.value(); -173 } -174 -175 UnknownHandler unknown = method.getAnnotation(UnknownHandler.class); -176 CatchAll catchAll = method.getAnnotation(CatchAll.class); -177 PreCommand preCommand = method.getAnnotation(PreCommand.class); -178 boolean hasCatchAll = catchAll != null || unknown != null; -179 if (hasCatchAll || (!foundCatchAll && helpCommand != null)) { -180 if (!foundCatchAll) { -181 if (hasCatchAll) { -182 this.subCommands.get(CATCHALL).clear(); -183 foundCatchAll = true; -184 } -185 registerSubcommand(method, CATCHALL); -186 } else { -187 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); -188 } -189 } else if (preCommand != null) { -190 if (this.preCommandHandler == null) { -191 this.preCommandHandler = method; -192 } else { -193 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); -194 } -195 } -196 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) { -197 registerSubcommand(method, sublist); -198 } -199 } -200 -201 if (rootCmdAlias != null) { -202 Set<String> cmdList = new HashSet<>(); -203 Collections.addAll(cmdList, ACFPatterns.PIPE.split(rootCmdAlias)); -204 cmdList.remove(cmd); -205 for (String cmdAlias : cmdList) { -206 register(cmdAlias, this); -207 } -208 } -209 -210 if (cmd != null) { -211 register(cmd, this); -212 } -213 for (Class<?> clazz : this.getClass().getDeclaredClasses()) { -214 if (BaseCommand.class.isAssignableFrom(clazz)) { -215 try { -216 BaseCommand subCommand = null; -217 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); -218 for (Constructor<?> declaredConstructor : declaredConstructors) { -219 -220 declaredConstructor.setAccessible(true); -221 Parameter[] parameters = declaredConstructor.getParameters(); -222 if (parameters.length == 1) { -223 subCommand = (BaseCommand) declaredConstructor.newInstance(this); -224 } else { -225 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")"); -226 } -227 } -228 if (subCommand != null) { -229 subCommand.setParentCommand(this); -230 subCommand.onRegister(manager, cmd); -231 this.subCommands.putAll(subCommand.subCommands); -232 this.registeredCommands.putAll(subCommand.registeredCommands); -233 } else { -234 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName()); -235 } -236 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { -237 e.printStackTrace(); -238 } -239 } -240 } -241 -242 } +074 private String execSubcommand; +075 @SuppressWarnings("WeakerAccess") +076 private String[] origArgs; +077 CommandManager<?, ?, ?, ?, ?, ?> manager = null; +078 BaseCommand parentCommand; +079 Map<String, RootCommand> registeredCommands = new HashMap<>(); +080 String description; +081 String commandName; +082 String usageMessage; +083 String permission; +084 +085 private ExceptionHandler exceptionHandler = null; +086 CommandOperationContext lastCommandOperationContext; +087 private String parentSubcommand; +088 +089 public BaseCommand() {} +090 public BaseCommand(String cmd) { +091 this.commandName = cmd; +092 } +093 +094 /** +095 * Gets the root command name that the user actually typed +096 * @return Name +097 */ +098 public String getExecCommandLabel() { +099 return execLabel; +100 } +101 +102 /** +103 * Gets the actual sub command name the user typed +104 * @return Name +105 */ +106 public String getExecSubcommand() { +107 return execSubcommand; +108 } +109 +110 /** +111 * Gets the actual args in string form the user typed +112 * @return Args +113 */ +114 public String[] getOrigArgs() { +115 return origArgs; +116 } +117 +118 void setParentCommand(BaseCommand command) { +119 this.parentCommand = command; +120 } +121 void onRegister(CommandManager manager) { +122 onRegister(manager, this.commandName); +123 } +124 void onRegister(CommandManager manager, String cmd) { +125 this.manager = manager; +126 final Class<? extends BaseCommand> self = this.getClass(); +127 CommandAlias rootCmdAliasAnno = self.getAnnotation(CommandAlias.class); +128 String rootCmdAlias = rootCmdAliasAnno != null ? manager.getCommandReplacements().replace(rootCmdAliasAnno.value()).toLowerCase() : null; +129 if (cmd == null && rootCmdAlias != null) { +130 cmd = ACFPatterns.PIPE.split(rootCmdAlias)[0]; +131 } +132 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase(); +133 +134 this.description = this.commandName + " commands"; +135 this.usageMessage = "/" + this.commandName; +136 this.parentSubcommand = getParentSubcommand(this.getClass()); +137 +138 final CommandPermission perm = self.getAnnotation(CommandPermission.class); +139 if (perm != null) { +140 this.permission = manager.getCommandReplacements().replace(perm.value()); +141 } +142 +143 boolean foundDefault = false; +144 boolean foundCatchAll = false; +145 boolean isParentEmpty = parentSubcommand.isEmpty(); +146 for (Method method : self.getMethods()) { +147 method.setAccessible(true); +148 String sublist = null; +149 String sub = getSubcommandValue(method); +150 final Default def = method.getAnnotation(Default.class); +151 final HelpCommand helpCommand = method.getAnnotation(HelpCommand.class); +152 final CommandAlias commandAliases = method.getAnnotation(CommandAlias.class); +153 +154 if (!isParentEmpty && def != null) { +155 sub = parentSubcommand; +156 } +157 if (isParentEmpty && (def != null || (!foundDefault && helpCommand != null))) { +158 if (!foundDefault) { +159 if (def != null) { +160 this.subCommands.get(DEFAULT).clear(); +161 foundDefault = true; +162 } +163 registerSubcommand(method, DEFAULT); +164 } else { +165 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +166 } +167 } +168 +169 if (sub != null) { +170 sublist = sub; +171 } else if (commandAliases != null) { +172 sublist = commandAliases.value(); +173 } else if (helpCommand != null) { +174 sublist = helpCommand.value(); +175 } +176 +177 UnknownHandler unknown = method.getAnnotation(UnknownHandler.class); +178 CatchAll catchAll = method.getAnnotation(CatchAll.class); +179 PreCommand preCommand = method.getAnnotation(PreCommand.class); +180 boolean hasCatchAll = catchAll != null || unknown != null; +181 if (hasCatchAll || (!foundCatchAll && helpCommand != null)) { +182 if (!foundCatchAll) { +183 if (hasCatchAll) { +184 this.subCommands.get(CATCHALL).clear(); +185 foundCatchAll = true; +186 } +187 registerSubcommand(method, CATCHALL); +188 } else { +189 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +190 } +191 } else if (preCommand != null) { +192 if (this.preCommandHandler == null) { +193 this.preCommandHandler = method; +194 } else { +195 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +196 } +197 } +198 if (Objects.equals(method.getDeclaringClass(), this.getClass()) && sublist != null) { +199 registerSubcommand(method, sublist); +200 } +201 } +202 +203 if (rootCmdAlias != null) { +204 Set<String> cmdList = new HashSet<>(); +205 Collections.addAll(cmdList, ACFPatterns.PIPE.split(rootCmdAlias)); +206 cmdList.remove(cmd); +207 for (String cmdAlias : cmdList) { +208 register(cmdAlias, this); +209 } +210 } +211 +212 if (cmd != null) { +213 register(cmd, this); +214 } +215 for (Class<?> clazz : this.getClass().getDeclaredClasses()) { +216 if (BaseCommand.class.isAssignableFrom(clazz)) { +217 try { +218 BaseCommand subCommand = null; +219 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); +220 for (Constructor<?> declaredConstructor : declaredConstructors) { +221 +222 declaredConstructor.setAccessible(true); +223 Parameter[] parameters = declaredConstructor.getParameters(); +224 if (parameters.length == 1) { +225 subCommand = (BaseCommand) declaredConstructor.newInstance(this); +226 } else { +227 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")"); +228 } +229 } +230 if (subCommand != null) { +231 subCommand.setParentCommand(this); +232 subCommand.onRegister(manager, cmd); +233 this.subCommands.putAll(subCommand.subCommands); +234 this.registeredCommands.putAll(subCommand.registeredCommands); +235 } else { +236 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName()); +237 } +238 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { +239 e.printStackTrace(); +240 } +241 } +242 } 243 -244 private String getSubcommandValue(Method method) { -245 final Subcommand sub = method.getAnnotation(Subcommand.class); -246 if (sub == null) { -247 return null; -248 } -249 Class<?> clazz = method.getDeclaringClass(); -250 String parent = getParentSubcommand(clazz); -251 return parent == null || parent.isEmpty() ? sub.value() : parent + " " + sub.value(); -252 } -253 -254 private String getParentSubcommand(Class<?> clazz) { -255 List<String> subList = new ArrayList<>(); -256 while (clazz != null) { -257 Subcommand classSub = clazz.getAnnotation(Subcommand.class); -258 if (classSub != null) { -259 subList.add(classSub.value()); -260 } -261 clazz = clazz.getEnclosingClass(); -262 } -263 Collections.reverse(subList); -264 return ACFUtil.join(subList, " "); -265 } -266 -267 private void register(String name, BaseCommand cmd) { -268 String nameLower = name.toLowerCase(); -269 RootCommand rootCommand = manager.obtainRootCommand(nameLower); -270 rootCommand.addChild(cmd); -271 -272 this.registeredCommands.put(nameLower, rootCommand); -273 } -274 -275 private void registerSubcommand(Method method, String subCommand) { -276 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase()); -277 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand); -278 // Must run getSubcommandPossibility BEFORE we rewrite it just after this. -279 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts); -280 -281 // Strip pipes off for auto complete addition -282 for (int i = 0; i < subCommandParts.length; i++) { -283 subCommandParts[i] = ACFPatterns.PIPE.split(subCommandParts[i])[0]; -284 } -285 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " "); -286 final CommandAlias cmdAlias = method.getAnnotation(CommandAlias.class); -287 -288 final String[] aliasNames = cmdAlias != null ? ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(cmdAlias.value().toLowerCase())) : null; -289 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; -290 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); -291 -292 for (String subcmd : cmdList) { -293 subCommands.put(subcmd, cmd); -294 } -295 cmd.addSubcommands(cmdList); -296 -297 if (aliasNames != null) { -298 for (String name : aliasNames) { -299 register(name, new ForwardingCommand(this, subCommandParts)); -300 } -301 } -302 } -303 -304 /** -305 * Takes a string like "foo|bar baz|qux" and generates a list of -306 * - foo baz -307 * - foo qux -308 * - bar baz -309 * - bar qux -310 * -311 * For every possible sub command combination +244 } +245 +246 private String getSubcommandValue(Method method) { +247 final Subcommand sub = method.getAnnotation(Subcommand.class); +248 if (sub == null) { +249 return null; +250 } +251 Class<?> clazz = method.getDeclaringClass(); +252 String parent = getParentSubcommand(clazz); +253 return parent == null || parent.isEmpty() ? sub.value() : parent + " " + sub.value(); +254 } +255 +256 private String getParentSubcommand(Class<?> clazz) { +257 List<String> subList = new ArrayList<>(); +258 while (clazz != null) { +259 Subcommand classSub = clazz.getAnnotation(Subcommand.class); +260 if (classSub != null) { +261 subList.add(classSub.value()); +262 } +263 clazz = clazz.getEnclosingClass(); +264 } +265 Collections.reverse(subList); +266 return ACFUtil.join(subList, " "); +267 } +268 +269 private void register(String name, BaseCommand cmd) { +270 String nameLower = name.toLowerCase(); +271 RootCommand rootCommand = manager.obtainRootCommand(nameLower); +272 rootCommand.addChild(cmd); +273 +274 this.registeredCommands.put(nameLower, rootCommand); +275 } +276 +277 private void registerSubcommand(Method method, String subCommand) { +278 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase()); +279 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand); +280 // Must run getSubcommandPossibility BEFORE we rewrite it just after this. +281 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts); +282 +283 // Strip pipes off for auto complete addition +284 for (int i = 0; i < subCommandParts.length; i++) { +285 subCommandParts[i] = ACFPatterns.PIPE.split(subCommandParts[i])[0]; +286 } +287 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " "); +288 final CommandAlias cmdAlias = method.getAnnotation(CommandAlias.class); +289 +290 final String[] aliasNames = cmdAlias != null ? ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(cmdAlias.value().toLowerCase())) : null; +291 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; +292 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); +293 +294 for (String subcmd : cmdList) { +295 subCommands.put(subcmd, cmd); +296 } +297 cmd.addSubcommands(cmdList); +298 +299 if (aliasNames != null) { +300 for (String name : aliasNames) { +301 register(name, new ForwardingCommand(this, subCommandParts)); +302 } +303 } +304 } +305 +306 /** +307 * Takes a string like "foo|bar baz|qux" and generates a list of +308 * - foo baz +309 * - foo qux +310 * - bar baz +311 * - bar qux 312 * -313 * @param subCommandParts -314 * @return List of all sub command possibilities -315 */ -316 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) { -317 int i = 0; -318 Set<String> current = null; -319 while (true) { -320 Set<String> newList = new HashSet<>(); -321 -322 if (i < subCommandParts.length) { -323 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) { -324 if (current != null) { -325 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); -326 } else { -327 newList.add(s1); -328 } -329 } -330 } -331 -332 if (i + 1 < subCommandParts.length) { -333 current = newList; -334 i = i + 1; -335 continue; -336 } -337 -338 return newList; -339 } -340 } -341 -342 public void execute(CommandIssuer issuer, String commandLabel, String[] args) { -343 commandLabel = commandLabel.toLowerCase(); -344 try { -345 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false); -346 -347 if (args.length > 0) { -348 CommandSearch cmd = findSubCommand(args); -349 if (cmd != null) { -350 execSubcommand = cmd.getCheckSub(); -351 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); -352 executeCommand(commandContext, issuer, execargs, cmd.cmd); -353 return; -354 } -355 } -356 -357 if (subCommands.get(DEFAULT) != null && args.length == 0) { -358 executeSubcommand(commandContext, DEFAULT, issuer, args); -359 } else if (subCommands.get(CATCHALL) != null) { -360 if (!executeSubcommand(commandContext, CATCHALL, issuer, args)) { -361 help(issuer, args); -362 } -363 } else if (subCommands.get(DEFAULT) != null) { -364 executeSubcommand(commandContext, DEFAULT, issuer, args); -365 } -366 -367 } finally { -368 postCommandOperation(); -369 } -370 } -371 -372 RegisteredCommand<?> getRegisteredCommand(String[] args) { -373 final CommandSearch cmd = findSubCommand(args); -374 return cmd != null ? cmd.cmd : null; -375 } -376 -377 private void postCommandOperation() { -378 CommandManager.commandOperationContext.get().pop(); -379 execSubcommand = null; -380 execLabel = null; -381 origArgs = new String[]{}; -382 } -383 -384 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) { -385 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get(); -386 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync); -387 contexts.push(context); -388 lastCommandOperationContext = context; -389 execSubcommand = null; -390 execLabel = commandLabel; -391 origArgs = args; -392 return context; -393 } -394 -395 public CommandIssuer getCurrentCommandIssuer() { -396 return CommandManager.getCurrentCommandIssuer(); -397 } -398 public CommandManager getCurrentCommandManager() { -399 return CommandManager.getCurrentCommandManager(); -400 } -401 -402 private CommandSearch findSubCommand(String[] args) { -403 return findSubCommand(args, false); -404 } -405 private CommandSearch findSubCommand(String[] args, boolean completion) { -406 for (int i = args.length; i >= 0; i--) { -407 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); -408 Set<RegisteredCommand> cmds = subCommands.get(checkSub); -409 -410 final int extraArgs = args.length - i; -411 if (!cmds.isEmpty()) { -412 RegisteredCommand cmd = null; -413 if (cmds.size() == 1) { -414 cmd = Iterables.getOnlyElement(cmds); -415 } else { -416 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> { -417 int required = c.requiredResolvers; -418 int optional = c.optionalResolvers; -419 return extraArgs <= required + optional && (completion || extraArgs >= required); -420 }).sorted((c1, c2) -> { -421 int a = c1.requiredResolvers + c1.optionalResolvers; -422 int b = c2.requiredResolvers + c2.optionalResolvers; -423 -424 if (a == b) { -425 return 0; -426 } -427 return a < b ? 1 : -1; -428 }).findFirst(); -429 if (optCmd.isPresent()) { -430 cmd = optCmd.get(); -431 } -432 } -433 if (cmd != null) { -434 return new CommandSearch(cmd, i, checkSub); -435 } -436 } -437 } -438 return null; -439 } -440 -441 private void executeCommand(CommandOperationContext commandOperationContext, -442 CommandIssuer issuer, String[] args, RegisteredCommand cmd) { -443 if (cmd.hasPermission(issuer)) { -444 commandOperationContext.setRegisteredCommand(cmd); -445 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) { -446 return; -447 } -448 List<String> sargs = Lists.newArrayList(args); -449 cmd.invoke(issuer, sargs, commandOperationContext); -450 } else { -451 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED); -452 } -453 } -454 -455 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) { -456 return true; -457 } -458 -459 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) { -460 return tabComplete(issuer, commandLabel, args, false); -461 } -462 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) -463 throws IllegalArgumentException { -464 -465 commandLabel = commandLabel.toLowerCase(); -466 if (args.length == 0) { -467 args = new String[]{""}; -468 } -469 try { -470 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync); -471 -472 final CommandSearch search = findSubCommand(args, true); +313 * For every possible sub command combination +314 * +315 * @param subCommandParts +316 * @return List of all sub command possibilities +317 */ +318 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) { +319 int i = 0; +320 Set<String> current = null; +321 while (true) { +322 Set<String> newList = new HashSet<>(); +323 +324 if (i < subCommandParts.length) { +325 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) { +326 if (current != null) { +327 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); +328 } else { +329 newList.add(s1); +330 } +331 } +332 } +333 +334 if (i + 1 < subCommandParts.length) { +335 current = newList; +336 i = i + 1; +337 continue; +338 } +339 +340 return newList; +341 } +342 } +343 +344 public void execute(CommandIssuer issuer, String commandLabel, String[] args) { +345 commandLabel = commandLabel.toLowerCase(); +346 try { +347 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args, false); +348 +349 if (args.length > 0) { +350 CommandSearch cmd = findSubCommand(args); +351 if (cmd != null) { +352 execSubcommand = cmd.getCheckSub(); +353 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); +354 executeCommand(commandContext, issuer, execargs, cmd.cmd); +355 return; +356 } +357 } +358 +359 if (subCommands.get(DEFAULT) != null && args.length == 0) { +360 executeSubcommand(commandContext, DEFAULT, issuer, args); +361 } else if (subCommands.get(CATCHALL) != null) { +362 if (!executeSubcommand(commandContext, CATCHALL, issuer, args)) { +363 help(issuer, args); +364 } +365 } else if (subCommands.get(DEFAULT) != null) { +366 executeSubcommand(commandContext, DEFAULT, issuer, args); +367 } +368 +369 } finally { +370 postCommandOperation(); +371 } +372 } +373 +374 RegisteredCommand<?> getRegisteredCommand(String[] args) { +375 final CommandSearch cmd = findSubCommand(args); +376 return cmd != null ? cmd.cmd : null; +377 } +378 +379 private void postCommandOperation() { +380 CommandManager.commandOperationContext.get().pop(); +381 execSubcommand = null; +382 execLabel = null; +383 origArgs = new String[]{}; +384 } +385 +386 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) { +387 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get(); +388 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args, isAsync); +389 contexts.push(context); +390 lastCommandOperationContext = context; +391 execSubcommand = null; +392 execLabel = commandLabel; +393 origArgs = args; +394 return context; +395 } +396 +397 public CommandIssuer getCurrentCommandIssuer() { +398 return CommandManager.getCurrentCommandIssuer(); +399 } +400 public CommandManager getCurrentCommandManager() { +401 return CommandManager.getCurrentCommandManager(); +402 } +403 +404 private CommandSearch findSubCommand(String[] args) { +405 return findSubCommand(args, false); +406 } +407 private CommandSearch findSubCommand(String[] args, boolean completion) { +408 for (int i = args.length; i >= 0; i--) { +409 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); +410 Set<RegisteredCommand> cmds = subCommands.get(checkSub); +411 +412 final int extraArgs = args.length - i; +413 if (!cmds.isEmpty()) { +414 RegisteredCommand cmd = null; +415 if (cmds.size() == 1) { +416 cmd = Iterables.getOnlyElement(cmds); +417 } else { +418 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> { +419 int required = c.requiredResolvers; +420 int optional = c.optionalResolvers; +421 return extraArgs <= required + optional && (completion || extraArgs >= required); +422 }).sorted((c1, c2) -> { +423 int a = c1.requiredResolvers + c1.optionalResolvers; +424 int b = c2.requiredResolvers + c2.optionalResolvers; +425 +426 if (a == b) { +427 return 0; +428 } +429 return a < b ? 1 : -1; +430 }).findFirst(); +431 if (optCmd.isPresent()) { +432 cmd = optCmd.get(); +433 } +434 } +435 if (cmd != null) { +436 return new CommandSearch(cmd, i, checkSub); +437 } +438 } +439 } +440 return null; +441 } +442 +443 private void executeCommand(CommandOperationContext commandOperationContext, +444 CommandIssuer issuer, String[] args, RegisteredCommand cmd) { +445 if (cmd.hasPermission(issuer)) { +446 commandOperationContext.setRegisteredCommand(cmd); +447 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) { +448 return; +449 } +450 List<String> sargs = Lists.newArrayList(args); +451 cmd.invoke(issuer, sargs, commandOperationContext); +452 } else { +453 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED); +454 } +455 } +456 +457 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) { +458 return true; +459 } +460 +461 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) { +462 return tabComplete(issuer, commandLabel, args, false); +463 } +464 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args, boolean isAsync) +465 throws IllegalArgumentException { +466 +467 commandLabel = commandLabel.toLowerCase(); +468 if (args.length == 0) { +469 args = new String[]{""}; +470 } +471 try { +472 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args, isAsync); 473 -474 -475 final List<String> cmds = new ArrayList<>(); +474 final CommandSearch search = findSubCommand(args, true); +475 476 -477 if (search != null) { -478 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync)); -479 } else if (subCommands.get(CATCHALL).size() == 1) { -480 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHALL)), args, commandLabel, isAsync)); -481 } else if (subCommands.get(DEFAULT).size() == 1) { -482 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync)); -483 } -484 -485 return filterTabComplete(args[args.length - 1], cmds); -486 } finally { -487 postCommandOperation(); -488 } -489 } -490 -491 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) { -492 final Set<String> cmds = new HashSet<>(); -493 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase(); -494 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) { -495 final String key = entry.getKey(); -496 if (key.startsWith(argString) && !CATCHALL.equals(key) && !DEFAULT.equals(key)) { -497 final RegisteredCommand value = entry.getValue(); -498 if (!value.hasPermission(issuer)) { -499 continue; -500 } -501 -502 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand); -503 cmds.add(split[args.length - 1]); -504 } -505 } -506 return new ArrayList<>(cmds); -507 } -508 -509 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) { -510 if (!cmd.hasPermission(issuer) || args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0 -511 || cmd.complete == null) { -512 return ImmutableList.of(); -513 } -514 -515 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +477 final List<String> cmds = new ArrayList<>(); +478 +479 if (search != null) { +480 cmds.addAll(completeCommand(issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel, isAsync)); +481 } else if (subCommands.get(CATCHALL).size() == 1) { +482 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(CATCHALL)), args, commandLabel, isAsync)); +483 } else if (subCommands.get(DEFAULT).size() == 1) { +484 cmds.addAll(completeCommand(issuer, Iterables.getOnlyElement(subCommands.get(DEFAULT)), args, commandLabel, isAsync)); +485 } +486 +487 return filterTabComplete(args[args.length - 1], cmds); +488 } finally { +489 postCommandOperation(); +490 } +491 } +492 +493 List<String> getCommandsForCompletion(CommandIssuer issuer, String[] args) { +494 final Set<String> cmds = new HashSet<>(); +495 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase(); +496 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) { +497 final String key = entry.getKey(); +498 if (key.startsWith(argString) && !CATCHALL.equals(key) && !DEFAULT.equals(key)) { +499 final RegisteredCommand value = entry.getValue(); +500 if (!value.hasPermission(issuer)) { +501 continue; +502 } +503 +504 String[] split = ACFPatterns.SPACE.split(value.prefSubCommand); +505 cmds.add(split[args.length - 1]); +506 } +507 } +508 return new ArrayList<>(cmds); +509 } +510 +511 private List<String> completeCommand(CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel, boolean isAsync) { +512 if (!cmd.hasPermission(issuer) || args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0 +513 || cmd.complete == null) { +514 return ImmutableList.of(); +515 } 516 -517 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, completions, args, isAsync); -518 return filterTabComplete(args[args.length-1], cmds); -519 } -520 -521 private static List<String> filterTabComplete(String arg, List<String> cmds) { -522 return cmds.stream() -523 .distinct() -524 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) -525 .collect(Collectors.toList()); -526 } -527 -528 RegisteredCommand getSubcommand(String subcommand) { -529 return getSubcommand(subcommand, false); -530 } -531 -532 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) { -533 final Set<RegisteredCommand> commands = subCommands.get(subcommand); -534 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) { -535 return commands.iterator().next(); -536 } -537 return null; -538 } -539 -540 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) { -541 final RegisteredCommand cmd = this.getSubcommand(subcommand); -542 if (cmd != null) { -543 executeCommand(commandContext, issuer, args, cmd); -544 return true; -545 } -546 -547 return false; -548 } -549 -550 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { -551 Method pre = this.preCommandHandler; -552 if (pre != null) { -553 try { -554 Class<?>[] types = pre.getParameterTypes(); -555 Object[] parameters = new Object[pre.getParameterCount()]; -556 for (int i = 0; i < parameters.length; i++) { -557 Class<?> type = types[i]; -558 Object issuerObject = issuer.getIssuer(); -559 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { -560 parameters[i] = issuerObject; -561 } else if (CommandIssuer.class.isAssignableFrom(type)) { -562 parameters[i] = issuer; -563 } else if (RegisteredCommand.class.isAssignableFrom(type)) { -564 parameters[i] = cmd; -565 } else if (String[].class.isAssignableFrom((type))) { -566 parameters[i] = args; -567 } else { -568 parameters[i] = null; -569 } -570 } -571 -572 return (boolean) pre.invoke(this, parameters); -573 } catch (IllegalAccessException | InvocationTargetException e) { -574 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); -575 } -576 } -577 return false; -578 } -579 -580 /** @deprecated Unstable API */ @Deprecated @UnstableAPI -581 public CommandHelp getCommandHelp() { -582 return manager.generateCommandHelp(); -583 } -584 -585 /** @deprecated Unstable API */ @Deprecated @UnstableAPI -586 public void showCommandHelp() { -587 getCommandHelp().showHelp(); -588 } -589 -590 public void help(Object issuer, String[] args) { -591 help(manager.getCommandIssuer(issuer), args); -592 } -593 public void help(CommandIssuer issuer, String[] args) { -594 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); -595 } -596 public void doHelp(Object issuer, String... args) { -597 doHelp(manager.getCommandIssuer(issuer), args); -598 } -599 public void doHelp(CommandIssuer issuer, String... args) { -600 help(issuer, args); -601 } -602 -603 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { -604 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, -605 "{command}", "/" + cmd.command, -606 "{syntax}", cmd.syntaxText -607 ); -608 } -609 -610 public boolean hasPermission(Object issuer) { -611 return hasPermission(manager.getCommandIssuer(issuer)); -612 } -613 -614 public boolean hasPermission(CommandIssuer issuer) { -615 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer))); -616 } -617 -618 -619 public Set<String> getRequiredPermissions() { -620 if (this.permission == null || this.permission.isEmpty()) { -621 return ImmutableSet.of(); -622 } -623 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission)); -624 } -625 -626 public boolean requiresPermission(String permission) { -627 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission); -628 } -629 -630 public String getName() { -631 return commandName; -632 } -633 -634 public ExceptionHandler getExceptionHandler() { -635 return exceptionHandler; -636 } -637 -638 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { -639 this.exceptionHandler = exceptionHandler; -640 return this; -641 } -642 -643 public RegisteredCommand getDefaultRegisteredCommand() { -644 return this.getSubcommand(DEFAULT); -645 } -646 -647 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub; +517 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +518 +519 List<String> cmds = manager.getCommandCompletions().of(cmd, issuer, completions, args, isAsync); +520 return filterTabComplete(args[args.length-1], cmds); +521 } +522 +523 private static List<String> filterTabComplete(String arg, List<String> cmds) { +524 return cmds.stream() +525 .distinct() +526 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) +527 .collect(Collectors.toList()); +528 } +529 +530 RegisteredCommand getSubcommand(String subcommand) { +531 return getSubcommand(subcommand, false); +532 } +533 +534 RegisteredCommand getSubcommand(String subcommand, boolean requireOne) { +535 final Set<RegisteredCommand> commands = subCommands.get(subcommand); +536 if (!commands.isEmpty() && (!requireOne || commands.size() == 1)) { +537 return commands.iterator().next(); +538 } +539 return null; +540 } +541 +542 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) { +543 final RegisteredCommand cmd = this.getSubcommand(subcommand); +544 if (cmd != null) { +545 executeCommand(commandContext, issuer, args, cmd); +546 return true; +547 } +548 +549 return false; +550 } +551 +552 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { +553 Method pre = this.preCommandHandler; +554 if (pre != null) { +555 try { +556 Class<?>[] types = pre.getParameterTypes(); +557 Object[] parameters = new Object[pre.getParameterCount()]; +558 for (int i = 0; i < parameters.length; i++) { +559 Class<?> type = types[i]; +560 Object issuerObject = issuer.getIssuer(); +561 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { +562 parameters[i] = issuerObject; +563 } else if (CommandIssuer.class.isAssignableFrom(type)) { +564 parameters[i] = issuer; +565 } else if (RegisteredCommand.class.isAssignableFrom(type)) { +566 parameters[i] = cmd; +567 } else if (String[].class.isAssignableFrom((type))) { +568 parameters[i] = args; +569 } else { +570 parameters[i] = null; +571 } +572 } +573 +574 return (boolean) pre.invoke(this, parameters); +575 } catch (IllegalAccessException | InvocationTargetException e) { +576 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); +577 } +578 } +579 return false; +580 } +581 +582 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +583 public CommandHelp getCommandHelp() { +584 return manager.generateCommandHelp(); +585 } +586 +587 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +588 public void showCommandHelp() { +589 getCommandHelp().showHelp(); +590 } +591 +592 public void help(Object issuer, String[] args) { +593 help(manager.getCommandIssuer(issuer), args); +594 } +595 public void help(CommandIssuer issuer, String[] args) { +596 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); +597 } +598 public void doHelp(Object issuer, String... args) { +599 doHelp(manager.getCommandIssuer(issuer), args); +600 } +601 public void doHelp(CommandIssuer issuer, String... args) { +602 help(issuer, args); +603 } +604 +605 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { +606 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, +607 "{command}", "/" + cmd.command, +608 "{syntax}", cmd.syntaxText +609 ); +610 } +611 +612 public boolean hasPermission(Object issuer) { +613 return hasPermission(manager.getCommandIssuer(issuer)); +614 } +615 +616 public boolean hasPermission(CommandIssuer issuer) { +617 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer))); +618 } +619 +620 +621 public Set<String> getRequiredPermissions() { +622 if (this.permission == null || this.permission.isEmpty()) { +623 return ImmutableSet.of(); +624 } +625 return Sets.newHashSet(ACFPatterns.COMMA.split(this.permission)); +626 } +627 +628 public boolean requiresPermission(String permission) { +629 return getRequiredPermissions().contains(permission) || this.parentCommand != null && parentCommand.requiresPermission(permission); +630 } +631 +632 public String getName() { +633 return commandName; +634 } +635 +636 public ExceptionHandler getExceptionHandler() { +637 return exceptionHandler; +638 } +639 +640 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { +641 this.exceptionHandler = exceptionHandler; +642 return this; +643 } +644 +645 public RegisteredCommand getDefaultRegisteredCommand() { +646 return this.getSubcommand(DEFAULT); +647 } 648 -649 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) { -650 this.cmd = cmd; -651 this.argIndex = argIndex; -652 this.checkSub = checkSub; -653 } -654 -655 String getCheckSub() { -656 return this.checkSub; -657 } +649 public String setContextFlags(Class<?> cls, String flags) { +650 return this.contextFlags.put(cls, flags); +651 } +652 +653 public String getContextFlags(Class<?> cls) { +654 return this.contextFlags.get(cls); +655 } +656 +657 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub; 658 -659 @Override -660 public boolean equals(Object o) { -661 if (this == o) return true; -662 if (o == null || getClass() != o.getClass()) return false; -663 CommandSearch that = (CommandSearch) o; -664 return argIndex == that.argIndex && -665 Objects.equals(cmd, that.cmd) && -666 Objects.equals(checkSub, that.checkSub); +659 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) { +660 this.cmd = cmd; +661 this.argIndex = argIndex; +662 this.checkSub = checkSub; +663 } +664 +665 String getCheckSub() { +666 return this.checkSub; 667 } 668 669 @Override -670 public int hashCode() { -671 return Objects.hash(cmd, argIndex, checkSub); -672 } -673 } -674} +670 public boolean equals(Object o) { +671 if (this == o) return true; +672 if (o == null || getClass() != o.getClass()) return false; +673 CommandSearch that = (CommandSearch) o; +674 return argIndex == that.argIndex && +675 Objects.equals(cmd, that.cmd) && +676 Objects.equals(checkSub, that.checkSub); +677 } +678 +679 @Override +680 public int hashCode() { +681 return Objects.hash(cmd, argIndex, checkSub); +682 } +683 } +684} diff --git a/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html b/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html index 5332a2f7..8bc7185f 100644 --- a/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html +++ b/docs/acf-core/src-html/co/aikar/commands/CommandExecutionContext.html @@ -37,170 +37,190 @@ 029import co.aikar.commands.contexts.ContextResolver; 030import co.aikar.commands.contexts.IssuerAwareContextResolver; 031import co.aikar.commands.contexts.IssuerOnlyContextResolver; -032import com.google.common.collect.ImmutableMap; -033import com.google.common.collect.Maps; -034 -035import java.lang.annotation.Annotation; -036import java.lang.reflect.Parameter; -037import java.util.List; -038import java.util.Map; -039 -040@SuppressWarnings({"WeakerAccess", "unused"}) -041public class CommandExecutionContext <CEC extends CommandExecutionContext, I extends CommandIssuer> { -042 private final RegisteredCommand cmd; -043 private final Parameter param; -044 protected final I issuer; -045 private final List<String> args; -046 private final int index; -047 private final Map<String, Object> passedArgs; -048 private final Map<String, String> flags; +032import com.google.common.collect.Maps; +033 +034import java.lang.annotation.Annotation; +035import java.lang.reflect.Parameter; +036import java.util.List; +037import java.util.Map; +038 +039@SuppressWarnings({"WeakerAccess", "unused"}) +040public class CommandExecutionContext <CEC extends CommandExecutionContext, I extends CommandIssuer> { +041 private final RegisteredCommand cmd; +042 private final Parameter param; +043 protected final I issuer; +044 private final List<String> args; +045 private final int index; +046 private final Map<String, Object> passedArgs; +047 private final Map<String, String> flags; +048 private final CommandManager manager; 049 050 CommandExecutionContext(RegisteredCommand cmd, Parameter param, I sender, List<String> args, 051 int index, Map<String, Object> passedArgs) { 052 this.cmd = cmd; -053 this.param = param; -054 this.issuer = sender; -055 this.args = args; -056 this.index = index; -057 this.passedArgs = passedArgs; -058 Flags flags = param.getAnnotation(Flags.class); -059 if (flags != null) { -060 this.flags = Maps.newHashMap(); -061 for (String s : ACFPatterns.COMMA.split(cmd.scope.manager.getCommandReplacements().replace(flags.value()))) { -062 String[] v = ACFPatterns.EQUALS.split(s, 2); -063 this.flags.put(v[0], v.length > 1 ? v[1] : null); -064 } -065 } else { -066 this.flags = ImmutableMap.of(); -067 } -068 } -069 -070 public String popFirstArg() { -071 return !args.isEmpty() ? args.remove(0) : null; -072 } -073 -074 public String popLastArg() { -075 return !args.isEmpty() ? args.remove(args.size() - 1) : null; -076 } -077 -078 public String getFirstArg() { -079 return !args.isEmpty() ? args.get(0) : null; -080 } -081 -082 public String getLastArg() { -083 return !args.isEmpty() ? args.get(args.size() - 1) : null; -084 } -085 -086 public boolean isLastArg() { -087 return cmd.parameters.length -1 == index; +053 this.manager = cmd.scope.manager; +054 this.param = param; +055 this.issuer = sender; +056 this.args = args; +057 this.index = index; +058 this.passedArgs = passedArgs; +059 this.flags = Maps.newHashMap(); +060 Flags flags = param.getAnnotation(Flags.class); +061 if (flags != null) { +062 parseFlags(flags.value()); +063 } +064 inheritContextFlagsFlags(cmd.scope); +065 } +066 +067 private void inheritContextFlagsFlags(BaseCommand scope) { +068 if (!scope.contextFlags.isEmpty()) { +069 Class<?> pCls = param.getType(); +070 do { +071 parseFlags(scope.contextFlags.get(pCls)); +072 } while ((pCls = pCls.getSuperclass()) != null); +073 } +074 if (scope.parentCommand != null) { +075 inheritContextFlagsFlags(scope.parentCommand); +076 } +077 } +078 +079 private void parseFlags(String flags) { +080 if (flags != null) { +081 for (String s : ACFPatterns.COMMA.split(manager.getCommandReplacements().replace(flags))) { +082 String[] v = ACFPatterns.EQUALS.split(s, 2); +083 if (!this.flags.containsKey(v[0])) { +084 this.flags.put(v[0], v.length > 1 ? v[1] : null); +085 } +086 } +087 } 088 } 089 -090 public int getNumParams() { -091 return cmd.parameters.length; +090 public String popFirstArg() { +091 return !args.isEmpty() ? args.remove(0) : null; 092 } 093 -094 public boolean canOverridePlayerContext() { -095 int numRequired = getNumParams(); -096 for (int i = 0; i < cmd.resolvers.length; i++) { -097 Parameter parameter = cmd.parameters[i]; -098 //noinspection unchecked -099 ContextResolver<?, ?> resolver = cmd.resolvers[i]; -100 if (parameter.getAnnotation(Optional.class) != null || parameter.getAnnotation(Default.class) != null) { -101 numRequired--; -102 } else if (resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver) { -103 numRequired--; -104 } -105 } -106 -107 return numRequired >= args.size(); +094 public String popLastArg() { +095 return !args.isEmpty() ? args.remove(args.size() - 1) : null; +096 } +097 +098 public String getFirstArg() { +099 return !args.isEmpty() ? args.get(0) : null; +100 } +101 +102 public String getLastArg() { +103 return !args.isEmpty() ? args.get(args.size() - 1) : null; +104 } +105 +106 public boolean isLastArg() { +107 return cmd.parameters.length -1 == index; 108 } 109 -110 public Object getResolvedArg(String arg) { -111 return passedArgs.get(arg); +110 public int getNumParams() { +111 return cmd.parameters.length; 112 } 113 -114 public Object getResolvedArg(Class<?>... classes) { -115 for (Class<?> clazz : classes) { -116 for (Object passedArg : passedArgs.values()) { -117 if (clazz.isInstance(passedArg)) { -118 return passedArg; -119 } -120 } -121 } -122 -123 return null; -124 } -125 -126 public <T> T getResolvedArg(String key, Class<?>... classes) { -127 final Object o = passedArgs.get(key); -128 for (Class<?> clazz : classes) { -129 if (clazz.isInstance(o)) { -130 //noinspection unchecked -131 return (T) o; -132 } -133 } -134 -135 return null; -136 } -137 -138 public boolean isOptional() { -139 return param.getAnnotation(Optional.class) != null; -140 } -141 public boolean hasFlag(String flag) { -142 return flags.containsKey(flag); -143 } -144 -145 public String getFlagValue(String flag, String def) { -146 return flags.getOrDefault(flag, def); -147 } -148 -149 public Integer getFlagValue(String flag, Integer def) { -150 return ACFUtil.parseInt(this.flags.get(flag), def); -151 } -152 -153 public <T extends Annotation> T getAnnotation(Class<T> cls) { -154 return param.getAnnotation(cls); -155 } -156 -157 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) { -158 return param.getAnnotation(cls) != null; -159 } -160 -161 public RegisteredCommand getCmd() { -162 return this.cmd; +114 public boolean canOverridePlayerContext() { +115 int numRequired = getNumParams(); +116 for (int i = 0; i < cmd.resolvers.length; i++) { +117 Parameter parameter = cmd.parameters[i]; +118 //noinspection unchecked +119 ContextResolver<?, ?> resolver = cmd.resolvers[i]; +120 if (parameter.getAnnotation(Optional.class) != null || parameter.getAnnotation(Default.class) != null) { +121 numRequired--; +122 } else if (resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver) { +123 numRequired--; +124 } +125 } +126 +127 return numRequired >= args.size(); +128 } +129 +130 public Object getResolvedArg(String arg) { +131 return passedArgs.get(arg); +132 } +133 +134 public Object getResolvedArg(Class<?>... classes) { +135 for (Class<?> clazz : classes) { +136 for (Object passedArg : passedArgs.values()) { +137 if (clazz.isInstance(passedArg)) { +138 return passedArg; +139 } +140 } +141 } +142 +143 return null; +144 } +145 +146 public <T> T getResolvedArg(String key, Class<?>... classes) { +147 final Object o = passedArgs.get(key); +148 for (Class<?> clazz : classes) { +149 if (clazz.isInstance(o)) { +150 //noinspection unchecked +151 return (T) o; +152 } +153 } +154 +155 return null; +156 } +157 +158 public boolean isOptional() { +159 return param.getAnnotation(Optional.class) != null; +160 } +161 public boolean hasFlag(String flag) { +162 return flags.containsKey(flag); 163 } 164 -165 public Parameter getParam() { -166 return this.param; +165 public String getFlagValue(String flag, String def) { +166 return flags.getOrDefault(flag, def); 167 } 168 -169 public I getIssuer() { -170 return this.issuer; +169 public Integer getFlagValue(String flag, Integer def) { +170 return ACFUtil.parseInt(this.flags.get(flag), def); 171 } 172 -173 public List<String> getArgs() { -174 return this.args; +173 public <T extends Annotation> T getAnnotation(Class<T> cls) { +174 return param.getAnnotation(cls); 175 } 176 -177 public int getIndex() { -178 return this.index; +177 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) { +178 return param.getAnnotation(cls) != null; 179 } 180 -181 public Map<String, Object> getPassedArgs() { -182 return this.passedArgs; +181 public RegisteredCommand getCmd() { +182 return this.cmd; 183 } 184 -185 public Map<String, String> getFlags() { -186 return this.flags; +185 public Parameter getParam() { +186 return this.param; 187 } 188 -189 public String joinArgs() { -190 return ACFUtil.join(args, " "); +189 public I getIssuer() { +190 return this.issuer; 191 } -192 public String joinArgs(String sep) { -193 return ACFUtil.join(args, sep); -194 } -195} +192 +193 public List<String> getArgs() { +194 return this.args; +195 } +196 +197 public int getIndex() { +198 return this.index; +199 } +200 +201 public Map<String, Object> getPassedArgs() { +202 return this.passedArgs; +203 } +204 +205 public Map<String, String> getFlags() { +206 return this.flags; +207 } +208 +209 public String joinArgs() { +210 return ACFUtil.join(args, " "); +211 } +212 public String joinArgs(String sep) { +213 return ACFUtil.join(args, sep); +214 } +215}ExceptionHandlerthat is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.