diff --git a/docs/allclasses-frame.html b/docs/allclasses-frame.html new file mode 100644 index 00000000..3f715548 --- /dev/null +++ b/docs/allclasses-frame.html @@ -0,0 +1,65 @@ + + + +
+ + +| Modifier and Type | +Field and Description | +
|---|---|
static Random |
+RANDOM |
+
| Modifier and Type | +Method and Description | +
|---|---|
static String |
+capitalize(String str,
+ char[] delimiters) |
+
static String |
+capitalizeFirst(String name) |
+
static String |
+capitalizeFirst(String name,
+ char separator) |
+
static String |
+combine(String[] args) |
+
static String |
+combine(String[] args,
+ int start) |
+
static List<String> |
+enumNames(Class<? extends Enum<?>> cls) |
+
static List<String> |
+enumNames(Enum<?>[] values) |
+
static String |
+formatNumber(Integer balance) |
+
static <T extends Enum> |
+getEnumFromName(T[] types,
+ String name) |
+
static <T extends Enum> |
+getEnumFromName(T[] types,
+ String name,
+ T def) |
+
static <T extends Enum> |
+getEnumFromOrdinal(T[] types,
+ int ordinal) |
+
static <T> boolean |
+hasIntersection(Collection<T> list1,
+ Collection<T> list2) |
+
static int |
+indexOf(String arg,
+ String[] split) |
+
static <T> Collection<T> |
+intersection(Collection<T> list1,
+ Collection<T> list2) |
+
static String |
+intToRoman(int integer) |
+
static boolean |
+isBetween(float num,
+ double min,
+ double max) |
+
static boolean |
+isDouble(String string) |
+
static boolean |
+isFloat(String string) |
+
static boolean |
+isInteger(String string) |
+
static boolean |
+isNumber(String str) |
+
static boolean |
+isTruthy(String test) |
+
static String |
+join(Collection<String> args) |
+
static String |
+join(Collection<String> args,
+ String sep) |
+
static String |
+join(String[] args) |
+
static String |
+join(String[] args,
+ char sep) |
+
static String |
+join(String[] args,
+ int index) |
+
static String |
+join(String[] args,
+ int index,
+ char sep) |
+
static String |
+join(String[] args,
+ String sep) |
+
static String |
+limit(String str,
+ int limit) |
+
static String |
+ltrim(String s) |
+
static String |
+normalize(String s) |
+
static <T> T |
+nullDefault(Object val,
+ Object def) |
+
static String |
+padLeft(String s,
+ int n) |
+
static String |
+padRight(String s,
+ int n) |
+
static Double |
+parseDouble(String var) |
+
static Double |
+parseDouble(String var,
+ Double def) |
+
static Float |
+parseFloat(String var) |
+
static Float |
+parseFloat(String var,
+ Float def) |
+
static Integer |
+parseInt(String var) |
+
static Integer |
+parseInt(String var,
+ Integer def) |
+
static Long |
+parseLong(String var) |
+
static Long |
+parseLong(String var,
+ Long def) |
+
static Number |
+parseNumber(String num,
+ boolean suffixes) |
+
static double |
+precision(double x,
+ int p) |
+
static double |
+rand(double min,
+ double max) |
+
static int |
+rand(int min,
+ int max) |
+
static int |
+rand(int min1,
+ int max1,
+ int min2,
+ int max2)
+Calculate random between 2 points, excluding a center
+ ex: Util.rand(-12, -6, 6, 12) would not return -5 to 5
+ |
+
static boolean |
+randBool() |
+
static <T extends Enum<?>> |
+random(Class<? extends T> enm)
+Deprecated.
+ |
+
static <T> T |
+random(List<T> arr) |
+
static <T> T |
+random(T[] arr) |
+
static String |
+replace(String string,
+ Pattern pattern,
+ String repl)
+Plain string replacement, escapes replace value.
+ |
+
static String |
+replace(String string,
+ String pattern,
+ String repl)
+Plain String replacement.
+ |
+
static String |
+replacePattern(String string,
+ Pattern pattern,
+ String repl)
+Regex version of
+replace(String, Pattern, String) |
+
static String |
+replacePattern(String string,
+ String pattern,
+ String repl)
+Regex version of
+replace(String, String, String) |
+
static String |
+replacePatternMatch(String string,
+ Pattern pattern,
+ String repl)
+Pure Regex Pattern matching and replacement, no escaping
+ |
+
static String |
+replacePatternMatch(String string,
+ String pattern,
+ String repl)
+Pure Regex Pattern matching and replacement, no escaping
+ |
+
static String |
+replacePatterns(String string,
+ String... replacements) |
+
static String |
+replaceStrings(String string,
+ String... replacements) |
+
static double |
+round(double x,
+ int scale) |
+
static int |
+roundUp(int num,
+ int multiple) |
+
static String |
+rtrim(String s) |
+
static <E extends Enum<E>> |
+simpleMatch(Class<? extends Enum<?>> list,
+ String item) |
+
static String |
+simplifyString(String str) |
+
static void |
+sneaky(Throwable t) |
+
static String |
+ucfirst(String str) |
+
public static String formatNumber(Integer balance)+
public static <T extends Enum> T getEnumFromName(T[] types, + String name)+
public static <T extends Enum> T getEnumFromName(T[] types, + String name, + T def)+
public static <T extends Enum> T getEnumFromOrdinal(T[] types, + int ordinal)+
public static Double parseDouble(String var)+
public static Double parseDouble(String var, + Double def)+
public static Float parseFloat(String var)+
public static Float parseFloat(String var, + Float def)+
public static boolean randBool()+
public static <T> T nullDefault(Object val, + Object def)+
public static String join(Collection<String> args)+
public static String join(Collection<String> args, + String sep)+
public static String simplifyString(String str)+
public static double round(double x, + int scale)+
public static int roundUp(int num, + int multiple)+
public static String replace(String string, + Pattern pattern, + String repl)+
string - pattern - repl - public static String replacePattern(String string, + Pattern pattern, + String repl)+
replace(String, Pattern, String)string - pattern - repl - public static String replace(String string, + String pattern, + String repl)+
replacePattern(String, String, String)string - pattern - repl - public static String replacePattern(String string, + String pattern, + String repl)+
replace(String, String, String)string - pattern - repl - public static String replacePatternMatch(String string, + Pattern pattern, + String repl)+
string - pattern - repl - public static String replacePatternMatch(String string, + String pattern, + String repl)+
string - pattern - repl - public static String replaceStrings(String string, + String... replacements)+
public static String replacePatterns(String string, + String... replacements)+
public static String capitalize(String str, + char[] delimiters)+
public static <T> T random(T[] arr)+
@Deprecated +public static <T extends Enum<?>> T random(Class<? extends T> enm)+
T - enm - public static String capitalizeFirst(String name)+
public static String capitalizeFirst(String name, + char separator)+
@Nullable +public static <E extends Enum<E>> E simpleMatch(Class<? extends Enum<?>> list, + String item)+
public static Number parseNumber(String num, + boolean suffixes)+
public static <T> boolean hasIntersection(Collection<T> list1, + Collection<T> list2)+
public static <T> Collection<T> intersection(Collection<T> list1, + Collection<T> list2)+
public static int rand(int min, + int max)+
public static int rand(int min1, + int max1, + int min2, + int max2)+
min1 - max1 - min2 - max2 - public static double rand(double min, + double max)+
public static String intToRoman(int integer)+
public static boolean isBetween(float num, + double min, + double max)+
public static double precision(double x, + int p)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/BaseCommand.html b/docs/co/aikar/commands/BaseCommand.html new file mode 100644 index 00000000..b9b42773 --- /dev/null +++ b/docs/co/aikar/commands/BaseCommand.html @@ -0,0 +1,650 @@ + + + + + + +public abstract class BaseCommand +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
static String |
+DEFAULT |
+
static String |
+UNKNOWN |
+
| Constructor and Description | +
|---|
BaseCommand() |
+
BaseCommand(String cmd) |
+
| Modifier and Type | +Method and Description | +
|---|---|
boolean |
+canExecute(CommandIssuer issuer,
+ RegisteredCommand<?> cmd) |
+
void |
+doHelp(CommandIssuer issuer,
+ String... args) |
+
void |
+doHelp(Object issuer,
+ String... args) |
+
void |
+execute(CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
CommandHelp |
+getCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
CommandIssuer |
+getCurrentCommandIssuer() |
+
CommandManager |
+getCurrentCommandManager() |
+
ExceptionHandler |
+getExceptionHandler() |
+
String |
+getExecCommandLabel()
+Gets the root command name that the user actually typed
+ |
+
String |
+getExecSubcommand()
+Gets the actual sub command name the user typed
+ |
+
String |
+getName() |
+
String[] |
+getOrigArgs()
+Gets the actual args in string form the user typed
+ |
+
boolean |
+hasPermission(CommandIssuer issuer) |
+
boolean |
+hasPermission(Object issuer) |
+
void |
+help(CommandIssuer issuer,
+ String[] args) |
+
void |
+help(Object issuer,
+ String[] args) |
+
BaseCommand |
+setExceptionHandler(ExceptionHandler exceptionHandler) |
+
void |
+showCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
void |
+showSyntax(CommandIssuer issuer,
+ RegisteredCommand<?> cmd) |
+
List<String> |
+tabComplete(CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
public static final String UNKNOWN+
public static final String DEFAULT+
public BaseCommand()+
public BaseCommand(String cmd)+
public String getExecCommandLabel()+
public String getExecSubcommand()+
public String[] getOrigArgs()+
public void execute(CommandIssuer issuer, + String commandLabel, + String[] args)+
public CommandIssuer getCurrentCommandIssuer()+
public CommandManager getCurrentCommandManager()+
public boolean canExecute(CommandIssuer issuer, + RegisteredCommand<?> cmd)+
public List<String> tabComplete(CommandIssuer issuer, + String commandLabel, + String[] args) + throws IllegalArgumentException+
IllegalArgumentException@Deprecated +public CommandHelp getCommandHelp()+
@Deprecated +public void showCommandHelp()+
public void help(CommandIssuer issuer, + String[] args)+
public void doHelp(CommandIssuer issuer, + String... args)+
public void showSyntax(CommandIssuer issuer, + RegisteredCommand<?> cmd)+
public boolean hasPermission(Object issuer)+
public boolean hasPermission(CommandIssuer issuer)+
public ExceptionHandler getExceptionHandler()+
public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandCompletionContext.html b/docs/co/aikar/commands/CommandCompletionContext.html new file mode 100644 index 00000000..c6b440ff --- /dev/null +++ b/docs/co/aikar/commands/CommandCompletionContext.html @@ -0,0 +1,392 @@ + + + + + + +public class CommandCompletionContext +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
protected CommandIssuer |
+issuer |
+
| Modifier and Type | +Method and Description | +
|---|---|
String |
+getConfig() |
+
String |
+getConfig(String key) |
+
String |
+getConfig(String key,
+ String def) |
+
Map<String,String> |
+getConfigs() |
+
<T> T |
+getContextValue(Class<? extends T> clazz) |
+
<T> T |
+getContextValue(Class<? extends T> clazz,
+ Integer paramIdx) |
+
String |
+getInput() |
+
CommandIssuer |
+getIssuer() |
+
boolean |
+hasConfig(String key) |
+
protected final CommandIssuer issuer+
public Map<String,String> getConfigs()+
public <T> T getContextValue(Class<? extends T> clazz) + throws InvalidCommandArgument+
InvalidCommandArgumentpublic <T> T getContextValue(Class<? extends T> clazz, + Integer paramIdx) + throws InvalidCommandArgument+
InvalidCommandArgumentpublic CommandIssuer getIssuer()+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html b/docs/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html new file mode 100644 index 00000000..eca6a0b7 --- /dev/null +++ b/docs/co/aikar/commands/CommandCompletions.CommandCompletionHandler.html @@ -0,0 +1,233 @@ + + + + + + +public static interface CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext>+
| Modifier and Type | +Method and Description | +
|---|---|
Collection<String> |
+getCompletions(C context) |
+
Collection<String> getCompletions(C context) + throws InvalidCommandArgument+
InvalidCommandArgumentCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandCompletions.html b/docs/co/aikar/commands/CommandCompletions.html new file mode 100644 index 00000000..3e6bb11f --- /dev/null +++ b/docs/co/aikar/commands/CommandCompletions.html @@ -0,0 +1,293 @@ + + + + + + +public class CommandCompletions<C extends CommandCompletionContext> +extends Object+
| Modifier and Type | +Class and Description | +
|---|---|
static interface |
+CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext> |
+
| Constructor and Description | +
|---|
CommandCompletions(CommandManager manager) |
+
| Modifier and Type | +Method and Description | +
|---|---|
CommandCompletions.CommandCompletionHandler |
+registerCompletion(String id,
+ CommandCompletions.CommandCompletionHandler<C> handler) |
+
public CommandCompletions(CommandManager manager)+
public CommandCompletions.CommandCompletionHandler registerCompletion(String id, + CommandCompletions.CommandCompletionHandler<C> handler)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandContexts.html b/docs/co/aikar/commands/CommandContexts.html new file mode 100644 index 00000000..78786dca --- /dev/null +++ b/docs/co/aikar/commands/CommandContexts.html @@ -0,0 +1,393 @@ + + + + + + +public class CommandContexts<R extends CommandExecutionContext<?,? extends CommandIssuer>> +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
protected Map<Class<?>,ContextResolver<?,R>> |
+contextMap |
+
protected CommandManager |
+manager |
+
| Modifier and Type | +Method and Description | +
|---|---|
ContextResolver<?,R> |
+getResolver(Class<?> type) |
+
<T> void |
+registerContext(Class<T> context,
+ ContextResolver<T,R> supplier)
+Registers a context that requires input from the command issuer to resolve.
+ |
+
<T> void |
+registerIssuerAwareContext(Class<T> context,
+ IssuerAwareContextResolver<T,R> supplier)
+Registers a context resolver that may conditionally consume input, falling back to using the context of the
+ issuer to potentially fulfill this context.
+ |
+
<T> void |
+registerIssuerOnlyContext(Class<T> context,
+ IssuerOnlyContextResolver<T,R> supplier)
+Registers a context resolver that will never consume input.
+ |
+
<T> void |
+registerOptionalContext(Class<T> context,
+ OptionalContextResolver<T,R> supplier)
+Registers a context that can safely accept a null input from the command issuer to resolve.
+ |
+
<T> void |
+registerSenderAwareContext(Class<T> context,
+ IssuerAwareContextResolver<T,R> supplier)
+Deprecated.
+
+Please switch to
+registerIssuerAwareContext(Class, IssuerAwareContextResolver)
+ as the core wants to use the platform agnostic term of "Issuer" instead of Sender |
+
protected final Map<Class<?>,ContextResolver<?,R extends CommandExecutionContext<?,? extends CommandIssuer>>> contextMap+
protected final CommandManager manager+
@Deprecated +public <T> void registerSenderAwareContext(Class<T> context, + IssuerAwareContextResolver<T,R> supplier)+
registerIssuerAwareContext(Class, IssuerAwareContextResolver)
+ as the core wants to use the platform agnostic term of "Issuer" instead of Senderpublic <T> void registerIssuerAwareContext(Class<T> context, + IssuerAwareContextResolver<T,R> supplier)+
CommandExecutionContext.getFirstArg() and then conditionally call CommandExecutionContext.popFirstArg()
+ if you want to consume that input.public <T> void registerIssuerOnlyContext(Class<T> context, + IssuerOnlyContextResolver<T,R> supplier)+
public <T> void registerOptionalContext(Class<T> context, + OptionalContextResolver<T,R> supplier)+
CommandExecutionContext.popFirstArg()public <T> void registerContext(Class<T> context, + ContextResolver<T,R> supplier)+
CommandExecutionContext.popFirstArg()public ContextResolver<?,R> getResolver(Class<?> type)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandExecutionContext.html b/docs/co/aikar/commands/CommandExecutionContext.html new file mode 100644 index 00000000..924f2b97 --- /dev/null +++ b/docs/co/aikar/commands/CommandExecutionContext.html @@ -0,0 +1,592 @@ + + + + + + +public class CommandExecutionContext<T extends CommandExecutionContext,I extends CommandIssuer> +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
protected I |
+issuer |
+
protected final I extends CommandIssuer issuer+
public String popFirstArg()+
public String popLastArg()+
public String getFirstArg()+
public String getLastArg()+
public boolean isLastArg()+
public int getNumParams()+
public boolean canOverridePlayerContext()+
public Object getResolvedArg(String arg)+
public Object getResolvedArg(Class<?>... classes)+
public <T> T getResolvedArg(String key, + Class<?>... classes)+
public boolean isOptional()+
public String getFlagValue(String flag, + String def)+
public Integer getFlagValue(String flag, + Integer def)+
public <T extends Annotation> T getAnnotation(Class<T> cls)+
public <T extends Annotation> boolean hasAnnotation(Class<T> cls)+
public RegisteredCommand getCmd()+
public int getIndex()+
public Map<String,Object> getPassedArgs()+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandHelp.html b/docs/co/aikar/commands/CommandHelp.html new file mode 100644 index 00000000..87176c8e --- /dev/null +++ b/docs/co/aikar/commands/CommandHelp.html @@ -0,0 +1,391 @@ + + + + + + +public class CommandHelp +extends Object+
| Constructor and Description | +
|---|
CommandHelp(CommandManager manager,
+ co.aikar.commands.RootCommand rootCommand,
+ CommandIssuer issuer) |
+
| Modifier and Type | +Method and Description | +
|---|---|
@NotNull String[] |
+getFormatReplacements(HelpEntry e)
+Override this to control replacements
+ |
+
List<HelpEntry> |
+getHelpEntries() |
+
CommandManager |
+getManager() |
+
void |
+setPage(int page) |
+
void |
+setSearch(List<String> search) |
+
void |
+showHelp() |
+
void |
+showHelp(CommandIssuer issuer) |
+
void |
+showHelp(CommandIssuer issuer,
+ co.aikar.locales.MessageKeyProvider format) |
+
protected void |
+updateSearchScore(HelpEntry help) |
+
public CommandHelp(CommandManager manager, + co.aikar.commands.RootCommand rootCommand, + CommandIssuer issuer)+
protected void updateSearchScore(HelpEntry help)+
public CommandManager getManager()+
public void showHelp()+
public void showHelp(CommandIssuer issuer)+
public void showHelp(CommandIssuer issuer, + co.aikar.locales.MessageKeyProvider format)+
@NotNull +public @NotNull String[] getFormatReplacements(HelpEntry e)+
e - public List<HelpEntry> getHelpEntries()+
public void setPage(int page)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandIssuer.html b/docs/co/aikar/commands/CommandIssuer.html new file mode 100644 index 00000000..f0ebb390 --- /dev/null +++ b/docs/co/aikar/commands/CommandIssuer.html @@ -0,0 +1,450 @@ + + + + + + +public interface CommandIssuer+
| Modifier and Type | +Method and Description | +
|---|---|
<T> T |
+getIssuer()
+Gets the issuer in the platforms native object
+ |
+
CommandManager |
+getManager() |
+
boolean |
+hasPermission(String permission)
+Has permission node
+ |
+
boolean |
+isPlayer()
+Is this issue a player, or server/console sender
+ |
+
default void |
+sendError(co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
default void |
+sendError(co.aikar.locales.MessageKey key,
+ String... replacements) |
+
default void |
+sendInfo(co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
default void |
+sendInfo(co.aikar.locales.MessageKey key,
+ String... replacements) |
+
default void |
+sendMessage(MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
default void |
+sendMessage(MessageType type,
+ co.aikar.locales.MessageKey key,
+ String... replacements) |
+
default void |
+sendMessage(String message)
+Send the Command Issuer a message
+ |
+
void |
+sendMessageInternal(String message)
+Deprecated.
+
+Do not call this, for internal use. Not considered part of the API and may break.
+ |
+
default void |
+sendSyntax(co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
default void |
+sendSyntax(co.aikar.locales.MessageKey key,
+ String... replacements) |
+
<T> T getIssuer()+
T - CommandManager getManager()+
boolean isPlayer()+
default void sendMessage(String message)+
message - boolean hasPermission(String permission)+
permission - default void sendError(co.aikar.locales.MessageKeyProvider key, + String... replacements)+
default void sendSyntax(co.aikar.locales.MessageKeyProvider key, + String... replacements)+
default void sendInfo(co.aikar.locales.MessageKeyProvider key, + String... replacements)+
default void sendSyntax(co.aikar.locales.MessageKey key, + String... replacements)+
default void sendMessage(MessageType type, + co.aikar.locales.MessageKeyProvider key, + String... replacements)+
default void sendMessage(MessageType type, + co.aikar.locales.MessageKey key, + String... replacements)+
@Deprecated +void sendMessageInternal(String message)+
message - Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandManager.html b/docs/co/aikar/commands/CommandManager.html new file mode 100644 index 00000000..cba7d3b6 --- /dev/null +++ b/docs/co/aikar/commands/CommandManager.html @@ -0,0 +1,1069 @@ + + + + + + +public abstract class CommandManager<I,FT,F extends MessageFormatter<FT>> +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
protected ExceptionHandler |
+defaultExceptionHandler |
+
protected F |
+defaultFormatter |
+
protected Map<MessageType,F> |
+formatters |
+
protected CommandReplacements |
+replacements |
+
protected Map<String,co.aikar.commands.RootCommand> |
+rootCommands |
+
protected Set<Locale> |
+supportedLanguages |
+
| Constructor and Description | +
|---|
CommandManager() |
+
| Modifier and Type | +Method and Description | +
|---|---|
void |
+addSupportedLanguage(Locale locale)
+Adds a new locale to the list of automatic Locales to load Message Bundles for.
+ |
+
abstract <R extends CommandExecutionContext> |
+createCommandContext(RegisteredCommand command,
+ Parameter parameter,
+ CommandIssuer sender,
+ List<String> args,
+ int i,
+ Map<String,Object> passedArgs) |
+
CommandOperationContext |
+createCommandOperationContext(BaseCommand command,
+ CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
abstract CommandCompletionContext |
+createCompletionContext(RegisteredCommand command,
+ CommandIssuer sender,
+ String input,
+ String config,
+ String[] args) |
+
RegisteredCommand |
+createRegisteredCommand(BaseCommand command,
+ String cmdName,
+ Method method,
+ String prefSubCommand) |
+
abstract co.aikar.commands.RootCommand |
+createRootCommand(String cmd) |
+
void |
+enableUnstableAPI(String api)
+Deprecated.
+
+Use this with caution! If you enable and use Unstable API's, your next compile using ACF
+ may require you to update your implementation to those unstable API's
+ |
+
String |
+formatMessage(CommandIssuer issuer,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
CommandHelp |
+generateCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+generateCommandHelp(CommandIssuer issuer,
+ co.aikar.commands.RootCommand rootCommand)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+generateCommandHelp(CommandIssuer issuer,
+ @NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+generateCommandHelp(@NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
abstract CommandCompletions<?> |
+getCommandCompletions()
+Gets the command completions manager
+ |
+
abstract CommandContexts<?> |
+getCommandContexts()
+Gets the command contexts manager
+ |
+
abstract CommandIssuer |
+getCommandIssuer(Object issuer) |
+
CommandReplacements |
+getCommandReplacements()
+Lets you add custom string replacements that can be applied to annotation values,
+ to reduce duplication/repetition of common values such as permission nodes and command prefixes.
+ |
+
static CommandIssuer |
+getCurrentCommandIssuer() |
+
static CommandManager |
+getCurrentCommandManager() |
+
static CommandOperationContext |
+getCurrentCommandOperationContext() |
+
ExceptionHandler |
+getDefaultExceptionHandler()
+Gets the current default exception handler, might be null.
+ |
+
F |
+getDefaultFormatter() |
+
F |
+getFormat(MessageType type) |
+
Locale |
+getIssuerLocale(CommandIssuer issuer) |
+
abstract Locales |
+getLocales()
+Returns a Locales Manager to add and modify language tables for your commands.
+ |
+
Set<Locale> |
+getSupportedLanguages()
+Gets a list of all currently supported languages for this manager.
+ |
+
protected boolean |
+handleUncaughtException(BaseCommand scope,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t) |
+
boolean |
+hasPermission(CommandIssuer issuer,
+ String permission) |
+
abstract boolean |
+hasRegisteredCommands() |
+
abstract boolean |
+isCommandIssuer(Class<?> type) |
+
void |
+log(co.aikar.commands.LogLevel level,
+ String message) |
+
abstract void |
+log(co.aikar.commands.LogLevel level,
+ String message,
+ Throwable throwable) |
+
co.aikar.commands.RootCommand |
+obtainRootCommand(String cmd) |
+
abstract void |
+registerCommand(BaseCommand command)
+Registers a command with ACF
+ |
+
void |
+sendMessage(CommandIssuer issuer,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
void |
+sendMessage(I issuerArg,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
void |
+setDefaultExceptionHandler(ExceptionHandler exceptionHandler)
+Sets the default
+ExceptionHandler that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered. |
+
void |
+setDefaultFormatter(F defaultFormatter) |
+
F |
+setFormat(MessageType type,
+ F formatter) |
+
void |
+setFormat(MessageType type,
+ FT... colors) |
+
void |
+setFormat(MessageType type,
+ int i,
+ FT color) |
+
protected Map<String,co.aikar.commands.RootCommand> rootCommands+
protected CommandReplacements replacements+
protected ExceptionHandler defaultExceptionHandler+
protected Set<Locale> supportedLanguages+
protected Map<MessageType,F extends MessageFormatter<FT>> formatters+
protected F extends MessageFormatter<FT> defaultFormatter+
public CommandManager()+
public static CommandOperationContext getCurrentCommandOperationContext()+
public static CommandIssuer getCurrentCommandIssuer()+
public static CommandManager getCurrentCommandManager()+
public F setFormat(MessageType type, + F formatter)+
public F getFormat(MessageType type)+
public void setFormat(MessageType type, + FT... colors)+
public void setFormat(MessageType type, + int i, + FT color)+
public F getDefaultFormatter()+
public void setDefaultFormatter(F defaultFormatter)+
public abstract CommandContexts<?> getCommandContexts()+
public abstract CommandCompletions<?> getCommandCompletions()+
@Deprecated +public CommandHelp generateCommandHelp(@NotNull + @NotNull String command)+
@Deprecated +public CommandHelp generateCommandHelp(CommandIssuer issuer, + @NotNull + @NotNull String command)+
@Deprecated +public CommandHelp generateCommandHelp()+
@Deprecated +public CommandHelp generateCommandHelp(CommandIssuer issuer, + co.aikar.commands.RootCommand rootCommand)+
public abstract void registerCommand(BaseCommand command)+
command - The command to registerpublic abstract boolean hasRegisteredCommands()+
public abstract boolean isCommandIssuer(Class<?> type)+
public abstract CommandIssuer getCommandIssuer(Object issuer)+
public abstract co.aikar.commands.RootCommand createRootCommand(String cmd)+
public abstract Locales getLocales()+
public abstract <R extends CommandExecutionContext> R createCommandContext(RegisteredCommand command, + Parameter parameter, + CommandIssuer sender, + List<String> args, + int i, + Map<String,Object> passedArgs)+
public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, + CommandIssuer sender, + String input, + String config, + String[] args)+
public abstract void log(co.aikar.commands.LogLevel level, + String message, + Throwable throwable)+
public CommandReplacements getCommandReplacements()+
public boolean hasPermission(CommandIssuer issuer, + String permission)+
public co.aikar.commands.RootCommand obtainRootCommand(String cmd)+
public RegisteredCommand createRegisteredCommand(BaseCommand command, + String cmdName, + Method method, + String prefSubCommand)+
public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler)+
ExceptionHandler that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.exceptionHandler - the handler that should handle uncaught exceptionspublic ExceptionHandler getDefaultExceptionHandler()+
protected boolean handleUncaughtException(BaseCommand scope, + RegisteredCommand registeredCommand, + CommandIssuer sender, + List<String> args, + Throwable t)+
public void sendMessage(I issuerArg, + MessageType type, + co.aikar.locales.MessageKeyProvider key, + String... replacements)+
public void sendMessage(CommandIssuer issuer, + MessageType type, + co.aikar.locales.MessageKeyProvider key, + String... replacements)+
public String formatMessage(CommandIssuer issuer, + MessageType type, + co.aikar.locales.MessageKeyProvider key, + String... replacements)+
public Locale getIssuerLocale(CommandIssuer issuer)+
public CommandOperationContext createCommandOperationContext(BaseCommand command, + CommandIssuer issuer, + String commandLabel, + String[] args)+
public Set<Locale> getSupportedLanguages()+
public void addSupportedLanguage(Locale locale)+
locale - @Deprecated +public void enableUnstableAPI(String api)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandOperationContext.html b/docs/co/aikar/commands/CommandOperationContext.html new file mode 100644 index 00000000..ef2260ec --- /dev/null +++ b/docs/co/aikar/commands/CommandOperationContext.html @@ -0,0 +1,343 @@ + + + + + + +public class CommandOperationContext +extends Object+
| Modifier and Type | +Method and Description | +
|---|---|
<T extends Annotation> |
+getAnnotation(Class<T> anno) |
+
String[] |
+getArgs() |
+
BaseCommand |
+getCommand() |
+
CommandIssuer |
+getCommandIssuer() |
+
String |
+getCommandLabel() |
+
CommandManager |
+getCommandManager() |
+
RegisteredCommand |
+getRegisteredCommand() |
+
boolean |
+hasAnnotation(Class<? extends Annotation> anno) |
+
void |
+setRegisteredCommand(RegisteredCommand registeredCommand) |
+
public CommandManager getCommandManager()+
public CommandIssuer getCommandIssuer()+
public BaseCommand getCommand()+
public String getCommandLabel()+
public void setRegisteredCommand(RegisteredCommand registeredCommand)+
public RegisteredCommand getRegisteredCommand()+
public <T extends Annotation> T getAnnotation(Class<T> anno)+
public boolean hasAnnotation(Class<? extends Annotation> anno)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/CommandReplacements.html b/docs/co/aikar/commands/CommandReplacements.html new file mode 100644 index 00000000..4b388568 --- /dev/null +++ b/docs/co/aikar/commands/CommandReplacements.html @@ -0,0 +1,267 @@ + + + + + + +public class CommandReplacements +extends Object+
| Modifier and Type | +Method and Description | +
|---|---|
String |
+addReplacement(String key,
+ String val) |
+
void |
+addReplacements(String... replacements) |
+
String |
+replace(String text) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/ExceptionHandler.html b/docs/co/aikar/commands/ExceptionHandler.html new file mode 100644 index 00000000..87e509d5 --- /dev/null +++ b/docs/co/aikar/commands/ExceptionHandler.html @@ -0,0 +1,253 @@ + + + + + + +@FunctionalInterface +public interface ExceptionHandler+
| Modifier and Type | +Method and Description | +
|---|---|
boolean |
+execute(BaseCommand command,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t)
+Called when an exception occurs while executing a command
++ If an exception handler properly handles an exception, the user will not be noticied by the + framework that something went unexceptected. |
+
boolean execute(BaseCommand command, + RegisteredCommand registeredCommand, + CommandIssuer sender, + List<String> args, + Throwable t)+
command - the command that was executedregisteredCommand - the registered commandsender - the issuer who send the commandargs - the args he usedt - the throwable that caused this exceptionCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/ForwardingCommand.html b/docs/co/aikar/commands/ForwardingCommand.html new file mode 100644 index 00000000..55e10a89 --- /dev/null +++ b/docs/co/aikar/commands/ForwardingCommand.html @@ -0,0 +1,314 @@ + + + + + + +public class ForwardingCommand +extends BaseCommand+
DEFAULT, UNKNOWN| Modifier and Type | +Method and Description | +
|---|---|
void |
+execute(CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
boolean |
+hasPermission(CommandIssuer sender) |
+
List<String> |
+tabComplete(CommandIssuer issuer,
+ String alias,
+ String[] args) |
+
canExecute, doHelp, doHelp, getCommandHelp, getCurrentCommandIssuer, getCurrentCommandManager, getExceptionHandler, getExecCommandLabel, getExecSubcommand, getName, getOrigArgs, hasPermission, help, help, setExceptionHandler, showCommandHelp, showSyntaxpublic boolean hasPermission(CommandIssuer sender)+
hasPermission in class BaseCommandpublic List<String> tabComplete(CommandIssuer issuer, + String alias, + String[] args) + throws IllegalArgumentException+
tabComplete in class BaseCommandIllegalArgumentExceptionpublic void execute(CommandIssuer issuer, + String commandLabel, + String[] args)+
execute in class BaseCommandCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/HelpEntry.html b/docs/co/aikar/commands/HelpEntry.html new file mode 100644 index 00000000..0883758d --- /dev/null +++ b/docs/co/aikar/commands/HelpEntry.html @@ -0,0 +1,316 @@ + + + + + + +| Modifier and Type | +Method and Description | +
|---|---|
String |
+getCommand() |
+
String |
+getDescription() |
+
String |
+getParameterSyntax() |
+
int |
+getSearchScore() |
+
String |
+getSearchTags() |
+
void |
+setSearchScore(int searchScore) |
+
boolean |
+shouldShow() |
+
public String getCommand()+
public String getParameterSyntax()+
public String getDescription()+
public void setSearchScore(int searchScore)+
public boolean shouldShow()+
public int getSearchScore()+
public String getSearchTags()+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/InvalidCommandArgument.html b/docs/co/aikar/commands/InvalidCommandArgument.html new file mode 100644 index 00000000..8903ef18 --- /dev/null +++ b/docs/co/aikar/commands/InvalidCommandArgument.html @@ -0,0 +1,371 @@ + + + + + + +public class InvalidCommandArgument +extends Exception+
| Constructor and Description | +
|---|
InvalidCommandArgument() |
+
InvalidCommandArgument(boolean showSyntax) |
+
InvalidCommandArgument(co.aikar.locales.MessageKey key,
+ boolean showSyntax,
+ String... replacements) |
+
InvalidCommandArgument(co.aikar.locales.MessageKeyProvider key,
+ boolean showSyntax,
+ String... replacements) |
+
InvalidCommandArgument(co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
InvalidCommandArgument(co.aikar.locales.MessageKey key,
+ String... replacements) |
+
InvalidCommandArgument(String message)
+Deprecated.
+ |
+
InvalidCommandArgument(String message,
+ boolean showSyntax)
+Deprecated.
+ |
+
addSuppressed, fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, getSuppressed, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toStringpublic InvalidCommandArgument()+
public InvalidCommandArgument(boolean showSyntax)+
public InvalidCommandArgument(co.aikar.locales.MessageKeyProvider key, + String... replacements)+
public InvalidCommandArgument(co.aikar.locales.MessageKey key, + String... replacements)+
public InvalidCommandArgument(co.aikar.locales.MessageKeyProvider key, + boolean showSyntax, + String... replacements)+
public InvalidCommandArgument(co.aikar.locales.MessageKey key, + boolean showSyntax, + String... replacements)+
@Deprecated +public InvalidCommandArgument(String message)+
@Deprecated +public InvalidCommandArgument(String message, + boolean showSyntax)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/InvalidCommandContextException.html b/docs/co/aikar/commands/InvalidCommandContextException.html new file mode 100644 index 00000000..1f5aadb8 --- /dev/null +++ b/docs/co/aikar/commands/InvalidCommandContextException.html @@ -0,0 +1,228 @@ + + + + + + +public class InvalidCommandContextException +extends RuntimeException+
addSuppressed, fillInStackTrace, getCause, getLocalizedMessage, getMessage, getStackTrace, getSuppressed, initCause, printStackTrace, printStackTrace, printStackTrace, setStackTrace, toStringCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/Locales.html b/docs/co/aikar/commands/Locales.html new file mode 100644 index 00000000..be5b54af --- /dev/null +++ b/docs/co/aikar/commands/Locales.html @@ -0,0 +1,845 @@ + + + + + + +| Modifier and Type | +Field and Description | +
|---|---|
static Locale |
+AFRIKAANS |
+
static Locale |
+ARABIC |
+
static Locale |
+BULGARIAN |
+
static Locale |
+CHINESE |
+
static Locale |
+CZECH |
+
static Locale |
+DANISH |
+
static Locale |
+DUTCH |
+
static Locale |
+ENGLISH |
+
static Locale |
+FINNISH |
+
static Locale |
+FRENCH |
+
static Locale |
+GERMAN |
+
static Locale |
+GREEK |
+
static Locale |
+HEBREW |
+
static Locale |
+HINDI |
+
static Locale |
+ITALIAN |
+
static Locale |
+JAPANESE |
+
static Locale |
+KOREAN |
+
static Locale |
+LATIN |
+
static Locale |
+POLISH |
+
static Locale |
+PORTUGUESE |
+
static Locale |
+ROMANIAN |
+
static Locale |
+RUSSIAN |
+
static Locale |
+SIMPLIFIED_CHINESE |
+
static Locale |
+SPANISH |
+
static Locale |
+SWEDISH |
+
static Locale |
+THAI |
+
static Locale |
+TRADITIONAL_CHINESE |
+
static Locale |
+TURKISH |
+
static Locale |
+UKRANIAN |
+
static Locale |
+VIETNAMESE |
+
static Locale |
+WELSH |
+
| Constructor and Description | +
|---|
Locales(CommandManager manager) |
+
| Modifier and Type | +Method and Description | +
|---|---|
String |
+addMessage(Locale locale,
+ co.aikar.locales.MessageKey key,
+ String message) |
+
void |
+addMessageBundle(String bundleName,
+ Locale locale) |
+
void |
+addMessageBundles(String... bundleNames) |
+
void |
+addMessages(Locale locale,
+ @NotNull Map<co.aikar.locales.MessageKey,String> messages) |
+
void |
+addMessageStrings(Locale locale,
+ @NotNull Map<String,String> messages) |
+
Locale |
+getDefaultLocale() |
+
String |
+getMessage(CommandIssuer issuer,
+ co.aikar.locales.MessageKeyProvider key) |
+
void |
+loadLanguages() |
+
void |
+loadMissingBundles()
+Looks for all previously loaded bundles, and if any new Supported Languages have been added, load them.
+ |
+
String |
+replaceI18NStrings(String message) |
+
Locale |
+setDefaultLocale(Locale locale) |
+
public static final Locale SIMPLIFIED_CHINESE+
public static final Locale TRADITIONAL_CHINESE+
public static final Locale PORTUGUESE+
public static final Locale VIETNAMESE+
public Locales(CommandManager manager)+
public void loadLanguages()+
public Locale getDefaultLocale()+
public Locale setDefaultLocale(Locale locale)+
public void loadMissingBundles()+
public void addMessageBundles(String... bundleNames)+
public void addMessageBundle(String bundleName, + Locale locale)+
public void addMessageStrings(Locale locale, + @NotNull + @NotNull Map<String,String> messages)+
public void addMessages(Locale locale, + @NotNull + @NotNull Map<co.aikar.locales.MessageKey,String> messages)+
public String addMessage(Locale locale, + co.aikar.locales.MessageKey key, + String message)+
public String getMessage(CommandIssuer issuer, + co.aikar.locales.MessageKeyProvider key)+
public String replaceI18NStrings(String message)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/MessageFormatter.html b/docs/co/aikar/commands/MessageFormatter.html new file mode 100644 index 00000000..88090c4b --- /dev/null +++ b/docs/co/aikar/commands/MessageFormatter.html @@ -0,0 +1,338 @@ + + + + + + +C - The platform specific color objectpublic abstract class MessageFormatter<C> +extends Object+
| Constructor and Description | +
|---|
MessageFormatter(C... colors) |
+
| Modifier and Type | +Method and Description | +
|---|---|
String |
+format(int index,
+ String message) |
+
String |
+format(String message) |
+
C |
+getColor(int index) |
+
C |
+getDefaultColor() |
+
C |
+setColor(int index,
+ C color) |
+
@SafeVarargs +public MessageFormatter(C... colors)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/MessageKeys.html b/docs/co/aikar/commands/MessageKeys.html new file mode 100644 index 00000000..c365b5e5 --- /dev/null +++ b/docs/co/aikar/commands/MessageKeys.html @@ -0,0 +1,517 @@ + + + + + + +public enum MessageKeys +extends Enum<MessageKeys> +implements co.aikar.locales.MessageKeyProvider+
| Enum Constant and Description | +
|---|
COULD_NOT_FIND_PLAYER |
+
ERROR_GENERIC_LOGGED |
+
ERROR_PERFORMING_COMMAND |
+
ERROR_PREFIX |
+
HELP_FORMAT |
+
INFO_MESSAGE |
+
INVALID_SYNTAX |
+
MUST_BE_A_NUMBER |
+
MUST_BE_MAX_LENGTH |
+
MUST_BE_MIN_LENGTH |
+
NO_COMMAND_MATCHED_SEARCH |
+
NOT_ALLOWED_ON_CONSOLE |
+
PERMISSION_DENIED |
+
PLEASE_SPECIFY_ONE_OF |
+
UNKNOWN_COMMAND |
+
| Modifier and Type | +Method and Description | +
|---|---|
co.aikar.locales.MessageKey |
+getMessageKey() |
+
static MessageKeys |
+valueOf(String name)
+Returns the enum constant of this type with the specified name.
+ |
+
static MessageKeys[] |
+values()
+Returns an array containing the constants of this enum type, in
+the order they are declared.
+ |
+
public static final MessageKeys PERMISSION_DENIED+
public static final MessageKeys ERROR_GENERIC_LOGGED+
public static final MessageKeys UNKNOWN_COMMAND+
public static final MessageKeys INVALID_SYNTAX+
public static final MessageKeys ERROR_PREFIX+
public static final MessageKeys ERROR_PERFORMING_COMMAND+
public static final MessageKeys INFO_MESSAGE+
public static final MessageKeys PLEASE_SPECIFY_ONE_OF+
public static final MessageKeys MUST_BE_A_NUMBER+
public static final MessageKeys MUST_BE_MIN_LENGTH+
public static final MessageKeys MUST_BE_MAX_LENGTH+
public static final MessageKeys NOT_ALLOWED_ON_CONSOLE+
public static final MessageKeys COULD_NOT_FIND_PLAYER+
public static final MessageKeys HELP_FORMAT+
public static final MessageKeys NO_COMMAND_MATCHED_SEARCH+
public static MessageKeys[] values()+
+for (MessageKeys c : MessageKeys.values()) + System.out.println(c); +
public static MessageKeys valueOf(String name)+
name - the name of the enum constant to be returned.IllegalArgumentException - if this enum type has no constant with the specified nameNullPointerException - if the argument is nullpublic co.aikar.locales.MessageKey getMessageKey()+
getMessageKey in interface co.aikar.locales.MessageKeyProviderCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/MessageType.html b/docs/co/aikar/commands/MessageType.html new file mode 100644 index 00000000..f483495c --- /dev/null +++ b/docs/co/aikar/commands/MessageType.html @@ -0,0 +1,368 @@ + + + + + + +public class MessageType +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
static MessageType |
+ERROR |
+
static MessageType |
+HELP |
+
static MessageType |
+INFO |
+
static MessageType |
+SYNTAX |
+
| Constructor and Description | +
|---|
MessageType() |
+
| Modifier and Type | +Method and Description | +
|---|---|
boolean |
+equals(Object o) |
+
int |
+hashCode() |
+
public static final MessageType INFO+
public static final MessageType SYNTAX+
public static final MessageType ERROR+
public static final MessageType HELP+
public MessageType()+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/RegisteredCommand.html b/docs/co/aikar/commands/RegisteredCommand.html new file mode 100644 index 00000000..1b44eebf --- /dev/null +++ b/docs/co/aikar/commands/RegisteredCommand.html @@ -0,0 +1,329 @@ + + + + + + +public class RegisteredCommand<R extends CommandExecutionContext<? extends CommandExecutionContext,? extends CommandIssuer>> +extends Object+
| Modifier and Type | +Method and Description | +
|---|---|
void |
+addSubcommand(String cmd) |
+
void |
+addSubcommands(Collection<String> cmd) |
+
String |
+getCommand() |
+
String |
+getPermission() |
+
String |
+getPrefSubCommand() |
+
String |
+getSyntaxText() |
+
void |
+postCommand() |
+
void |
+preCommand() |
+
public void preCommand()+
public void postCommand()+
public String getPermission()+
public String getPrefSubCommand()+
public String getSyntaxText()+
public String getCommand()+
public void addSubcommand(String cmd)+
public void addSubcommands(Collection<String> cmd)+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/UnstableAPI.html b/docs/co/aikar/commands/UnstableAPI.html new file mode 100644 index 00000000..9fd197ca --- /dev/null +++ b/docs/co/aikar/commands/UnstableAPI.html @@ -0,0 +1,167 @@ + + + + + + +@Retention(value=RUNTIME) + @Deprecated +public @interface UnstableAPI+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/CommandAlias.html b/docs/co/aikar/commands/annotation/CommandAlias.html new file mode 100644 index 00000000..645cf12a --- /dev/null +++ b/docs/co/aikar/commands/annotation/CommandAlias.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface CommandAlias+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/CommandCompletion.html b/docs/co/aikar/commands/annotation/CommandCompletion.html new file mode 100644 index 00000000..bc204418 --- /dev/null +++ b/docs/co/aikar/commands/annotation/CommandCompletion.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface CommandCompletion+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/CommandPermission.html b/docs/co/aikar/commands/annotation/CommandPermission.html new file mode 100644 index 00000000..88212d57 --- /dev/null +++ b/docs/co/aikar/commands/annotation/CommandPermission.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface CommandPermission+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Default.html b/docs/co/aikar/commands/annotation/Default.html new file mode 100644 index 00000000..aa9887fa --- /dev/null +++ b/docs/co/aikar/commands/annotation/Default.html @@ -0,0 +1,217 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Default+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Description.html b/docs/co/aikar/commands/annotation/Description.html new file mode 100644 index 00000000..07ed8fea --- /dev/null +++ b/docs/co/aikar/commands/annotation/Description.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Description+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Flags.html b/docs/co/aikar/commands/annotation/Flags.html new file mode 100644 index 00000000..42999638 --- /dev/null +++ b/docs/co/aikar/commands/annotation/Flags.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Flags+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/HelpCommand.html b/docs/co/aikar/commands/annotation/HelpCommand.html new file mode 100644 index 00000000..f5ea9ce4 --- /dev/null +++ b/docs/co/aikar/commands/annotation/HelpCommand.html @@ -0,0 +1,217 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface HelpCommand+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/HelpSearchTags.html b/docs/co/aikar/commands/annotation/HelpSearchTags.html new file mode 100644 index 00000000..c03b262e --- /dev/null +++ b/docs/co/aikar/commands/annotation/HelpSearchTags.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface HelpSearchTags+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Optional.html b/docs/co/aikar/commands/annotation/Optional.html new file mode 100644 index 00000000..dd7d1acb --- /dev/null +++ b/docs/co/aikar/commands/annotation/Optional.html @@ -0,0 +1,165 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Optional+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/PreCommand.html b/docs/co/aikar/commands/annotation/PreCommand.html new file mode 100644 index 00000000..93c86ee5 --- /dev/null +++ b/docs/co/aikar/commands/annotation/PreCommand.html @@ -0,0 +1,165 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface PreCommand+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Single.html b/docs/co/aikar/commands/annotation/Single.html new file mode 100644 index 00000000..b8dce39c --- /dev/null +++ b/docs/co/aikar/commands/annotation/Single.html @@ -0,0 +1,166 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Single+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Split.html b/docs/co/aikar/commands/annotation/Split.html new file mode 100644 index 00000000..8938ff64 --- /dev/null +++ b/docs/co/aikar/commands/annotation/Split.html @@ -0,0 +1,217 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Split+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Subcommand.html b/docs/co/aikar/commands/annotation/Subcommand.html new file mode 100644 index 00000000..6d2fb5f3 --- /dev/null +++ b/docs/co/aikar/commands/annotation/Subcommand.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Subcommand+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Syntax.html b/docs/co/aikar/commands/annotation/Syntax.html new file mode 100644 index 00000000..91988759 --- /dev/null +++ b/docs/co/aikar/commands/annotation/Syntax.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Syntax+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/UnknownHandler.html b/docs/co/aikar/commands/annotation/UnknownHandler.html new file mode 100644 index 00000000..1cf6f28b --- /dev/null +++ b/docs/co/aikar/commands/annotation/UnknownHandler.html @@ -0,0 +1,165 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface UnknownHandler+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/Values.html b/docs/co/aikar/commands/annotation/Values.html new file mode 100644 index 00000000..5c645dbf --- /dev/null +++ b/docs/co/aikar/commands/annotation/Values.html @@ -0,0 +1,213 @@ + + + + + + +@Retention(value=RUNTIME) +public @interface Values+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/CommandAlias.html b/docs/co/aikar/commands/annotation/class-use/CommandAlias.html new file mode 100644 index 00000000..530c61d1 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/CommandAlias.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/CommandCompletion.html b/docs/co/aikar/commands/annotation/class-use/CommandCompletion.html new file mode 100644 index 00000000..0f590472 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/CommandCompletion.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/CommandPermission.html b/docs/co/aikar/commands/annotation/class-use/CommandPermission.html new file mode 100644 index 00000000..83f55d8d --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/CommandPermission.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Default.html b/docs/co/aikar/commands/annotation/class-use/Default.html new file mode 100644 index 00000000..881cfb36 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Default.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Description.html b/docs/co/aikar/commands/annotation/class-use/Description.html new file mode 100644 index 00000000..e384798d --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Description.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Flags.html b/docs/co/aikar/commands/annotation/class-use/Flags.html new file mode 100644 index 00000000..b2c664d5 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Flags.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/HelpCommand.html b/docs/co/aikar/commands/annotation/class-use/HelpCommand.html new file mode 100644 index 00000000..a111d8c8 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/HelpCommand.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/HelpSearchTags.html b/docs/co/aikar/commands/annotation/class-use/HelpSearchTags.html new file mode 100644 index 00000000..4dd75899 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/HelpSearchTags.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Optional.html b/docs/co/aikar/commands/annotation/class-use/Optional.html new file mode 100644 index 00000000..f9e6ce52 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Optional.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/PreCommand.html b/docs/co/aikar/commands/annotation/class-use/PreCommand.html new file mode 100644 index 00000000..8ebfc261 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/PreCommand.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Single.html b/docs/co/aikar/commands/annotation/class-use/Single.html new file mode 100644 index 00000000..1663b005 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Single.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Split.html b/docs/co/aikar/commands/annotation/class-use/Split.html new file mode 100644 index 00000000..56b11a20 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Split.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Subcommand.html b/docs/co/aikar/commands/annotation/class-use/Subcommand.html new file mode 100644 index 00000000..3f5faa83 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Subcommand.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Syntax.html b/docs/co/aikar/commands/annotation/class-use/Syntax.html new file mode 100644 index 00000000..1b39a578 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Syntax.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/UnknownHandler.html b/docs/co/aikar/commands/annotation/class-use/UnknownHandler.html new file mode 100644 index 00000000..7b2b9dc4 --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/UnknownHandler.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/class-use/Values.html b/docs/co/aikar/commands/annotation/class-use/Values.html new file mode 100644 index 00000000..c4f308ab --- /dev/null +++ b/docs/co/aikar/commands/annotation/class-use/Values.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/package-frame.html b/docs/co/aikar/commands/annotation/package-frame.html new file mode 100644 index 00000000..10f61507 --- /dev/null +++ b/docs/co/aikar/commands/annotation/package-frame.html @@ -0,0 +1,35 @@ + + + + + + +| Annotation Type | +Description | +
|---|---|
| CommandAlias | ++ |
| CommandCompletion | ++ |
| CommandPermission | ++ |
| Default | ++ |
| Description | ++ |
| Flags | ++ |
| HelpCommand | ++ |
| HelpSearchTags | ++ |
| Optional | ++ |
| PreCommand | ++ |
| Single | +
+ Don't join remaining arguments
+ |
+
| Split | ++ |
| Subcommand | ++ |
| Syntax | ++ |
| UnknownHandler | ++ |
| Values | ++ |
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/package-tree.html b/docs/co/aikar/commands/annotation/package-tree.html new file mode 100644 index 00000000..ff09441a --- /dev/null +++ b/docs/co/aikar/commands/annotation/package-tree.html @@ -0,0 +1,149 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/annotation/package-use.html b/docs/co/aikar/commands/annotation/package-use.html new file mode 100644 index 00000000..4fc2b8aa --- /dev/null +++ b/docs/co/aikar/commands/annotation/package-use.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/ApacheCommonsExceptionUtil.Nestable.html b/docs/co/aikar/commands/apachecommonslang/ApacheCommonsExceptionUtil.Nestable.html new file mode 100644 index 00000000..107a20fe --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/ApacheCommonsExceptionUtil.Nestable.html @@ -0,0 +1,542 @@ + + + + + + +public static interface ApacheCommonsExceptionUtil.Nestable+
Throwable
+ extensions which would like to be able to nest root exceptions
+ inside themselves.| Modifier and Type | +Method and Description | +
|---|---|
Throwable |
+getCause()
+Returns the reference to the exception or error that caused the
+ exception implementing the
+Nestable to be thrown. |
+
String |
+getMessage()
+Returns the error message of this and any nested
+
+Throwable. |
+
String |
+getMessage(int index)
+Returns the error message of the
+Throwable in the chain
+ of Throwables at the specified index, numbered from 0. |
+
String[] |
+getMessages()
+Returns the error message of this and any nested
+Throwables
+ in an array of Strings, one element for each message. |
+
Throwable |
+getThrowable(int index)
+Returns the
+Throwable in the chain of
+ Throwables at the specified index, numbered from 0. |
+
int |
+getThrowableCount()
+Returns the number of nested
+Throwables represented by
+ this Nestable, including this Nestable. |
+
Throwable[] |
+getThrowables()
+Returns this
+Nestable and any nested Throwables
+ in an array of Throwables, one element for each
+ Throwable. |
+
int |
+indexOfThrowable(Class type)
+Returns the index, numbered from 0, of the first occurrence of the
+ specified type, or a subclass, in the chain of
+Throwables. |
+
int |
+indexOfThrowable(Class type,
+ int fromIndex)
+Returns the index, numbered from 0, of the first
+Throwable
+ that matches the specified type, or a subclass, in the chain of Throwables
+ with an index greater than or equal to the specified index. |
+
void |
+printPartialStackTrace(PrintWriter out)
+Prints the stack trace for this exception only--root cause not
+ included--using the provided writer.
+ |
+
void |
+printStackTrace(PrintStream out)
+Prints the stack trace of this exception to the specified print
+ stream.
+ |
+
void |
+printStackTrace(PrintWriter out)
+Prints the stack trace of this exception to the specified print
+ writer.
+ |
+
Throwable getCause()+
Nestable to be thrown.String getMessage()+
Throwable.String getMessage(int index)+
Throwable in the chain
+ of Throwables at the specified index, numbered from 0.index - the index of the Throwable in the chain of
+ ThrowablesThrowable at the
+ specified index in the chain does not contain a messageIndexOutOfBoundsException - if the index argument is
+ negative or not less than the count of Throwables in the
+ chainString[] getMessages()+
Throwables
+ in an array of Strings, one element for each message. Any
+ Throwable not containing a message is represented in the
+ array by a null. This has the effect of cause the length of the returned
+ array to be equal to the result of the getThrowableCount()
+ operation.Throwable getThrowable(int index)+
Throwable in the chain of
+ Throwables at the specified index, numbered from 0.index - the index, numbered from 0, of the Throwable in
+ the chain of ThrowablesThrowableIndexOutOfBoundsException - if the index argument is
+ negative or not less than the count of Throwables in the
+ chainint getThrowableCount()+
Throwables represented by
+ this Nestable, including this Nestable.Throwable[] getThrowables()+
Nestable and any nested Throwables
+ in an array of Throwables, one element for each
+ Throwable.Throwablesint indexOfThrowable(Class type)+
Throwables.
+ The method returns -1 if the specified type is not found in the chain.
+
+ NOTE: From v2.1, we have clarified the Nestable interface
+ such that this method matches subclasses.
+ If you want to NOT match subclasses, please use
+ (which is avaiable in all versions of lang).
type - the type to find, subclasses match, null returns -1int indexOfThrowable(Class type, + int fromIndex)+
Throwable
+ that matches the specified type, or a subclass, in the chain of Throwables
+ with an index greater than or equal to the specified index.
+ The method returns -1 if the specified type is not found in the chain.
+
+ NOTE: From v2.1, we have clarified the Nestable interface
+ such that this method matches subclasses.
+ If you want to NOT match subclasses, please use
+ (which is avaiable in all versions of lang).
type - the type to find, subclasses match, null returns -1fromIndex - the index, numbered from 0, of the starting position in
+ the chain to be searchedIndexOutOfBoundsException - if the fromIndex argument
+ is negative or not less than the count of Throwables in the
+ chainvoid printStackTrace(PrintWriter out)+
out - PrintWriter to use for output.void printStackTrace(PrintStream out)+
out - PrintStream to use for output.void printPartialStackTrace(PrintWriter out)+
super.printStackTrace(out); in most cases.out - The writer to use.Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/ApacheCommonsExceptionUtil.html b/docs/co/aikar/commands/apachecommonslang/ApacheCommonsExceptionUtil.html new file mode 100644 index 00000000..4aa9564a --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/ApacheCommonsExceptionUtil.html @@ -0,0 +1,933 @@ + + + + + + +public class ApacheCommonsExceptionUtil +extends Object+
Provides utilities for manipulating and examining
+ Throwable objects.
| Modifier and Type | +Class and Description | +
|---|---|
static interface |
+ApacheCommonsExceptionUtil.Nestable
+An interface to be implemented by
+Throwable
+ extensions which would like to be able to nest root exceptions
+ inside themselves. |
+
| Constructor and Description | +
|---|
ApacheCommonsExceptionUtil()
+
+ Public constructor allows an instance of
+ExceptionUtils to be created, although that is not
+ normally necessary. |
+
| Modifier and Type | +Method and Description | +
|---|---|
static void |
+addCauseMethodName(String methodName)
+Adds to the list of method names used in the search for
+Throwable
+ objects. |
+
static Throwable |
+getCause(Throwable throwable)
+Introspects the
+Throwable to obtain the cause. |
+
static Throwable |
+getCause(Throwable throwable,
+ String[] methodNames)
+Introspects the
+Throwable to obtain the cause. |
+
static String |
+getFullStackTrace(Throwable throwable)
+A way to get the entire nested stack-trace of an throwable.
+ |
+
static Throwable |
+getRootCause(Throwable throwable)
+Introspects the
+Throwable to obtain the root cause. |
+
static String |
+getStackTrace(Throwable throwable)
+Gets the stack trace from a Throwable as a String.
+ |
+
static int |
+getThrowableCount(Throwable throwable)
+Counts the number of
+Throwable objects in the
+ exception chain. |
+
static List |
+getThrowableList(Throwable throwable)
+Returns the list of
+Throwable objects in the
+ exception chain. |
+
static Throwable[] |
+getThrowables(Throwable throwable)
+Returns the list of
+Throwable objects in the
+ exception chain. |
+
static int |
+indexOfThrowable(Throwable throwable,
+ Class clazz)
+Returns the (zero based) index of the first
+Throwable
+ that matches the specified class (exactly) in the exception chain. |
+
static int |
+indexOfThrowable(Throwable throwable,
+ Class clazz,
+ int fromIndex)
+Returns the (zero based) index of the first
+Throwable
+ that matches the specified type in the exception chain from
+ a specified index. |
+
static int |
+indexOfType(Throwable throwable,
+ Class type)
+Returns the (zero based) index of the first
+Throwable
+ that matches the specified class or subclass in the exception chain. |
+
static int |
+indexOfType(Throwable throwable,
+ Class type,
+ int fromIndex)
+Returns the (zero based) index of the first
+Throwable
+ that matches the specified type in the exception chain from
+ a specified index. |
+
static boolean |
+isCauseMethodName(String methodName)
+Tests if the list of method names used in the search for
+Throwable
+ objects include the given name. |
+
static boolean |
+isNestedThrowable(Throwable throwable)
+Checks whether this
+Throwable class can store a cause. |
+
static boolean |
+isThrowableNested()
+Checks if the Throwable class has a
+getCause method. |
+
static void |
+removeCauseMethodName(String methodName)
+Removes from the list of method names used in the search for
+Throwable
+ objects. |
+
static void |
+removeCommonFrames(List causeFrames,
+ List wrapperFrames)
+Removes common frames from the cause trace given the two stack traces.
+ |
+
static boolean |
+setCause(Throwable target,
+ Throwable cause)
+Sets the cause of a
+Throwable using introspection, allowing
+ source code compatibility between pre-1.4 and post-1.4 Java releases. |
+
public ApacheCommonsExceptionUtil()+
+ Public constructor allows an instance of ExceptionUtils to be created, although that is not
+ normally necessary.
+
public static void addCauseMethodName(String methodName)+
Adds to the list of method names used in the search for Throwable
+ objects.
methodName - the methodName to add to the list, null
+ and empty strings are ignoredpublic static void removeCauseMethodName(String methodName)+
Removes from the list of method names used in the search for Throwable
+ objects.
methodName - the methodName to remove from the list, null
+ and empty strings are ignoredpublic static boolean setCause(Throwable target, + Throwable cause)+
Sets the cause of a Throwable using introspection, allowing
+ source code compatibility between pre-1.4 and post-1.4 Java releases.
The typical use of this method is inside a constructor as in + the following example:
+ +
+ import org.apache.commons.lang.exception.ExceptionUtils;
+
+ public class MyException extends Exception {
+
+ public MyException(String msg) {
+ super(msg);
+ }
+
+ public MyException(String msg, Throwable cause) {
+ super(msg);
+ ExceptionUtils.setCause(this, cause);
+ }
+ }
+ target - the target Throwablecause - the Throwable to set in the targettrue if the target has been modifiedpublic static boolean isCauseMethodName(String methodName)+
Tests if the list of method names used in the search for Throwable
+ objects include the given name.
methodName - the methodName to search in the list.Throwable
+ objects include the given name.public static Throwable getCause(Throwable throwable)+
Introspects the Throwable to obtain the cause.
The method searches for methods with specific names that return a
+ Throwable object. This will pick up most wrapping exceptions,
+ including those from JDK 1.4, and
+ The method names can be added to using addCauseMethodName(String).
The default list searched for are:
+getCause()getNextException()getTargetException()getException()getSourceException()getRootCause()getCausedByException()getNested()In the absence of any such method, the object is inspected for a
+ detail field assignable to a Throwable.
If none of the above is found, returns null.
throwable - the throwable to introspect for a cause, may be nullThrowable,
+ null if none found or null throwable inputpublic static Throwable getCause(Throwable throwable, + String[] methodNames)+
Introspects the Throwable to obtain the cause.
A null set of method names means use the default set.
+ A null in the set of method names will be ignored.
throwable - the throwable to introspect for a cause, may be nullmethodNames - the method names, null treated as default setThrowable,
+ null if none found or null throwable inputpublic static Throwable getRootCause(Throwable throwable)+
Introspects the Throwable to obtain the root cause.
This method walks through the exception chain to the last element,
+ "root" of the tree, using getCause(Throwable), and
+ returns that exception.
From version 2.2, this method handles recursive cause structures + that might otherwise cause infinite loops. If the throwable parameter + has a cause of itself, then null will be returned. If the throwable + parameter cause chain loops, the last element in the chain before the + loop is returned.
throwable - the throwable to get the root cause for, may be nullThrowable,
+ null if none found or null throwable inputpublic static boolean isThrowableNested()+
Checks if the Throwable class has a getCause method.
This is true for JDK 1.4 and above.
public static boolean isNestedThrowable(Throwable throwable)+
Checks whether this Throwable class can store a cause.
This method does not check whether it actually does store a cause.
throwable - the Throwable to examine, may be nulltrue if nested otherwise falsepublic static int getThrowableCount(Throwable throwable)+
Counts the number of Throwable objects in the
+ exception chain.
A throwable without cause will return 1.
+ A throwable with one cause will return 2 and so on.
+ A null throwable will return 0.
From version 2.2, this method handles recursive cause structures + that might otherwise cause infinite loops. The cause chain is + processed until the end is reached, or until the next item in the + chain is already in the result set.
throwable - the throwable to inspect, may be nullpublic static Throwable[] getThrowables(Throwable throwable)+
Returns the list of Throwable objects in the
+ exception chain.
A throwable without cause will return an array containing
+ one element - the input throwable.
+ A throwable with one cause will return an array containing
+ two elements. - the input throwable and the cause throwable.
+ A null throwable will return an array of size zero.
From version 2.2, this method handles recursive cause structures + that might otherwise cause infinite loops. The cause chain is + processed until the end is reached, or until the next item in the + chain is already in the result set.
throwable - the throwable to inspect, may be nullgetThrowableList(Throwable)public static List getThrowableList(Throwable throwable)+
Returns the list of Throwable objects in the
+ exception chain.
A throwable without cause will return a list containing
+ one element - the input throwable.
+ A throwable with one cause will return a list containing
+ two elements. - the input throwable and the cause throwable.
+ A null throwable will return a list of size zero.
This method handles recursive cause structures that might + otherwise cause infinite loops. The cause chain is processed until + the end is reached, or until the next item in the chain is already + in the result set.
throwable - the throwable to inspect, may be nullpublic static int indexOfThrowable(Throwable throwable, + Class clazz)+
Returns the (zero based) index of the first Throwable
+ that matches the specified class (exactly) in the exception chain.
+ Subclasses of the specified class do not match - see
+ indexOfType(Throwable, Class) for the opposite.
A null throwable returns -1.
+ A null type returns -1.
+ No match in the chain returns -1.
throwable - the throwable to inspect, may be nullclazz - the class to search for, subclasses do not match, null returns -1public static int indexOfThrowable(Throwable throwable, + Class clazz, + int fromIndex)+
Returns the (zero based) index of the first Throwable
+ that matches the specified type in the exception chain from
+ a specified index.
+ Subclasses of the specified class do not match - see
+ indexOfType(Throwable, Class, int) for the opposite.
A null throwable returns -1.
+ A null type returns -1.
+ No match in the chain returns -1.
+ A negative start index is treated as zero.
+ A start index greater than the number of throwables returns -1.
throwable - the throwable to inspect, may be nullclazz - the class to search for, subclasses do not match, null returns -1fromIndex - the (zero based) index of the starting position,
+ negative treated as zero, larger than chain size returns -1public static int indexOfType(Throwable throwable, + Class type)+
Returns the (zero based) index of the first Throwable
+ that matches the specified class or subclass in the exception chain.
+ Subclasses of the specified class do match - see
+ indexOfThrowable(Throwable, Class) for the opposite.
A null throwable returns -1.
+ A null type returns -1.
+ No match in the chain returns -1.
throwable - the throwable to inspect, may be nulltype - the type to search for, subclasses match, null returns -1public static int indexOfType(Throwable throwable, + Class type, + int fromIndex)+
Returns the (zero based) index of the first Throwable
+ that matches the specified type in the exception chain from
+ a specified index.
+ Subclasses of the specified class do match - see
+ indexOfThrowable(Throwable, Class) for the opposite.
A null throwable returns -1.
+ A null type returns -1.
+ No match in the chain returns -1.
+ A negative start index is treated as zero.
+ A start index greater than the number of throwables returns -1.
throwable - the throwable to inspect, may be nulltype - the type to search for, subclasses match, null returns -1fromIndex - the (zero based) index of the starting position,
+ negative treated as zero, larger than chain size returns -1public static void removeCommonFrames(List causeFrames, + List wrapperFrames)+
Removes common frames from the cause trace given the two stack traces.
causeFrames - stack trace of a cause throwablewrapperFrames - stack trace of a wrapper throwableIllegalArgumentException - if either argument is nullpublic static String getFullStackTrace(Throwable throwable)+
A way to get the entire nested stack-trace of an throwable.
+ +The result of this method is highly dependent on the JDK version + and whether the exceptions override printStackTrace or not.
throwable - the Throwable to be examinedpublic static String getStackTrace(Throwable throwable)+
Gets the stack trace from a Throwable as a String.
+ +The result of this method vary by JDK version as this method
+ uses Throwable.printStackTrace(java.io.PrintWriter).
+ On JDK1.3 and earlier, the cause exception will not be shown
+ unless the specified throwable alters printStackTrace.
throwable - the Throwable to be examinedprintStackTrace(PrintWriter) methodCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/ApacheCommonsLangUtil.html b/docs/co/aikar/commands/apachecommonslang/ApacheCommonsLangUtil.html new file mode 100644 index 00000000..a8c471bc --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/ApacheCommonsLangUtil.html @@ -0,0 +1,1822 @@ + + + + + + +public class ApacheCommonsLangUtil +extends Object+
| Modifier and Type | +Field and Description | +
|---|---|
static String |
+EMPTY
+The empty String
+"". |
+
static int |
+INDEX_NOT_FOUND
+The index value when an element is not found in a list or array:
+-1. |
+
| Constructor and Description | +
|---|
ApacheCommonsLangUtil() |
+
| Modifier and Type | +Method and Description | +
|---|---|
static <T> T[] |
+addAll(T[] array1,
+ T... array2)
+Adds all the elements of the given arrays into a new array.
+ |
+
static String |
+capitalize(String str)
+Capitalizes all the whitespace separated words in a String.
+ |
+
static String |
+capitalize(String str,
+ char... delimiters)
+Capitalizes all the delimiter separated words in a String.
+ |
+
static String |
+capitalizeFully(String str)
+Converts all the whitespace separated words in a String into capitalized words,
+ that is each word is made up of a titlecase character and then a series of
+ lowercase characters.
+ |
+
static String |
+capitalizeFully(String str,
+ char... delimiters)
+Converts all the delimiter separated words in a String into capitalized words,
+ that is each word is made up of a titlecase character and then a series of
+ lowercase characters.
+ |
+
static <T> T[] |
+clone(T[] array)
+Shallow clones an array returning a typecast result and handling
+
+null. |
+
static int |
+indexOf(Object[] array,
+ Object objectToFind)
+Finds the index of the given object in the array.
+ |
+
static int |
+indexOf(Object[] array,
+ Object objectToFind,
+ int startIndex)
+Finds the index of the given object in the array starting at the given index.
+ |
+
static boolean |
+isDelimiter(char ch,
+ char[] delimiters)
+Is the character a delimiter.
+ |
+
static boolean |
+isNumeric(CharSequence cs)
+Checks if the CharSequence contains only Unicode digits.
+ |
+
static String |
+join(byte[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(byte[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(char[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(char[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(double[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(double[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(float[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(float[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(int[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(int[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(Iterable<?> iterable,
+ char separator)
+Joins the elements of the provided
+Iterable into
+ a single String containing the provided elements. |
+
static String |
+join(Iterable<?> iterable,
+ String separator)
+Joins the elements of the provided
+Iterable into
+ a single String containing the provided elements. |
+
static String |
+join(Iterator<?> iterator,
+ char separator)
+Joins the elements of the provided
+Iterator into
+ a single String containing the provided elements. |
+
static String |
+join(Iterator<?> iterator,
+ String separator)
+Joins the elements of the provided
+Iterator into
+ a single String containing the provided elements. |
+
static String |
+join(long[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(long[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(Object[] array,
+ char separator)
+Joins the elements of the provided array into a single String
+ containing the provided list of elements.
+ |
+
static String |
+join(Object[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+Joins the elements of the provided array into a single String
+ containing the provided list of elements.
+ |
+
static String |
+join(Object[] array,
+ String separator)
+Joins the elements of the provided array into a single String
+ containing the provided list of elements.
+ |
+
static String |
+join(Object[] array,
+ String separator,
+ int startIndex,
+ int endIndex)
+Joins the elements of the provided array into a single String
+ containing the provided list of elements.
+ |
+
static String |
+join(short[] array,
+ char separator)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static String |
+join(short[] array,
+ char separator,
+ int startIndex,
+ int endIndex)
+
+ Joins the elements of the provided array into a single String containing the provided list of elements.
+ |
+
static <T> String |
+join(T... elements)
+Joins the elements of the provided array into a single String
+ containing the provided list of elements.
+ |
+
static boolean |
+startsWith(CharSequence str,
+ CharSequence prefix)
+Check if a CharSequence starts with a specified prefix.
+ |
+
static boolean |
+startsWithIgnoreCase(CharSequence str,
+ CharSequence prefix)
+Case insensitive check if a CharSequence starts with a specified prefix.
+ |
+
public static final String EMPTY+
"".public static final int INDEX_NOT_FOUND+
-1.
+ This value is returned by methods in this class and can also be used in comparisons with values returned by
+ various method from List.public ApacheCommonsLangUtil()+
public static <T> T[] clone(T[] array)+
Shallow clones an array returning a typecast result and handling
+ null.
+
+
The objects in the array are not cloned, thus there is no special + handling for multi-dimensional arrays. + +
This method returns null for a null input array.
T - the component type of the arrayarray - the array to shallow clone, may be nullnull if null inputpublic static <T> T[] addAll(T[] array1, + T... array2)+
Adds all the elements of the given arrays into a new array. +
The new array contains all of the element of array1 followed
+ by all of the elements array2. When an array is returned, it is always
+ a new array.
+
+
+ ArrayUtils.addAll(null, null) = null + ArrayUtils.addAll(array1, null) = cloned copy of array1 + ArrayUtils.addAll(null, array2) = cloned copy of array2 + ArrayUtils.addAll([], []) = [] + ArrayUtils.addAll([null], [null]) = [null, null] + ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"] +
T - the component type of the arrayarray1 - the first array whose elements are added to the new array, may be nullarray2 - the second array whose elements are added to the new array, may be nullnull if both arrays are null.
+ The type of the new array is the type of the first array,
+ unless the first array is null, in which case the type is the same as the second array.IllegalArgumentException - if the array types are incompatiblepublic static String capitalizeFully(String str)+
Converts all the whitespace separated words in a String into capitalized words, + that is each word is made up of a titlecase character and then a series of + lowercase characters.
+ +Whitespace is defined by Character.isWhitespace(char).
+ A null input String returns null.
+ Capitalization uses the Unicode title case, normally equivalent to
+ upper case.
+ WordUtils.capitalizeFully(null) = null
+ WordUtils.capitalizeFully("") = ""
+ WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
+ str - the String to capitalize, may be nullnull if null String inputpublic static String capitalizeFully(String str, + char... delimiters)+
Converts all the delimiter separated words in a String into capitalized words, + that is each word is made up of a titlecase character and then a series of + lowercase characters.
+ +The delimiters represent a set of characters understood to separate words. + The first string character and the first non-delimiter character after a + delimiter will be capitalized.
+ +A null input String returns null.
+ Capitalization uses the Unicode title case, normally equivalent to
+ upper case.
+ WordUtils.capitalizeFully(null, *) = null
+ WordUtils.capitalizeFully("", *) = ""
+ WordUtils.capitalizeFully(*, null) = *
+ WordUtils.capitalizeFully(*, new char[0]) = *
+ WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
+ str - the String to capitalize, may be nulldelimiters - set of characters to determine capitalization, null means whitespacenull if null String inputpublic static String capitalize(String str)+
Capitalizes all the whitespace separated words in a String.
+ Only the first character of each word is changed. To convert the
+ rest of each word to lowercase at the same time,
+ use capitalizeFully(String).
Whitespace is defined by Character.isWhitespace(char).
+ A null input String returns null.
+ Capitalization uses the Unicode title case, normally equivalent to
+ upper case.
+ WordUtils.capitalize(null) = null
+ WordUtils.capitalize("") = ""
+ WordUtils.capitalize("i am FINE") = "I Am FINE"
+ str - the String to capitalize, may be nullnull if null String inputcapitalizeFully(String)public static String capitalize(String str, + char... delimiters)+
Capitalizes all the delimiter separated words in a String.
+ Only the first character of each word is changed. To convert the
+ rest of each word to lowercase at the same time,
+ use capitalizeFully(String, char[]).
The delimiters represent a set of characters understood to separate words. + The first string character and the first non-delimiter character after a + delimiter will be capitalized.
+ +A null input String returns null.
+ Capitalization uses the Unicode title case, normally equivalent to
+ upper case.
+ WordUtils.capitalize(null, *) = null
+ WordUtils.capitalize("", *) = ""
+ WordUtils.capitalize(*, new char[0]) = *
+ WordUtils.capitalize("i am fine", null) = "I Am Fine"
+ WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
+ str - the String to capitalize, may be nulldelimiters - set of characters to determine capitalization, null means whitespacenull if null String inputcapitalizeFully(String)public static boolean isDelimiter(char ch, + char[] delimiters)+
ch - the character to checkdelimiters - the delimiters@SafeVarargs +public static <T> String join(T... elements)+
Joins the elements of the provided array into a single String + containing the provided list of elements.
+ +No separator is added to the joined String. + Null objects or empty strings within the array are represented by + empty strings.
+ ++ StringUtils.join(null) = null + StringUtils.join([]) = "" + StringUtils.join([null]) = "" + StringUtils.join(["a", "b", "c"]) = "abc" + StringUtils.join([null, "", "a"]) = "a" +
T - the specific type of values to join togetherelements - the values to join together, may be nullnull if null array inputpublic static String join(Object[] array, + char separator)+
Joins the elements of the provided array into a single String + containing the provided list of elements.
+ +No delimiter is added before or after the list. + Null objects or empty strings within the array are represented by + empty strings.
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join(["a", "b", "c"], ';') = "a;b;c" + StringUtils.join(["a", "b", "c"], null) = "abc" + StringUtils.join([null, "", "a"], ';') = ";;a" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(long[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(int[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(short[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(byte[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(char[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(float[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(double[] array, + char separator)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usenull if null array inputpublic static String join(Object[] array, + char separator, + int startIndex, + int endIndex)+
Joins the elements of the provided array into a single String + containing the provided list of elements.
+ +No delimiter is added before or after the list. + Null objects or empty strings within the array are represented by + empty strings.
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join(["a", "b", "c"], ';') = "a;b;c" + StringUtils.join(["a", "b", "c"], null) = "abc" + StringUtils.join([null, "", "a"], ';') = ";;a" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is
+ an error to pass in an end index past the end of the arrayendIndex - the index to stop joining from (exclusive). It is
+ an error to pass in an end index past the end of the arraynull if null array inputpublic static String join(long[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(int[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(byte[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(short[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(char[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(double[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(float[] array, + char separator, + int startIndex, + int endIndex)+
+ Joins the elements of the provided array into a single String containing the provided list of elements. +
+ ++ No delimiter is added before or after the list. Null objects or empty strings within the array are represented + by empty strings. +
+ ++ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join([1, 2, 3], ';') = "1;2;3" + StringUtils.join([1, 2, 3], null) = "123" +
array - the array of values to join together, may be nullseparator - the separator character to usestartIndex - the first index to start joining from. It is an error to pass in an end index past the end of the
+ arrayendIndex - the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ the arraynull if null array inputpublic static String join(Object[] array, + String separator)+
Joins the elements of the provided array into a single String + containing the provided list of elements.
+ +No delimiter is added before or after the list.
+ A null separator is the same as an empty String ("").
+ Null objects or empty strings within the array are represented by
+ empty strings.
+ StringUtils.join(null, *) = null + StringUtils.join([], *) = "" + StringUtils.join([null], *) = "" + StringUtils.join(["a", "b", "c"], "--") = "a--b--c" + StringUtils.join(["a", "b", "c"], null) = "abc" + StringUtils.join(["a", "b", "c"], "") = "abc" + StringUtils.join([null, "", "a"], ',') = ",,a" +
array - the array of values to join together, may be nullseparator - the separator character to use, null treated as ""null if null array inputpublic static String join(Object[] array, + String separator, + int startIndex, + int endIndex)+
Joins the elements of the provided array into a single String + containing the provided list of elements.
+ +No delimiter is added before or after the list.
+ A null separator is the same as an empty String ("").
+ Null objects or empty strings within the array are represented by
+ empty strings.
+ StringUtils.join(null, *, *, *) = null + StringUtils.join([], *, *, *) = "" + StringUtils.join([null], *, *, *) = "" + StringUtils.join(["a", "b", "c"], "--", 0, 3) = "a--b--c" + StringUtils.join(["a", "b", "c"], "--", 1, 3) = "b--c" + StringUtils.join(["a", "b", "c"], "--", 2, 3) = "c" + StringUtils.join(["a", "b", "c"], "--", 2, 2) = "" + StringUtils.join(["a", "b", "c"], null, 0, 3) = "abc" + StringUtils.join(["a", "b", "c"], "", 0, 3) = "abc" + StringUtils.join([null, "", "a"], ',', 0, 3) = ",,a" +
array - the array of values to join together, may be nullseparator - the separator character to use, null treated as ""startIndex - the first index to start joining from.endIndex - the index to stop joining from (exclusive).null if null array input; or the empty string
+ if endIndex - startIndex <= 0. The number of joined entries is given by
+ endIndex - startIndexArrayIndexOutOfBoundsException - ifestartIndex < 0 or startIndex >= array.length() or endIndex < 0 or endIndex > array.length()public static String join(Iterator<?> iterator, + char separator)+
Joins the elements of the provided Iterator into
+ a single String containing the provided elements.
No delimiter is added before or after the list. Null objects or empty + strings within the iteration are represented by empty strings.
+ +See the examples here: join(Object[],char).
iterator - the Iterator of values to join together, may be nullseparator - the separator character to usenull if null iterator inputpublic static String join(Iterator<?> iterator, + String separator)+
Joins the elements of the provided Iterator into
+ a single String containing the provided elements.
No delimiter is added before or after the list.
+ A null separator is the same as an empty String ("").
See the examples here: join(Object[],String).
iterator - the Iterator of values to join together, may be nullseparator - the separator character to use, null treated as ""null if null iterator inputpublic static String join(Iterable<?> iterable, + char separator)+
Joins the elements of the provided Iterable into
+ a single String containing the provided elements.
No delimiter is added before or after the list. Null objects or empty + strings within the iteration are represented by empty strings.
+ +See the examples here: join(Object[],char).
iterable - the Iterable providing the values to join together, may be nullseparator - the separator character to usenull if null iterator inputpublic static String join(Iterable<?> iterable, + String separator)+
Joins the elements of the provided Iterable into
+ a single String containing the provided elements.
No delimiter is added before or after the list.
+ A null separator is the same as an empty String ("").
See the examples here: join(Object[],String).
iterable - the Iterable providing the values to join together, may be nullseparator - the separator character to use, null treated as ""null if null iterator inputpublic static boolean isNumeric(CharSequence cs)+
Checks if the CharSequence contains only Unicode digits. + A decimal point is not a Unicode digit and returns false.
+ +null will return false.
+ An empty CharSequence (length()=0) will return false.
Note that the method does not allow for a leading sign, either positive or negative. + Also, if a String passes the numeric test, it may still generate a NumberFormatException + when parsed by Integer.parseInt or Long.parseLong, e.g. if the value is outside the range + for int or long respectively.
+ +
+ StringUtils.isNumeric(null) = false
+ StringUtils.isNumeric("") = false
+ StringUtils.isNumeric(" ") = false
+ StringUtils.isNumeric("123") = true
+ StringUtils.isNumeric("१२३") = true
+ StringUtils.isNumeric("12 3") = false
+ StringUtils.isNumeric("ab2c") = false
+ StringUtils.isNumeric("12-3") = false
+ StringUtils.isNumeric("12.3") = false
+ StringUtils.isNumeric("-123") = false
+ StringUtils.isNumeric("+123") = false
+ cs - the CharSequence to check, may be nulltrue if only contains digits, and is non-nullpublic static boolean startsWith(CharSequence str, + CharSequence prefix)+
Check if a CharSequence starts with a specified prefix.
+ +nulls are handled without exceptions. Two null
+ references are considered to be equal. The comparison is case sensitive.
+ StringUtils.startsWith(null, null) = true
+ StringUtils.startsWith(null, "abc") = false
+ StringUtils.startsWith("abcdef", null) = false
+ StringUtils.startsWith("abcdef", "abc") = true
+ StringUtils.startsWith("ABCDEF", "abc") = false
+ str - the CharSequence to check, may be nullprefix - the prefix to find, may be nulltrue if the CharSequence starts with the prefix, case sensitive, or
+ both nullString.startsWith(String)public static boolean startsWithIgnoreCase(CharSequence str, + CharSequence prefix)+
Case insensitive check if a CharSequence starts with a specified prefix.
+ +nulls are handled without exceptions. Two null
+ references are considered to be equal. The comparison is case insensitive.
+ StringUtils.startsWithIgnoreCase(null, null) = true
+ StringUtils.startsWithIgnoreCase(null, "abc") = false
+ StringUtils.startsWithIgnoreCase("abcdef", null) = false
+ StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
+ StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
+ str - the CharSequence to check, may be nullprefix - the prefix to find, may be nulltrue if the CharSequence starts with the prefix, case insensitive, or
+ both nullString.startsWith(String)public static int indexOf(Object[] array, + Object objectToFind)+
Finds the index of the given object in the array.
+ +This method returns INDEX_NOT_FOUND (-1) for a null input array.
array - the array to search through for the object, may be nullobjectToFind - the object to find, may be nullINDEX_NOT_FOUND (-1) if not found or null array inputpublic static int indexOf(Object[] array, + Object objectToFind, + int startIndex)+
Finds the index of the given object in the array starting at the given index.
+ +This method returns INDEX_NOT_FOUND (-1) for a null input array.
A negative startIndex is treated as zero. A startIndex larger than the array
+ length will return INDEX_NOT_FOUND (-1).
array - the array to search through for the object, may be nullobjectToFind - the object to find, may be nullstartIndex - the index to start searching atINDEX_NOT_FOUND (-1) if not found or null array inputCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsExceptionUtil.Nestable.html b/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsExceptionUtil.Nestable.html new file mode 100644 index 00000000..d4854424 --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsExceptionUtil.Nestable.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsExceptionUtil.html b/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsExceptionUtil.html new file mode 100644 index 00000000..385533d5 --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsExceptionUtil.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsLangUtil.html b/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsLangUtil.html new file mode 100644 index 00000000..ee14b4c5 --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/class-use/ApacheCommonsLangUtil.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/package-frame.html b/docs/co/aikar/commands/apachecommonslang/package-frame.html new file mode 100644 index 00000000..e2b47a1c --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/package-frame.html @@ -0,0 +1,25 @@ + + + + + + +| Interface | +Description | +
|---|---|
| ApacheCommonsExceptionUtil.Nestable | +
+ An interface to be implemented by
+Throwable
+ extensions which would like to be able to nest root exceptions
+ inside themselves. |
+
| Class | +Description | +
|---|---|
| ApacheCommonsExceptionUtil | +
+ Provides utilities for manipulating and examining
+
+Throwable objects. |
+
| ApacheCommonsLangUtil | +
+ Select methods copied from Apache Commons to avoid importing entire lib
+ No changes to logic
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/package-tree.html b/docs/co/aikar/commands/apachecommonslang/package-tree.html new file mode 100644 index 00000000..f98622d3 --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/package-tree.html @@ -0,0 +1,143 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/apachecommonslang/package-use.html b/docs/co/aikar/commands/apachecommonslang/package-use.html new file mode 100644 index 00000000..9e19be1f --- /dev/null +++ b/docs/co/aikar/commands/apachecommonslang/package-use.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/ACFUtil.html b/docs/co/aikar/commands/class-use/ACFUtil.html new file mode 100644 index 00000000..9ff6d4a9 --- /dev/null +++ b/docs/co/aikar/commands/class-use/ACFUtil.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/BaseCommand.html b/docs/co/aikar/commands/class-use/BaseCommand.html new file mode 100644 index 00000000..82ad2df2 --- /dev/null +++ b/docs/co/aikar/commands/class-use/BaseCommand.html @@ -0,0 +1,231 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Class and Description | +
|---|---|
class |
+ForwardingCommand |
+
| Modifier and Type | +Method and Description | +
|---|---|
BaseCommand |
+CommandOperationContext.getCommand() |
+
BaseCommand |
+BaseCommand.setExceptionHandler(ExceptionHandler exceptionHandler) |
+
| Modifier and Type | +Method and Description | +
|---|---|
CommandOperationContext |
+CommandManager.createCommandOperationContext(BaseCommand command,
+ CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
RegisteredCommand |
+CommandManager.createRegisteredCommand(BaseCommand command,
+ String cmdName,
+ Method method,
+ String prefSubCommand) |
+
boolean |
+ExceptionHandler.execute(BaseCommand command,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t)
+Called when an exception occurs while executing a command
++ If an exception handler properly handles an exception, the user will not be noticied by the + framework that something went unexceptected. |
+
protected boolean |
+CommandManager.handleUncaughtException(BaseCommand scope,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t) |
+
abstract void |
+CommandManager.registerCommand(BaseCommand command)
+Registers a command with ACF
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandCompletionContext.html b/docs/co/aikar/commands/class-use/CommandCompletionContext.html new file mode 100644 index 00000000..fd475f20 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandCompletionContext.html @@ -0,0 +1,186 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Class and Description | +
|---|---|
class |
+CommandCompletions<C extends CommandCompletionContext> |
+
static interface |
+CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext> |
+
| Modifier and Type | +Method and Description | +
|---|---|
abstract CommandCompletionContext |
+CommandManager.createCompletionContext(RegisteredCommand command,
+ CommandIssuer sender,
+ String input,
+ String config,
+ String[] args) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandCompletions.CommandCompletionHandler.html b/docs/co/aikar/commands/class-use/CommandCompletions.CommandCompletionHandler.html new file mode 100644 index 00000000..2890f337 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandCompletions.CommandCompletionHandler.html @@ -0,0 +1,180 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
CommandCompletions.CommandCompletionHandler |
+CommandCompletions.registerCompletion(String id,
+ CommandCompletions.CommandCompletionHandler<C> handler) |
+
| Modifier and Type | +Method and Description | +
|---|---|
CommandCompletions.CommandCompletionHandler |
+CommandCompletions.registerCompletion(String id,
+ CommandCompletions.CommandCompletionHandler<C> handler) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandCompletions.html b/docs/co/aikar/commands/class-use/CommandCompletions.html new file mode 100644 index 00000000..6857e70e --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandCompletions.html @@ -0,0 +1,167 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
abstract CommandCompletions<?> |
+CommandManager.getCommandCompletions()
+Gets the command completions manager
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandContexts.html b/docs/co/aikar/commands/class-use/CommandContexts.html new file mode 100644 index 00000000..1781929c --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandContexts.html @@ -0,0 +1,167 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
abstract CommandContexts<?> |
+CommandManager.getCommandContexts()
+Gets the command contexts manager
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandExecutionContext.html b/docs/co/aikar/commands/class-use/CommandExecutionContext.html new file mode 100644 index 00000000..db3ce306 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandExecutionContext.html @@ -0,0 +1,241 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Modifier and Type | +Class and Description | +
|---|---|
class |
+CommandContexts<R extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
class |
+CommandExecutionContext<T extends CommandExecutionContext,I extends CommandIssuer> |
+
class |
+RegisteredCommand<R extends CommandExecutionContext<? extends CommandExecutionContext,? extends CommandIssuer>> |
+
class |
+RegisteredCommand<R extends CommandExecutionContext<? extends CommandExecutionContext,? extends CommandIssuer>> |
+
| Modifier and Type | +Method and Description | +
|---|---|
abstract <R extends CommandExecutionContext> |
+CommandManager.createCommandContext(RegisteredCommand command,
+ Parameter parameter,
+ CommandIssuer sender,
+ List<String> args,
+ int i,
+ Map<String,Object> passedArgs) |
+
| Modifier and Type | +Interface and Description | +
|---|---|
interface |
+ContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
interface |
+IssuerAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
interface |
+IssuerOnlyContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+A context resolver that will never consume input, and only resolves using the context of the issuer of the command
+ |
+
interface |
+OptionalContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Context Resolver that can accept null input
+ |
+
interface |
+SenderAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Deprecated.
+
+Please use
+IssuerAwareContextResolver |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandHelp.html b/docs/co/aikar/commands/class-use/CommandHelp.html new file mode 100644 index 00000000..dc00f45c --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandHelp.html @@ -0,0 +1,203 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
CommandHelp |
+CommandManager.generateCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(CommandIssuer issuer,
+ co.aikar.commands.RootCommand rootCommand)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(CommandIssuer issuer,
+ @NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(@NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+BaseCommand.getCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandIssuer.html b/docs/co/aikar/commands/class-use/CommandIssuer.html new file mode 100644 index 00000000..65414c59 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandIssuer.html @@ -0,0 +1,438 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Modifier and Type | +Class and Description | +
|---|---|
class |
+CommandContexts<R extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
class |
+CommandExecutionContext<T extends CommandExecutionContext,I extends CommandIssuer> |
+
class |
+RegisteredCommand<R extends CommandExecutionContext<? extends CommandExecutionContext,? extends CommandIssuer>> |
+
| Modifier and Type | +Field and Description | +
|---|---|
protected I |
+CommandExecutionContext.issuer |
+
protected CommandIssuer |
+CommandCompletionContext.issuer |
+
| Modifier and Type | +Method and Description | +
|---|---|
CommandIssuer |
+CommandOperationContext.getCommandIssuer() |
+
abstract CommandIssuer |
+CommandManager.getCommandIssuer(Object issuer) |
+
static CommandIssuer |
+CommandManager.getCurrentCommandIssuer() |
+
CommandIssuer |
+BaseCommand.getCurrentCommandIssuer() |
+
CommandIssuer |
+CommandCompletionContext.getIssuer() |
+
| Modifier and Type | +Method and Description | +
|---|---|
boolean |
+BaseCommand.canExecute(CommandIssuer issuer,
+ RegisteredCommand<?> cmd) |
+
abstract <R extends CommandExecutionContext> |
+CommandManager.createCommandContext(RegisteredCommand command,
+ Parameter parameter,
+ CommandIssuer sender,
+ List<String> args,
+ int i,
+ Map<String,Object> passedArgs) |
+
CommandOperationContext |
+CommandManager.createCommandOperationContext(BaseCommand command,
+ CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
abstract CommandCompletionContext |
+CommandManager.createCompletionContext(RegisteredCommand command,
+ CommandIssuer sender,
+ String input,
+ String config,
+ String[] args) |
+
void |
+BaseCommand.doHelp(CommandIssuer issuer,
+ String... args) |
+
boolean |
+ExceptionHandler.execute(BaseCommand command,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t)
+Called when an exception occurs while executing a command
++ If an exception handler properly handles an exception, the user will not be noticied by the + framework that something went unexceptected. |
+
void |
+ForwardingCommand.execute(CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
void |
+BaseCommand.execute(CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
String |
+CommandManager.formatMessage(CommandIssuer issuer,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
CommandHelp |
+CommandManager.generateCommandHelp(CommandIssuer issuer,
+ co.aikar.commands.RootCommand rootCommand)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(CommandIssuer issuer,
+ @NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
Locale |
+CommandManager.getIssuerLocale(CommandIssuer issuer) |
+
String |
+Locales.getMessage(CommandIssuer issuer,
+ co.aikar.locales.MessageKeyProvider key) |
+
protected boolean |
+CommandManager.handleUncaughtException(BaseCommand scope,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t) |
+
boolean |
+ForwardingCommand.hasPermission(CommandIssuer sender) |
+
boolean |
+BaseCommand.hasPermission(CommandIssuer issuer) |
+
boolean |
+CommandManager.hasPermission(CommandIssuer issuer,
+ String permission) |
+
void |
+BaseCommand.help(CommandIssuer issuer,
+ String[] args) |
+
void |
+CommandManager.sendMessage(CommandIssuer issuer,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
void |
+CommandHelp.showHelp(CommandIssuer issuer) |
+
void |
+CommandHelp.showHelp(CommandIssuer issuer,
+ co.aikar.locales.MessageKeyProvider format) |
+
void |
+BaseCommand.showSyntax(CommandIssuer issuer,
+ RegisteredCommand<?> cmd) |
+
List<String> |
+ForwardingCommand.tabComplete(CommandIssuer issuer,
+ String alias,
+ String[] args) |
+
List<String> |
+BaseCommand.tabComplete(CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
| Constructor and Description | +
|---|
CommandHelp(CommandManager manager,
+ co.aikar.commands.RootCommand rootCommand,
+ CommandIssuer issuer) |
+
| Modifier and Type | +Interface and Description | +
|---|---|
interface |
+ContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
interface |
+IssuerAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
interface |
+IssuerOnlyContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+A context resolver that will never consume input, and only resolves using the context of the issuer of the command
+ |
+
interface |
+OptionalContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Context Resolver that can accept null input
+ |
+
interface |
+SenderAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Deprecated.
+
+Please use
+IssuerAwareContextResolver |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandManager.html b/docs/co/aikar/commands/class-use/CommandManager.html new file mode 100644 index 00000000..72818ad4 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandManager.html @@ -0,0 +1,213 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Field and Description | +
|---|---|
protected CommandManager |
+CommandContexts.manager |
+
| Modifier and Type | +Method and Description | +
|---|---|
CommandManager |
+CommandOperationContext.getCommandManager() |
+
static CommandManager |
+CommandManager.getCurrentCommandManager() |
+
CommandManager |
+BaseCommand.getCurrentCommandManager() |
+
CommandManager |
+CommandIssuer.getManager() |
+
CommandManager |
+CommandHelp.getManager() |
+
| Constructor and Description | +
|---|
CommandCompletions(CommandManager manager) |
+
CommandHelp(CommandManager manager,
+ co.aikar.commands.RootCommand rootCommand,
+ CommandIssuer issuer) |
+
Locales(CommandManager manager) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandOperationContext.html b/docs/co/aikar/commands/class-use/CommandOperationContext.html new file mode 100644 index 00000000..6197f971 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandOperationContext.html @@ -0,0 +1,172 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
CommandOperationContext |
+CommandManager.createCommandOperationContext(BaseCommand command,
+ CommandIssuer issuer,
+ String commandLabel,
+ String[] args) |
+
static CommandOperationContext |
+CommandManager.getCurrentCommandOperationContext() |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/CommandReplacements.html b/docs/co/aikar/commands/class-use/CommandReplacements.html new file mode 100644 index 00000000..42aa01a0 --- /dev/null +++ b/docs/co/aikar/commands/class-use/CommandReplacements.html @@ -0,0 +1,181 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Field and Description | +
|---|---|
protected CommandReplacements |
+CommandManager.replacements |
+
| Modifier and Type | +Method and Description | +
|---|---|
CommandReplacements |
+CommandManager.getCommandReplacements()
+Lets you add custom string replacements that can be applied to annotation values,
+ to reduce duplication/repetition of common values such as permission nodes and command prefixes.
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/ExceptionHandler.html b/docs/co/aikar/commands/class-use/ExceptionHandler.html new file mode 100644 index 00000000..ca0d9bba --- /dev/null +++ b/docs/co/aikar/commands/class-use/ExceptionHandler.html @@ -0,0 +1,203 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Field and Description | +
|---|---|
protected ExceptionHandler |
+CommandManager.defaultExceptionHandler |
+
| Modifier and Type | +Method and Description | +
|---|---|
ExceptionHandler |
+CommandManager.getDefaultExceptionHandler()
+Gets the current default exception handler, might be null.
+ |
+
ExceptionHandler |
+BaseCommand.getExceptionHandler() |
+
| Modifier and Type | +Method and Description | +
|---|---|
void |
+CommandManager.setDefaultExceptionHandler(ExceptionHandler exceptionHandler)
+Sets the default
+ExceptionHandler that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered. |
+
BaseCommand |
+BaseCommand.setExceptionHandler(ExceptionHandler exceptionHandler) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/ForwardingCommand.html b/docs/co/aikar/commands/class-use/ForwardingCommand.html new file mode 100644 index 00000000..c50f88fa --- /dev/null +++ b/docs/co/aikar/commands/class-use/ForwardingCommand.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/HelpEntry.html b/docs/co/aikar/commands/class-use/HelpEntry.html new file mode 100644 index 00000000..ed88f08c --- /dev/null +++ b/docs/co/aikar/commands/class-use/HelpEntry.html @@ -0,0 +1,184 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
List<HelpEntry> |
+CommandHelp.getHelpEntries() |
+
| Modifier and Type | +Method and Description | +
|---|---|
@NotNull String[] |
+CommandHelp.getFormatReplacements(HelpEntry e)
+Override this to control replacements
+ |
+
protected void |
+CommandHelp.updateSearchScore(HelpEntry help) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/InvalidCommandArgument.html b/docs/co/aikar/commands/class-use/InvalidCommandArgument.html new file mode 100644 index 00000000..6920690a --- /dev/null +++ b/docs/co/aikar/commands/class-use/InvalidCommandArgument.html @@ -0,0 +1,196 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
Collection<String> |
+CommandCompletions.CommandCompletionHandler.getCompletions(C context) |
+
<T> T |
+CommandCompletionContext.getContextValue(Class<? extends T> clazz) |
+
<T> T |
+CommandCompletionContext.getContextValue(Class<? extends T> clazz,
+ Integer paramIdx) |
+
| Modifier and Type | +Method and Description | +
|---|---|
T |
+ContextResolver.getContext(C c) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/InvalidCommandContextException.html b/docs/co/aikar/commands/class-use/InvalidCommandContextException.html new file mode 100644 index 00000000..497d351b --- /dev/null +++ b/docs/co/aikar/commands/class-use/InvalidCommandContextException.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/Locales.html b/docs/co/aikar/commands/class-use/Locales.html new file mode 100644 index 00000000..fa6a39d2 --- /dev/null +++ b/docs/co/aikar/commands/class-use/Locales.html @@ -0,0 +1,167 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
abstract Locales |
+CommandManager.getLocales()
+Returns a Locales Manager to add and modify language tables for your commands.
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/MessageFormatter.html b/docs/co/aikar/commands/class-use/MessageFormatter.html new file mode 100644 index 00000000..e5d5755a --- /dev/null +++ b/docs/co/aikar/commands/class-use/MessageFormatter.html @@ -0,0 +1,178 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Class and Description | +
|---|---|
class |
+CommandManager<I,FT,F extends MessageFormatter<FT>> |
+
| Modifier and Type | +Field and Description | +
|---|---|
protected F |
+CommandManager.defaultFormatter |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/MessageKeys.html b/docs/co/aikar/commands/class-use/MessageKeys.html new file mode 100644 index 00000000..00d66016 --- /dev/null +++ b/docs/co/aikar/commands/class-use/MessageKeys.html @@ -0,0 +1,174 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
static MessageKeys |
+MessageKeys.valueOf(String name)
+Returns the enum constant of this type with the specified name.
+ |
+
static MessageKeys[] |
+MessageKeys.values()
+Returns an array containing the constants of this enum type, in
+the order they are declared.
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/MessageType.html b/docs/co/aikar/commands/class-use/MessageType.html new file mode 100644 index 00000000..2ff97fe1 --- /dev/null +++ b/docs/co/aikar/commands/class-use/MessageType.html @@ -0,0 +1,252 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Field and Description | +
|---|---|
static MessageType |
+MessageType.ERROR |
+
static MessageType |
+MessageType.HELP |
+
static MessageType |
+MessageType.INFO |
+
static MessageType |
+MessageType.SYNTAX |
+
| Modifier and Type | +Field and Description | +
|---|---|
protected Map<MessageType,F> |
+CommandManager.formatters |
+
| Modifier and Type | +Method and Description | +
|---|---|
String |
+CommandManager.formatMessage(CommandIssuer issuer,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
F |
+CommandManager.getFormat(MessageType type) |
+
void |
+CommandManager.sendMessage(CommandIssuer issuer,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
void |
+CommandManager.sendMessage(I issuerArg,
+ MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
default void |
+CommandIssuer.sendMessage(MessageType type,
+ co.aikar.locales.MessageKeyProvider key,
+ String... replacements) |
+
default void |
+CommandIssuer.sendMessage(MessageType type,
+ co.aikar.locales.MessageKey key,
+ String... replacements) |
+
F |
+CommandManager.setFormat(MessageType type,
+ F formatter) |
+
void |
+CommandManager.setFormat(MessageType type,
+ FT... colors) |
+
void |
+CommandManager.setFormat(MessageType type,
+ int i,
+ FT color) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/RegisteredCommand.html b/docs/co/aikar/commands/class-use/RegisteredCommand.html new file mode 100644 index 00000000..d7f4e02c --- /dev/null +++ b/docs/co/aikar/commands/class-use/RegisteredCommand.html @@ -0,0 +1,236 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
RegisteredCommand |
+CommandManager.createRegisteredCommand(BaseCommand command,
+ String cmdName,
+ Method method,
+ String prefSubCommand) |
+
RegisteredCommand |
+CommandExecutionContext.getCmd() |
+
RegisteredCommand |
+CommandOperationContext.getRegisteredCommand() |
+
| Modifier and Type | +Method and Description | +
|---|---|
boolean |
+BaseCommand.canExecute(CommandIssuer issuer,
+ RegisteredCommand<?> cmd) |
+
abstract <R extends CommandExecutionContext> |
+CommandManager.createCommandContext(RegisteredCommand command,
+ Parameter parameter,
+ CommandIssuer sender,
+ List<String> args,
+ int i,
+ Map<String,Object> passedArgs) |
+
abstract CommandCompletionContext |
+CommandManager.createCompletionContext(RegisteredCommand command,
+ CommandIssuer sender,
+ String input,
+ String config,
+ String[] args) |
+
boolean |
+ExceptionHandler.execute(BaseCommand command,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t)
+Called when an exception occurs while executing a command
++ If an exception handler properly handles an exception, the user will not be noticied by the + framework that something went unexceptected. |
+
protected boolean |
+CommandManager.handleUncaughtException(BaseCommand scope,
+ RegisteredCommand registeredCommand,
+ CommandIssuer sender,
+ List<String> args,
+ Throwable t) |
+
void |
+CommandOperationContext.setRegisteredCommand(RegisteredCommand registeredCommand) |
+
void |
+BaseCommand.showSyntax(CommandIssuer issuer,
+ RegisteredCommand<?> cmd) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/class-use/UnstableAPI.html b/docs/co/aikar/commands/class-use/UnstableAPI.html new file mode 100644 index 00000000..0f659363 --- /dev/null +++ b/docs/co/aikar/commands/class-use/UnstableAPI.html @@ -0,0 +1,215 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
CommandHelp |
+CommandManager.generateCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(CommandIssuer issuer,
+ co.aikar.commands.RootCommand rootCommand)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(CommandIssuer issuer,
+ @NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+CommandManager.generateCommandHelp(@NotNull String command)
+Deprecated.
+
+Unstable API
+ |
+
CommandHelp |
+BaseCommand.getCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
void |
+BaseCommand.showCommandHelp()
+Deprecated.
+
+Unstable API
+ |
+
protected void |
+CommandHelp.updateSearchScore(HelpEntry help) |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/ContextResolver.html b/docs/co/aikar/commands/contexts/ContextResolver.html new file mode 100644 index 00000000..ac78089e --- /dev/null +++ b/docs/co/aikar/commands/contexts/ContextResolver.html @@ -0,0 +1,238 @@ + + + + + + +@FunctionalInterface +public interface ContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>+
| Modifier and Type | +Method and Description | +
|---|---|
T |
+getContext(C c) |
+
T getContext(C c) + throws InvalidCommandArgument+
InvalidCommandArgumentCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/IssuerAwareContextResolver.html b/docs/co/aikar/commands/contexts/IssuerAwareContextResolver.html new file mode 100644 index 00000000..f16b1f95 --- /dev/null +++ b/docs/co/aikar/commands/contexts/IssuerAwareContextResolver.html @@ -0,0 +1,198 @@ + + + + + + +public interface IssuerAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> +extends ContextResolver<T,C>+
getContextCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/IssuerOnlyContextResolver.html b/docs/co/aikar/commands/contexts/IssuerOnlyContextResolver.html new file mode 100644 index 00000000..9c13a541 --- /dev/null +++ b/docs/co/aikar/commands/contexts/IssuerOnlyContextResolver.html @@ -0,0 +1,200 @@ + + + + + + +T - C - public interface IssuerOnlyContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> +extends ContextResolver<T,C>+
getContextCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/OptionalContextResolver.html b/docs/co/aikar/commands/contexts/OptionalContextResolver.html new file mode 100644 index 00000000..d47a060d --- /dev/null +++ b/docs/co/aikar/commands/contexts/OptionalContextResolver.html @@ -0,0 +1,195 @@ + + + + + + +public interface OptionalContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> +extends ContextResolver<T,C>+
getContextCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/SenderAwareContextResolver.html b/docs/co/aikar/commands/contexts/SenderAwareContextResolver.html new file mode 100644 index 00000000..d18b6f82 --- /dev/null +++ b/docs/co/aikar/commands/contexts/SenderAwareContextResolver.html @@ -0,0 +1,203 @@ + + + + + + +IssuerAwareContextResolver@Deprecated +public interface SenderAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> +extends IssuerAwareContextResolver<T,C>+
IssuerAwareContextResolvergetContextCopyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/class-use/ContextResolver.html b/docs/co/aikar/commands/contexts/class-use/ContextResolver.html new file mode 100644 index 00000000..4a6a528a --- /dev/null +++ b/docs/co/aikar/commands/contexts/class-use/ContextResolver.html @@ -0,0 +1,236 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Modifier and Type | +Field and Description | +
|---|---|
protected Map<Class<?>,ContextResolver<?,R>> |
+CommandContexts.contextMap |
+
| Modifier and Type | +Method and Description | +
|---|---|
ContextResolver<?,R> |
+CommandContexts.getResolver(Class<?> type) |
+
| Modifier and Type | +Method and Description | +
|---|---|
<T> void |
+CommandContexts.registerContext(Class<T> context,
+ ContextResolver<T,R> supplier)
+Registers a context that requires input from the command issuer to resolve.
+ |
+
| Modifier and Type | +Interface and Description | +
|---|---|
interface |
+IssuerAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> |
+
interface |
+IssuerOnlyContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+A context resolver that will never consume input, and only resolves using the context of the issuer of the command
+ |
+
interface |
+OptionalContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Context Resolver that can accept null input
+ |
+
interface |
+SenderAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Deprecated.
+
+Please use
+IssuerAwareContextResolver |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/class-use/IssuerAwareContextResolver.html b/docs/co/aikar/commands/contexts/class-use/IssuerAwareContextResolver.html new file mode 100644 index 00000000..e4ebfaaa --- /dev/null +++ b/docs/co/aikar/commands/contexts/class-use/IssuerAwareContextResolver.html @@ -0,0 +1,205 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
<T> void |
+CommandContexts.registerIssuerAwareContext(Class<T> context,
+ IssuerAwareContextResolver<T,R> supplier)
+Registers a context resolver that may conditionally consume input, falling back to using the context of the
+ issuer to potentially fulfill this context.
+ |
+
<T> void |
+CommandContexts.registerSenderAwareContext(Class<T> context,
+ IssuerAwareContextResolver<T,R> supplier)
+Deprecated.
+
+Please switch to
+CommandContexts.registerIssuerAwareContext(Class, IssuerAwareContextResolver)
+ as the core wants to use the platform agnostic term of "Issuer" instead of Sender |
+
| Modifier and Type | +Interface and Description | +
|---|---|
interface |
+SenderAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>>
+Deprecated.
+
+Please use
+IssuerAwareContextResolver |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/class-use/IssuerOnlyContextResolver.html b/docs/co/aikar/commands/contexts/class-use/IssuerOnlyContextResolver.html new file mode 100644 index 00000000..fa41b3db --- /dev/null +++ b/docs/co/aikar/commands/contexts/class-use/IssuerOnlyContextResolver.html @@ -0,0 +1,168 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
<T> void |
+CommandContexts.registerIssuerOnlyContext(Class<T> context,
+ IssuerOnlyContextResolver<T,R> supplier)
+Registers a context resolver that will never consume input.
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/class-use/OptionalContextResolver.html b/docs/co/aikar/commands/contexts/class-use/OptionalContextResolver.html new file mode 100644 index 00000000..68e27c94 --- /dev/null +++ b/docs/co/aikar/commands/contexts/class-use/OptionalContextResolver.html @@ -0,0 +1,168 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| Modifier and Type | +Method and Description | +
|---|---|
<T> void |
+CommandContexts.registerOptionalContext(Class<T> context,
+ OptionalContextResolver<T,R> supplier)
+Registers a context that can safely accept a null input from the command issuer to resolve.
+ |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/class-use/SenderAwareContextResolver.html b/docs/co/aikar/commands/contexts/class-use/SenderAwareContextResolver.html new file mode 100644 index 00000000..8f12e7d6 --- /dev/null +++ b/docs/co/aikar/commands/contexts/class-use/SenderAwareContextResolver.html @@ -0,0 +1,125 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/package-frame.html b/docs/co/aikar/commands/contexts/package-frame.html new file mode 100644 index 00000000..c51ac7bc --- /dev/null +++ b/docs/co/aikar/commands/contexts/package-frame.html @@ -0,0 +1,24 @@ + + + + + + +| Interface | +Description | +
|---|---|
| ContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> | ++ |
| IssuerAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> | ++ |
| IssuerOnlyContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> | +
+ A context resolver that will never consume input, and only resolves using the context of the issuer of the command
+ |
+
| OptionalContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> | +
+ Context Resolver that can accept null input
+ |
+
| SenderAwareContextResolver<T,C extends CommandExecutionContext<?,? extends CommandIssuer>> | +Deprecated
+ Please use
+IssuerAwareContextResolver |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/package-tree.html b/docs/co/aikar/commands/contexts/package-tree.html new file mode 100644 index 00000000..351b3ed0 --- /dev/null +++ b/docs/co/aikar/commands/contexts/package-tree.html @@ -0,0 +1,144 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/contexts/package-use.html b/docs/co/aikar/commands/contexts/package-use.html new file mode 100644 index 00000000..d146b1eb --- /dev/null +++ b/docs/co/aikar/commands/contexts/package-use.html @@ -0,0 +1,193 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Class and Description | +
|---|
| ContextResolver | +
| IssuerAwareContextResolver | +
| IssuerOnlyContextResolver
+ A context resolver that will never consume input, and only resolves using the context of the issuer of the command
+ |
+
| OptionalContextResolver
+ Context Resolver that can accept null input
+ |
+
| Class and Description | +
|---|
| ContextResolver | +
| IssuerAwareContextResolver | +
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/package-frame.html b/docs/co/aikar/commands/package-frame.html new file mode 100644 index 00000000..11994b2f --- /dev/null +++ b/docs/co/aikar/commands/package-frame.html @@ -0,0 +1,54 @@ + + + + + + +| Interface | +Description | +
|---|---|
| CommandCompletions.CommandCompletionHandler<C extends CommandCompletionContext> | ++ |
| CommandIssuer | ++ |
| ExceptionHandler | +
+ Functional interface to allow plugins to handle uncaught excetpions
+ |
+
| Class | +Description | +
|---|---|
| ACFUtil | ++ |
| BaseCommand | ++ |
| CommandCompletionContext | ++ |
| CommandCompletions<C extends CommandCompletionContext> | ++ |
| CommandContexts<R extends CommandExecutionContext<?,? extends CommandIssuer>> | ++ |
| CommandExecutionContext<T extends CommandExecutionContext,I extends CommandIssuer> | ++ |
| CommandHelp | ++ |
| CommandManager<I,FT,F extends MessageFormatter<FT>> | ++ |
| CommandOperationContext | +
+ Holds information about the currently executing command on this thread
+ |
+
| CommandReplacements | +
+ Manages replacement template strings
+ |
+
| ForwardingCommand | ++ |
| HelpEntry | ++ |
| Locales | ++ |
| MessageFormatter<C> | +
+ Handles formatting Messages and managing colors
+ |
+
| MessageType | ++ |
| RegisteredCommand<R extends CommandExecutionContext<? extends CommandExecutionContext,? extends CommandIssuer>> | ++ |
| Enum | +Description | +
|---|---|
| MessageKeys | +
+ Enum Name = MessageKey in lowercase prefixed with acf-core.
+ |
+
| Exception | +Description | +
|---|---|
| InvalidCommandArgument | ++ |
| InvalidCommandContextException | ++ |
| Annotation Type | +Description | +
|---|---|
| UnstableAPI | +Deprecated | +
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/package-tree.html b/docs/co/aikar/commands/package-tree.html new file mode 100644 index 00000000..ed0334a1 --- /dev/null +++ b/docs/co/aikar/commands/package-tree.html @@ -0,0 +1,192 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/co/aikar/commands/package-use.html b/docs/co/aikar/commands/package-use.html new file mode 100644 index 00000000..9044c8d7 --- /dev/null +++ b/docs/co/aikar/commands/package-use.html @@ -0,0 +1,252 @@ + + + + + + +| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.contexts | ++ |
| Class and Description | +
|---|
| BaseCommand | +
| CommandCompletionContext | +
| CommandCompletions | +
| CommandCompletions.CommandCompletionHandler | +
| CommandContexts | +
| CommandExecutionContext | +
| CommandHelp | +
| CommandIssuer | +
| CommandManager | +
| CommandOperationContext
+ Holds information about the currently executing command on this thread
+ |
+
| CommandReplacements
+ Manages replacement template strings
+ |
+
| ExceptionHandler
+ Functional interface to allow plugins to handle uncaught excetpions
+ |
+
| HelpEntry | +
| InvalidCommandArgument | +
| Locales | +
| MessageFormatter
+ Handles formatting Messages and managing colors
+ |
+
| MessageKeys
+ Enum Name = MessageKey in lowercase prefixed with acf-core.
+ |
+
| MessageType | +
| RegisteredCommand | +
| UnstableAPI
+ Deprecated.
+ |
+
| Class and Description | +
|---|
| CommandExecutionContext | +
| CommandIssuer | +
| InvalidCommandArgument | +
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/constant-values.html b/docs/constant-values.html new file mode 100644 index 00000000..217447db --- /dev/null +++ b/docs/constant-values.html @@ -0,0 +1,189 @@ + + + + + + +| Modifier and Type | +Constant Field | +Value | +
|---|---|---|
+
+public static final String |
+DEFAULT |
+"__default" |
+
+
+public static final String |
+UNKNOWN |
+"__unknown" |
+
| Modifier and Type | +Constant Field | +Value | +
|---|---|---|
+
+public static final String |
+EMPTY |
+"" |
+
+
+public static final int |
+INDEX_NOT_FOUND |
+-1 |
+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/deprecated-list.html b/docs/deprecated-list.html new file mode 100644 index 00000000..b3885c29 --- /dev/null +++ b/docs/deprecated-list.html @@ -0,0 +1,256 @@ + + + + + + +| Interface and Description | +
|---|
| co.aikar.commands.contexts.SenderAwareContextResolver
+ Please use
+IssuerAwareContextResolver |
+
| Annotation Type and Description | +
|---|
| co.aikar.commands.UnstableAPI | +
| Constructor and Description | +
|---|
| co.aikar.commands.InvalidCommandArgument(String) | +
| co.aikar.commands.InvalidCommandArgument(String, boolean) | +
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/help-doc.html b/docs/help-doc.html new file mode 100644 index 00000000..2ba08f17 --- /dev/null +++ b/docs/help-doc.html @@ -0,0 +1,230 @@ + + + + + + +The Overview page is the front page of this API document and provides a list of all packages with a summary for each. This page can also contain an overall description of the set of packages.
+Each package has a page that contains a list of its classes and interfaces, with a summary for each. This page can contain six categories:
+Each class, interface, nested class and nested interface has its own separate page. Each of these pages has three sections consisting of a class/interface description, summary tables, and detailed member descriptions:
+Each summary entry contains the first sentence from the detailed description for that item. The summary entries are alphabetical, while the detailed descriptions are in the order they appear in the source code. This preserves the logical groupings established by the programmer.
+Each annotation type has its own separate page with the following sections:
+Each enum has its own separate page with the following sections:
+Each documented package, class and interface has its own Use page. This page describes what packages, classes, methods, constructors and fields use any part of the given class or package. Given a class or interface A, its Use page includes subclasses of A, fields declared as A, methods that return A, and methods and constructors with parameters of type A. You can access this page by first going to the package, class or interface, then clicking on the "Use" link in the navigation bar.
+There is a Class Hierarchy page for all packages, plus a hierarchy for each package. Each hierarchy page contains a list of classes and a list of interfaces. The classes are organized by inheritance structure starting with java.lang.Object. The interfaces do not inherit from java.lang.Object.
The Deprecated API page lists all of the API that have been deprecated. A deprecated API is not recommended for use, generally due to improvements, and a replacement API is usually given. Deprecated APIs may be removed in future implementations.
+The Index contains an alphabetic list of all classes, interfaces, constructors, methods, and fields.
+These links take you to the next or previous class, interface, package, or related page.
+These links show and hide the HTML frames. All pages are available with or without frames.
+The All Classes link shows all classes and interfaces except non-static nested types.
+Each serializable or externalizable class has a description of its serialization fields and methods. This information is of interest to re-implementors, not to developers using the API. While there is no link in the navigation bar, you can get to this information by going to any serialized class and clicking "Serialized Form" in the "See also" section of the class description.
+The Constant Field Values page lists the static final fields and their values.
+Copyright © 2017. All rights reserved.
+ + diff --git a/docs/index-all.html b/docs/index-all.html new file mode 100644 index 00000000..ff58a63d --- /dev/null +++ b/docs/index-all.html @@ -0,0 +1,1453 @@ + + + + + + +Throwable
+ objects.Throwable objects.ExceptionUtils to be created, although that is not
+ normally necessary.Throwable
+ extensions which would like to be able to nest root exceptions
+ inside themselves.null."".Throwable to obtain the cause.Throwable to obtain the cause.Nestable to be thrown.Throwable.Throwable in the chain
+ of Throwables at the specified index, numbered from 0.Throwables
+ in an array of Strings, one element for each message.Throwable to obtain the root cause.Throwable in the chain of
+ Throwables at the specified index, numbered from 0.Throwable objects in the
+ exception chain.Throwables represented by
+ this Nestable, including this Nestable.Throwable objects in the
+ exception chain.Throwable objects in the
+ exception chain.Nestable and any nested Throwables
+ in an array of Throwables, one element for each
+ Throwable.-1.Throwable
+ that matches the specified class (exactly) in the exception chain.Throwable
+ that matches the specified type in the exception chain from
+ a specified index.Throwables.Throwable
+ that matches the specified type, or a subclass, in the chain of Throwables
+ with an index greater than or equal to the specified index.Throwable
+ that matches the specified class or subclass in the exception chain.Throwable
+ that matches the specified type in the exception chain from
+ a specified index.Throwable
+ objects include the given name.Throwable class can store a cause.getCause method.Iterator into
+ a single String containing the provided elements.Iterator into
+ a single String containing the provided elements.Iterable into
+ a single String containing the provided elements.Iterable into
+ a single String containing the provided elements.CommandContexts.registerIssuerAwareContext(Class, IssuerAwareContextResolver)
+ as the core wants to use the platform agnostic term of "Issuer" instead of SenderThrowable
+ objects.ACFUtil.replace(String, Pattern, String)ACFUtil.replace(String, String, String)IssuerAwareContextResolverThrowable using introspection, allowing
+ source code compatibility between pre-1.4 and post-1.4 Java releases.ExceptionHandler that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered.Copyright © 2017. All rights reserved.
+ + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..8cc046fc --- /dev/null +++ b/docs/index.html @@ -0,0 +1,76 @@ + + + + + + ++ + diff --git a/docs/overview-summary.html b/docs/overview-summary.html new file mode 100644 index 00000000..532d6d26 --- /dev/null +++ b/docs/overview-summary.html @@ -0,0 +1,151 @@ + + + + + + +
| Package | +Description | +
|---|---|
| co.aikar.commands | ++ |
| co.aikar.commands.annotation | ++ |
| co.aikar.commands.apachecommonslang | ++ |
| co.aikar.commands.contexts | ++ |
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/overview-tree.html b/docs/overview-tree.html new file mode 100644 index 00000000..b0c06e99 --- /dev/null +++ b/docs/overview-tree.html @@ -0,0 +1,226 @@ + + + + + + +Copyright © 2017. All rights reserved.
+ + diff --git a/docs/package-list b/docs/package-list new file mode 100644 index 00000000..6a9c4b6d --- /dev/null +++ b/docs/package-list @@ -0,0 +1,4 @@ +co.aikar.commands +co.aikar.commands.annotation +co.aikar.commands.apachecommonslang +co.aikar.commands.contexts diff --git a/docs/script.js b/docs/script.js new file mode 100644 index 00000000..b3463569 --- /dev/null +++ b/docs/script.js @@ -0,0 +1,30 @@ +function show(type) +{ + count = 0; + for (var key in methods) { + var row = document.getElementById(key); + if ((methods[key] & type) != 0) { + row.style.display = ''; + row.className = (count++ % 2) ? rowColor : altColor; + } + else + row.style.display = 'none'; + } + updateTabs(type); +} + +function updateTabs(type) +{ + for (var value in tabs) { + var sNode = document.getElementById(tabs[value][0]); + var spanNode = sNode.firstChild; + if (value == type) { + sNode.className = activeTableTab; + spanNode.innerHTML = tabs[value][1]; + } + else { + sNode.className = tableTab; + spanNode.innerHTML = "" + tabs[value][1] + ""; + } + } +} diff --git a/docs/serialized-form.html b/docs/serialized-form.html new file mode 100644 index 00000000..2b7bfc2f --- /dev/null +++ b/docs/serialized-form.html @@ -0,0 +1,162 @@ + + + + + + +boolean showSyntax+
co.aikar.locales.MessageKey key+
String[] replacements+
Copyright © 2017. All rights reserved.
+ + diff --git a/docs/src-html/co/aikar/commands/ACFUtil.html b/docs/src-html/co/aikar/commands/ACFUtil.html new file mode 100644 index 00000000..23bb43ff --- /dev/null +++ b/docs/src-html/co/aikar/commands/ACFUtil.html @@ -0,0 +1,654 @@ + + + +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 +026 +027import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +028import org.jetbrains.annotations.NotNull; +029import org.jetbrains.annotations.Nullable; +030 +031import java.math.BigDecimal; +032import java.text.Normalizer; +033import java.text.Normalizer.Form; +034import java.text.NumberFormat; +035import java.util.ArrayList; +036import java.util.Collection; +037import java.util.List; +038import java.util.Random; +039import java.util.regex.Matcher; +040import java.util.regex.Pattern; +041import java.util.stream.Collectors; +042import java.util.stream.Stream; +043 +044@SuppressWarnings({"WeakerAccess", "unused"}) +045public final class ACFUtil { +046 +047 public static final Random RANDOM = new Random(); +048 +049 private ACFUtil() {} +050 +051 public static String padRight(String s, int n) { +052 return String.format("%1$-" + n + "s", s); +053 } +054 +055 public static String padLeft(String s, int n) { +056 return String.format("%1$" + n + "s", s); +057 } +058 +059 public static String formatNumber(Integer balance) { +060 return NumberFormat.getInstance().format(balance); +061 } +062 +063 public static <T extends Enum> T getEnumFromName(T[] types, String name) { +064 return getEnumFromName(types, name, null); +065 } +066 public static <T extends Enum> T getEnumFromName(T[] types, String name, T def) { +067 for (T type : types) { +068 if (type.name().equalsIgnoreCase(name)) { +069 return type; +070 } +071 } +072 return def; +073 } +074 public static <T extends Enum> T getEnumFromOrdinal(T[] types, int ordinal) { +075 for (T type : types) { +076 if (type.ordinal() == ordinal) { +077 return type; +078 } +079 } +080 return null; +081 } +082 +083 public static String ucfirst(String str) { +084 return ApacheCommonsLangUtil.capitalizeFully(str); +085 } +086 +087 public static Double parseDouble(String var) { +088 return parseDouble(var, null); +089 } +090 +091 public static Double parseDouble(String var, Double def) { +092 if (var == null) { +093 return def; +094 } +095 try { +096 return Double.parseDouble(var); +097 } catch (NumberFormatException ignored) {} +098 return def; +099 } +100 +101 public static Float parseFloat(String var) { +102 return parseFloat(var, null); +103 } +104 public static Float parseFloat(String var, Float def) { +105 if (var == null) { +106 return def; +107 } +108 try { +109 return Float.parseFloat(var); +110 } catch (NumberFormatException ignored) {} +111 return def; +112 } +113 public static Long parseLong(String var) { +114 return parseLong(var, null); +115 } +116 public static Long parseLong(String var, Long def) { +117 if (var == null) { +118 return def; +119 } +120 try { +121 return Long.parseLong(var); +122 } catch (NumberFormatException ignored) {} +123 return def; +124 } +125 +126 public static Integer parseInt(String var) { +127 return parseInt(var, null); +128 } +129 public static Integer parseInt(String var, Integer def) { +130 if (var == null) { +131 return def; +132 } +133 try { +134 return Integer.parseInt(var); +135 } catch (NumberFormatException ignored) {} +136 return def; +137 } +138 +139 public static boolean randBool() { +140 return RANDOM.nextBoolean(); +141 } +142 +143 public static <T> T nullDefault(Object val, Object def) { +144 //noinspection unchecked +145 return (T) (val != null ? val : def); +146 } +147 +148 public static String join(Collection<String> args) { +149 return ApacheCommonsLangUtil.join(args, " "); +150 } +151 public static String join(Collection<String> args, String sep) { +152 return ApacheCommonsLangUtil.join(args, sep); +153 } +154 public static String join(String[] args) { +155 return join(args, 0, ' '); +156 } +157 +158 public static String join(String[] args, String sep) { +159 return ApacheCommonsLangUtil.join(args, sep); +160 } +161 public static String join(String[] args, char sep) { +162 return join(args, 0, sep); +163 } +164 +165 public static String join(String[] args, int index) { +166 return join(args, index, ' '); +167 } +168 +169 public static String join(String[] args, int index, char sep) { +170 return ApacheCommonsLangUtil.join(args, sep, index, args.length); +171 } +172 +173 public static String simplifyString(String str) { +174 if (str == null) { +175 return null; +176 } +177 return ACFPatterns.NON_ALPHA_NUMERIC.matcher(str.toLowerCase()).replaceAll(""); +178 } +179 +180 public static double round(double x, int scale) { +181 try { +182 return (new BigDecimal +183 (Double.toString(x)) +184 .setScale(scale, BigDecimal.ROUND_HALF_UP)) +185 .doubleValue(); +186 } catch (NumberFormatException ex) { +187 if (Double.isInfinite(x)) { +188 return x; +189 } else { +190 return Double.NaN; +191 } +192 } +193 } +194 public static int roundUp(int num, int multiple) { +195 if(multiple == 0) { +196 return num; +197 } +198 +199 int remainder = num % multiple; +200 if (remainder == 0) { +201 return num; +202 } +203 return num + multiple - remainder; +204 +205 } +206 +207 public static String limit(String str, int limit) { +208 return str.length() > limit ? str.substring(0, limit) : str; +209 } +210 +211 /** +212 * Plain string replacement, escapes replace value. +213 * @param string +214 * @param pattern +215 * @param repl +216 * @return +217 */ +218 public static String replace(String string, Pattern pattern, String repl) { +219 return pattern.matcher(string).replaceAll(Matcher.quoteReplacement(repl)); +220 } +221 +222 /** +223 * Regex version of {@link #replace(String, Pattern, String)} +224 * @param string +225 * @param pattern +226 * @param repl +227 * @return +228 */ +229 public static String replacePattern(String string, Pattern pattern, String repl) { +230 return pattern.matcher(string).replaceAll(repl); +231 } +232 +233 /** +234 * Plain String replacement. If you need regex patterns, see {@link #replacePattern(String, String, String)} +235 * @param string +236 * @param pattern +237 * @param repl +238 * @return +239 */ +240 public static String replace(String string, String pattern, String repl) { +241 return replace(string, ACFPatterns.getPattern(Pattern.quote(pattern)), repl); +242 } +243 +244 /** +245 * Regex version of {@link #replace(String, String, String)} +246 * @param string +247 * @param pattern +248 * @param repl +249 * @return +250 */ +251 public static String replacePattern(String string, String pattern, String repl) { +252 return replace(string, ACFPatterns.getPattern(pattern), repl); +253 } +254 /** +255 * Pure Regex Pattern matching and replacement, no escaping +256 * @param string +257 * @param pattern +258 * @param repl +259 * @return +260 */ +261 public static String replacePatternMatch(String string, Pattern pattern, String repl) { +262 return pattern.matcher(string).replaceAll(repl); +263 } +264 +265 /** +266 * Pure Regex Pattern matching and replacement, no escaping +267 * @param string +268 * @param pattern +269 * @param repl +270 * @return +271 */ +272 public static String replacePatternMatch(String string, String pattern, String repl) { +273 return replacePatternMatch(string, ACFPatterns.getPattern(pattern), repl); +274 } +275 +276 public static String replaceStrings(String string, String... replacements) { +277 if (replacements.length < 2 || replacements.length % 2 != 0) { +278 throw new IllegalArgumentException("Invalid Replacements"); +279 } +280 for (int i = 0; i < replacements.length; i += 2) { +281 String key = replacements[i]; +282 String value = replacements[i+1]; +283 if (value == null) value = ""; +284 string = replace(string, key, value); +285 } +286 return string; +287 } +288 public static String replacePatterns(String string, String... replacements) { +289 if (replacements.length < 2 || replacements.length % 2 != 0) { +290 throw new IllegalArgumentException("Invalid Replacements"); +291 } +292 for (int i = 0; i < replacements.length; i += 2) { +293 String key = replacements[i]; +294 String value = replacements[i+1]; +295 if (value == null) value = ""; +296 string = replacePattern(string, key, value); +297 } +298 return string; +299 } +300 +301 public static String capitalize(String str, char[] delimiters) { +302 return ApacheCommonsLangUtil.capitalize(str, delimiters); +303 } +304 private static boolean isDelimiter(char ch, char[] delimiters) { +305 return ApacheCommonsLangUtil.isDelimiter(ch, delimiters); +306 } +307 +308 public static <T> T random(List<T> arr) { +309 if (arr == null || arr.isEmpty()) { +310 return null; +311 } +312 return arr.get(RANDOM.nextInt(arr.size())); +313 } +314 public static <T> T random(T[] arr) { +315 if (arr == null || arr.length == 0) { +316 return null; +317 } +318 return arr[RANDOM.nextInt(arr.length)]; +319 } +320 +321 /** +322 * Added as im sure we will try to "Find this" again. This is no different than Enum.values() passed to above method logically +323 * but the array version is slightly faster. +324 * @param enm +325 * @param <T> +326 * @return +327 */ +328 @Deprecated +329 public static <T extends Enum<?>> T random(Class<? extends T> enm) { +330 return random(enm.getEnumConstants()); +331 } +332 +333 public static String normalize(String s) { +334 if (s == null) { +335 return null; +336 } +337 return ACFPatterns.NON_PRINTABLE_CHARACTERS.matcher(Normalizer.normalize(s, Form.NFD)).replaceAll(""); +338 } +339 +340 public static int indexOf(String arg, String[] split) { +341 for (int i = 0; i < split.length; i++) { +342 if (arg == null) { +343 if (split[i] == null) { +344 return i; +345 } +346 } else if (arg.equals(split[i])) { +347 return i; +348 } +349 } +350 return -1; +351 } +352 +353 public static String capitalizeFirst(String name) { +354 return capitalizeFirst(name, '_'); +355 } +356 +357 public static String capitalizeFirst(String name, char separator) { +358 name = name.toLowerCase(); +359 String[] split = name.split(Character.toString(separator)); +360 StringBuilder total = new StringBuilder(3); +361 for (String s : split) { +362 total.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).append(' '); +363 } +364 +365 return total.toString().trim(); +366 } +367 +368 public static String ltrim(String s) { +369 int i = 0; +370 while (i < s.length() && Character.isWhitespace(s.charAt(i))) { +371 i++; +372 } +373 return s.substring(i); +374 } +375 +376 public static String rtrim(String s) { +377 int i = s.length()-1; +378 while (i >= 0 && Character.isWhitespace(s.charAt(i))) { +379 i--; +380 } +381 return s.substring(0,i+1); +382 } +383 +384 public static List<String> enumNames(Enum<?>[] values) { +385 return Stream.of(values).map(Enum::name).collect(Collectors.toList()); +386 } +387 +388 public static List<String> enumNames(Class<? extends Enum<?>> cls) { +389 return enumNames(cls.getEnumConstants()); +390 } +391 +392 public static String combine(String[] args) { +393 return combine(args, 0); +394 } +395 public static String combine(String[] args, int start) { +396 int size = 0; +397 for (int i = start; i < args.length; i++) { +398 size += args[i].length(); +399 } +400 StringBuilder sb = new StringBuilder(size); +401 for (int i = start; i < args.length; i++) { +402 sb.append(args[i]); +403 } +404 return sb.toString(); +405 } +406 +407 +408 @Nullable public static <E extends Enum<E>> E simpleMatch(Class<? extends Enum<?>> list, String item) { +409 if (item == null) { +410 return null; +411 } +412 item = ACFUtil.simplifyString(item); +413 for (Enum<?> s : list.getEnumConstants()) { +414 String simple = ACFUtil.simplifyString(s.name()); +415 if (item.equals(simple)) { +416 //noinspection unchecked +417 return (E) s; +418 } +419 } +420 +421 return null; +422 } +423 +424 public static boolean isTruthy(String test) { +425 switch (test) { +426 case "t": +427 case "true": +428 case "on": +429 case "y": +430 case "yes": +431 case "1": +432 return true; +433 } +434 return false; +435 } +436 +437 +438 public static Number parseNumber(String num, boolean suffixes) { +439 double mod = 1; +440 if (suffixes) { +441 switch (num.charAt(num.length()-1)) { +442 case 'M': +443 case 'm': +444 mod = 1000000D; +445 num = num.substring(0, num.length()-1); +446 break; +447 case 'K': +448 case 'k': +449 mod = 1000D; +450 num = num.substring(0, num.length()-1); +451 } +452 } +453 +454 return Double.parseDouble(num) * mod; +455 } +456 +457 public static <T> boolean hasIntersection(Collection<T> list1, Collection<T> list2) { +458 for (T t : list1) { +459 if (list2.contains(t)) { +460 return true; +461 } +462 } +463 +464 return false; +465 } +466 +467 public static <T> Collection<T> intersection(Collection<T> list1, Collection<T> list2) { +468 List<T> list = new ArrayList<>(); +469 +470 for (T t : list1) { +471 if(list2.contains(t)) { +472 list.add(t); +473 } +474 } +475 +476 return list; +477 } +478 +479 public static int rand(int min, int max) { +480 return min + RANDOM.nextInt(max - min + 1); +481 } +482 +483 /** +484 * Calculate random between 2 points, excluding a center +485 * ex: Util.rand(-12, -6, 6, 12) would not return -5 to 5 +486 * @param min1 +487 * @param max1 +488 * @param min2 +489 * @param max2 +490 * @return +491 */ +492 public static int rand(int min1, int max1, int min2, int max2) { +493 return randBool() ? rand(min1, max1) : rand(min2, max2); +494 } +495 +496 public static double rand(double min, double max) { +497 return RANDOM.nextDouble() * (max - min) + min; +498 } +499 +500 public static boolean isNumber(String str) { +501 return ApacheCommonsLangUtil.isNumeric(str); +502 } +503 +504 public static String intToRoman(int integer) { +505 if (integer == 1) { +506 return "I"; +507 } +508 if (integer == 2) { +509 return "II"; +510 } +511 if (integer == 3) { +512 return "III"; +513 } +514 if (integer == 4) { +515 return "IV"; +516 } +517 if (integer == 5) { +518 return "V"; +519 } +520 if (integer == 6) { +521 return "VI"; +522 } +523 if (integer == 7) { +524 return "VII"; +525 } +526 if (integer == 8) { +527 return "VIII"; +528 } +529 if (integer == 9) { +530 return "IX"; +531 } +532 if (integer == 10) { +533 return "X"; +534 } +535 return null; +536 } +537 +538 public static boolean isInteger(String string) { +539 return ACFPatterns.INTEGER.matcher(string).matches(); +540 } +541 +542 public static boolean isFloat(String string) { +543 try { +544 //noinspection ResultOfMethodCallIgnored +545 Float.parseFloat(string); +546 return true; +547 } catch (Exception e) { +548 return false; +549 } +550 } +551 +552 public static boolean isDouble(String string) { +553 try { +554 //noinspection ResultOfMethodCallIgnored +555 Double.parseDouble(string); +556 return true; +557 } catch (Exception e) { +558 return false; +559 } +560 } +561 +562 public static boolean isBetween(float num, double min, double max) { +563 return num >= min && num <= max; +564 } +565 +566 @SuppressWarnings("SameParameterValue") +567 public static double precision(double x, int p) { +568 double pow = Math.pow(10, p); +569 return Math.round(x * pow) / pow; +570 } +571 +572 public static void sneaky(Throwable t) { +573 //noinspection RedundantTypeArguments +574 throw ACFUtil.<RuntimeException>superSneaky( t ); +575 } +576 +577 private static <T extends Throwable> T superSneaky(Throwable t) throws T { +578 //noinspection ConstantConditions,unchecked +579 throw (T) t; +580 } +581 +582} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.*; +027import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; +028import com.google.common.collect.HashMultimap; +029import com.google.common.collect.ImmutableList; +030import com.google.common.collect.Iterables; +031import com.google.common.collect.Lists; +032import com.google.common.collect.SetMultimap; +033 +034import java.lang.reflect.Constructor; +035import java.lang.reflect.InvocationTargetException; +036import java.lang.reflect.Method; +037import java.lang.reflect.Parameter; +038import java.util.*; +039import java.util.Optional; +040import java.util.stream.Collectors; +041import java.util.stream.Stream; +042 +043@SuppressWarnings("unused") +044public abstract class BaseCommand { +045 +046 public static final String UNKNOWN = "__unknown"; +047 public static final String DEFAULT = "__default"; +048 final SetMultimap<String, RegisteredCommand> subCommands = HashMultimap.create(); +049 private Method preCommandHandler; +050 +051 @SuppressWarnings("WeakerAccess") +052 private String execLabel; +053 @SuppressWarnings("WeakerAccess") +054 private String execSubcommand; +055 @SuppressWarnings("WeakerAccess") +056 private String[] origArgs; +057 CommandManager<?, ?, ?> manager = null; +058 BaseCommand parentCommand; +059 Map<String, RootCommand> registeredCommands = new HashMap<>(); +060 String description; +061 String commandName; +062 String usageMessage; +063 String permission; +064 +065 private ExceptionHandler exceptionHandler = null; +066 CommandOperationContext lastCommandOperationContext; +067 +068 public BaseCommand() {} +069 public BaseCommand(String cmd) { +070 this.commandName = cmd; +071 } +072 +073 /** +074 * Gets the root command name that the user actually typed +075 * @return Name +076 */ +077 public String getExecCommandLabel() { +078 return execLabel; +079 } +080 +081 /** +082 * Gets the actual sub command name the user typed +083 * @return Name +084 */ +085 public String getExecSubcommand() { +086 return execSubcommand; +087 } +088 +089 /** +090 * Gets the actual args in string form the user typed +091 * @return Args +092 */ +093 public String[] getOrigArgs() { +094 return origArgs; +095 } +096 +097 void setParentCommand(BaseCommand command) { +098 this.parentCommand = command; +099 } +100 void onRegister(CommandManager manager) { +101 onRegister(manager, this.commandName); +102 } +103 void onRegister(CommandManager manager, String cmd) { +104 this.manager = manager; +105 final Class<? extends BaseCommand> self = this.getClass(); +106 CommandAlias rootCmdAliasAnno = self.getAnnotation(CommandAlias.class); +107 String rootCmdAlias = rootCmdAliasAnno != null ? manager.getCommandReplacements().replace(rootCmdAliasAnno.value()).toLowerCase() : null; +108 if (cmd == null && rootCmdAlias != null) { +109 cmd = ACFPatterns.PIPE.split(rootCmdAlias)[0]; +110 } +111 this.commandName = cmd != null ? cmd : self.getSimpleName().toLowerCase(); +112 +113 this.description = this.commandName + " commands"; +114 this.usageMessage = "/" + this.commandName; +115 +116 final CommandPermission perm = self.getAnnotation(CommandPermission.class); +117 if (perm != null) { +118 this.permission = manager.getCommandReplacements().replace(perm.value()); +119 } +120 +121 boolean foundDefault = false; +122 boolean foundUnknown = false; +123 for (Method method : self.getDeclaredMethods()) { +124 method.setAccessible(true); +125 String sublist = null; +126 String sub = getSubcommandValue(method); +127 final Default def = method.getAnnotation(Default.class); +128 final HelpCommand helpCommand = method.getAnnotation(HelpCommand.class); +129 +130 final CommandAlias commandAliases = method.getAnnotation(CommandAlias.class); +131 +132 if (def != null || (!foundDefault && helpCommand != null)) { +133 if (!foundDefault) { +134 registerSubcommand(method, DEFAULT); +135 if (def != null) { +136 foundDefault = true; +137 } +138 } else { +139 ACFUtil.sneaky(new IllegalStateException("Multiple @Default/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +140 } +141 } +142 +143 if (sub != null) { +144 sublist = sub; +145 } else if (commandAliases != null) { +146 sublist = commandAliases.value(); +147 } else if (helpCommand != null) { +148 sublist = helpCommand.value(); +149 } +150 +151 UnknownHandler unknown = method.getAnnotation(UnknownHandler.class); +152 PreCommand preCommand = method.getAnnotation(PreCommand.class); +153 if (unknown != null || (!foundUnknown && helpCommand != null)) { +154 if (!foundUnknown) { +155 registerSubcommand(method, UNKNOWN); +156 if (unknown != null) { +157 foundUnknown = true; +158 } +159 } else { +160 ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler/@HelpCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +161 } +162 } else if (preCommand != null) { +163 if (this.preCommandHandler == null) { +164 this.preCommandHandler = method; +165 } else { +166 ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); +167 } +168 } +169 if (sublist != null) { +170 registerSubcommand(method, sublist); +171 } +172 } +173 +174 if (rootCmdAlias != null) { +175 Set<String> cmdList = new HashSet<>(); +176 Collections.addAll(cmdList, ACFPatterns.PIPE.split(rootCmdAlias)); +177 cmdList.remove(cmd); +178 for (String cmdAlias : cmdList) { +179 register(cmdAlias, this); +180 } +181 } +182 +183 if (cmd != null) { +184 register(cmd, this); +185 } +186 for (Class<?> clazz : this.getClass().getDeclaredClasses()) { +187 if (BaseCommand.class.isAssignableFrom(clazz)) { +188 try { +189 BaseCommand subCommand = null; +190 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); +191 for (Constructor<?> declaredConstructor : declaredConstructors) { +192 +193 declaredConstructor.setAccessible(true); +194 Parameter[] parameters = declaredConstructor.getParameters(); +195 if (parameters.length == 1) { +196 subCommand = (BaseCommand) declaredConstructor.newInstance(this); +197 } else { +198 manager.log(LogLevel.INFO, "Found unusable constructor: " + declaredConstructor.getName() + "(" + Stream.of(parameters).map(p -> p.getType().getSimpleName() + " " + p.getName()).collect(Collectors.joining("<c2>,</c2> ")) + ")"); +199 } +200 } +201 if (subCommand != null) { +202 subCommand.setParentCommand(this); +203 subCommand.onRegister(manager, cmd); +204 this.subCommands.putAll(subCommand.subCommands); +205 this.registeredCommands.putAll(subCommand.registeredCommands); +206 } else { +207 this.manager.log(LogLevel.ERROR, "Could not find a subcommand ctor for " + clazz.getName()); +208 } +209 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { +210 e.printStackTrace(); +211 } +212 } +213 } +214 +215 } +216 +217 private String getSubcommandValue(Method method) { +218 final Subcommand sub = method.getAnnotation(Subcommand.class); +219 if (sub == null) { +220 return null; +221 } +222 List<String> subList = new ArrayList<>(); +223 subList.add(sub.value()); +224 Class<?> clazz = method.getDeclaringClass(); +225 while (clazz != null) { +226 Subcommand classSub = clazz.getAnnotation(Subcommand.class); +227 if (classSub != null) { +228 subList.add(classSub.value()); +229 } +230 clazz = clazz.getEnclosingClass(); +231 } +232 Collections.reverse(subList); +233 return ACFUtil.join(subList, " "); +234 } +235 +236 private void register(String name, BaseCommand cmd) { +237 String nameLower = name.toLowerCase(); +238 RootCommand rootCommand = manager.obtainRootCommand(nameLower); +239 rootCommand.addChild(cmd); +240 +241 this.registeredCommands.put(nameLower, rootCommand); +242 } +243 +244 private void registerSubcommand(Method method, String subCommand) { +245 subCommand = manager.getCommandReplacements().replace(subCommand.toLowerCase()); +246 final String[] subCommandParts = ACFPatterns.SPACE.split(subCommand); +247 // Must run getSubcommandPossibility BEFORE we rewrite it just after this. +248 Set<String> cmdList = getSubCommandPossibilityList(subCommandParts); +249 +250 // Strip pipes off for auto complete addition +251 for (int i = 0; i < subCommandParts.length; i++) { +252 subCommandParts[i] = ACFPatterns.PIPE.split(subCommandParts[i])[0]; +253 } +254 String prefSubCommand = ApacheCommonsLangUtil.join(subCommandParts, " "); +255 final CommandAlias cmdAlias = method.getAnnotation(CommandAlias.class); +256 +257 final String[] aliasNames = cmdAlias != null ? ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(cmdAlias.value().toLowerCase())) : null; +258 String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; +259 RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); +260 +261 for (String subcmd : cmdList) { +262 subCommands.put(subcmd, cmd); +263 } +264 cmd.addSubcommands(cmdList); +265 +266 if (aliasNames != null) { +267 for (String name : aliasNames) { +268 register(name, new ForwardingCommand(this, subCommandParts)); +269 } +270 } +271 } +272 +273 /** +274 * Takes a string like "foo|bar baz|qux" and generates a list of +275 * - foo baz +276 * - foo qux +277 * - bar baz +278 * - bar qux +279 * +280 * For every possible sub command combination +281 * +282 * @param subCommandParts +283 * @return List of all sub command possibilities +284 */ +285 private static Set<String> getSubCommandPossibilityList(String[] subCommandParts) { +286 int i = 0; +287 Set<String> current = null; +288 while (true) { +289 Set<String> newList = new HashSet<>(); +290 +291 if (i < subCommandParts.length) { +292 for (String s1 : ACFPatterns.PIPE.split(subCommandParts[i])) { +293 if (current != null) { +294 newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); +295 } else { +296 newList.add(s1); +297 } +298 } +299 } +300 +301 if (i + 1 < subCommandParts.length) { +302 current = newList; +303 i = i + 1; +304 continue; +305 } +306 +307 return newList; +308 } +309 } +310 +311 public void execute(CommandIssuer issuer, String commandLabel, String[] args) { +312 commandLabel = commandLabel.toLowerCase(); +313 try { +314 CommandOperationContext commandContext = preCommandOperation(issuer, commandLabel, args); +315 +316 if (args.length > 0) { +317 CommandSearch cmd = findSubCommand(args); +318 if (cmd != null) { +319 execSubcommand = cmd.getCheckSub(); +320 final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); +321 executeCommand(commandContext, issuer, execargs, cmd.cmd); +322 return; +323 } +324 } +325 +326 if (subCommands.get(DEFAULT) != null) { +327 executeSubcommand(commandContext, DEFAULT, issuer, args); +328 return; +329 } +330 +331 if (!executeSubcommand(commandContext, UNKNOWN, issuer, args)) { +332 help(issuer, args); +333 } +334 } finally { +335 postCommandOperation(); +336 } +337 } +338 +339 private void postCommandOperation() { +340 CommandManager.commandOperationContext.get().pop(); +341 execSubcommand = null; +342 execLabel = null; +343 origArgs = new String[]{}; +344 } +345 +346 private CommandOperationContext preCommandOperation(CommandIssuer issuer, String commandLabel, String[] args) { +347 Stack<CommandOperationContext> contexts = CommandManager.commandOperationContext.get(); +348 CommandOperationContext context = this.manager.createCommandOperationContext(this, issuer, commandLabel, args); +349 contexts.push(context); +350 lastCommandOperationContext = context; +351 execSubcommand = null; +352 execLabel = commandLabel; +353 origArgs = args; +354 return context; +355 } +356 +357 public CommandIssuer getCurrentCommandIssuer() { +358 return CommandManager.getCurrentCommandIssuer(); +359 } +360 public CommandManager getCurrentCommandManager() { +361 return CommandManager.getCurrentCommandManager(); +362 } +363 +364 private CommandSearch findSubCommand(String[] args) { +365 return findSubCommand(args, false); +366 } +367 private CommandSearch findSubCommand(String[] args, boolean completion) { +368 for (int i = args.length; i >= 0; i--) { +369 String checkSub = ApacheCommonsLangUtil.join(args, " ", 0, i).toLowerCase(); +370 Set<RegisteredCommand> cmds = subCommands.get(checkSub); +371 +372 final int extraArgs = args.length - i; +373 if (!cmds.isEmpty()) { +374 RegisteredCommand cmd = null; +375 if (cmds.size() == 1) { +376 cmd = Iterables.getOnlyElement(cmds); +377 } else { +378 Optional<RegisteredCommand> optCmd = cmds.stream().filter(c -> { +379 int required = c.requiredResolvers; +380 int optional = c.optionalResolvers; +381 return extraArgs <= required + optional && (completion || extraArgs >= required); +382 }).sorted((c1, c2) -> { +383 int a = c1.requiredResolvers + c1.optionalResolvers; +384 int b = c2.requiredResolvers + c2.optionalResolvers; +385 +386 if (a == b) { +387 return 0; +388 } +389 return a < b ? 1 : -1; +390 }).findFirst(); +391 if (optCmd.isPresent()) { +392 cmd = optCmd.get(); +393 } +394 } +395 if (cmd != null) { +396 return new CommandSearch(cmd, i, checkSub); +397 } +398 } +399 } +400 return null; +401 } +402 +403 private void executeCommand(CommandOperationContext commandOperationContext, +404 CommandIssuer issuer, String[] args, RegisteredCommand cmd) { +405 if (cmd.hasPermission(issuer)) { +406 commandOperationContext.setRegisteredCommand(cmd); +407 if (checkPrecommand(commandOperationContext, cmd, issuer, args)) { +408 return; +409 } +410 List<String> sargs = Lists.newArrayList(args); +411 cmd.invoke(issuer, sargs); +412 } else { +413 issuer.sendMessage(MessageType.ERROR, MessageKeys.PERMISSION_DENIED); +414 } +415 } +416 +417 public boolean canExecute(CommandIssuer issuer, RegisteredCommand<?> cmd) { +418 return true; +419 } +420 +421 public List<String> tabComplete(CommandIssuer issuer, String commandLabel, String[] args) +422 throws IllegalArgumentException { +423 +424 commandLabel = commandLabel.toLowerCase(); +425 try { +426 CommandOperationContext commandOperationContext = preCommandOperation(issuer, commandLabel, args); +427 +428 final CommandSearch search = findSubCommand(args, true); +429 +430 String argString = ApacheCommonsLangUtil.join(args, " ").toLowerCase(); +431 +432 final List<String> cmds = new ArrayList<>(); +433 +434 if (search != null) { +435 cmds.addAll(completeCommand(commandOperationContext, issuer, search.cmd, Arrays.copyOfRange(args, search.argIndex, args.length), commandLabel)); +436 } else if (subCommands.get(UNKNOWN).size() == 1) { +437 cmds.addAll(completeCommand(commandOperationContext, issuer, Iterables.getOnlyElement(subCommands.get(UNKNOWN)), args, commandLabel)); +438 } +439 +440 for (Map.Entry<String, RegisteredCommand> entry : subCommands.entries()) { +441 final String key = entry.getKey(); +442 if (key.startsWith(argString) && !UNKNOWN.equals(key) && !DEFAULT.equals(key)) { +443 final RegisteredCommand value = entry.getValue(); +444 if (!value.hasPermission(issuer)) { +445 continue; +446 } +447 String prefCommand = value.prefSubCommand; +448 +449 final String[] psplit = ACFPatterns.SPACE.split(prefCommand); +450 cmds.add(psplit[args.length - 1]); +451 } +452 } +453 +454 return filterTabComplete(args[args.length - 1], cmds); +455 } finally { +456 postCommandOperation(); +457 } +458 } +459 +460 private List<String> completeCommand(CommandOperationContext commandOperationContext, CommandIssuer issuer, RegisteredCommand cmd, String[] args, String commandLabel) { +461 if (!cmd.hasPermission(issuer) || args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0 +462 || cmd.complete == null) { +463 return ImmutableList.of(); +464 } +465 +466 String[] completions = ACFPatterns.SPACE.split(cmd.complete); +467 +468 List<String> cmds = manager.getCommandCompletions().of(commandOperationContext, cmd, issuer, completions, args); +469 return filterTabComplete(args[args.length-1], cmds); +470 } +471 +472 private static List<String> filterTabComplete(String arg, List<String> cmds) { +473 return cmds.stream() +474 .distinct() +475 .filter(cmd -> cmd != null && (arg.isEmpty() || ApacheCommonsLangUtil.startsWithIgnoreCase(cmd, arg))) +476 .collect(Collectors.toList()); +477 } +478 +479 +480 private boolean executeSubcommand(CommandOperationContext commandContext, String subcommand, CommandIssuer issuer, String... args) { +481 final Set<RegisteredCommand> defs = subCommands.get(subcommand); +482 RegisteredCommand def = null; +483 if (!defs.isEmpty()) { +484 if (defs.size() == 1) { +485 def = defs.iterator().next(); +486 } +487 if (def != null) { +488 executeCommand(commandContext, issuer, args, def); +489 return true; +490 } +491 } +492 return false; +493 } +494 +495 private boolean checkPrecommand(CommandOperationContext commandOperationContext, RegisteredCommand cmd, CommandIssuer issuer, String[] args) { +496 Method pre = this.preCommandHandler; +497 if (pre != null) { +498 try { +499 Class<?>[] types = pre.getParameterTypes(); +500 Object[] parameters = new Object[pre.getParameterCount()]; +501 for (int i = 0; i < parameters.length; i++) { +502 Class<?> type = types[i]; +503 Object issuerObject = issuer.getIssuer(); +504 if (manager.isCommandIssuer(type) && type.isAssignableFrom(issuerObject.getClass())) { +505 parameters[i] = issuerObject; +506 } else if (CommandIssuer.class.isAssignableFrom(type)) { +507 parameters[i] = issuer; +508 } else if (RegisteredCommand.class.isAssignableFrom(type)) { +509 parameters[i] = cmd; +510 } else if (String[].class.isAssignableFrom((type))) { +511 parameters[i] = args; +512 } else { +513 parameters[i] = null; +514 } +515 } +516 +517 return (boolean) pre.invoke(this, parameters); +518 } catch (IllegalAccessException | InvocationTargetException e) { +519 this.manager.log(LogLevel.ERROR, "Exception encountered while command pre-processing", e); +520 } +521 } +522 return false; +523 } +524 +525 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +526 public CommandHelp getCommandHelp() { +527 return manager.generateCommandHelp(); +528 } +529 +530 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +531 public void showCommandHelp() { +532 getCommandHelp().showHelp(); +533 } +534 +535 public void help(Object issuer, String[] args) { +536 help(manager.getCommandIssuer(issuer), args); +537 } +538 public void help(CommandIssuer issuer, String[] args) { +539 issuer.sendMessage(MessageType.ERROR, MessageKeys.UNKNOWN_COMMAND); +540 } +541 public void doHelp(Object issuer, String... args) { +542 doHelp(manager.getCommandIssuer(issuer), args); +543 } +544 public void doHelp(CommandIssuer issuer, String... args) { +545 help(issuer, args); +546 } +547 +548 public void showSyntax(CommandIssuer issuer, RegisteredCommand<?> cmd) { +549 issuer.sendMessage(MessageType.SYNTAX, MessageKeys.INVALID_SYNTAX, +550 "{command}", "/" + cmd.command, +551 "{syntax}", cmd.syntaxText +552 ); +553 } +554 +555 public boolean hasPermission(Object issuer) { +556 return hasPermission(manager.getCommandIssuer(issuer)); +557 } +558 +559 public boolean hasPermission(CommandIssuer issuer) { +560 return permission == null || permission.isEmpty() || (manager.hasPermission(issuer, permission) && (parentCommand == null || parentCommand.hasPermission(issuer))); +561 } +562 +563 public String getName() { +564 return commandName; +565 } +566 +567 public ExceptionHandler getExceptionHandler() { +568 return exceptionHandler; +569 } +570 +571 public BaseCommand setExceptionHandler(ExceptionHandler exceptionHandler) { +572 this.exceptionHandler = exceptionHandler; +573 return this; +574 } +575 +576 private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub; +577 +578 CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) { +579 this.cmd = cmd; +580 this.argIndex = argIndex; +581 this.checkSub = checkSub; +582 } +583 +584 String getCheckSub() { +585 return this.checkSub; +586 } +587 +588 @Override +589 public boolean equals(Object o) { +590 if (this == o) return true; +591 if (o == null || getClass() != o.getClass()) return false; +592 CommandSearch that = (CommandSearch) o; +593 return argIndex == that.argIndex && +594 Objects.equals(cmd, that.cmd) && +595 Objects.equals(checkSub, that.checkSub); +596 } +597 +598 @Override +599 public int hashCode() { +600 return Objects.hash(cmd, argIndex, checkSub); +601 } +602 } +603} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 com.google.common.collect.Lists; +027import com.google.common.collect.Maps; +028 +029import java.lang.reflect.Parameter; +030import java.util.List; +031import java.util.Map; +032 +033public class CommandCompletionContext { +034 private final RegisteredCommand command; +035 protected final CommandIssuer issuer; +036 private final String input; +037 private final String config; +038 private final Map<String, String> configs = Maps.newHashMap(); +039 private final List<String> args; +040 +041 CommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) { +042 this.command = command; +043 this.issuer = issuer; +044 this.input = input; +045 if (config != null) { +046 String[] configs = ACFPatterns.COMMA.split(config); +047 for (String conf : configs) { +048 String[] confsplit = ACFPatterns.EQUALS.split(conf, 2); +049 this.configs.put(confsplit[0].toLowerCase(), confsplit.length > 1 ? confsplit[1] : null); +050 } +051 this.config = configs[0]; +052 } else { +053 this.config = null; +054 } +055 +056 this.args = Lists.newArrayList(args); +057 } +058 +059 public Map<String, String> getConfigs() { +060 return configs; +061 } +062 +063 public String getConfig(String key) { +064 return getConfig(key, null); +065 } +066 +067 public String getConfig(String key, String def) { +068 return this.configs.getOrDefault(key.toLowerCase(), def); +069 } +070 +071 public boolean hasConfig(String key) { +072 return this.configs.containsKey(key.toLowerCase()); +073 } +074 +075 public <T> T getContextValue(Class<? extends T> clazz) throws InvalidCommandArgument { +076 return getContextValue(clazz, null); +077 } +078 +079 public <T> T getContextValue(Class<? extends T> clazz, Integer paramIdx) throws InvalidCommandArgument { +080 String name = null; +081 if (paramIdx != null) { +082 if (paramIdx >= command.parameters.length) { +083 throw new IllegalArgumentException("Param index is higher than number of parameters"); +084 } +085 Parameter param = command.parameters[paramIdx]; +086 Class<?> paramType = param.getType(); +087 if (!clazz.isAssignableFrom(paramType)) { +088 throw new IllegalArgumentException(param.getName() +":" + paramType.getName() + " can not satisfy " + clazz.getName()); +089 } +090 name = param.getName(); +091 } else { +092 Parameter[] parameters = command.parameters; +093 for (int i = 0; i < parameters.length; i++) { +094 Parameter param = parameters[i]; +095 if (clazz.isAssignableFrom(param.getType())) { +096 paramIdx = i; +097 name = param.getName(); +098 break; +099 } +100 } +101 if (paramIdx == null) { +102 throw new IllegalStateException("Can not find any parameter that can satisfy " + clazz.getName()); +103 } +104 } +105 //noinspection unchecked +106 Map<String, Object> resolved = command.resolveContexts(issuer, args, args.size()); +107 if (resolved == null || paramIdx > resolved.size()) { +108 this.command.scope.manager.log(LogLevel.ERROR, "resolved: " + resolved + " paramIdx: " + paramIdx + " - size: " + (resolved != null ? resolved.size() : null )); +109 ACFUtil.sneaky(new CommandCompletionTextLookupException()); +110 } +111 +112 //noinspection unchecked +113 return (T) resolved.get(name); +114 } +115 +116 public CommandIssuer getIssuer() { +117 return issuer; +118 } +119 +120 public String getInput() { +121 return input; +122 } +123 +124 public String getConfig() { +125 return config; +126 } +127} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 com.google.common.collect.ImmutableList; +027import com.google.common.collect.Lists; +028import org.jetbrains.annotations.NotNull; +029 +030import java.util.Collection; +031import java.util.HashMap; +032import java.util.List; +033import java.util.Map; +034import java.util.stream.Collectors; +035import java.util.stream.IntStream; +036 +037 +038@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) +039public class CommandCompletions <C extends CommandCompletionContext> { +040 private final CommandManager manager; +041 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); +042 +043 public CommandCompletions(CommandManager manager) { +044 this.manager = manager; +045 registerCompletion("nothing", c -> ImmutableList.of()); +046 registerCompletion("range", (c) -> { +047 String config = c.getConfig(); +048 if (config == null) { +049 return ImmutableList.of(); +050 } +051 final String[] ranges = ACFPatterns.DASH.split(config); +052 int start; +053 int end; +054 if (ranges.length != 2) { +055 start = 0; +056 end = ACFUtil.parseInt(ranges[0], 0); +057 } else { +058 start = ACFUtil.parseInt(ranges[0], 0); +059 end = ACFUtil.parseInt(ranges[1], 0); +060 } +061 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); +062 }); +063 registerCompletion("timeunits", (c) -> ImmutableList.of("minutes", "hours", "days", "weeks", "months", "years")); +064 } +065 +066 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { +067 return this.completionMap.put("@" + id.toLowerCase(), handler); +068 } +069 +070 @NotNull +071 List<String> of(CommandOperationContext commandOperationContext, RegisteredCommand command, CommandIssuer sender, String[] completionInfo, String[] args) { +072 final int argIndex = args.length - 1; +073 +074 String input = args[argIndex]; +075 final String completion = argIndex < completionInfo.length ? completionInfo[argIndex] : null; +076 if (completion == null) { +077 return ImmutableList.of(input); +078 } +079 +080 return getCompletionValues(command, sender, completion, args); +081 } +082 +083 @NotNull +084 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args) { +085 completion = manager.getCommandReplacements().replace(completion); +086 +087 List<String> allCompletions = Lists.newArrayList(); +088 String input = args.length > 0 ? args[args.length - 1] : ""; +089 +090 for (String value : ACFPatterns.PIPE.split(completion)) { +091 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); +092 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); +093 if (handler != null) { +094 String config = complete.length == 1 ? null : complete[1]; +095 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); +096 +097 try { +098 //noinspection unchecked +099 Collection<String> completions = handler.getCompletions(context); +100 if (completions != null) { +101 allCompletions.addAll(completions); +102 continue; +103 } +104 //noinspection ConstantIfStatement,ConstantConditions +105 if (false) { // Hack to fool compiler. since its sneakily thrown. +106 throw new CommandCompletionTextLookupException(); +107 } +108 } catch (CommandCompletionTextLookupException ignored) { +109 // This should only happen if some other feedback error occured. +110 } catch (Exception e) { +111 command.handleException(sender, Lists.newArrayList(args), e); +112 } +113 // Something went wrong in lookup, fall back to input +114 return ImmutableList.of(input); +115 } else { +116 // Plaintext value +117 allCompletions.add(value); +118 } +119 } +120 return allCompletions; +121 } +122 +123 public interface CommandCompletionHandler <C extends CommandCompletionContext> { +124 Collection<String> getCompletions(C context) throws InvalidCommandArgument; +125 } +126 +127} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 com.google.common.collect.ImmutableList; +027import com.google.common.collect.Lists; +028import org.jetbrains.annotations.NotNull; +029 +030import java.util.Collection; +031import java.util.HashMap; +032import java.util.List; +033import java.util.Map; +034import java.util.stream.Collectors; +035import java.util.stream.IntStream; +036 +037 +038@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) +039public class CommandCompletions <C extends CommandCompletionContext> { +040 private final CommandManager manager; +041 private Map<String, CommandCompletionHandler> completionMap = new HashMap<>(); +042 +043 public CommandCompletions(CommandManager manager) { +044 this.manager = manager; +045 registerCompletion("nothing", c -> ImmutableList.of()); +046 registerCompletion("range", (c) -> { +047 String config = c.getConfig(); +048 if (config == null) { +049 return ImmutableList.of(); +050 } +051 final String[] ranges = ACFPatterns.DASH.split(config); +052 int start; +053 int end; +054 if (ranges.length != 2) { +055 start = 0; +056 end = ACFUtil.parseInt(ranges[0], 0); +057 } else { +058 start = ACFUtil.parseInt(ranges[0], 0); +059 end = ACFUtil.parseInt(ranges[1], 0); +060 } +061 return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); +062 }); +063 registerCompletion("timeunits", (c) -> ImmutableList.of("minutes", "hours", "days", "weeks", "months", "years")); +064 } +065 +066 public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler<C> handler) { +067 return this.completionMap.put("@" + id.toLowerCase(), handler); +068 } +069 +070 @NotNull +071 List<String> of(CommandOperationContext commandOperationContext, RegisteredCommand command, CommandIssuer sender, String[] completionInfo, String[] args) { +072 final int argIndex = args.length - 1; +073 +074 String input = args[argIndex]; +075 final String completion = argIndex < completionInfo.length ? completionInfo[argIndex] : null; +076 if (completion == null) { +077 return ImmutableList.of(input); +078 } +079 +080 return getCompletionValues(command, sender, completion, args); +081 } +082 +083 @NotNull +084 List<String> getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args) { +085 completion = manager.getCommandReplacements().replace(completion); +086 +087 List<String> allCompletions = Lists.newArrayList(); +088 String input = args.length > 0 ? args[args.length - 1] : ""; +089 +090 for (String value : ACFPatterns.PIPE.split(completion)) { +091 String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); +092 CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase()); +093 if (handler != null) { +094 String config = complete.length == 1 ? null : complete[1]; +095 CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); +096 +097 try { +098 //noinspection unchecked +099 Collection<String> completions = handler.getCompletions(context); +100 if (completions != null) { +101 allCompletions.addAll(completions); +102 continue; +103 } +104 //noinspection ConstantIfStatement,ConstantConditions +105 if (false) { // Hack to fool compiler. since its sneakily thrown. +106 throw new CommandCompletionTextLookupException(); +107 } +108 } catch (CommandCompletionTextLookupException ignored) { +109 // This should only happen if some other feedback error occured. +110 } catch (Exception e) { +111 command.handleException(sender, Lists.newArrayList(args), e); +112 } +113 // Something went wrong in lookup, fall back to input +114 return ImmutableList.of(input); +115 } else { +116 // Plaintext value +117 allCompletions.add(value); +118 } +119 } +120 return allCompletions; +121 } +122 +123 public interface CommandCompletionHandler <C extends CommandCompletionContext> { +124 Collection<String> getCompletions(C context) throws InvalidCommandArgument; +125 } +126 +127} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.Single; +027import co.aikar.commands.annotation.Split; +028import co.aikar.commands.annotation.Values; +029import co.aikar.commands.contexts.ContextResolver; +030import co.aikar.commands.contexts.IssuerAwareContextResolver; +031import co.aikar.commands.contexts.IssuerOnlyContextResolver; +032import co.aikar.commands.contexts.OptionalContextResolver; +033import com.google.common.collect.Maps; +034 +035import java.util.List; +036import java.util.Map; +037 +038@SuppressWarnings("WeakerAccess") +039public class CommandContexts <R extends CommandExecutionContext<?, ? extends CommandIssuer>> { +040 protected final Map<Class<?>, ContextResolver<?, R>> contextMap = Maps.newHashMap(); +041 protected final CommandManager manager; +042 +043 CommandContexts(CommandManager manager) { +044 this.manager = manager; +045 registerContext(Integer.class, (c) -> { +046 try { +047 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).intValue(); +048 } catch (NumberFormatException e) { +049 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER); +050 } +051 }); +052 registerContext(Long.class, (c) -> { +053 try { +054 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).longValue(); +055 } catch (NumberFormatException e) { +056 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER); +057 } +058 +059 }); +060 registerContext(Float.class, (c) -> { +061 try { +062 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).floatValue(); +063 } catch (NumberFormatException e) { +064 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER); +065 } +066 }); +067 registerContext(Double.class, (c) -> { +068 try { +069 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).doubleValue(); +070 } catch (NumberFormatException e) { +071 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER); +072 } +073 }); +074 registerContext(Number.class, (c) -> { +075 try { +076 return ACFUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")); +077 } catch (NumberFormatException e) { +078 throw new InvalidCommandArgument(MessageKeys.MUST_BE_A_NUMBER); +079 } +080 }); +081 registerContext(Boolean.class, (c) -> { +082 String test = c.popFirstArg(); +083 if (test == null) { +084 return null; +085 } +086 return ACFUtil.isTruthy(test); +087 }); +088 registerContext(String.class, (c) -> { +089 final Values values = c.getParam().getAnnotation(Values.class); +090 if (values != null) { +091 return c.popFirstArg(); +092 } +093 String ret = (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) ? +094 ACFUtil.join(c.getArgs()) +095 : +096 c.popFirstArg(); +097 +098 Integer minLen = c.getFlagValue("minlen", (Integer) null); +099 Integer maxLen = c.getFlagValue("maxlen", (Integer) null); +100 if (minLen != null) { +101 if (ret.length() < minLen) { +102 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MIN_LENGTH, "{min}", String.valueOf(minLen)); +103 } +104 } +105 if (maxLen != null) { +106 if (ret.length() > maxLen) { +107 throw new InvalidCommandArgument(MessageKeys.MUST_BE_MAX_LENGTH, "{max}", String.valueOf(maxLen)); +108 } +109 } +110 +111 return ret; +112 }); +113 registerContext(String[].class, (c) -> { +114 String val; +115 // Go home IDEA, you're drunk +116 //noinspection unchecked +117 List<String> args = c.getArgs(); +118 if (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) { +119 val = ACFUtil.join(args); +120 } else { +121 val = c.popFirstArg(); +122 } +123 Split split = c.getParam().getAnnotation(Split.class); +124 if (split != null) { +125 if (val.isEmpty()) { +126 throw new InvalidCommandArgument(); +127 } +128 return ACFPatterns.getPattern(split.value()).split(val); +129 } else if (!c.isLastArg()) { +130 ACFUtil.sneaky(new IllegalStateException("Weird Command signature... String[] should be last or @Split")); +131 } +132 +133 String[] result = args.toArray(new String[args.size()]); +134 args.clear(); +135 return result; +136 }); +137 +138 registerContext(Enum.class, (c) -> { +139 final String first = c.popFirstArg(); +140 //noinspection unchecked +141 Class<? extends Enum<?>> enumCls = (Class<? extends Enum<?>>) c.getParam().getType(); +142 Enum<?> match = ACFUtil.simpleMatch(enumCls, first); +143 if (match == null) { +144 List<String> names = ACFUtil.enumNames(enumCls); +145 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, "{valid}", ACFUtil.join(names)); +146 } +147 return match; +148 }); +149 registerOptionalContext(CommandHelp.class, (c) -> { +150 String first = c.getFirstArg(); +151 String last = c.getLastArg(); +152 int page = 1; +153 List<String> search = null; +154 if (last != null && ACFUtil.isInteger(last)) { +155 c.popLastArg(); +156 page = ACFUtil.parseInt(last); +157 if (!c.getArgs().isEmpty()) { +158 search = c.getArgs(); +159 } +160 } else if (first != null && ACFUtil.isInteger(first)) { +161 c.popFirstArg(); +162 page = ACFUtil.parseInt(first); +163 if (!c.getArgs().isEmpty()) { +164 search = c.getArgs(); +165 } +166 } else if (!c.getArgs().isEmpty()) { +167 search = c.getArgs(); +168 } +169 CommandHelp commandHelp = manager.generateCommandHelp(); +170 commandHelp.setPage(page); +171 commandHelp.setSearch(search); +172 return commandHelp; +173 }); +174 } +175 +176 /** +177 * @deprecated Please switch to {@link #registerIssuerAwareContext(Class, IssuerAwareContextResolver)} +178 * as the core wants to use the platform agnostic term of "Issuer" instead of Sender +179 * @see #registerIssuerAwareContext(Class, IssuerAwareContextResolver) +180 */ +181 @Deprecated +182 public <T> void registerSenderAwareContext(Class<T> context, IssuerAwareContextResolver<T, R> supplier) { +183 contextMap.put(context, supplier); +184 } +185 +186 /** +187 * Registers a context resolver that may conditionally consume input, falling back to using the context of the +188 * issuer to potentially fulfill this context. +189 * You may call {@link CommandExecutionContext#getFirstArg()} and then conditionally call {@link CommandExecutionContext#popFirstArg()} +190 * if you want to consume that input. +191 */ +192 public <T> void registerIssuerAwareContext(Class<T> context, IssuerAwareContextResolver<T, R> supplier) { +193 contextMap.put(context, supplier); +194 } +195 +196 /** +197 * Registers a context resolver that will never consume input. It will always satisfy its context based on the +198 * issuer of the command, so it will not appear in syntax strings. +199 */ +200 public <T> void registerIssuerOnlyContext(Class<T> context, IssuerOnlyContextResolver<T, R> supplier) { +201 contextMap.put(context, supplier); +202 } +203 +204 /** +205 * Registers a context that can safely accept a null input from the command issuer to resolve. This resolver should always +206 * call {@link CommandExecutionContext#popFirstArg()} +207 */ +208 public <T> void registerOptionalContext(Class<T> context, OptionalContextResolver<T, R> supplier) { +209 contextMap.put(context, supplier); +210 } +211 +212 /** +213 * Registers a context that requires input from the command issuer to resolve. This resolver should always +214 * call {@link CommandExecutionContext#popFirstArg()} +215 */ +216 public <T> void registerContext(Class<T> context, ContextResolver<T, R> supplier) { +217 contextMap.put(context, supplier); +218 } +219 +220 public ContextResolver<?, R> getResolver(Class<?> type) { +221 Class<?> rootType = type; +222 do { +223 if (type == Object.class) { +224 break; +225 } +226 +227 final ContextResolver<?, R> resolver = contextMap.get(type); +228 if (resolver != null) { +229 return resolver; +230 } +231 } while ((type = type.getSuperclass()) != null); +232 +233 this.manager.log(LogLevel.ERROR, "Could not find context resolver", new IllegalStateException("No context resolver defined for " + rootType.getName())); +234 return null; +235 } +236} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.Default; +027import co.aikar.commands.annotation.Flags; +028import co.aikar.commands.annotation.Optional; +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; +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 <T 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 +049 CommandExecutionContext(RegisteredCommand cmd, Parameter param, I sender, List<String> args, +050 int index, Map<String, Object> passedArgs) { +051 this.cmd = cmd; +052 this.param = param; +053 this.issuer = sender; +054 this.args = args; +055 this.index = index; +056 this.passedArgs = passedArgs; +057 Flags flags = param.getAnnotation(Flags.class); +058 if (flags != null) { +059 this.flags = Maps.newHashMap(); +060 for (String s : ACFPatterns.COMMA.split(cmd.scope.manager.getCommandReplacements().replace(flags.value()))) { +061 String[] v = ACFPatterns.EQUALS.split(s, 2); +062 this.flags.put(v[0], v.length > 1 ? v[1] : null); +063 } +064 } else { +065 this.flags = ImmutableMap.of(); +066 } +067 } +068 +069 public String popFirstArg() { +070 return !args.isEmpty() ? args.remove(0) : null; +071 } +072 +073 public String popLastArg() { +074 return !args.isEmpty() ? args.remove(args.size() - 1) : null; +075 } +076 +077 public String getFirstArg() { +078 return !args.isEmpty() ? args.get(0) : null; +079 } +080 +081 public String getLastArg() { +082 return !args.isEmpty() ? args.get(args.size() - 1) : null; +083 } +084 +085 public boolean isLastArg() { +086 return cmd.parameters.length -1 == index; +087 } +088 +089 public int getNumParams() { +090 return cmd.parameters.length; +091 } +092 +093 public boolean canOverridePlayerContext() { +094 int numRequired = getNumParams(); +095 for (int i = 0; i < cmd.resolvers.length; i++) { +096 Parameter parameter = cmd.parameters[i]; +097 //noinspection unchecked +098 ContextResolver<?, ?> resolver = cmd.resolvers[i]; +099 if (parameter.getAnnotation(Optional.class) != null || parameter.getAnnotation(Default.class) != null) { +100 numRequired--; +101 } else if (resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver) { +102 numRequired--; +103 } +104 } +105 +106 return numRequired >= args.size(); +107 } +108 +109 public Object getResolvedArg(String arg) { +110 return passedArgs.get(arg); +111 } +112 +113 public Object getResolvedArg(Class<?>... classes) { +114 for (Class<?> clazz : classes) { +115 for (Object passedArg : passedArgs.values()) { +116 if (clazz.isInstance(passedArg)) { +117 return passedArg; +118 } +119 } +120 } +121 +122 return null; +123 } +124 +125 public <T> T getResolvedArg(String key, Class<?>... classes) { +126 final Object o = passedArgs.get(key); +127 for (Class<?> clazz : classes) { +128 if (clazz.isInstance(o)) { +129 //noinspection unchecked +130 return (T) o; +131 } +132 } +133 +134 return null; +135 } +136 +137 public boolean isOptional() { +138 return param.getAnnotation(Optional.class) != null; +139 } +140 public boolean hasFlag(String flag) { +141 return flags.containsKey(flag); +142 } +143 +144 public String getFlagValue(String flag, String def) { +145 return flags.getOrDefault(flag, def); +146 } +147 +148 public Integer getFlagValue(String flag, Integer def) { +149 return ACFUtil.parseInt(this.flags.get(flag), def); +150 } +151 +152 public <T extends Annotation> T getAnnotation(Class<T> cls) { +153 return param.getAnnotation(cls); +154 } +155 +156 public <T extends Annotation> boolean hasAnnotation(Class<T> cls) { +157 return param.getAnnotation(cls) != null; +158 } +159 +160 public RegisteredCommand getCmd() { +161 return this.cmd; +162 } +163 +164 public Parameter getParam() { +165 return this.param; +166 } +167 +168 public I getIssuer() { +169 return this.issuer; +170 } +171 +172 public List<String> getArgs() { +173 return this.args; +174 } +175 +176 public int getIndex() { +177 return this.index; +178 } +179 +180 public Map<String, Object> getPassedArgs() { +181 return this.passedArgs; +182 } +183 +184 public Map<String, String> getFlags() { +185 return this.flags; +186 } +187 +188 public String joinArgs() { +189 return ACFUtil.join(args, " "); +190 } +191 public String joinArgs(String sep) { +192 return ACFUtil.join(args, sep); +193 } +194} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.locales.MessageKeyProvider; +027import com.google.common.collect.SetMultimap; +028import org.jetbrains.annotations.NotNull; +029 +030import java.util.*; +031import java.util.regex.Pattern; +032import java.util.stream.Stream; +033 +034@SuppressWarnings("WeakerAccess") +035public class CommandHelp { +036 private final CommandManager manager; +037 private final CommandIssuer issuer; +038 private final List<HelpEntry> helpEntries = new ArrayList<>(); +039 private int page; +040 private List<String> search; +041 +042 public CommandHelp(CommandManager manager, RootCommand rootCommand, CommandIssuer issuer) { +043 this.manager = manager; +044 this.issuer = issuer; +045 +046 SetMultimap<String, RegisteredCommand> subCommands = rootCommand.getSubCommands(); +047 Set<RegisteredCommand> seen = new HashSet<>(); +048 subCommands.entries().forEach(e -> { +049 String key = e.getKey(); +050 if (key.equals("__default") || key.equals("__unknown")){ +051 return; +052 } +053 +054 RegisteredCommand regCommand = e.getValue(); +055 if (regCommand.hasPermission(issuer) && !seen.contains(regCommand)) { +056 this.helpEntries.add(new HelpEntry(regCommand)); +057 seen.add(regCommand); +058 } +059 }); +060 } +061 +062 @UnstableAPI // Not sure on this one yet even when API becomes unstable +063 protected void updateSearchScore(HelpEntry help) { +064 if (this.search == null || this.search.isEmpty()) { +065 help.setSearchScore(1); +066 return; +067 } +068 final RegisteredCommand<?> cmd = help.getRegisteredCommand(); +069 +070 int searchScore = 0; +071 for (String word : this.search) { +072 Pattern pattern = Pattern.compile(".*" + Pattern.quote(word) + ".*", Pattern.CASE_INSENSITIVE); +073 for (String subCmd : cmd.registeredSubcommands) { +074 Pattern subCmdPattern = Pattern.compile(".*" + Pattern.quote(subCmd) + ".*", Pattern.CASE_INSENSITIVE); +075 if (pattern.matcher(subCmd).matches()) { +076 searchScore += 3; +077 } else if (subCmdPattern.matcher(word).matches()) { +078 searchScore++; +079 } +080 } +081 +082 +083 if (pattern.matcher(help.getDescription()).matches()) { +084 searchScore += 2; +085 } +086 if (pattern.matcher(help.getParameterSyntax()).matches()) { +087 searchScore++; +088 } +089 if (help.getSearchTags() != null && pattern.matcher(help.getSearchTags()).matches()) { +090 searchScore += 2; +091 } +092 } +093 help.setSearchScore(searchScore); +094 } +095 +096 public CommandManager getManager() { +097 return manager; +098 } +099 +100 public void showHelp() { +101 showHelp(issuer, MessageKeys.HELP_FORMAT); +102 } +103 +104 public void showHelp(CommandIssuer issuer) { +105 showHelp(issuer, MessageKeys.HELP_FORMAT); +106 } +107 +108 public void showHelp(CommandIssuer issuer, MessageKeyProvider format) { +109 Iterator<HelpEntry> results = getHelpEntries().stream() +110 .filter(HelpEntry::shouldShow) +111 .sorted(Comparator.comparingInt(helpEntry -> helpEntry.getSearchScore() * -1)).iterator(); +112 if (!results.hasNext()) { +113 issuer.sendMessage(MessageType.ERROR, MessageKeys.NO_COMMAND_MATCHED_SEARCH, "{search}", ACFUtil.join(this.search, " ")); +114 results = getHelpEntries().iterator(); +115 } +116 +117 while (results.hasNext()) { +118 HelpEntry e = results.next(); +119 String formatted = this.manager.formatMessage(issuer, MessageType.HELP, format, getFormatReplacements(e)); +120 for (String msg : ACFPatterns.NEWLINE.split(formatted)) { +121 issuer.sendMessageInternal(ACFUtil.rtrim(msg)); +122 } +123 } +124 } +125 +126 /** +127 * Override this to control replacements +128 * @param e +129 * @return +130 */ +131 @NotNull +132 public String[] getFormatReplacements(HelpEntry e) { +133 //{command} {parameters} {seperator} {description} +134 return new String[] { +135 "{command}", e.getCommand(), +136 "{parameters}", e.getParameterSyntax(), +137 "{seperator}", e.getDescription().isEmpty() ? "" : "-", +138 "{description}", e.getDescription() +139 }; +140 } +141 +142 public List<HelpEntry> getHelpEntries() { +143 return helpEntries; +144 } +145 +146 public void setPage(int page) { +147 this.page = page; +148 } +149 +150 public void setSearch(List<String> search) { +151 this.search = search; +152 getHelpEntries().forEach(this::updateSearchScore); +153 } +154} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.locales.MessageKey; +027import co.aikar.locales.MessageKeyProvider; +028 +029public interface CommandIssuer { +030 /** +031 * Gets the issuer in the platforms native object +032 * @param <T> +033 * @return +034 */ +035 <T> T getIssuer(); +036 +037 CommandManager getManager(); +038 +039 /** +040 * Is this issue a player, or server/console sender +041 * @return +042 */ +043 boolean isPlayer(); +044 +045 /** +046 * Send the Command Issuer a message +047 * @param message +048 */ +049 default void sendMessage(String message) { +050 getManager().sendMessage(this, MessageType.INFO, MessageKeys.INFO_MESSAGE, "{message}", message); +051 } +052 +053 /** +054 * Has permission node +055 * @param permission +056 * @return +057 */ +058 boolean hasPermission(String permission); +059 +060 default void sendError(MessageKeyProvider key, String... replacements) { +061 sendMessage(MessageType.ERROR, key.getMessageKey(), replacements); +062 } +063 default void sendSyntax(MessageKeyProvider key, String... replacements) { +064 sendMessage(MessageType.SYNTAX, key.getMessageKey(), replacements); +065 } +066 default void sendInfo(MessageKeyProvider key, String... replacements) { +067 sendMessage(MessageType.INFO, key.getMessageKey(), replacements); +068 } +069 default void sendError(MessageKey key, String... replacements) { +070 sendMessage(MessageType.ERROR, key, replacements); +071 } +072 default void sendSyntax(MessageKey key, String... replacements) { +073 sendMessage(MessageType.SYNTAX, key, replacements); +074 } +075 default void sendInfo(MessageKey key, String... replacements) { +076 sendMessage(MessageType.INFO, key, replacements); +077 } +078 default void sendMessage(MessageType type, MessageKeyProvider key, String... replacements) { +079 sendMessage(type, key.getMessageKey(), replacements); +080 } +081 default void sendMessage(MessageType type, MessageKey key, String... replacements) { +082 getManager().sendMessage(this, type, key, replacements); +083 } +084 +085 /** +086 * @deprecated Do not call this, for internal use. Not considered part of the API and may break. +087 * @param message +088 */ +089 @Deprecated +090 void sendMessageInternal(String message); +091} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.locales.MessageKeyProvider; +027import com.google.common.collect.Sets; +028import org.jetbrains.annotations.NotNull; +029 +030import java.lang.reflect.Method; +031import java.lang.reflect.Parameter; +032import java.util.*; +033 +034@SuppressWarnings("WeakerAccess") +035public abstract class CommandManager <I, FT, F extends MessageFormatter<FT>> { +036 +037 /** +038 * This is a stack incase a command calls a command +039 */ +040 static ThreadLocal<Stack<CommandOperationContext>> commandOperationContext = ThreadLocal.withInitial(() -> { +041 return new Stack<CommandOperationContext>() { +042 @Override +043 public synchronized CommandOperationContext peek() { +044 return super.size() == 0 ? null : super.peek(); +045 } +046 }; +047 }); +048 protected Map<String, RootCommand> rootCommands = new HashMap<>(); +049 protected CommandReplacements replacements = new CommandReplacements(this); +050 protected ExceptionHandler defaultExceptionHandler = null; +051 +052 protected Set<Locale> supportedLanguages = Sets.newHashSet(Locales.ENGLISH, Locales.GERMAN, Locales.SPANISH, Locales.CZECH); +053 protected Map<MessageType, F> formatters = new IdentityHashMap<>(); +054 protected F defaultFormatter; +055 private Set<String> unstableAPIs = Sets.newHashSet(); +056 +057 public static CommandOperationContext getCurrentCommandOperationContext() { +058 return commandOperationContext.get().peek(); +059 } +060 +061 public static CommandIssuer getCurrentCommandIssuer() { +062 CommandOperationContext context = commandOperationContext.get().peek(); +063 return context != null ? context.getCommandIssuer() : null; +064 } +065 +066 public static CommandManager getCurrentCommandManager() { +067 CommandOperationContext context = commandOperationContext.get().peek(); +068 return context != null ? context.getCommandManager() : null; +069 } +070 +071 public F setFormat(MessageType type, F formatter) { +072 return formatters.put(type, formatter); +073 } +074 +075 public F getFormat(MessageType type) { +076 return formatters.getOrDefault(type, defaultFormatter); +077 } +078 +079 public void setFormat(MessageType type, FT... colors) { +080 F format = getFormat(type); +081 for (int i = 0; i < colors.length; i++) { +082 format.setColor(i, colors[i]); +083 } +084 } +085 +086 public void setFormat(MessageType type, int i, FT color) { +087 F format = getFormat(type); +088 format.setColor(i, color); +089 } +090 +091 public F getDefaultFormatter() { +092 return defaultFormatter; +093 } +094 +095 public void setDefaultFormatter(F defaultFormatter) { +096 this.defaultFormatter = defaultFormatter; +097 } +098 +099 /** +100 * Gets the command contexts manager +101 * @return Command Contexts +102 */ +103 public abstract CommandContexts<?> getCommandContexts(); +104 +105 /** +106 * Gets the command completions manager +107 * @return Command Completions +108 */ +109 public abstract CommandCompletions<?> getCommandCompletions(); +110 +111 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +112 public CommandHelp generateCommandHelp(@NotNull String command) { +113 verifyUnstableAPI("help"); +114 CommandOperationContext context = getCurrentCommandOperationContext(); +115 if (context == null) { +116 throw new IllegalStateException("This method can only be called as part of a command execution."); +117 } +118 return generateCommandHelp(context.getCommandIssuer(), command); +119 } +120 +121 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +122 public CommandHelp generateCommandHelp(CommandIssuer issuer, @NotNull String command) { +123 verifyUnstableAPI("help"); +124 return generateCommandHelp(issuer, obtainRootCommand(ACFPatterns.SPACE.split(command, 2)[0])); +125 } +126 +127 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +128 public CommandHelp generateCommandHelp() { +129 verifyUnstableAPI("help"); +130 CommandOperationContext context = getCurrentCommandOperationContext(); +131 if (context == null) { +132 throw new IllegalStateException("This method can only be called as part of a command execution."); +133 } +134 String commandLabel = context.getCommandLabel(); +135 return generateCommandHelp(context.getCommandIssuer(), this.obtainRootCommand(commandLabel)); +136 } +137 +138 /** @deprecated Unstable API */ @Deprecated @UnstableAPI +139 public CommandHelp generateCommandHelp(CommandIssuer issuer, RootCommand rootCommand) { +140 verifyUnstableAPI("help"); +141 return new CommandHelp(this, rootCommand, issuer); +142 } +143 +144 /** +145 * Registers a command with ACF +146 * +147 * @param command The command to register +148 * @return boolean +149 */ +150 public abstract void registerCommand(BaseCommand command); +151 public abstract boolean hasRegisteredCommands(); +152 public abstract boolean isCommandIssuer(Class<?> type); +153 +154 // TODO: Change this to I if we make a breaking change +155 public abstract CommandIssuer getCommandIssuer(Object issuer); +156 +157 public abstract RootCommand createRootCommand(String cmd); +158 +159 /** +160 * Returns a Locales Manager to add and modify language tables for your commands. +161 * @return +162 */ +163 public abstract Locales getLocales(); +164 +165 public abstract <R extends CommandExecutionContext> R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs); +166 +167 public abstract CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args); +168 +169 public abstract void log(final LogLevel level, final String message, final Throwable throwable); +170 +171 public void log(final LogLevel level, final String message) { +172 log(level, message, null); +173 } +174 +175 /** +176 * Lets you add custom string replacements that can be applied to annotation values, +177 * to reduce duplication/repetition of common values such as permission nodes and command prefixes. +178 * +179 * Any replacement registered starts with a % +180 * +181 * So for ex @CommandPermission("%staff") +182 * @return Replacements Manager +183 */ +184 public CommandReplacements getCommandReplacements() { +185 return replacements; +186 } +187 +188 public boolean hasPermission(CommandIssuer issuer, String permission) { +189 return permission == null || permission.isEmpty() || issuer.hasPermission(permission); +190 } +191 +192 public synchronized RootCommand obtainRootCommand(String cmd) { +193 return rootCommands.computeIfAbsent(cmd.toLowerCase(), this::createRootCommand); +194 } +195 +196 public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) { +197 return new RegisteredCommand(command, cmdName, method, prefSubCommand); +198 } +199 +200 /** +201 * Sets the default {@link ExceptionHandler} that is called when an exception occurs while executing a command, if the command doesn't have it's own exception handler registered. +202 * +203 * @param exceptionHandler the handler that should handle uncaught exceptions +204 */ +205 public void setDefaultExceptionHandler(ExceptionHandler exceptionHandler) { +206 defaultExceptionHandler = exceptionHandler; +207 } +208 +209 /** +210 * Gets the current default exception handler, might be null. +211 * +212 * @return the default exception handler +213 */ +214 public ExceptionHandler getDefaultExceptionHandler() { +215 return defaultExceptionHandler; +216 } +217 +218 protected boolean handleUncaughtException(BaseCommand scope, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t) { +219 boolean result = false; +220 if (scope.getExceptionHandler() != null) { +221 result = scope.getExceptionHandler().execute(scope, registeredCommand, sender, args, t); +222 } else if (defaultExceptionHandler != null) { +223 result = defaultExceptionHandler.execute(scope, registeredCommand, sender, args, t); +224 } +225 return result; +226 } +227 +228 public void sendMessage(I issuerArg, MessageType type, MessageKeyProvider key, String... replacements) { +229 sendMessage(getCommandIssuer(issuerArg), type, key, replacements); +230 } +231 +232 public void sendMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) { +233 String message = formatMessage(issuer, type, key, replacements); +234 +235 for (String msg : ACFPatterns.NEWLINE.split(message)) { +236 issuer.sendMessageInternal(ACFUtil.rtrim(msg)); +237 } +238 } +239 +240 public String formatMessage(CommandIssuer issuer, MessageType type, MessageKeyProvider key, String... replacements) { +241 String message = getLocales().getMessage(issuer, key.getMessageKey()); +242 if (replacements.length > 0) { +243 message = ACFUtil.replaceStrings(message, replacements); +244 } +245 +246 message = getCommandReplacements().replace(message); +247 +248 MessageFormatter formatter = formatters.getOrDefault(type, defaultFormatter); +249 if (formatter != null) { +250 message = formatter.format(message); +251 } +252 return message; +253 } +254 +255 public Locale getIssuerLocale(CommandIssuer issuer) { +256 return getLocales().getDefaultLocale(); +257 } +258 +259 +260 public CommandOperationContext createCommandOperationContext(BaseCommand command, CommandIssuer issuer, String commandLabel, String[] args) { +261 return new CommandOperationContext( +262 this, +263 issuer, +264 command, +265 commandLabel, +266 args +267 ); +268 } +269 +270 /** +271 * Gets a list of all currently supported languages for this manager. +272 * These locales will be automatically loaded from +273 * @return +274 */ +275 public Set<Locale> getSupportedLanguages() { +276 return supportedLanguages; +277 } +278 +279 /** +280 * Adds a new locale to the list of automatic Locales to load Message Bundles for. +281 * All bundles loaded under the previous supported languages will now automatically load for this new locale too. +282 * +283 * @param locale +284 */ +285 public void addSupportedLanguage(Locale locale) { +286 supportedLanguages.add(locale); +287 getLocales().loadMissingBundles(); +288 } +289 +290 /** +291 * @deprecated Use this with caution! If you enable and use Unstable API's, your next compile using ACF +292 * may require you to update your implementation to those unstable API's +293 */ +294 @Deprecated +295 public void enableUnstableAPI(String api) { +296 unstableAPIs.add(api); +297 } +298 void verifyUnstableAPI(String api) { +299 if (!unstableAPIs.contains(api)) { +300 throw new IllegalStateException("Using an unstable API that has not been enabled ( " + api + "). See https://acfunstable.emc.gs"); +301 } +302 } +303} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 java.lang.annotation.Annotation; +027 +028/** +029 * Holds information about the currently executing command on this thread +030 */ +031public class CommandOperationContext { +032 +033 private final CommandManager manager; +034 private final CommandIssuer issuer; +035 private final BaseCommand command; +036 private final String commandLabel; +037 private final String[] args; +038 private RegisteredCommand registeredCommand; +039 +040 CommandOperationContext(CommandManager manager, CommandIssuer issuer, BaseCommand command, String commandLabel, String[] args) { +041 this.manager = manager; +042 this.issuer = issuer; +043 this.command = command; +044 this.commandLabel = commandLabel; +045 this.args = args; +046 } +047 +048 public CommandManager getCommandManager() { +049 return manager; +050 } +051 +052 public CommandIssuer getCommandIssuer() { +053 return issuer; +054 } +055 +056 public BaseCommand getCommand() { +057 return command; +058 } +059 +060 public String getCommandLabel() { +061 return commandLabel; +062 } +063 +064 public String[] getArgs() { +065 return args; +066 } +067 +068 public void setRegisteredCommand(RegisteredCommand registeredCommand) { +069 this.registeredCommand = registeredCommand; +070 } +071 +072 public RegisteredCommand getRegisteredCommand() { +073 return registeredCommand; +074 } +075 +076 public <T extends Annotation> T getAnnotation(Class<T> anno) { +077 return registeredCommand.method.getAnnotation(anno); +078 } +079 +080 public boolean hasAnnotation(Class<? extends Annotation> anno) { +081 return getAnnotation(anno) != null; +082 } +083 +084} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 java.util.AbstractMap; +027import java.util.LinkedHashMap; +028import java.util.Map; +029import java.util.regex.Pattern; +030 +031/** +032 * Manages replacement template strings +033 */ +034public class CommandReplacements { +035 +036 private final CommandManager manager; +037 +038 CommandReplacements(CommandManager manager) { +039 this.manager = manager; +040 } +041 private final Map<String, Map.Entry<Pattern, String>> replacements = new LinkedHashMap<>(); +042 +043 public void addReplacements(String... replacements) { +044 if (replacements.length == 0 || replacements.length % 2 != 0) { +045 throw new IllegalArgumentException("Must pass a number of arguments divisible by 2."); +046 } +047 for (int i = 0; i < replacements.length; i += 2) { +048 addReplacement(replacements[i], replacements[i+1]); +049 } +050 } +051 +052 public String addReplacement(String key, String val) { +053 if (this.manager.hasRegisteredCommands()) { +054 this.manager.log(LogLevel.ERROR, "You are registering replacements after you have registered your commands!"); +055 this.manager.log(LogLevel.ERROR, "This is not allowed, and this replacement (" + key + ") will not work for any previously registered command."); +056 } +057 +058 key = ACFPatterns.PERCENTAGE.matcher(key.toLowerCase()).replaceAll(""); +059 Pattern pattern = Pattern.compile("%" + Pattern.quote(key) + "\\b", Pattern.CASE_INSENSITIVE); +060 +061 Map.Entry<Pattern, String> entry = new AbstractMap.SimpleImmutableEntry<>(pattern, val); +062 Map.Entry<Pattern, String> replaced = replacements.put(key, entry); +063 +064 if (replaced != null) { +065 return replaced.getValue(); +066 } +067 +068 return null; +069 } +070 public String replace(String text) { +071 if (text == null) { +072 return null; +073 } +074 +075 for (Map.Entry<Pattern, String> entry : replacements.values()) { +076 text = entry.getKey().matcher(text).replaceAll(entry.getValue()); +077 } +078 +079 return manager.getLocales().replaceI18NStrings(text); +080 } +081} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 java.util.List; +027 +028/** +029 * Functional interface to allow plugins to handle uncaught excetpions +030 */ +031@FunctionalInterface +032public interface ExceptionHandler { +033 +034 /** +035 * Called when an exception occurs while executing a command<br> +036 * If an exception handler properly handles an exception, the user will not be noticied by the +037 * framework that something went unexceptected. +038 * +039 * @param command the command that was executed +040 * @param registeredCommand the registered command +041 * @param sender the issuer who send the command +042 * @param args the args he used +043 * @param t the throwable that caused this exception +044 * +045 * @return if the exception was handeled by the exception handler. +046 */ +047 boolean execute(BaseCommand command, RegisteredCommand registeredCommand, CommandIssuer sender, List<String> args, Throwable t); +048} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.apachecommonslang.ApacheCommonsLangUtil; +027 +028import java.util.List; +029 +030public class ForwardingCommand extends BaseCommand { +031 private final BaseCommand command; +032 private final String[] baseArgs; +033 +034 ForwardingCommand(BaseCommand command, String[] baseArgs) { +035 this.commandName = command.commandName; +036 this.command = command; +037 this.baseArgs = baseArgs; +038 } +039 +040 @Override +041 public boolean hasPermission(CommandIssuer sender) { +042 return command.hasPermission(sender); +043 } +044 +045 @Override +046 public List<String> tabComplete(CommandIssuer issuer, String alias, String[] args) throws IllegalArgumentException { +047 return command.tabComplete(issuer, alias, ApacheCommonsLangUtil.addAll(baseArgs, args)); +048 } +049 +050 @Override +051 public void execute(CommandIssuer issuer, String commandLabel, String[] args) { +052 command.execute(issuer, commandLabel, ApacheCommonsLangUtil.addAll(baseArgs, args)); +053 } +054 +055 BaseCommand getCommand(){ +056 return command; +057 } +058} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.HelpSearchTags; +027 +028public class HelpEntry { +029 +030 private final RegisteredCommand command; +031 private final String searchTags; +032 private int searchScore = 1; +033 +034 HelpEntry(RegisteredCommand command) { +035 this.command = command; +036 HelpSearchTags tagsAnno = command.method.getAnnotation(HelpSearchTags.class); +037 this.searchTags = tagsAnno != null ? tagsAnno.value() : null; +038 } +039 +040 RegisteredCommand getRegisteredCommand() { +041 return this.command; +042 } +043 +044 +045 public String getCommand(){ +046 return "/" + this.command.command; +047 } +048 +049 public String getParameterSyntax(){ +050 return this.command.syntaxText; +051 } +052 +053 public String getDescription(){ +054 return this.command.helpText; +055 } +056 +057 public void setSearchScore(int searchScore) { +058 this.searchScore = searchScore; +059 } +060 +061 public boolean shouldShow() { +062 return this.searchScore > 0; +063 } +064 +065 public int getSearchScore() { +066 return searchScore; +067 } +068 +069 public String getSearchTags() { +070 return searchTags; +071 } +072} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.locales.MessageKey; +027import co.aikar.locales.MessageKeyProvider; +028 +029public class InvalidCommandArgument extends Exception { +030 final boolean showSyntax; +031 final MessageKey key; +032 final String[] replacements; +033 +034 public InvalidCommandArgument() { +035 this((String) null, true); +036 } +037 public InvalidCommandArgument(boolean showSyntax) { +038 this(null, showSyntax); +039 } +040 public InvalidCommandArgument(MessageKeyProvider key, String... replacements) { +041 this(key.getMessageKey(), replacements); +042 } +043 public InvalidCommandArgument(MessageKey key, String... replacements) { +044 this(key, true, replacements); +045 } +046 public InvalidCommandArgument(MessageKeyProvider key, boolean showSyntax, String... replacements) { +047 this(key.getMessageKey(), showSyntax, replacements); +048 } +049 public InvalidCommandArgument(MessageKey key, boolean showSyntax, String... replacements) { +050 super(key.getKey(), null, false, false); +051 this.showSyntax = showSyntax; +052 this.key = key; +053 this.replacements = replacements; +054 } +055 +056 /** +057 * Please move to a MessageKey +058 * @deprecated +059 */ +060 @Deprecated +061 public InvalidCommandArgument(String message) { +062 this(message, true); +063 } +064 /** +065 * Please move to a MessageKey +066 * @deprecated +067 */ +068 @Deprecated +069 public InvalidCommandArgument(String message, boolean showSyntax) { +070 super(message, null, false, false); +071 this.showSyntax = showSyntax; +072 this.replacements = null; +073 this.key = null; +074 } +075} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 +026public class InvalidCommandContextException extends RuntimeException { +027 InvalidCommandContextException(String message) { +028 super(message); +029 } +030} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.locales.LocaleManager; +027import co.aikar.locales.MessageKey; +028import co.aikar.locales.MessageKeyProvider; +029import com.google.common.collect.HashMultimap; +030import com.google.common.collect.SetMultimap; +031import com.google.common.collect.Sets; +032import org.jetbrains.annotations.NotNull; +033 +034import java.util.HashMap; +035import java.util.Locale; +036import java.util.Map; +037import java.util.regex.Matcher; +038 +039@SuppressWarnings("WeakerAccess") +040public class Locales { +041 // Locales for reference since Locale doesn't have as many, add our own here for ease of use. +042 public static final Locale ENGLISH = Locale.ENGLISH; +043 public static final Locale GERMAN = Locale.GERMAN; +044 public static final Locale FRENCH = Locale.FRENCH; +045 public static final Locale JAPANESE = Locale.JAPANESE; +046 public static final Locale ITALIAN = Locale.ITALIAN; +047 public static final Locale KOREAN = Locale.KOREAN; +048 public static final Locale CHINESE = Locale.CHINESE; +049 public static final Locale SIMPLIFIED_CHINESE = Locale.SIMPLIFIED_CHINESE; +050 public static final Locale TRADITIONAL_CHINESE = Locale.TRADITIONAL_CHINESE; +051 public static final Locale SPANISH = new Locale("es"); +052 public static final Locale DUTCH = new Locale("nl"); +053 public static final Locale DANISH = new Locale("da"); +054 public static final Locale CZECH = new Locale("cs"); +055 public static final Locale GREEK = new Locale("el"); +056 public static final Locale LATIN = new Locale("la"); +057 public static final Locale BULGARIAN = new Locale("bg"); +058 public static final Locale AFRIKAANS = new Locale("af"); +059 public static final Locale HINDI = new Locale("hi"); +060 public static final Locale HEBREW = new Locale("he"); +061 public static final Locale POLISH = new Locale("pl"); +062 public static final Locale PORTUGUESE = new Locale("pt"); +063 public static final Locale FINNISH = new Locale("fi"); +064 public static final Locale SWEDISH = new Locale("sv"); +065 public static final Locale RUSSIAN = new Locale("ru"); +066 public static final Locale ROMANIAN = new Locale("ro"); +067 public static final Locale VIETNAMESE = new Locale("vi"); +068 public static final Locale THAI = new Locale("th"); +069 public static final Locale TURKISH = new Locale("tr"); +070 public static final Locale UKRANIAN = new Locale("uk"); +071 public static final Locale ARABIC = new Locale("ar"); +072 public static final Locale WELSH = new Locale("cy"); +073 +074 +075 private final CommandManager<?, ?, ?> manager; +076 private final LocaleManager<CommandIssuer> localeManager; +077 private final SetMultimap<String, Locale> loadedBundles = HashMultimap.create(); +078 +079 public Locales(CommandManager manager) { +080 this.manager = manager; +081 this.localeManager = LocaleManager.create(manager::getIssuerLocale); +082 } +083 +084 public void loadLanguages() { +085 addMessageBundles("acf-core"); +086 } +087 +088 public Locale getDefaultLocale() { +089 return this.localeManager.getDefaultLocale(); +090 } +091 +092 public Locale setDefaultLocale(Locale locale) { +093 return this.localeManager.setDefaultLocale(locale); +094 } +095 +096 /** +097 * Looks for all previously loaded bundles, and if any new Supported Languages have been added, load them. +098 */ +099 public void loadMissingBundles() { +100 for (Locale locale : manager.getSupportedLanguages()) { +101 for (String bundleName : Sets.newHashSet(loadedBundles.keys())) { +102 addMessageBundle(bundleName, locale); +103 } +104 } +105 } +106 +107 public void addMessageBundles(String... bundleNames) { +108 for (String bundleName : bundleNames) { +109 for (Locale locale : manager.getSupportedLanguages()) { +110 addMessageBundle(bundleName, locale); +111 } +112 } +113 } +114 +115 public void addMessageBundle(String bundleName, Locale locale) { +116 if (!loadedBundles.containsEntry(bundleName, locale)) { +117 loadedBundles.put(bundleName, locale); +118 this.localeManager.addMessageBundle(bundleName, locale); +119 } +120 } +121 +122 public void addMessageStrings(Locale locale, @NotNull Map<String, String> messages) { +123 Map<MessageKey, String> map = new HashMap<>(messages.size()); +124 messages.forEach((key, value) -> map.put(MessageKey.of(key), value)); +125 addMessages(locale, map); +126 } +127 public void addMessages(Locale locale, @NotNull Map<MessageKey, String> messages) { +128 this.localeManager.addMessages(locale, messages); +129 } +130 +131 public String addMessage(Locale locale, MessageKey key, String message) { +132 return this.localeManager.addMessage(locale, key, message); +133 } +134 +135 public String getMessage(CommandIssuer issuer, MessageKeyProvider key) { +136 final MessageKey msgKey = key.getMessageKey(); +137 String message = this.localeManager.getMessage(issuer, msgKey); +138 if (message == null) { +139 manager.log(LogLevel.ERROR, "Missing Language Key: " + msgKey.getKey()); +140 message = "<MISSING_LANGUAGE_KEY:" + msgKey.getKey() + ">"; +141 } +142 return message; +143 } +144 +145 public String replaceI18NStrings(String message) { +146 if (message == null) { +147 return null; +148 } +149 Matcher matcher = ACFPatterns.I18N_STRING.matcher(message); +150 if (!matcher.matches()) { +151 return message; +152 } +153 +154 CommandIssuer issuer = CommandManager.getCurrentCommandIssuer(); +155 +156 matcher.reset(); +157 StringBuffer sb = new StringBuffer(message.length()); +158 while (matcher.find()) { +159 MessageKey key = MessageKey.of(matcher.group("key")); +160 matcher.appendReplacement(sb, Matcher.quoteReplacement(getMessage(issuer, key))); +161 } +162 matcher.appendTail(sb); +163 return sb.toString(); +164 } +165} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 java.util.*; +027import java.util.regex.Matcher; +028 +029/** +030 * Handles formatting Messages and managing colors +031 * @param <C> The platform specific color object +032 */ +033public abstract class MessageFormatter <C> { +034 +035 private final List<C> colors = new ArrayList<>(); +036 +037 @SafeVarargs +038 public MessageFormatter(C... colors) { +039 this.colors.addAll(Arrays.asList(colors)); +040 +041 } +042 public C setColor(int index, C color) { +043 if (index > 0) { +044 index--; +045 } else { +046 index = 0; +047 } +048 if (this.colors.size() <= index) { +049 int needed = index - this.colors.size(); +050 if (needed > 0) { +051 this.colors.addAll(Collections.nCopies(needed, null)); +052 } +053 colors.add(color); +054 return null; +055 } else { +056 return colors.set(index, color); +057 } +058 } +059 +060 public C getColor(int index) { +061 if (index > 0) { +062 index--; +063 } else { +064 index = 0; +065 } +066 C color = colors.get(index); +067 if (color == null) { +068 color = getDefaultColor(); +069 } +070 return color; +071 } +072 +073 public C getDefaultColor() { +074 return getColor(1); +075 } +076 +077 abstract String format(C color, String message); +078 +079 public String format(int index, String message) { +080 return format(getColor(index), message); +081 } +082 +083 public String format(String message) { +084 String def = format(1, ""); +085 Matcher matcher = ACFPatterns.FORMATTER.matcher(message); +086 StringBuffer sb = new StringBuffer(message.length()); +087 while (matcher.find()) { +088 Integer color = ACFUtil.parseInt(matcher.group("color"), 1); +089 String msg = format(color, matcher.group("msg")) + def; +090 matcher.appendReplacement(sb, Matcher.quoteReplacement(msg)); +091 } +092 matcher.appendTail(sb); +093 return def + sb.toString(); +094 } +095} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.locales.MessageKey; +027import co.aikar.locales.MessageKeyProvider; +028 +029/** +030 * Enum Name = MessageKey in lowercase prefixed with acf-core. +031 */ +032@SuppressWarnings("WeakerAccess") +033public enum MessageKeys implements MessageKeyProvider { +034 PERMISSION_DENIED, +035 ERROR_GENERIC_LOGGED, +036 UNKNOWN_COMMAND, +037 INVALID_SYNTAX, +038 ERROR_PREFIX, +039 ERROR_PERFORMING_COMMAND, +040 INFO_MESSAGE, +041 PLEASE_SPECIFY_ONE_OF, +042 MUST_BE_A_NUMBER, +043 MUST_BE_MIN_LENGTH, +044 MUST_BE_MAX_LENGTH, +045 NOT_ALLOWED_ON_CONSOLE, +046 COULD_NOT_FIND_PLAYER, +047 HELP_FORMAT, +048 NO_COMMAND_MATCHED_SEARCH; +049 +050 private final MessageKey key = MessageKey.of("acf-core." + this.name().toLowerCase()); +051 public MessageKey getMessageKey() { +052 return key; +053 } +054} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 java.util.concurrent.atomic.AtomicInteger; +027 +028@SuppressWarnings("WeakerAccess") +029public class MessageType { +030 private static final AtomicInteger counter = new AtomicInteger(1); +031 +032 public static final MessageType INFO = new MessageType(); +033 public static final MessageType SYNTAX = new MessageType(); +034 public static final MessageType ERROR = new MessageType(); +035 public static final MessageType HELP = new MessageType(); +036 +037 private final int id = counter.getAndIncrement(); +038 +039 public int hashCode() { +040 return id; +041 } +042 +043 @Override +044 public boolean equals(Object o) { +045 return (this == o); +046 } +047} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.*; +027import co.aikar.commands.contexts.ContextResolver; +028import co.aikar.commands.contexts.IssuerAwareContextResolver; +029import co.aikar.commands.contexts.IssuerOnlyContextResolver; +030import co.aikar.commands.contexts.OptionalContextResolver; +031import com.google.common.collect.Lists; +032import com.google.common.collect.Maps; +033import com.google.common.collect.Sets; +034import org.jetbrains.annotations.Nullable; +035 +036import java.lang.reflect.InvocationTargetException; +037import java.lang.reflect.Method; +038import java.lang.reflect.Parameter; +039import java.util.ArrayList; +040import java.util.Collection; +041import java.util.List; +042import java.util.Map; +043import java.util.Set; +044import java.util.stream.Collectors; +045 +046public class RegisteredCommand <R extends CommandExecutionContext<? extends CommandExecutionContext, ? extends CommandIssuer>> { +047 final BaseCommand scope; +048 final String command; +049 final Method method; +050 final String prefSubCommand; +051 final Parameter[] parameters; +052 final ContextResolver<?, R>[] resolvers; +053 final String syntaxText; +054 final String helpText; +055 +056 private final String permission; +057 final String complete; +058 final int requiredResolvers; +059 final int optionalResolvers; +060 final List<String> registeredSubcommands = new ArrayList<>(); +061 +062 RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) { +063 this.scope = scope; +064 if ("__unknown".equals(prefSubCommand) || "__default".equals(prefSubCommand)) { +065 prefSubCommand = ""; +066 } +067 this.command = command + (method.getAnnotation(CommandAlias.class) == null && !prefSubCommand.isEmpty() ? prefSubCommand : ""); +068 this.method = method; +069 this.prefSubCommand = prefSubCommand; +070 CommandPermission permissionAnno = method.getAnnotation(CommandPermission.class); +071 this.permission = permissionAnno != null ? scope.manager.getCommandReplacements().replace(permissionAnno.value()) : null; +072 CommandCompletion completionAnno = method.getAnnotation(CommandCompletion.class); +073 this.complete = completionAnno != null ? scope.manager.getCommandReplacements().replace(completionAnno.value()) : null; +074 this.parameters = method.getParameters(); +075 +076 Description descriptionAnno = method.getAnnotation(Description.class); +077 this.helpText = descriptionAnno != null ? descriptionAnno.value() : ""; +078 //noinspection unchecked +079 this.resolvers = new ContextResolver[this.parameters.length]; +080 final Syntax syntaxStr = method.getAnnotation(Syntax.class); +081 final CommandManager manager = scope.manager; +082 final CommandContexts commandContexts = manager.getCommandContexts(); +083 +084 int requiredResolvers = 0; +085 int optionalResolvers = 0; +086 StringBuilder syntaxB = new StringBuilder(64); +087 +088 for (int i = 0; i < parameters.length; i++) { +089 final Parameter parameter = parameters[i]; +090 final Class<?> type = parameter.getType(); +091 +092 //noinspection unchecked +093 final ContextResolver<?, R> resolver = commandContexts.getResolver(type); +094 if (resolver != null) { +095 resolvers[i] = resolver; +096 +097 if (!scope.manager.isCommandIssuer(type)) { +098 String name = parameter.getName(); +099 if (isOptionalResolver(resolver, parameter)) { +100 optionalResolvers++; +101 if (!(resolver instanceof IssuerOnlyContextResolver)) { +102 syntaxB.append('[').append(name).append("] "); +103 } +104 } else { +105 requiredResolvers++; +106 syntaxB.append('<').append(name).append("> "); +107 } +108 } +109 } else { +110 ACFUtil.sneaky(new InvalidCommandContextException( +111 "Parameter " + type.getSimpleName() + " of " + this.command + " has no applicable context resolver" +112 )); +113 } +114 } +115 String syntaxText = syntaxB.toString(); +116 this.syntaxText = manager.getCommandReplacements().replace(syntaxStr != null ? +117 ACFUtil.replace(syntaxStr.value(), "@syntax", syntaxText) : syntaxText); +118 this.requiredResolvers = requiredResolvers; +119 this.optionalResolvers = optionalResolvers; +120 } +121 +122 private boolean isOptionalResolver(ContextResolver<?, R> resolver, Parameter parameter) { +123 return isOptionalResolver(resolver) +124 || parameter.getAnnotation(Optional.class) != null +125 || parameter.getAnnotation(Default.class) != null; +126 } +127 +128 private boolean isOptionalResolver(ContextResolver<?, R> resolver) { +129 return resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver +130 || resolver instanceof OptionalContextResolver; +131 } +132 +133 void invoke(CommandIssuer sender, List<String> args) { +134 if (!scope.canExecute(sender, this)) { +135 return; +136 } +137 preCommand(); +138 try { +139 Map<String, Object> passedArgs = resolveContexts(sender, args); +140 if (passedArgs == null) return; +141 +142 method.invoke(scope, passedArgs.values().toArray()); +143 } catch (Exception e) { +144 handleException(sender, args, e); +145 } +146 postCommand(); +147 } +148 public void preCommand() {} +149 public void postCommand() {} +150 +151 void handleException(CommandIssuer sender, List<String> args, Exception e) { +152 if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) { +153 e = (Exception) e.getCause(); +154 } +155 if (e instanceof InvalidCommandArgument) { +156 InvalidCommandArgument ica = (InvalidCommandArgument) e; +157 if (ica.key != null) { +158 sender.sendMessage(MessageType.ERROR, ica.key, ica.replacements); +159 } else if (e.getMessage() != null && !e.getMessage().isEmpty()) { +160 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PREFIX, "{message}", e.getMessage()); +161 } +162 if (ica.showSyntax) { +163 scope.showSyntax(sender, this); +164 } +165 } else { +166 boolean handeled = this.scope.manager.handleUncaughtException(scope, this, sender, args, e); +167 if(!handeled){ +168 sender.sendMessage(MessageType.ERROR, MessageKeys.ERROR_PERFORMING_COMMAND); +169 } +170 this.scope.manager.log(LogLevel.ERROR, "Exception in command: " + command + " " + ACFUtil.join(args), e); +171 } +172 } +173 +174 @Nullable +175 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args) throws InvalidCommandArgument { +176 return resolveContexts(sender, args, parameters.length); +177 } +178 @Nullable +179 Map<String, Object> resolveContexts(CommandIssuer sender, List<String> args, int argLimit) throws InvalidCommandArgument { +180 args = Lists.newArrayList(args); +181 String[] origArgs = args.toArray(new String[args.size()]); +182 Map<String, Object> passedArgs = Maps.newLinkedHashMap(); +183 int remainingRequired = requiredResolvers; +184 for (int i = 0; i < parameters.length && i < argLimit; i++) { +185 boolean isLast = i == parameters.length - 1; +186 boolean allowOptional = remainingRequired == 0; +187 final Parameter parameter = parameters[i]; +188 final String parameterName = parameter.getName(); +189 final Class<?> type = parameter.getType(); +190 //noinspection unchecked +191 final ContextResolver<?, R> resolver = resolvers[i]; +192 R context = this.scope.manager.createCommandContext(this, parameter, sender, args, i, passedArgs); +193 boolean isOptionalResolver = isOptionalResolver(resolver, parameter); +194 if (!isOptionalResolver) { +195 remainingRequired--; +196 } +197 if (args.isEmpty() && !(isLast && type == String[].class)) { +198 Default def = parameter.getAnnotation(Default.class); +199 Optional opt = parameter.getAnnotation(Optional.class); +200 if (allowOptional && def != null) { +201 args.add(scope.manager.getCommandReplacements().replace(def.value())); +202 } else if (allowOptional && opt != null) { +203 passedArgs.put(parameterName, isOptionalResolver(resolver) ? resolver.getContext(context) : null); +204 //noinspection UnnecessaryContinue +205 continue; +206 } else if (!isOptionalResolver) { +207 scope.showSyntax(sender, this); +208 return null; +209 } +210 } +211 final Values values = parameter.getAnnotation(Values.class); +212 if (values != null) { +213 String arg = !args.isEmpty() ? args.get(0) : ""; +214 +215 final String[] split = ACFPatterns.PIPE.split(scope.manager.getCommandReplacements().replace(values.value())); +216 Set<String> possible = Sets.newHashSet(); +217 for (String s : split) { +218 List<String> check = this.scope.manager.getCommandCompletions().getCompletionValues(this, sender, s, origArgs); +219 if (!check.isEmpty()) { +220 possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList())); +221 } else { +222 possible.add(s.toLowerCase()); +223 } +224 } +225 +226 if (!possible.contains(arg.toLowerCase())) { +227 throw new InvalidCommandArgument(MessageKeys.PLEASE_SPECIFY_ONE_OF, +228 "{valid}", ACFUtil.join(possible, ", ")); +229 } +230 } +231 passedArgs.put(parameterName, resolver.getContext(context)); +232 } +233 return passedArgs; +234 } +235 +236 boolean hasPermission(CommandIssuer issuer) { +237 return (permission == null || permission.isEmpty() || scope.manager.hasPermission(issuer, permission)) && scope.hasPermission(issuer); +238 } +239 +240 public String getPermission() { +241 return permission; +242 } +243 +244 public String getPrefSubCommand() { +245 return prefSubCommand; +246 } +247 +248 public String getSyntaxText() { +249 return syntaxText; +250 } +251 +252 public String getCommand() { +253 return command; +254 } +255 +256 public void addSubcommand(String cmd) { +257 this.registeredSubcommands.add(cmd); +258 } +259 public void addSubcommands(Collection<String> cmd) { +260 this.registeredSubcommands.addAll(cmd); +261 } +262} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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 java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030@Deprecated +031public @interface UnstableAPI {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface CommandAlias { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface CommandCompletion { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface CommandPermission { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Default { +031 String value() default ""; +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Description { +031 +032 String value(); +033} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Flags { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface HelpCommand { +031 String value() default "help|?|-help|-h|-?"; +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface HelpSearchTags { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Optional { +031} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026 +027import java.lang.annotation.Retention; +028import java.lang.annotation.RetentionPolicy; +029 +030@Retention(RetentionPolicy.RUNTIME) +031public @interface PreCommand {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029/** +030 * Don't join remaining arguments +031 */ +032 +033@Retention(RetentionPolicy.RUNTIME) +034public @interface Single {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026 +027import java.lang.annotation.Retention; +028import java.lang.annotation.RetentionPolicy; +029 +030@Retention(RetentionPolicy.RUNTIME) +031public @interface Split { +032 String value() default ","; +033} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Subcommand { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Syntax { +031 String value(); +032} +033 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026 +027import java.lang.annotation.Retention; +028import java.lang.annotation.RetentionPolicy; +029 +030@Retention(RetentionPolicy.RUNTIME) +031public @interface UnknownHandler {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.annotation; +025 +026import java.lang.annotation.Retention; +027import java.lang.annotation.RetentionPolicy; +028 +029@Retention(RetentionPolicy.RUNTIME) +030public @interface Values { +031 String value(); +032} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Licensed to the Apache Software Foundation (ASF) under one or more +003 * contributor license agreements. See the NOTICE file distributed with +004 * this work for additional information regarding copyright ownership. +005 * The ASF licenses this file to You under the Apache License, Version 2.0 +006 * (the "License"); you may not use this file except in compliance with +007 * the License. You may obtain a copy of the License at +008 * +009 * http://www.apache.org/licenses/LICENSE-2.0 +010 * +011 * Unless required by applicable law or agreed to in writing, software +012 * distributed under the License is distributed on an "AS IS" BASIS, +013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +014 * See the License for the specific language governing permissions and +015 * limitations under the License. +016 */ +017package co.aikar.commands.apachecommonslang; +018 +019import java.io.PrintStream; +020import java.io.PrintWriter; +021import java.io.StringWriter; +022import java.lang.reflect.Field; +023import java.lang.reflect.InvocationTargetException; +024import java.lang.reflect.Method; +025import java.sql.SQLException; +026import java.util.ArrayList; +027import java.util.Arrays; +028import java.util.List; +029import java.util.StringTokenizer; +030 +031/** +032 * <p>Provides utilities for manipulating and examining +033 * <code>Throwable</code> objects.</p> +034 * +035 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> +036 * @author Dmitri Plotnikov +037 * @author Stephen Colebourne +038 * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a> +039 * @author Pete Gieser +040 * @since 1.0 +041 * @version $Id$ +042 */ +043public class ApacheCommonsExceptionUtil { +044 private static final String LINE_SEPARATOR = System.getProperty("line.separator"); +045 +046 /** +047 * <p>Used when printing stack frames to denote the start of a +048 * wrapped exception.</p> +049 * +050 * <p>Package private for accessibility by test suite.</p> +051 */ +052 static final String WRAPPED_MARKER = " [wrapped] "; +053 +054 /** +055 * <p>The names of methods commonly used to access a wrapped exception.</p> +056 */ +057 private static String[] CAUSE_METHOD_NAMES = { +058 "getCause", +059 "getNextException", +060 "getTargetException", +061 "getException", +062 "getSourceException", +063 "getRootCause", +064 "getCausedByException", +065 "getNested", +066 "getLinkedException", +067 "getNestedException", +068 "getLinkedCause", +069 "getThrowable", +070 }; +071 +072 /** +073 * <p>The Method object for Java 1.4 getCause.</p> +074 */ +075 private static final Method THROWABLE_CAUSE_METHOD; +076 +077 /** +078 * <p>The Method object for Java 1.4 initCause.</p> +079 */ +080 private static final Method THROWABLE_INITCAUSE_METHOD; +081 +082 static { +083 Method causeMethod; +084 try { +085 causeMethod = Throwable.class.getMethod("getCause", null); +086 } catch (Exception e) { +087 causeMethod = null; +088 } +089 THROWABLE_CAUSE_METHOD = causeMethod; +090 try { +091 causeMethod = Throwable.class.getMethod("initCause", new Class[]{Throwable.class}); +092 } catch (Exception e) { +093 causeMethod = null; +094 } +095 THROWABLE_INITCAUSE_METHOD = causeMethod; +096 } +097 +098 /** +099 * <p> +100 * Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not +101 * normally necessary. +102 * </p> +103 */ +104 public ApacheCommonsExceptionUtil() { +105 super(); +106 } +107 +108 //----------------------------------------------------------------------- +109 /** +110 * <p>Adds to the list of method names used in the search for <code>Throwable</code> +111 * objects.</p> +112 * +113 * @param methodName the methodName to add to the list, <code>null</code> +114 * and empty strings are ignored +115 * @since 2.0 +116 */ +117 public static void addCauseMethodName(String methodName) { +118 if (methodName != null && !methodName.isEmpty() && !isCauseMethodName(methodName)) { +119 List list = getCauseMethodNameList(); +120 if (list.add(methodName)) { +121 CAUSE_METHOD_NAMES = toArray(list); +122 } +123 } +124 } +125 +126 /** +127 * <p>Removes from the list of method names used in the search for <code>Throwable</code> +128 * objects.</p> +129 * +130 * @param methodName the methodName to remove from the list, <code>null</code> +131 * and empty strings are ignored +132 * @since 2.1 +133 */ +134 public static void removeCauseMethodName(String methodName) { +135 if (methodName != null && !methodName.isEmpty()) { +136 List list = getCauseMethodNameList(); +137 if (list.remove(methodName)) { +138 CAUSE_METHOD_NAMES = toArray(list); +139 } +140 } +141 } +142 +143 /** +144 * <p>Sets the cause of a <code>Throwable</code> using introspection, allowing +145 * source code compatibility between pre-1.4 and post-1.4 Java releases.</p> +146 * +147 * <p>The typical use of this method is inside a constructor as in +148 * the following example:</p> +149 * +150 * <pre> +151 * import org.apache.commons.lang.exception.ExceptionUtils; +152 * +153 * public class MyException extends Exception { +154 * +155 * public MyException(String msg) { +156 * super(msg); +157 * } +158 * +159 * public MyException(String msg, Throwable cause) { +160 * super(msg); +161 * ExceptionUtils.setCause(this, cause); +162 * } +163 * } +164 * </pre> +165 * +166 * @param target the target <code>Throwable</code> +167 * @param cause the <code>Throwable</code> to set in the target +168 * @return a <code>true</code> if the target has been modified +169 * @since 2.2 +170 */ +171 public static boolean setCause(Throwable target, Throwable cause) { +172 if (target == null) { +173 throw new IllegalArgumentException("target"); +174 } +175 Object[] causeArgs = new Object[]{cause}; +176 boolean modifiedTarget = false; +177 if (THROWABLE_INITCAUSE_METHOD != null) { +178 try { +179 THROWABLE_INITCAUSE_METHOD.invoke(target, causeArgs); +180 modifiedTarget = true; +181 } catch (IllegalAccessException ignored) { +182 // Exception ignored. +183 } catch (InvocationTargetException ignored) { +184 // Exception ignored. +185 } +186 } +187 try { +188 Method setCauseMethod = target.getClass().getMethod("setCause", new Class[]{Throwable.class}); +189 setCauseMethod.invoke(target, causeArgs); +190 modifiedTarget = true; +191 } catch (NoSuchMethodException ignored) { +192 // Exception ignored. +193 } catch (IllegalAccessException ignored) { +194 // Exception ignored. +195 } catch (InvocationTargetException ignored) { +196 // Exception ignored. +197 } +198 return modifiedTarget; +199 } +200 +201 /** +202 * Returns the given list as a <code>String[]</code>. +203 * @param list a list to transform. +204 * @return the given list as a <code>String[]</code>. +205 */ +206 private static String[] toArray(List list) { +207 return (String[]) list.toArray(new String[list.size()]); +208 } +209 +210 /** +211 * Returns {@link #CAUSE_METHOD_NAMES} as a List. +212 * +213 * @return {@link #CAUSE_METHOD_NAMES} as a List. +214 */ +215 private static ArrayList getCauseMethodNameList() { +216 return new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES)); +217 } +218 +219 /** +220 * <p>Tests if the list of method names used in the search for <code>Throwable</code> +221 * objects include the given name.</p> +222 * +223 * @param methodName the methodName to search in the list. +224 * @return if the list of method names used in the search for <code>Throwable</code> +225 * objects include the given name. +226 * @since 2.1 +227 */ +228 public static boolean isCauseMethodName(String methodName) { +229 return ApacheCommonsLangUtil.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0; +230 } +231 +232 //----------------------------------------------------------------------- +233 /** +234 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> +235 * +236 * <p>The method searches for methods with specific names that return a +237 * <code>Throwable</code> object. This will pick up most wrapping exceptions, +238 * including those from JDK 1.4, and +239 * The method names can be added to using {@link #addCauseMethodName(String)}.</p> +240 * +241 * <p>The default list searched for are:</p> +242 * <ul> +243 * <li><code>getCause()</code></li> +244 * <li><code>getNextException()</code></li> +245 * <li><code>getTargetException()</code></li> +246 * <li><code>getException()</code></li> +247 * <li><code>getSourceException()</code></li> +248 * <li><code>getRootCause()</code></li> +249 * <li><code>getCausedByException()</code></li> +250 * <li><code>getNested()</code></li> +251 * </ul> +252 * +253 * <p>In the absence of any such method, the object is inspected for a +254 * <code>detail</code> field assignable to a <code>Throwable</code>.</p> +255 * +256 * <p>If none of the above is found, returns <code>null</code>.</p> +257 * +258 * @param throwable the throwable to introspect for a cause, may be null +259 * @return the cause of the <code>Throwable</code>, +260 * <code>null</code> if none found or null throwable input +261 * @since 1.0 +262 */ +263 public static Throwable getCause(Throwable throwable) { +264 return getCause(throwable, CAUSE_METHOD_NAMES); +265 } +266 +267 /** +268 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> +269 * +270 * <ol> +271 * <li>Try known exception types.</li> +272 * <li>Try the supplied array of method names.</li> +273 * <li>Try the field 'detail'.</li> +274 * </ol> +275 * +276 * <p>A <code>null</code> set of method names means use the default set. +277 * A <code>null</code> in the set of method names will be ignored.</p> +278 * +279 * @param throwable the throwable to introspect for a cause, may be null +280 * @param methodNames the method names, null treated as default set +281 * @return the cause of the <code>Throwable</code>, +282 * <code>null</code> if none found or null throwable input +283 * @since 1.0 +284 */ +285 public static Throwable getCause(Throwable throwable, String[] methodNames) { +286 if (throwable == null) { +287 return null; +288 } +289 Throwable cause = getCauseUsingWellKnownTypes(throwable); +290 if (cause == null) { +291 if (methodNames == null) { +292 methodNames = CAUSE_METHOD_NAMES; +293 } +294 for (int i = 0; i < methodNames.length; i++) { +295 String methodName = methodNames[i]; +296 if (methodName != null) { +297 cause = getCauseUsingMethodName(throwable, methodName); +298 if (cause != null) { +299 break; +300 } +301 } +302 } +303 +304 if (cause == null) { +305 cause = getCauseUsingFieldName(throwable, "detail"); +306 } +307 } +308 return cause; +309 } +310 +311 /** +312 * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p> +313 * +314 * <p>This method walks through the exception chain to the last element, +315 * "root" of the tree, using {@link #getCause(Throwable)}, and +316 * returns that exception.</p> +317 * +318 * <p>From version 2.2, this method handles recursive cause structures +319 * that might otherwise cause infinite loops. If the throwable parameter +320 * has a cause of itself, then null will be returned. If the throwable +321 * parameter cause chain loops, the last element in the chain before the +322 * loop is returned.</p> +323 * +324 * @param throwable the throwable to get the root cause for, may be null +325 * @return the root cause of the <code>Throwable</code>, +326 * <code>null</code> if none found or null throwable input +327 */ +328 public static Throwable getRootCause(Throwable throwable) { +329 List list = getThrowableList(throwable); +330 return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1)); +331 } +332 +333 /** +334 * <p>Finds a <code>Throwable</code> for known types.</p> +335 * +336 * <p>Uses <code>instanceof</code> checks to examine the exception, +337 * looking for well known types which could contain chained or +338 * wrapped exceptions.</p> +339 * +340 * @param throwable the exception to examine +341 * @return the wrapped exception, or <code>null</code> if not found +342 */ +343 private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) { +344 if (throwable instanceof Nestable) { +345 return ((Nestable) throwable).getCause(); +346 } else if (throwable instanceof SQLException) { +347 return ((SQLException) throwable).getNextException(); +348 } else if (throwable instanceof InvocationTargetException) { +349 return ((InvocationTargetException) throwable).getTargetException(); +350 } else { +351 return null; +352 } +353 } +354 +355 /** +356 * <p>Finds a <code>Throwable</code> by method name.</p> +357 * +358 * @param throwable the exception to examine +359 * @param methodName the name of the method to find and invoke +360 * @return the wrapped exception, or <code>null</code> if not found +361 */ +362 private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { +363 Method method = null; +364 try { +365 method = throwable.getClass().getMethod(methodName, null); +366 } catch (NoSuchMethodException ignored) { +367 // exception ignored +368 } catch (SecurityException ignored) { +369 // exception ignored +370 } +371 +372 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { +373 try { +374 return (Throwable) method.invoke(throwable); +375 } catch (IllegalAccessException ignored) { +376 // exception ignored +377 } catch (IllegalArgumentException ignored) { +378 // exception ignored +379 } catch (InvocationTargetException ignored) { +380 // exception ignored +381 } +382 } +383 return null; +384 } +385 +386 /** +387 * <p>Finds a <code>Throwable</code> by field name.</p> +388 * +389 * @param throwable the exception to examine +390 * @param fieldName the name of the attribute to examine +391 * @return the wrapped exception, or <code>null</code> if not found +392 */ +393 private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) { +394 Field field = null; +395 try { +396 field = throwable.getClass().getField(fieldName); +397 } catch (NoSuchFieldException ignored) { +398 // exception ignored +399 } catch (SecurityException ignored) { +400 // exception ignored +401 } +402 +403 if (field != null && Throwable.class.isAssignableFrom(field.getType())) { +404 try { +405 return (Throwable) field.get(throwable); +406 } catch (IllegalAccessException ignored) { +407 // exception ignored +408 } catch (IllegalArgumentException ignored) { +409 // exception ignored +410 } +411 } +412 return null; +413 } +414 +415 //----------------------------------------------------------------------- +416 /** +417 * <p>Checks if the Throwable class has a <code>getCause</code> method.</p> +418 * +419 * <p>This is true for JDK 1.4 and above.</p> +420 * +421 * @return true if Throwable is nestable +422 * @since 2.0 +423 */ +424 public static boolean isThrowableNested() { +425 return THROWABLE_CAUSE_METHOD != null; +426 } +427 +428 /** +429 * <p>Checks whether this <code>Throwable</code> class can store a cause.</p> +430 * +431 * <p>This method does <b>not</b> check whether it actually does store a cause.<p> +432 * +433 * @param throwable the <code>Throwable</code> to examine, may be null +434 * @return boolean <code>true</code> if nested otherwise <code>false</code> +435 * @since 2.0 +436 */ +437 public static boolean isNestedThrowable(Throwable throwable) { +438 if (throwable == null) { +439 return false; +440 } +441 +442 if (throwable instanceof Nestable) { +443 return true; +444 } else if (throwable instanceof SQLException) { +445 return true; +446 } else if (throwable instanceof InvocationTargetException) { +447 return true; +448 } else if (isThrowableNested()) { +449 return true; +450 } +451 +452 Class cls = throwable.getClass(); +453 for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) { +454 try { +455 Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null); +456 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { +457 return true; +458 } +459 } catch (NoSuchMethodException ignored) { +460 // exception ignored +461 } catch (SecurityException ignored) { +462 // exception ignored +463 } +464 } +465 +466 try { +467 Field field = cls.getField("detail"); +468 if (field != null) { +469 return true; +470 } +471 } catch (NoSuchFieldException ignored) { +472 // exception ignored +473 } catch (SecurityException ignored) { +474 // exception ignored +475 } +476 +477 return false; +478 } +479 +480 //----------------------------------------------------------------------- +481 /** +482 * <p>Counts the number of <code>Throwable</code> objects in the +483 * exception chain.</p> +484 * +485 * <p>A throwable without cause will return <code>1</code>. +486 * A throwable with one cause will return <code>2</code> and so on. +487 * A <code>null</code> throwable will return <code>0</code>.</p> +488 * +489 * <p>From version 2.2, this method handles recursive cause structures +490 * that might otherwise cause infinite loops. The cause chain is +491 * processed until the end is reached, or until the next item in the +492 * chain is already in the result set.</p> +493 * +494 * @param throwable the throwable to inspect, may be null +495 * @return the count of throwables, zero if null input +496 */ +497 public static int getThrowableCount(Throwable throwable) { +498 return getThrowableList(throwable).size(); +499 } +500 +501 /** +502 * <p>Returns the list of <code>Throwable</code> objects in the +503 * exception chain.</p> +504 * +505 * <p>A throwable without cause will return an array containing +506 * one element - the input throwable. +507 * A throwable with one cause will return an array containing +508 * two elements. - the input throwable and the cause throwable. +509 * A <code>null</code> throwable will return an array of size zero.</p> +510 * +511 * <p>From version 2.2, this method handles recursive cause structures +512 * that might otherwise cause infinite loops. The cause chain is +513 * processed until the end is reached, or until the next item in the +514 * chain is already in the result set.</p> +515 * +516 * @see #getThrowableList(Throwable) +517 * @param throwable the throwable to inspect, may be null +518 * @return the array of throwables, never null +519 */ +520 public static Throwable[] getThrowables(Throwable throwable) { +521 List list = getThrowableList(throwable); +522 return (Throwable[]) list.toArray(new Throwable[list.size()]); +523 } +524 +525 /** +526 * <p>Returns the list of <code>Throwable</code> objects in the +527 * exception chain.</p> +528 * +529 * <p>A throwable without cause will return a list containing +530 * one element - the input throwable. +531 * A throwable with one cause will return a list containing +532 * two elements. - the input throwable and the cause throwable. +533 * A <code>null</code> throwable will return a list of size zero.</p> +534 * +535 * <p>This method handles recursive cause structures that might +536 * otherwise cause infinite loops. The cause chain is processed until +537 * the end is reached, or until the next item in the chain is already +538 * in the result set.</p> +539 * +540 * @param throwable the throwable to inspect, may be null +541 * @return the list of throwables, never null +542 * @since Commons Lang 2.2 +543 */ +544 public static List getThrowableList(Throwable throwable) { +545 List list = new ArrayList(); +546 while (throwable != null && list.contains(throwable) == false) { +547 list.add(throwable); +548 throwable = getCause(throwable); +549 } +550 return list; +551 } +552 +553 //----------------------------------------------------------------------- +554 /** +555 * <p>Returns the (zero based) index of the first <code>Throwable</code> +556 * that matches the specified class (exactly) in the exception chain. +557 * Subclasses of the specified class do not match - see +558 * {@link #indexOfType(Throwable, Class)} for the opposite.</p> +559 * +560 * <p>A <code>null</code> throwable returns <code>-1</code>. +561 * A <code>null</code> type returns <code>-1</code>. +562 * No match in the chain returns <code>-1</code>.</p> +563 * +564 * @param throwable the throwable to inspect, may be null +565 * @param clazz the class to search for, subclasses do not match, null returns -1 +566 * @return the index into the throwable chain, -1 if no match or null input +567 */ +568 public static int indexOfThrowable(Throwable throwable, Class clazz) { +569 return indexOf(throwable, clazz, 0, false); +570 } +571 +572 /** +573 * <p>Returns the (zero based) index of the first <code>Throwable</code> +574 * that matches the specified type in the exception chain from +575 * a specified index. +576 * Subclasses of the specified class do not match - see +577 * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p> +578 * +579 * <p>A <code>null</code> throwable returns <code>-1</code>. +580 * A <code>null</code> type returns <code>-1</code>. +581 * No match in the chain returns <code>-1</code>. +582 * A negative start index is treated as zero. +583 * A start index greater than the number of throwables returns <code>-1</code>.</p> +584 * +585 * @param throwable the throwable to inspect, may be null +586 * @param clazz the class to search for, subclasses do not match, null returns -1 +587 * @param fromIndex the (zero based) index of the starting position, +588 * negative treated as zero, larger than chain size returns -1 +589 * @return the index into the throwable chain, -1 if no match or null input +590 */ +591 public static int indexOfThrowable(Throwable throwable, Class clazz, int fromIndex) { +592 return indexOf(throwable, clazz, fromIndex, false); +593 } +594 +595 //----------------------------------------------------------------------- +596 /** +597 * <p>Returns the (zero based) index of the first <code>Throwable</code> +598 * that matches the specified class or subclass in the exception chain. +599 * Subclasses of the specified class do match - see +600 * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> +601 * +602 * <p>A <code>null</code> throwable returns <code>-1</code>. +603 * A <code>null</code> type returns <code>-1</code>. +604 * No match in the chain returns <code>-1</code>.</p> +605 * +606 * @param throwable the throwable to inspect, may be null +607 * @param type the type to search for, subclasses match, null returns -1 +608 * @return the index into the throwable chain, -1 if no match or null input +609 * @since 2.1 +610 */ +611 public static int indexOfType(Throwable throwable, Class type) { +612 return indexOf(throwable, type, 0, true); +613 } +614 +615 /** +616 * <p>Returns the (zero based) index of the first <code>Throwable</code> +617 * that matches the specified type in the exception chain from +618 * a specified index. +619 * Subclasses of the specified class do match - see +620 * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> +621 * +622 * <p>A <code>null</code> throwable returns <code>-1</code>. +623 * A <code>null</code> type returns <code>-1</code>. +624 * No match in the chain returns <code>-1</code>. +625 * A negative start index is treated as zero. +626 * A start index greater than the number of throwables returns <code>-1</code>.</p> +627 * +628 * @param throwable the throwable to inspect, may be null +629 * @param type the type to search for, subclasses match, null returns -1 +630 * @param fromIndex the (zero based) index of the starting position, +631 * negative treated as zero, larger than chain size returns -1 +632 * @return the index into the throwable chain, -1 if no match or null input +633 * @since 2.1 +634 */ +635 public static int indexOfType(Throwable throwable, Class type, int fromIndex) { +636 return indexOf(throwable, type, fromIndex, true); +637 } +638 +639 /** +640 * <p>Worker method for the <code>indexOfType</code> methods.</p> +641 * +642 * @param throwable the throwable to inspect, may be null +643 * @param type the type to search for, subclasses match, null returns -1 +644 * @param fromIndex the (zero based) index of the starting position, +645 * negative treated as zero, larger than chain size returns -1 +646 * @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares +647 * using references +648 * @return index of the <code>type</code> within throwables nested withing the specified <code>throwable</code> +649 */ +650 private static int indexOf(Throwable throwable, Class type, int fromIndex, boolean subclass) { +651 if (throwable == null || type == null) { +652 return -1; +653 } +654 if (fromIndex < 0) { +655 fromIndex = 0; +656 } +657 Throwable[] throwables = getThrowables(throwable); +658 if (fromIndex >= throwables.length) { +659 return -1; +660 } +661 if (subclass) { +662 for (int i = fromIndex; i < throwables.length; i++) { +663 if (type.isAssignableFrom(throwables[i].getClass())) { +664 return i; +665 } +666 } +667 } else { +668 for (int i = fromIndex; i < throwables.length; i++) { +669 if (type.equals(throwables[i].getClass())) { +670 return i; +671 } +672 } +673 } +674 return -1; +675 } +676 +677 /** +678 * <p>Removes common frames from the cause trace given the two stack traces.</p> +679 * +680 * @param causeFrames stack trace of a cause throwable +681 * @param wrapperFrames stack trace of a wrapper throwable +682 * @throws IllegalArgumentException if either argument is null +683 * @since 2.0 +684 */ +685 public static void removeCommonFrames(List causeFrames, List wrapperFrames) { +686 if (causeFrames == null || wrapperFrames == null) { +687 throw new IllegalArgumentException("The List must not be null"); +688 } +689 int causeFrameIndex = causeFrames.size() - 1; +690 int wrapperFrameIndex = wrapperFrames.size() - 1; +691 while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { +692 // Remove the frame from the cause trace if it is the same +693 // as in the wrapper trace +694 String causeFrame = (String) causeFrames.get(causeFrameIndex); +695 String wrapperFrame = (String) wrapperFrames.get(wrapperFrameIndex); +696 if (causeFrame.equals(wrapperFrame)) { +697 causeFrames.remove(causeFrameIndex); +698 } +699 causeFrameIndex--; +700 wrapperFrameIndex--; +701 } +702 } +703 +704 //----------------------------------------------------------------------- +705 /** +706 * <p>A way to get the entire nested stack-trace of an throwable.</p> +707 * +708 * <p>The result of this method is highly dependent on the JDK version +709 * and whether the exceptions override printStackTrace or not.</p> +710 * +711 * @param throwable the <code>Throwable</code> to be examined +712 * @return the nested stack trace, with the root cause first +713 * @since 2.0 +714 */ +715 public static String getFullStackTrace(Throwable throwable) { +716 StringWriter sw = new StringWriter(); +717 PrintWriter pw = new PrintWriter(sw, true); +718 Throwable[] ts = getThrowables(throwable); +719 for (int i = 0; i < ts.length; i++) { +720 ts[i].printStackTrace(pw); +721 if (isNestedThrowable(ts[i])) { +722 break; +723 } +724 } +725 return sw.getBuffer().toString(); +726 } +727 +728 //----------------------------------------------------------------------- +729 /** +730 * <p>Gets the stack trace from a Throwable as a String.</p> +731 * +732 * <p>The result of this method vary by JDK version as this method +733 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. +734 * On JDK1.3 and earlier, the cause exception will not be shown +735 * unless the specified throwable alters printStackTrace.</p> +736 * +737 * @param throwable the <code>Throwable</code> to be examined +738 * @return the stack trace as generated by the exception's +739 * <code>printStackTrace(PrintWriter)</code> method +740 */ +741 public static String getStackTrace(Throwable throwable) { +742 StringWriter sw = new StringWriter(); +743 PrintWriter pw = new PrintWriter(sw, true); +744 throwable.printStackTrace(pw); +745 return sw.getBuffer().toString(); +746 } +747 +748 /** +749 * <p>Captures the stack trace associated with the specified +750 * <code>Throwable</code> object, decomposing it into a list of +751 * stack frames.</p> +752 * +753 * <p>The result of this method vary by JDK version as this method +754 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. +755 * On JDK1.3 and earlier, the cause exception will not be shown +756 * unless the specified throwable alters printStackTrace.</p> +757 * +758 * @param throwable the <code>Throwable</code> to examine, may be null +759 * @return an array of strings describing each stack frame, never null +760 */ +761// public static String[] getStackFrames(Throwable throwable) { +762// if (throwable == null) { +763// return ArrayUtils.EMPTY_STRING_ARRAY; +764// } +765// return getStackFrames(getStackTrace(throwable)); +766// } +767 +768 //----------------------------------------------------------------------- +769 /** +770 * <p>Returns an array where each element is a line from the argument.</p> +771 * +772 * <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p> +773 * +774 * <p>Functionality shared between the +775 * <code>getStackFrames(Throwable)</code> methods of this and the +776 * {@link org.apache.commons.lang.exception.NestableDelegate} classes.</p> +777 * +778 * @param stackTrace a stack trace String +779 * @return an array where each element is a line from the argument +780 */ +781// static String[] getStackFrames(String stackTrace) { +782// String linebreak = SystemUtils.LINE_SEPARATOR; +783// StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); +784// List list = new ArrayList(); +785// while (frames.hasMoreTokens()) { +786// list.add(frames.nextToken()); +787// } +788// return toArray(list); +789// } +790 +791 /** +792 * <p>Produces a <code>List</code> of stack frames - the message +793 * is not included. Only the trace of the specified exception is +794 * returned, any caused by trace is stripped.</p> +795 * +796 * <p>This works in most cases - it will only fail if the exception +797 * message contains a line that starts with: +798 * <code>" at".</code></p> +799 * +800 * @param t is any throwable +801 * @return List of stack frames +802 */ +803 static List getStackFrameList(Throwable t) { +804 String stackTrace = getStackTrace(t); +805 String linebreak = LINE_SEPARATOR; +806 StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); +807 List list = new ArrayList(); +808 boolean traceStarted = false; +809 while (frames.hasMoreTokens()) { +810 String token = frames.nextToken(); +811 // Determine if the line starts with <whitespace>at +812 int at = token.indexOf("at"); +813 if (at != -1 && token.substring(0, at).trim().length() == 0) { +814 traceStarted = true; +815 list.add(token); +816 } else if (traceStarted) { +817 break; +818 } +819 } +820 return list; +821 } +822 +823 //----------------------------------------------------------------------- +824 /** +825 * Gets a short message summarising the exception. +826 * <p> +827 * The message returned is of the form +828 * {ClassNameWithoutPackage}: {ThrowableMessage} +829 * +830 * @param th the throwable to get a message for, null returns empty string +831 * @return the message, non-null +832 * @since Commons Lang 2.2 +833 */ +834// public static String getMessage(Throwable th) { +835// if (th == null) { +836// return ""; +837// } +838// String clsName = ClassUtils.getShortClassName(th, null); +839// String msg = th.getMessage(); +840// return clsName + ": " + StringUtils.defaultString(msg); +841// } +842 +843 //----------------------------------------------------------------------- +844 /** +845 * Gets a short message summarising the root cause exception. +846 * <p> +847 * The message returned is of the form +848 * {ClassNameWithoutPackage}: {ThrowableMessage} +849 * +850 * @param th the throwable to get a message for, null returns empty string +851 * @return the message, non-null +852 * @since Commons Lang 2.2 +853 */ +854// public static String getRootCauseMessage(Throwable th) { +855// Throwable root = ExceptionUtils.getRootCause(th); +856// root = (root == null ? th : root); +857// return getMessage(root); +858// } +859 +860 /** +861 * An interface to be implemented by {@link java.lang.Throwable} +862 * extensions which would like to be able to nest root exceptions +863 * inside themselves. +864 * +865 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a> +866 * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a> +867 * @author <a href="mailto:steven@caswell.name">Steven Caswell</a> +868 * @author Pete Gieser +869 * @since 1.0 +870 * @version $Id$ +871 */ +872 public interface Nestable { +873 +874 /** +875 * Returns the reference to the exception or error that caused the +876 * exception implementing the <code>Nestable</code> to be thrown. +877 * +878 * @return throwable that caused the original exception +879 */ +880 public Throwable getCause(); +881 +882 /** +883 * Returns the error message of this and any nested +884 * <code>Throwable</code>. +885 * +886 * @return the error message +887 */ +888 public String getMessage(); +889 +890 /** +891 * Returns the error message of the <code>Throwable</code> in the chain +892 * of <code>Throwable</code>s at the specified index, numbered from 0. +893 * +894 * @param index the index of the <code>Throwable</code> in the chain of +895 * <code>Throwable</code>s +896 * @return the error message, or null if the <code>Throwable</code> at the +897 * specified index in the chain does not contain a message +898 * @throws IndexOutOfBoundsException if the <code>index</code> argument is +899 * negative or not less than the count of <code>Throwable</code>s in the +900 * chain +901 */ +902 public String getMessage(int index); +903 +904 /** +905 * Returns the error message of this and any nested <code>Throwable</code>s +906 * in an array of Strings, one element for each message. Any +907 * <code>Throwable</code> not containing a message is represented in the +908 * array by a null. This has the effect of cause the length of the returned +909 * array to be equal to the result of the {@link #getThrowableCount()} +910 * operation. +911 * +912 * @return the error messages +913 */ +914 public String[] getMessages(); +915 +916 /** +917 * Returns the <code>Throwable</code> in the chain of +918 * <code>Throwable</code>s at the specified index, numbered from 0. +919 * +920 * @param index the index, numbered from 0, of the <code>Throwable</code> in +921 * the chain of <code>Throwable</code>s +922 * @return the <code>Throwable</code> +923 * @throws IndexOutOfBoundsException if the <code>index</code> argument is +924 * negative or not less than the count of <code>Throwable</code>s in the +925 * chain +926 */ +927 public Throwable getThrowable(int index); +928 +929 /** +930 * Returns the number of nested <code>Throwable</code>s represented by +931 * this <code>Nestable</code>, including this <code>Nestable</code>. +932 * +933 * @return the throwable count +934 */ +935 public int getThrowableCount(); +936 +937 /** +938 * Returns this <code>Nestable</code> and any nested <code>Throwable</code>s +939 * in an array of <code>Throwable</code>s, one element for each +940 * <code>Throwable</code>. +941 * +942 * @return the <code>Throwable</code>s +943 */ +944 public Throwable[] getThrowables(); +945 +946 /** +947 * Returns the index, numbered from 0, of the first occurrence of the +948 * specified type, or a subclass, in the chain of <code>Throwable</code>s. +949 * The method returns -1 if the specified type is not found in the chain. +950 * <p> +951 * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface +952 * such that this method matches subclasses. +953 * If you want to NOT match subclasses, please use +954 * (which is avaiable in all versions of lang). +955 * +956 * @param type the type to find, subclasses match, null returns -1 +957 * @return index of the first occurrence of the type in the chain, or -1 if +958 * the type is not found +959 */ +960 public int indexOfThrowable(Class type); +961 +962 /** +963 * Returns the index, numbered from 0, of the first <code>Throwable</code> +964 * that matches the specified type, or a subclass, in the chain of <code>Throwable</code>s +965 * with an index greater than or equal to the specified index. +966 * The method returns -1 if the specified type is not found in the chain. +967 * <p> +968 * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface +969 * such that this method matches subclasses. +970 * If you want to NOT match subclasses, please use +971 * (which is avaiable in all versions of lang). +972 * +973 * @param type the type to find, subclasses match, null returns -1 +974 * @param fromIndex the index, numbered from 0, of the starting position in +975 * the chain to be searched +976 * @return index of the first occurrence of the type in the chain, or -1 if +977 * the type is not found +978 * @throws IndexOutOfBoundsException if the <code>fromIndex</code> argument +979 * is negative or not less than the count of <code>Throwable</code>s in the +980 * chain +981 */ +982 public int indexOfThrowable(Class type, int fromIndex); +983 +984 /** +985 * Prints the stack trace of this exception to the specified print +986 * writer. Includes information from the exception, if any, +987 * which caused this exception. +988 * +989 * @param out <code>PrintWriter</code> to use for output. +990 */ +991 public void printStackTrace(PrintWriter out); +992 +993 /** +994 * Prints the stack trace of this exception to the specified print +995 * stream. Includes information from the exception, if any, +996 * which caused this exception. +997 * +998 * @param out <code>PrintStream</code> to use for output. +999 */ +1000 public void printStackTrace(PrintStream out); +1001 +1002 /** +1003 * Prints the stack trace for this exception only--root cause not +1004 * included--using the provided writer. Used by +1005 * individual stack traces to a buffer. The implementation of +1006 * this method should call +1007 * <code>super.printStackTrace(out);</code> in most cases. +1008 * +1009 * @param out The writer to use. +1010 */ +1011 public void printPartialStackTrace(PrintWriter out); +1012 +1013 } +1014} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002 * Licensed to the Apache Software Foundation (ASF) under one or more +003 * contributor license agreements. See the NOTICE file distributed with +004 * this work for additional information regarding copyright ownership. +005 * The ASF licenses this file to You under the Apache License, Version 2.0 +006 * (the "License"); you may not use this file except in compliance with +007 * the License. You may obtain a copy of the License at +008 * +009 * http://www.apache.org/licenses/LICENSE-2.0 +010 * +011 * Unless required by applicable law or agreed to in writing, software +012 * distributed under the License is distributed on an "AS IS" BASIS, +013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +014 * See the License for the specific language governing permissions and +015 * limitations under the License. +016 */ +017package co.aikar.commands.apachecommonslang; +018 +019import java.io.PrintStream; +020import java.io.PrintWriter; +021import java.io.StringWriter; +022import java.lang.reflect.Field; +023import java.lang.reflect.InvocationTargetException; +024import java.lang.reflect.Method; +025import java.sql.SQLException; +026import java.util.ArrayList; +027import java.util.Arrays; +028import java.util.List; +029import java.util.StringTokenizer; +030 +031/** +032 * <p>Provides utilities for manipulating and examining +033 * <code>Throwable</code> objects.</p> +034 * +035 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> +036 * @author Dmitri Plotnikov +037 * @author Stephen Colebourne +038 * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a> +039 * @author Pete Gieser +040 * @since 1.0 +041 * @version $Id$ +042 */ +043public class ApacheCommonsExceptionUtil { +044 private static final String LINE_SEPARATOR = System.getProperty("line.separator"); +045 +046 /** +047 * <p>Used when printing stack frames to denote the start of a +048 * wrapped exception.</p> +049 * +050 * <p>Package private for accessibility by test suite.</p> +051 */ +052 static final String WRAPPED_MARKER = " [wrapped] "; +053 +054 /** +055 * <p>The names of methods commonly used to access a wrapped exception.</p> +056 */ +057 private static String[] CAUSE_METHOD_NAMES = { +058 "getCause", +059 "getNextException", +060 "getTargetException", +061 "getException", +062 "getSourceException", +063 "getRootCause", +064 "getCausedByException", +065 "getNested", +066 "getLinkedException", +067 "getNestedException", +068 "getLinkedCause", +069 "getThrowable", +070 }; +071 +072 /** +073 * <p>The Method object for Java 1.4 getCause.</p> +074 */ +075 private static final Method THROWABLE_CAUSE_METHOD; +076 +077 /** +078 * <p>The Method object for Java 1.4 initCause.</p> +079 */ +080 private static final Method THROWABLE_INITCAUSE_METHOD; +081 +082 static { +083 Method causeMethod; +084 try { +085 causeMethod = Throwable.class.getMethod("getCause", null); +086 } catch (Exception e) { +087 causeMethod = null; +088 } +089 THROWABLE_CAUSE_METHOD = causeMethod; +090 try { +091 causeMethod = Throwable.class.getMethod("initCause", new Class[]{Throwable.class}); +092 } catch (Exception e) { +093 causeMethod = null; +094 } +095 THROWABLE_INITCAUSE_METHOD = causeMethod; +096 } +097 +098 /** +099 * <p> +100 * Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not +101 * normally necessary. +102 * </p> +103 */ +104 public ApacheCommonsExceptionUtil() { +105 super(); +106 } +107 +108 //----------------------------------------------------------------------- +109 /** +110 * <p>Adds to the list of method names used in the search for <code>Throwable</code> +111 * objects.</p> +112 * +113 * @param methodName the methodName to add to the list, <code>null</code> +114 * and empty strings are ignored +115 * @since 2.0 +116 */ +117 public static void addCauseMethodName(String methodName) { +118 if (methodName != null && !methodName.isEmpty() && !isCauseMethodName(methodName)) { +119 List list = getCauseMethodNameList(); +120 if (list.add(methodName)) { +121 CAUSE_METHOD_NAMES = toArray(list); +122 } +123 } +124 } +125 +126 /** +127 * <p>Removes from the list of method names used in the search for <code>Throwable</code> +128 * objects.</p> +129 * +130 * @param methodName the methodName to remove from the list, <code>null</code> +131 * and empty strings are ignored +132 * @since 2.1 +133 */ +134 public static void removeCauseMethodName(String methodName) { +135 if (methodName != null && !methodName.isEmpty()) { +136 List list = getCauseMethodNameList(); +137 if (list.remove(methodName)) { +138 CAUSE_METHOD_NAMES = toArray(list); +139 } +140 } +141 } +142 +143 /** +144 * <p>Sets the cause of a <code>Throwable</code> using introspection, allowing +145 * source code compatibility between pre-1.4 and post-1.4 Java releases.</p> +146 * +147 * <p>The typical use of this method is inside a constructor as in +148 * the following example:</p> +149 * +150 * <pre> +151 * import org.apache.commons.lang.exception.ExceptionUtils; +152 * +153 * public class MyException extends Exception { +154 * +155 * public MyException(String msg) { +156 * super(msg); +157 * } +158 * +159 * public MyException(String msg, Throwable cause) { +160 * super(msg); +161 * ExceptionUtils.setCause(this, cause); +162 * } +163 * } +164 * </pre> +165 * +166 * @param target the target <code>Throwable</code> +167 * @param cause the <code>Throwable</code> to set in the target +168 * @return a <code>true</code> if the target has been modified +169 * @since 2.2 +170 */ +171 public static boolean setCause(Throwable target, Throwable cause) { +172 if (target == null) { +173 throw new IllegalArgumentException("target"); +174 } +175 Object[] causeArgs = new Object[]{cause}; +176 boolean modifiedTarget = false; +177 if (THROWABLE_INITCAUSE_METHOD != null) { +178 try { +179 THROWABLE_INITCAUSE_METHOD.invoke(target, causeArgs); +180 modifiedTarget = true; +181 } catch (IllegalAccessException ignored) { +182 // Exception ignored. +183 } catch (InvocationTargetException ignored) { +184 // Exception ignored. +185 } +186 } +187 try { +188 Method setCauseMethod = target.getClass().getMethod("setCause", new Class[]{Throwable.class}); +189 setCauseMethod.invoke(target, causeArgs); +190 modifiedTarget = true; +191 } catch (NoSuchMethodException ignored) { +192 // Exception ignored. +193 } catch (IllegalAccessException ignored) { +194 // Exception ignored. +195 } catch (InvocationTargetException ignored) { +196 // Exception ignored. +197 } +198 return modifiedTarget; +199 } +200 +201 /** +202 * Returns the given list as a <code>String[]</code>. +203 * @param list a list to transform. +204 * @return the given list as a <code>String[]</code>. +205 */ +206 private static String[] toArray(List list) { +207 return (String[]) list.toArray(new String[list.size()]); +208 } +209 +210 /** +211 * Returns {@link #CAUSE_METHOD_NAMES} as a List. +212 * +213 * @return {@link #CAUSE_METHOD_NAMES} as a List. +214 */ +215 private static ArrayList getCauseMethodNameList() { +216 return new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES)); +217 } +218 +219 /** +220 * <p>Tests if the list of method names used in the search for <code>Throwable</code> +221 * objects include the given name.</p> +222 * +223 * @param methodName the methodName to search in the list. +224 * @return if the list of method names used in the search for <code>Throwable</code> +225 * objects include the given name. +226 * @since 2.1 +227 */ +228 public static boolean isCauseMethodName(String methodName) { +229 return ApacheCommonsLangUtil.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0; +230 } +231 +232 //----------------------------------------------------------------------- +233 /** +234 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> +235 * +236 * <p>The method searches for methods with specific names that return a +237 * <code>Throwable</code> object. This will pick up most wrapping exceptions, +238 * including those from JDK 1.4, and +239 * The method names can be added to using {@link #addCauseMethodName(String)}.</p> +240 * +241 * <p>The default list searched for are:</p> +242 * <ul> +243 * <li><code>getCause()</code></li> +244 * <li><code>getNextException()</code></li> +245 * <li><code>getTargetException()</code></li> +246 * <li><code>getException()</code></li> +247 * <li><code>getSourceException()</code></li> +248 * <li><code>getRootCause()</code></li> +249 * <li><code>getCausedByException()</code></li> +250 * <li><code>getNested()</code></li> +251 * </ul> +252 * +253 * <p>In the absence of any such method, the object is inspected for a +254 * <code>detail</code> field assignable to a <code>Throwable</code>.</p> +255 * +256 * <p>If none of the above is found, returns <code>null</code>.</p> +257 * +258 * @param throwable the throwable to introspect for a cause, may be null +259 * @return the cause of the <code>Throwable</code>, +260 * <code>null</code> if none found or null throwable input +261 * @since 1.0 +262 */ +263 public static Throwable getCause(Throwable throwable) { +264 return getCause(throwable, CAUSE_METHOD_NAMES); +265 } +266 +267 /** +268 * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> +269 * +270 * <ol> +271 * <li>Try known exception types.</li> +272 * <li>Try the supplied array of method names.</li> +273 * <li>Try the field 'detail'.</li> +274 * </ol> +275 * +276 * <p>A <code>null</code> set of method names means use the default set. +277 * A <code>null</code> in the set of method names will be ignored.</p> +278 * +279 * @param throwable the throwable to introspect for a cause, may be null +280 * @param methodNames the method names, null treated as default set +281 * @return the cause of the <code>Throwable</code>, +282 * <code>null</code> if none found or null throwable input +283 * @since 1.0 +284 */ +285 public static Throwable getCause(Throwable throwable, String[] methodNames) { +286 if (throwable == null) { +287 return null; +288 } +289 Throwable cause = getCauseUsingWellKnownTypes(throwable); +290 if (cause == null) { +291 if (methodNames == null) { +292 methodNames = CAUSE_METHOD_NAMES; +293 } +294 for (int i = 0; i < methodNames.length; i++) { +295 String methodName = methodNames[i]; +296 if (methodName != null) { +297 cause = getCauseUsingMethodName(throwable, methodName); +298 if (cause != null) { +299 break; +300 } +301 } +302 } +303 +304 if (cause == null) { +305 cause = getCauseUsingFieldName(throwable, "detail"); +306 } +307 } +308 return cause; +309 } +310 +311 /** +312 * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p> +313 * +314 * <p>This method walks through the exception chain to the last element, +315 * "root" of the tree, using {@link #getCause(Throwable)}, and +316 * returns that exception.</p> +317 * +318 * <p>From version 2.2, this method handles recursive cause structures +319 * that might otherwise cause infinite loops. If the throwable parameter +320 * has a cause of itself, then null will be returned. If the throwable +321 * parameter cause chain loops, the last element in the chain before the +322 * loop is returned.</p> +323 * +324 * @param throwable the throwable to get the root cause for, may be null +325 * @return the root cause of the <code>Throwable</code>, +326 * <code>null</code> if none found or null throwable input +327 */ +328 public static Throwable getRootCause(Throwable throwable) { +329 List list = getThrowableList(throwable); +330 return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1)); +331 } +332 +333 /** +334 * <p>Finds a <code>Throwable</code> for known types.</p> +335 * +336 * <p>Uses <code>instanceof</code> checks to examine the exception, +337 * looking for well known types which could contain chained or +338 * wrapped exceptions.</p> +339 * +340 * @param throwable the exception to examine +341 * @return the wrapped exception, or <code>null</code> if not found +342 */ +343 private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) { +344 if (throwable instanceof Nestable) { +345 return ((Nestable) throwable).getCause(); +346 } else if (throwable instanceof SQLException) { +347 return ((SQLException) throwable).getNextException(); +348 } else if (throwable instanceof InvocationTargetException) { +349 return ((InvocationTargetException) throwable).getTargetException(); +350 } else { +351 return null; +352 } +353 } +354 +355 /** +356 * <p>Finds a <code>Throwable</code> by method name.</p> +357 * +358 * @param throwable the exception to examine +359 * @param methodName the name of the method to find and invoke +360 * @return the wrapped exception, or <code>null</code> if not found +361 */ +362 private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { +363 Method method = null; +364 try { +365 method = throwable.getClass().getMethod(methodName, null); +366 } catch (NoSuchMethodException ignored) { +367 // exception ignored +368 } catch (SecurityException ignored) { +369 // exception ignored +370 } +371 +372 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { +373 try { +374 return (Throwable) method.invoke(throwable); +375 } catch (IllegalAccessException ignored) { +376 // exception ignored +377 } catch (IllegalArgumentException ignored) { +378 // exception ignored +379 } catch (InvocationTargetException ignored) { +380 // exception ignored +381 } +382 } +383 return null; +384 } +385 +386 /** +387 * <p>Finds a <code>Throwable</code> by field name.</p> +388 * +389 * @param throwable the exception to examine +390 * @param fieldName the name of the attribute to examine +391 * @return the wrapped exception, or <code>null</code> if not found +392 */ +393 private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) { +394 Field field = null; +395 try { +396 field = throwable.getClass().getField(fieldName); +397 } catch (NoSuchFieldException ignored) { +398 // exception ignored +399 } catch (SecurityException ignored) { +400 // exception ignored +401 } +402 +403 if (field != null && Throwable.class.isAssignableFrom(field.getType())) { +404 try { +405 return (Throwable) field.get(throwable); +406 } catch (IllegalAccessException ignored) { +407 // exception ignored +408 } catch (IllegalArgumentException ignored) { +409 // exception ignored +410 } +411 } +412 return null; +413 } +414 +415 //----------------------------------------------------------------------- +416 /** +417 * <p>Checks if the Throwable class has a <code>getCause</code> method.</p> +418 * +419 * <p>This is true for JDK 1.4 and above.</p> +420 * +421 * @return true if Throwable is nestable +422 * @since 2.0 +423 */ +424 public static boolean isThrowableNested() { +425 return THROWABLE_CAUSE_METHOD != null; +426 } +427 +428 /** +429 * <p>Checks whether this <code>Throwable</code> class can store a cause.</p> +430 * +431 * <p>This method does <b>not</b> check whether it actually does store a cause.<p> +432 * +433 * @param throwable the <code>Throwable</code> to examine, may be null +434 * @return boolean <code>true</code> if nested otherwise <code>false</code> +435 * @since 2.0 +436 */ +437 public static boolean isNestedThrowable(Throwable throwable) { +438 if (throwable == null) { +439 return false; +440 } +441 +442 if (throwable instanceof Nestable) { +443 return true; +444 } else if (throwable instanceof SQLException) { +445 return true; +446 } else if (throwable instanceof InvocationTargetException) { +447 return true; +448 } else if (isThrowableNested()) { +449 return true; +450 } +451 +452 Class cls = throwable.getClass(); +453 for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) { +454 try { +455 Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null); +456 if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { +457 return true; +458 } +459 } catch (NoSuchMethodException ignored) { +460 // exception ignored +461 } catch (SecurityException ignored) { +462 // exception ignored +463 } +464 } +465 +466 try { +467 Field field = cls.getField("detail"); +468 if (field != null) { +469 return true; +470 } +471 } catch (NoSuchFieldException ignored) { +472 // exception ignored +473 } catch (SecurityException ignored) { +474 // exception ignored +475 } +476 +477 return false; +478 } +479 +480 //----------------------------------------------------------------------- +481 /** +482 * <p>Counts the number of <code>Throwable</code> objects in the +483 * exception chain.</p> +484 * +485 * <p>A throwable without cause will return <code>1</code>. +486 * A throwable with one cause will return <code>2</code> and so on. +487 * A <code>null</code> throwable will return <code>0</code>.</p> +488 * +489 * <p>From version 2.2, this method handles recursive cause structures +490 * that might otherwise cause infinite loops. The cause chain is +491 * processed until the end is reached, or until the next item in the +492 * chain is already in the result set.</p> +493 * +494 * @param throwable the throwable to inspect, may be null +495 * @return the count of throwables, zero if null input +496 */ +497 public static int getThrowableCount(Throwable throwable) { +498 return getThrowableList(throwable).size(); +499 } +500 +501 /** +502 * <p>Returns the list of <code>Throwable</code> objects in the +503 * exception chain.</p> +504 * +505 * <p>A throwable without cause will return an array containing +506 * one element - the input throwable. +507 * A throwable with one cause will return an array containing +508 * two elements. - the input throwable and the cause throwable. +509 * A <code>null</code> throwable will return an array of size zero.</p> +510 * +511 * <p>From version 2.2, this method handles recursive cause structures +512 * that might otherwise cause infinite loops. The cause chain is +513 * processed until the end is reached, or until the next item in the +514 * chain is already in the result set.</p> +515 * +516 * @see #getThrowableList(Throwable) +517 * @param throwable the throwable to inspect, may be null +518 * @return the array of throwables, never null +519 */ +520 public static Throwable[] getThrowables(Throwable throwable) { +521 List list = getThrowableList(throwable); +522 return (Throwable[]) list.toArray(new Throwable[list.size()]); +523 } +524 +525 /** +526 * <p>Returns the list of <code>Throwable</code> objects in the +527 * exception chain.</p> +528 * +529 * <p>A throwable without cause will return a list containing +530 * one element - the input throwable. +531 * A throwable with one cause will return a list containing +532 * two elements. - the input throwable and the cause throwable. +533 * A <code>null</code> throwable will return a list of size zero.</p> +534 * +535 * <p>This method handles recursive cause structures that might +536 * otherwise cause infinite loops. The cause chain is processed until +537 * the end is reached, or until the next item in the chain is already +538 * in the result set.</p> +539 * +540 * @param throwable the throwable to inspect, may be null +541 * @return the list of throwables, never null +542 * @since Commons Lang 2.2 +543 */ +544 public static List getThrowableList(Throwable throwable) { +545 List list = new ArrayList(); +546 while (throwable != null && list.contains(throwable) == false) { +547 list.add(throwable); +548 throwable = getCause(throwable); +549 } +550 return list; +551 } +552 +553 //----------------------------------------------------------------------- +554 /** +555 * <p>Returns the (zero based) index of the first <code>Throwable</code> +556 * that matches the specified class (exactly) in the exception chain. +557 * Subclasses of the specified class do not match - see +558 * {@link #indexOfType(Throwable, Class)} for the opposite.</p> +559 * +560 * <p>A <code>null</code> throwable returns <code>-1</code>. +561 * A <code>null</code> type returns <code>-1</code>. +562 * No match in the chain returns <code>-1</code>.</p> +563 * +564 * @param throwable the throwable to inspect, may be null +565 * @param clazz the class to search for, subclasses do not match, null returns -1 +566 * @return the index into the throwable chain, -1 if no match or null input +567 */ +568 public static int indexOfThrowable(Throwable throwable, Class clazz) { +569 return indexOf(throwable, clazz, 0, false); +570 } +571 +572 /** +573 * <p>Returns the (zero based) index of the first <code>Throwable</code> +574 * that matches the specified type in the exception chain from +575 * a specified index. +576 * Subclasses of the specified class do not match - see +577 * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p> +578 * +579 * <p>A <code>null</code> throwable returns <code>-1</code>. +580 * A <code>null</code> type returns <code>-1</code>. +581 * No match in the chain returns <code>-1</code>. +582 * A negative start index is treated as zero. +583 * A start index greater than the number of throwables returns <code>-1</code>.</p> +584 * +585 * @param throwable the throwable to inspect, may be null +586 * @param clazz the class to search for, subclasses do not match, null returns -1 +587 * @param fromIndex the (zero based) index of the starting position, +588 * negative treated as zero, larger than chain size returns -1 +589 * @return the index into the throwable chain, -1 if no match or null input +590 */ +591 public static int indexOfThrowable(Throwable throwable, Class clazz, int fromIndex) { +592 return indexOf(throwable, clazz, fromIndex, false); +593 } +594 +595 //----------------------------------------------------------------------- +596 /** +597 * <p>Returns the (zero based) index of the first <code>Throwable</code> +598 * that matches the specified class or subclass in the exception chain. +599 * Subclasses of the specified class do match - see +600 * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> +601 * +602 * <p>A <code>null</code> throwable returns <code>-1</code>. +603 * A <code>null</code> type returns <code>-1</code>. +604 * No match in the chain returns <code>-1</code>.</p> +605 * +606 * @param throwable the throwable to inspect, may be null +607 * @param type the type to search for, subclasses match, null returns -1 +608 * @return the index into the throwable chain, -1 if no match or null input +609 * @since 2.1 +610 */ +611 public static int indexOfType(Throwable throwable, Class type) { +612 return indexOf(throwable, type, 0, true); +613 } +614 +615 /** +616 * <p>Returns the (zero based) index of the first <code>Throwable</code> +617 * that matches the specified type in the exception chain from +618 * a specified index. +619 * Subclasses of the specified class do match - see +620 * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> +621 * +622 * <p>A <code>null</code> throwable returns <code>-1</code>. +623 * A <code>null</code> type returns <code>-1</code>. +624 * No match in the chain returns <code>-1</code>. +625 * A negative start index is treated as zero. +626 * A start index greater than the number of throwables returns <code>-1</code>.</p> +627 * +628 * @param throwable the throwable to inspect, may be null +629 * @param type the type to search for, subclasses match, null returns -1 +630 * @param fromIndex the (zero based) index of the starting position, +631 * negative treated as zero, larger than chain size returns -1 +632 * @return the index into the throwable chain, -1 if no match or null input +633 * @since 2.1 +634 */ +635 public static int indexOfType(Throwable throwable, Class type, int fromIndex) { +636 return indexOf(throwable, type, fromIndex, true); +637 } +638 +639 /** +640 * <p>Worker method for the <code>indexOfType</code> methods.</p> +641 * +642 * @param throwable the throwable to inspect, may be null +643 * @param type the type to search for, subclasses match, null returns -1 +644 * @param fromIndex the (zero based) index of the starting position, +645 * negative treated as zero, larger than chain size returns -1 +646 * @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares +647 * using references +648 * @return index of the <code>type</code> within throwables nested withing the specified <code>throwable</code> +649 */ +650 private static int indexOf(Throwable throwable, Class type, int fromIndex, boolean subclass) { +651 if (throwable == null || type == null) { +652 return -1; +653 } +654 if (fromIndex < 0) { +655 fromIndex = 0; +656 } +657 Throwable[] throwables = getThrowables(throwable); +658 if (fromIndex >= throwables.length) { +659 return -1; +660 } +661 if (subclass) { +662 for (int i = fromIndex; i < throwables.length; i++) { +663 if (type.isAssignableFrom(throwables[i].getClass())) { +664 return i; +665 } +666 } +667 } else { +668 for (int i = fromIndex; i < throwables.length; i++) { +669 if (type.equals(throwables[i].getClass())) { +670 return i; +671 } +672 } +673 } +674 return -1; +675 } +676 +677 /** +678 * <p>Removes common frames from the cause trace given the two stack traces.</p> +679 * +680 * @param causeFrames stack trace of a cause throwable +681 * @param wrapperFrames stack trace of a wrapper throwable +682 * @throws IllegalArgumentException if either argument is null +683 * @since 2.0 +684 */ +685 public static void removeCommonFrames(List causeFrames, List wrapperFrames) { +686 if (causeFrames == null || wrapperFrames == null) { +687 throw new IllegalArgumentException("The List must not be null"); +688 } +689 int causeFrameIndex = causeFrames.size() - 1; +690 int wrapperFrameIndex = wrapperFrames.size() - 1; +691 while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { +692 // Remove the frame from the cause trace if it is the same +693 // as in the wrapper trace +694 String causeFrame = (String) causeFrames.get(causeFrameIndex); +695 String wrapperFrame = (String) wrapperFrames.get(wrapperFrameIndex); +696 if (causeFrame.equals(wrapperFrame)) { +697 causeFrames.remove(causeFrameIndex); +698 } +699 causeFrameIndex--; +700 wrapperFrameIndex--; +701 } +702 } +703 +704 //----------------------------------------------------------------------- +705 /** +706 * <p>A way to get the entire nested stack-trace of an throwable.</p> +707 * +708 * <p>The result of this method is highly dependent on the JDK version +709 * and whether the exceptions override printStackTrace or not.</p> +710 * +711 * @param throwable the <code>Throwable</code> to be examined +712 * @return the nested stack trace, with the root cause first +713 * @since 2.0 +714 */ +715 public static String getFullStackTrace(Throwable throwable) { +716 StringWriter sw = new StringWriter(); +717 PrintWriter pw = new PrintWriter(sw, true); +718 Throwable[] ts = getThrowables(throwable); +719 for (int i = 0; i < ts.length; i++) { +720 ts[i].printStackTrace(pw); +721 if (isNestedThrowable(ts[i])) { +722 break; +723 } +724 } +725 return sw.getBuffer().toString(); +726 } +727 +728 //----------------------------------------------------------------------- +729 /** +730 * <p>Gets the stack trace from a Throwable as a String.</p> +731 * +732 * <p>The result of this method vary by JDK version as this method +733 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. +734 * On JDK1.3 and earlier, the cause exception will not be shown +735 * unless the specified throwable alters printStackTrace.</p> +736 * +737 * @param throwable the <code>Throwable</code> to be examined +738 * @return the stack trace as generated by the exception's +739 * <code>printStackTrace(PrintWriter)</code> method +740 */ +741 public static String getStackTrace(Throwable throwable) { +742 StringWriter sw = new StringWriter(); +743 PrintWriter pw = new PrintWriter(sw, true); +744 throwable.printStackTrace(pw); +745 return sw.getBuffer().toString(); +746 } +747 +748 /** +749 * <p>Captures the stack trace associated with the specified +750 * <code>Throwable</code> object, decomposing it into a list of +751 * stack frames.</p> +752 * +753 * <p>The result of this method vary by JDK version as this method +754 * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. +755 * On JDK1.3 and earlier, the cause exception will not be shown +756 * unless the specified throwable alters printStackTrace.</p> +757 * +758 * @param throwable the <code>Throwable</code> to examine, may be null +759 * @return an array of strings describing each stack frame, never null +760 */ +761// public static String[] getStackFrames(Throwable throwable) { +762// if (throwable == null) { +763// return ArrayUtils.EMPTY_STRING_ARRAY; +764// } +765// return getStackFrames(getStackTrace(throwable)); +766// } +767 +768 //----------------------------------------------------------------------- +769 /** +770 * <p>Returns an array where each element is a line from the argument.</p> +771 * +772 * <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p> +773 * +774 * <p>Functionality shared between the +775 * <code>getStackFrames(Throwable)</code> methods of this and the +776 * {@link org.apache.commons.lang.exception.NestableDelegate} classes.</p> +777 * +778 * @param stackTrace a stack trace String +779 * @return an array where each element is a line from the argument +780 */ +781// static String[] getStackFrames(String stackTrace) { +782// String linebreak = SystemUtils.LINE_SEPARATOR; +783// StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); +784// List list = new ArrayList(); +785// while (frames.hasMoreTokens()) { +786// list.add(frames.nextToken()); +787// } +788// return toArray(list); +789// } +790 +791 /** +792 * <p>Produces a <code>List</code> of stack frames - the message +793 * is not included. Only the trace of the specified exception is +794 * returned, any caused by trace is stripped.</p> +795 * +796 * <p>This works in most cases - it will only fail if the exception +797 * message contains a line that starts with: +798 * <code>" at".</code></p> +799 * +800 * @param t is any throwable +801 * @return List of stack frames +802 */ +803 static List getStackFrameList(Throwable t) { +804 String stackTrace = getStackTrace(t); +805 String linebreak = LINE_SEPARATOR; +806 StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); +807 List list = new ArrayList(); +808 boolean traceStarted = false; +809 while (frames.hasMoreTokens()) { +810 String token = frames.nextToken(); +811 // Determine if the line starts with <whitespace>at +812 int at = token.indexOf("at"); +813 if (at != -1 && token.substring(0, at).trim().length() == 0) { +814 traceStarted = true; +815 list.add(token); +816 } else if (traceStarted) { +817 break; +818 } +819 } +820 return list; +821 } +822 +823 //----------------------------------------------------------------------- +824 /** +825 * Gets a short message summarising the exception. +826 * <p> +827 * The message returned is of the form +828 * {ClassNameWithoutPackage}: {ThrowableMessage} +829 * +830 * @param th the throwable to get a message for, null returns empty string +831 * @return the message, non-null +832 * @since Commons Lang 2.2 +833 */ +834// public static String getMessage(Throwable th) { +835// if (th == null) { +836// return ""; +837// } +838// String clsName = ClassUtils.getShortClassName(th, null); +839// String msg = th.getMessage(); +840// return clsName + ": " + StringUtils.defaultString(msg); +841// } +842 +843 //----------------------------------------------------------------------- +844 /** +845 * Gets a short message summarising the root cause exception. +846 * <p> +847 * The message returned is of the form +848 * {ClassNameWithoutPackage}: {ThrowableMessage} +849 * +850 * @param th the throwable to get a message for, null returns empty string +851 * @return the message, non-null +852 * @since Commons Lang 2.2 +853 */ +854// public static String getRootCauseMessage(Throwable th) { +855// Throwable root = ExceptionUtils.getRootCause(th); +856// root = (root == null ? th : root); +857// return getMessage(root); +858// } +859 +860 /** +861 * An interface to be implemented by {@link java.lang.Throwable} +862 * extensions which would like to be able to nest root exceptions +863 * inside themselves. +864 * +865 * @author <a href="mailto:dlr@collab.net">Daniel Rall</a> +866 * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a> +867 * @author <a href="mailto:steven@caswell.name">Steven Caswell</a> +868 * @author Pete Gieser +869 * @since 1.0 +870 * @version $Id$ +871 */ +872 public interface Nestable { +873 +874 /** +875 * Returns the reference to the exception or error that caused the +876 * exception implementing the <code>Nestable</code> to be thrown. +877 * +878 * @return throwable that caused the original exception +879 */ +880 public Throwable getCause(); +881 +882 /** +883 * Returns the error message of this and any nested +884 * <code>Throwable</code>. +885 * +886 * @return the error message +887 */ +888 public String getMessage(); +889 +890 /** +891 * Returns the error message of the <code>Throwable</code> in the chain +892 * of <code>Throwable</code>s at the specified index, numbered from 0. +893 * +894 * @param index the index of the <code>Throwable</code> in the chain of +895 * <code>Throwable</code>s +896 * @return the error message, or null if the <code>Throwable</code> at the +897 * specified index in the chain does not contain a message +898 * @throws IndexOutOfBoundsException if the <code>index</code> argument is +899 * negative or not less than the count of <code>Throwable</code>s in the +900 * chain +901 */ +902 public String getMessage(int index); +903 +904 /** +905 * Returns the error message of this and any nested <code>Throwable</code>s +906 * in an array of Strings, one element for each message. Any +907 * <code>Throwable</code> not containing a message is represented in the +908 * array by a null. This has the effect of cause the length of the returned +909 * array to be equal to the result of the {@link #getThrowableCount()} +910 * operation. +911 * +912 * @return the error messages +913 */ +914 public String[] getMessages(); +915 +916 /** +917 * Returns the <code>Throwable</code> in the chain of +918 * <code>Throwable</code>s at the specified index, numbered from 0. +919 * +920 * @param index the index, numbered from 0, of the <code>Throwable</code> in +921 * the chain of <code>Throwable</code>s +922 * @return the <code>Throwable</code> +923 * @throws IndexOutOfBoundsException if the <code>index</code> argument is +924 * negative or not less than the count of <code>Throwable</code>s in the +925 * chain +926 */ +927 public Throwable getThrowable(int index); +928 +929 /** +930 * Returns the number of nested <code>Throwable</code>s represented by +931 * this <code>Nestable</code>, including this <code>Nestable</code>. +932 * +933 * @return the throwable count +934 */ +935 public int getThrowableCount(); +936 +937 /** +938 * Returns this <code>Nestable</code> and any nested <code>Throwable</code>s +939 * in an array of <code>Throwable</code>s, one element for each +940 * <code>Throwable</code>. +941 * +942 * @return the <code>Throwable</code>s +943 */ +944 public Throwable[] getThrowables(); +945 +946 /** +947 * Returns the index, numbered from 0, of the first occurrence of the +948 * specified type, or a subclass, in the chain of <code>Throwable</code>s. +949 * The method returns -1 if the specified type is not found in the chain. +950 * <p> +951 * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface +952 * such that this method matches subclasses. +953 * If you want to NOT match subclasses, please use +954 * (which is avaiable in all versions of lang). +955 * +956 * @param type the type to find, subclasses match, null returns -1 +957 * @return index of the first occurrence of the type in the chain, or -1 if +958 * the type is not found +959 */ +960 public int indexOfThrowable(Class type); +961 +962 /** +963 * Returns the index, numbered from 0, of the first <code>Throwable</code> +964 * that matches the specified type, or a subclass, in the chain of <code>Throwable</code>s +965 * with an index greater than or equal to the specified index. +966 * The method returns -1 if the specified type is not found in the chain. +967 * <p> +968 * NOTE: From v2.1, we have clarified the <code>Nestable</code> interface +969 * such that this method matches subclasses. +970 * If you want to NOT match subclasses, please use +971 * (which is avaiable in all versions of lang). +972 * +973 * @param type the type to find, subclasses match, null returns -1 +974 * @param fromIndex the index, numbered from 0, of the starting position in +975 * the chain to be searched +976 * @return index of the first occurrence of the type in the chain, or -1 if +977 * the type is not found +978 * @throws IndexOutOfBoundsException if the <code>fromIndex</code> argument +979 * is negative or not less than the count of <code>Throwable</code>s in the +980 * chain +981 */ +982 public int indexOfThrowable(Class type, int fromIndex); +983 +984 /** +985 * Prints the stack trace of this exception to the specified print +986 * writer. Includes information from the exception, if any, +987 * which caused this exception. +988 * +989 * @param out <code>PrintWriter</code> to use for output. +990 */ +991 public void printStackTrace(PrintWriter out); +992 +993 /** +994 * Prints the stack trace of this exception to the specified print +995 * stream. Includes information from the exception, if any, +996 * which caused this exception. +997 * +998 * @param out <code>PrintStream</code> to use for output. +999 */ +1000 public void printStackTrace(PrintStream out); +1001 +1002 /** +1003 * Prints the stack trace for this exception only--root cause not +1004 * included--using the provided writer. Used by +1005 * individual stack traces to a buffer. The implementation of +1006 * this method should call +1007 * <code>super.printStackTrace(out);</code> in most cases. +1008 * +1009 * @param out The writer to use. +1010 */ +1011 public void printPartialStackTrace(PrintWriter out); +1012 +1013 } +1014} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
001/* +002* Licensed to the Apache Software Foundation (ASF) under one or more +003* contributor license agreements. See the NOTICE file distributed with +004* this work for additional information regarding copyright ownership. +005* The ASF licenses this file to You under the Apache License, Version 2.0 +006* (the "License"); you may not use this file except in compliance with +007* the License. You may obtain a copy of the License at +008* +009* http://www.apache.org/licenses/LICENSE-2.0 +010* +011* Unless required by applicable law or agreed to in writing, software +012* distributed under the License is distributed on an "AS IS" BASIS, +013* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +014* See the License for the specific language governing permissions and +015* limitations under the License. +016*/ +017 +018package co.aikar.commands.apachecommonslang; +019 +020import java.lang.reflect.Array; +021import java.util.Iterator; +022 +023/** +024 * Select methods copied from Apache Commons to avoid importing entire lib +025 * No changes to logic +026 */ +027public class ApacheCommonsLangUtil { +028 +029 /** +030 * The empty String {@code ""}. +031 * @since 2.0 +032 */ +033 public static final String EMPTY = ""; +034 /** +035 * <p>Shallow clones an array returning a typecast result and handling +036 * {@code null}. +037 * +038 * <p>The objects in the array are not cloned, thus there is no special +039 * handling for multi-dimensional arrays. +040 * +041 * <p>This method returns {@code null} for a {@code null} input array. +042 * +043 * @param <T> the component type of the array +044 * @param array the array to shallow clone, may be {@code null} +045 * @return the cloned array, {@code null} if {@code null} input +046 */ +047 public static <T> T[] clone(final T[] array) { +048 if (array == null) { +049 return null; +050 } +051 return array.clone(); +052 } +053 +054 /** +055 * <p>Adds all the elements of the given arrays into a new array. +056 * <p>The new array contains all of the element of {@code array1} followed +057 * by all of the elements {@code array2}. When an array is returned, it is always +058 * a new array. +059 * +060 * <pre> +061 * ArrayUtils.addAll(null, null) = null +062 * ArrayUtils.addAll(array1, null) = cloned copy of array1 +063 * ArrayUtils.addAll(null, array2) = cloned copy of array2 +064 * ArrayUtils.addAll([], []) = [] +065 * ArrayUtils.addAll([null], [null]) = [null, null] +066 * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"] +067 * </pre> +068 * +069 * @param <T> the component type of the array +070 * @param array1 the first array whose elements are added to the new array, may be {@code null} +071 * @param array2 the second array whose elements are added to the new array, may be {@code null} +072 * @return The new array, {@code null} if both arrays are {@code null}. +073 * The type of the new array is the type of the first array, +074 * unless the first array is null, in which case the type is the same as the second array. +075 * @since 2.1 +076 * @throws IllegalArgumentException if the array types are incompatible +077 */ +078 public static <T> T[] addAll(final T[] array1, final T... array2) { +079 if (array1 == null) { +080 return clone(array2); +081 } else if (array2 == null) { +082 return clone(array1); +083 } +084 final Class<?> type1 = array1.getClass().getComponentType(); +085 @SuppressWarnings("unchecked") // OK, because array is of type T +086 final T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length); +087 System.arraycopy(array1, 0, joinedArray, 0, array1.length); +088 try { +089 System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); +090 } catch (final ArrayStoreException ase) { +091 // Check if problem was due to incompatible types +092 /* +093 * We do this here, rather than before the copy because: +094 * - it would be a wasted check most of the time +095 * - safer, in case check turns out to be too strict +096 */ +097 final Class<?> type2 = array2.getClass().getComponentType(); +098 if (!type1.isAssignableFrom(type2)) { +099 throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of " +100 + type1.getName(), ase); +101 } +102 throw ase; // No, so rethrow original +103 } +104 return joinedArray; +105 } +106 +107 //----------------------------------------------------------------------- +108 /** +109 * <p>Converts all the whitespace separated words in a String into capitalized words, +110 * that is each word is made up of a titlecase character and then a series of +111 * lowercase characters. </p> +112 * +113 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. +114 * A <code>null</code> input String returns <code>null</code>. +115 * Capitalization uses the Unicode title case, normally equivalent to +116 * upper case.</p> +117 * +118 * <pre> +119 * WordUtils.capitalizeFully(null) = null +120 * WordUtils.capitalizeFully("") = "" +121 * WordUtils.capitalizeFully("i am FINE") = "I Am Fine" +122 * </pre> +123 * +124 * @param str the String to capitalize, may be null +125 * @return capitalized String, <code>null</code> if null String input +126 */ +127 public static String capitalizeFully(final String str) { +128 return capitalizeFully(str, null); +129 } +130 +131 /** +132 * <p>Converts all the delimiter separated words in a String into capitalized words, +133 * that is each word is made up of a titlecase character and then a series of +134 * lowercase characters. </p> +135 * +136 * <p>The delimiters represent a set of characters understood to separate words. +137 * The first string character and the first non-delimiter character after a +138 * delimiter will be capitalized. </p> +139 * +140 * <p>A <code>null</code> input String returns <code>null</code>. +141 * Capitalization uses the Unicode title case, normally equivalent to +142 * upper case.</p> +143 * +144 * <pre> +145 * WordUtils.capitalizeFully(null, *) = null +146 * WordUtils.capitalizeFully("", *) = "" +147 * WordUtils.capitalizeFully(*, null) = * +148 * WordUtils.capitalizeFully(*, new char[0]) = * +149 * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine" +150 * </pre> +151 * +152 * @param str the String to capitalize, may be null +153 * @param delimiters set of characters to determine capitalization, null means whitespace +154 * @return capitalized String, <code>null</code> if null String input +155 * @since 2.1 +156 */ +157 public static String capitalizeFully(String str, final char... delimiters) { +158 final int delimLen = delimiters == null ? -1 : delimiters.length; +159 if (str == null || str.isEmpty() || delimLen == 0) { +160 return str; +161 } +162 str = str.toLowerCase(); +163 return capitalize(str, delimiters); +164 } +165 +166 // Capitalizing +167 //----------------------------------------------------------------------- +168 /** +169 * <p>Capitalizes all the whitespace separated words in a String. +170 * Only the first character of each word is changed. To convert the +171 * rest of each word to lowercase at the same time, +172 * use {@link #capitalizeFully(String)}.</p> +173 * +174 * <p>Whitespace is defined by {@link Character#isWhitespace(char)}. +175 * A <code>null</code> input String returns <code>null</code>. +176 * Capitalization uses the Unicode title case, normally equivalent to +177 * upper case.</p> +178 * +179 * <pre> +180 * WordUtils.capitalize(null) = null +181 * WordUtils.capitalize("") = "" +182 * WordUtils.capitalize("i am FINE") = "I Am FINE" +183 * </pre> +184 * +185 * @param str the String to capitalize, may be null +186 * @return capitalized String, <code>null</code> if null String input +187 * @see #capitalizeFully(String) +188 */ +189 public static String capitalize(final String str) { +190 return capitalize(str, null); +191 } +192 +193 /** +194 * <p>Capitalizes all the delimiter separated words in a String. +195 * Only the first character of each word is changed. To convert the +196 * rest of each word to lowercase at the same time, +197 * use {@link #capitalizeFully(String, char[])}.</p> +198 * +199 * <p>The delimiters represent a set of characters understood to separate words. +200 * The first string character and the first non-delimiter character after a +201 * delimiter will be capitalized. </p> +202 * +203 * <p>A <code>null</code> input String returns <code>null</code>. +204 * Capitalization uses the Unicode title case, normally equivalent to +205 * upper case.</p> +206 * +207 * <pre> +208 * WordUtils.capitalize(null, *) = null +209 * WordUtils.capitalize("", *) = "" +210 * WordUtils.capitalize(*, new char[0]) = * +211 * WordUtils.capitalize("i am fine", null) = "I Am Fine" +212 * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine" +213 * </pre> +214 * +215 * @param str the String to capitalize, may be null +216 * @param delimiters set of characters to determine capitalization, null means whitespace +217 * @return capitalized String, <code>null</code> if null String input +218 * @see #capitalizeFully(String) +219 * @since 2.1 +220 */ +221 public static String capitalize(final String str, final char... delimiters) { +222 final int delimLen = delimiters == null ? -1 : delimiters.length; +223 if (str == null || str.isEmpty() || delimLen == 0) { +224 return str; +225 } +226 final char[] buffer = str.toCharArray(); +227 boolean capitalizeNext = true; +228 for (int i = 0; i < buffer.length; i++) { +229 final char ch = buffer[i]; +230 if (isDelimiter(ch, delimiters)) { +231 capitalizeNext = true; +232 } else if (capitalizeNext) { +233 buffer[i] = Character.toTitleCase(ch); +234 capitalizeNext = false; +235 } +236 } +237 return new String(buffer); +238 } +239 //----------------------------------------------------------------------- +240 /** +241 * Is the character a delimiter. +242 * +243 * @param ch the character to check +244 * @param delimiters the delimiters +245 * @return true if it is a delimiter +246 */ +247 public static boolean isDelimiter(final char ch, final char[] delimiters) { +248 if (delimiters == null) { +249 return Character.isWhitespace(ch); +250 } +251 for (final char delimiter : delimiters) { +252 if (ch == delimiter) { +253 return true; +254 } +255 } +256 return false; +257 } +258 +259 // Joining +260 //----------------------------------------------------------------------- +261 /** +262 * <p>Joins the elements of the provided array into a single String +263 * containing the provided list of elements.</p> +264 * +265 * <p>No separator is added to the joined String. +266 * Null objects or empty strings within the array are represented by +267 * empty strings.</p> +268 * +269 * <pre> +270 * StringUtils.join(null) = null +271 * StringUtils.join([]) = "" +272 * StringUtils.join([null]) = "" +273 * StringUtils.join(["a", "b", "c"]) = "abc" +274 * StringUtils.join([null, "", "a"]) = "a" +275 * </pre> +276 * +277 * @param <T> the specific type of values to join together +278 * @param elements the values to join together, may be null +279 * @return the joined String, {@code null} if null array input +280 * @since 2.0 +281 * @since 3.0 Changed signature to use varargs +282 */ +283 @SafeVarargs +284 public static <T> String join(final T... elements) { +285 return join(elements, null); +286 } +287 +288 /** +289 * <p>Joins the elements of the provided array into a single String +290 * containing the provided list of elements.</p> +291 * +292 * <p>No delimiter is added before or after the list. +293 * Null objects or empty strings within the array are represented by +294 * empty strings.</p> +295 * +296 * <pre> +297 * StringUtils.join(null, *) = null +298 * StringUtils.join([], *) = "" +299 * StringUtils.join([null], *) = "" +300 * StringUtils.join(["a", "b", "c"], ';') = "a;b;c" +301 * StringUtils.join(["a", "b", "c"], null) = "abc" +302 * StringUtils.join([null, "", "a"], ';') = ";;a" +303 * </pre> +304 * +305 * @param array the array of values to join together, may be null +306 * @param separator the separator character to use +307 * @return the joined String, {@code null} if null array input +308 * @since 2.0 +309 */ +310 public static String join(final Object[] array, final char separator) { +311 if (array == null) { +312 return null; +313 } +314 return join(array, separator, 0, array.length); +315 } +316 +317 /** +318 * <p> +319 * Joins the elements of the provided array into a single String containing the provided list of elements. +320 * </p> +321 * +322 * <p> +323 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +324 * by empty strings. +325 * </p> +326 * +327 * <pre> +328 * StringUtils.join(null, *) = null +329 * StringUtils.join([], *) = "" +330 * StringUtils.join([null], *) = "" +331 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +332 * StringUtils.join([1, 2, 3], null) = "123" +333 * </pre> +334 * +335 * @param array +336 * the array of values to join together, may be null +337 * @param separator +338 * the separator character to use +339 * @return the joined String, {@code null} if null array input +340 * @since 3.2 +341 */ +342 public static String join(final long[] array, final char separator) { +343 if (array == null) { +344 return null; +345 } +346 return join(array, separator, 0, array.length); +347 } +348 +349 /** +350 * <p> +351 * Joins the elements of the provided array into a single String containing the provided list of elements. +352 * </p> +353 * +354 * <p> +355 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +356 * by empty strings. +357 * </p> +358 * +359 * <pre> +360 * StringUtils.join(null, *) = null +361 * StringUtils.join([], *) = "" +362 * StringUtils.join([null], *) = "" +363 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +364 * StringUtils.join([1, 2, 3], null) = "123" +365 * </pre> +366 * +367 * @param array +368 * the array of values to join together, may be null +369 * @param separator +370 * the separator character to use +371 * @return the joined String, {@code null} if null array input +372 * @since 3.2 +373 */ +374 public static String join(final int[] array, final char separator) { +375 if (array == null) { +376 return null; +377 } +378 return join(array, separator, 0, array.length); +379 } +380 +381 /** +382 * <p> +383 * Joins the elements of the provided array into a single String containing the provided list of elements. +384 * </p> +385 * +386 * <p> +387 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +388 * by empty strings. +389 * </p> +390 * +391 * <pre> +392 * StringUtils.join(null, *) = null +393 * StringUtils.join([], *) = "" +394 * StringUtils.join([null], *) = "" +395 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +396 * StringUtils.join([1, 2, 3], null) = "123" +397 * </pre> +398 * +399 * @param array +400 * the array of values to join together, may be null +401 * @param separator +402 * the separator character to use +403 * @return the joined String, {@code null} if null array input +404 * @since 3.2 +405 */ +406 public static String join(final short[] array, final char separator) { +407 if (array == null) { +408 return null; +409 } +410 return join(array, separator, 0, array.length); +411 } +412 +413 /** +414 * <p> +415 * Joins the elements of the provided array into a single String containing the provided list of elements. +416 * </p> +417 * +418 * <p> +419 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +420 * by empty strings. +421 * </p> +422 * +423 * <pre> +424 * StringUtils.join(null, *) = null +425 * StringUtils.join([], *) = "" +426 * StringUtils.join([null], *) = "" +427 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +428 * StringUtils.join([1, 2, 3], null) = "123" +429 * </pre> +430 * +431 * @param array +432 * the array of values to join together, may be null +433 * @param separator +434 * the separator character to use +435 * @return the joined String, {@code null} if null array input +436 * @since 3.2 +437 */ +438 public static String join(final byte[] array, final char separator) { +439 if (array == null) { +440 return null; +441 } +442 return join(array, separator, 0, array.length); +443 } +444 +445 /** +446 * <p> +447 * Joins the elements of the provided array into a single String containing the provided list of elements. +448 * </p> +449 * +450 * <p> +451 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +452 * by empty strings. +453 * </p> +454 * +455 * <pre> +456 * StringUtils.join(null, *) = null +457 * StringUtils.join([], *) = "" +458 * StringUtils.join([null], *) = "" +459 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +460 * StringUtils.join([1, 2, 3], null) = "123" +461 * </pre> +462 * +463 * @param array +464 * the array of values to join together, may be null +465 * @param separator +466 * the separator character to use +467 * @return the joined String, {@code null} if null array input +468 * @since 3.2 +469 */ +470 public static String join(final char[] array, final char separator) { +471 if (array == null) { +472 return null; +473 } +474 return join(array, separator, 0, array.length); +475 } +476 +477 /** +478 * <p> +479 * Joins the elements of the provided array into a single String containing the provided list of elements. +480 * </p> +481 * +482 * <p> +483 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +484 * by empty strings. +485 * </p> +486 * +487 * <pre> +488 * StringUtils.join(null, *) = null +489 * StringUtils.join([], *) = "" +490 * StringUtils.join([null], *) = "" +491 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +492 * StringUtils.join([1, 2, 3], null) = "123" +493 * </pre> +494 * +495 * @param array +496 * the array of values to join together, may be null +497 * @param separator +498 * the separator character to use +499 * @return the joined String, {@code null} if null array input +500 * @since 3.2 +501 */ +502 public static String join(final float[] array, final char separator) { +503 if (array == null) { +504 return null; +505 } +506 return join(array, separator, 0, array.length); +507 } +508 +509 /** +510 * <p> +511 * Joins the elements of the provided array into a single String containing the provided list of elements. +512 * </p> +513 * +514 * <p> +515 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +516 * by empty strings. +517 * </p> +518 * +519 * <pre> +520 * StringUtils.join(null, *) = null +521 * StringUtils.join([], *) = "" +522 * StringUtils.join([null], *) = "" +523 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +524 * StringUtils.join([1, 2, 3], null) = "123" +525 * </pre> +526 * +527 * @param array +528 * the array of values to join together, may be null +529 * @param separator +530 * the separator character to use +531 * @return the joined String, {@code null} if null array input +532 * @since 3.2 +533 */ +534 public static String join(final double[] array, final char separator) { +535 if (array == null) { +536 return null; +537 } +538 return join(array, separator, 0, array.length); +539 } +540 +541 +542 /** +543 * <p>Joins the elements of the provided array into a single String +544 * containing the provided list of elements.</p> +545 * +546 * <p>No delimiter is added before or after the list. +547 * Null objects or empty strings within the array are represented by +548 * empty strings.</p> +549 * +550 * <pre> +551 * StringUtils.join(null, *) = null +552 * StringUtils.join([], *) = "" +553 * StringUtils.join([null], *) = "" +554 * StringUtils.join(["a", "b", "c"], ';') = "a;b;c" +555 * StringUtils.join(["a", "b", "c"], null) = "abc" +556 * StringUtils.join([null, "", "a"], ';') = ";;a" +557 * </pre> +558 * +559 * @param array the array of values to join together, may be null +560 * @param separator the separator character to use +561 * @param startIndex the first index to start joining from. It is +562 * an error to pass in an end index past the end of the array +563 * @param endIndex the index to stop joining from (exclusive). It is +564 * an error to pass in an end index past the end of the array +565 * @return the joined String, {@code null} if null array input +566 * @since 2.0 +567 */ +568 public static String join(final Object[] array, final char separator, final int startIndex, final int endIndex) { +569 if (array == null) { +570 return null; +571 } +572 final int noOfItems = endIndex - startIndex; +573 if (noOfItems <= 0) { +574 return EMPTY; +575 } +576 final StringBuilder buf = new StringBuilder(noOfItems * 16); +577 for (int i = startIndex; i < endIndex; i++) { +578 if (i > startIndex) { +579 buf.append(separator); +580 } +581 if (array[i] != null) { +582 buf.append(array[i]); +583 } +584 } +585 return buf.toString(); +586 } +587 +588 /** +589 * <p> +590 * Joins the elements of the provided array into a single String containing the provided list of elements. +591 * </p> +592 * +593 * <p> +594 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +595 * by empty strings. +596 * </p> +597 * +598 * <pre> +599 * StringUtils.join(null, *) = null +600 * StringUtils.join([], *) = "" +601 * StringUtils.join([null], *) = "" +602 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +603 * StringUtils.join([1, 2, 3], null) = "123" +604 * </pre> +605 * +606 * @param array +607 * the array of values to join together, may be null +608 * @param separator +609 * the separator character to use +610 * @param startIndex +611 * the first index to start joining from. It is an error to pass in an end index past the end of the +612 * array +613 * @param endIndex +614 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +615 * the array +616 * @return the joined String, {@code null} if null array input +617 * @since 3.2 +618 */ +619 public static String join(final long[] array, final char separator, final int startIndex, final int endIndex) { +620 if (array == null) { +621 return null; +622 } +623 final int noOfItems = endIndex - startIndex; +624 if (noOfItems <= 0) { +625 return EMPTY; +626 } +627 final StringBuilder buf = new StringBuilder(noOfItems * 16); +628 for (int i = startIndex; i < endIndex; i++) { +629 if (i > startIndex) { +630 buf.append(separator); +631 } +632 buf.append(array[i]); +633 } +634 return buf.toString(); +635 } +636 +637 /** +638 * <p> +639 * Joins the elements of the provided array into a single String containing the provided list of elements. +640 * </p> +641 * +642 * <p> +643 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +644 * by empty strings. +645 * </p> +646 * +647 * <pre> +648 * StringUtils.join(null, *) = null +649 * StringUtils.join([], *) = "" +650 * StringUtils.join([null], *) = "" +651 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +652 * StringUtils.join([1, 2, 3], null) = "123" +653 * </pre> +654 * +655 * @param array +656 * the array of values to join together, may be null +657 * @param separator +658 * the separator character to use +659 * @param startIndex +660 * the first index to start joining from. It is an error to pass in an end index past the end of the +661 * array +662 * @param endIndex +663 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +664 * the array +665 * @return the joined String, {@code null} if null array input +666 * @since 3.2 +667 */ +668 public static String join(final int[] array, final char separator, final int startIndex, final int endIndex) { +669 if (array == null) { +670 return null; +671 } +672 final int noOfItems = endIndex - startIndex; +673 if (noOfItems <= 0) { +674 return EMPTY; +675 } +676 final StringBuilder buf = new StringBuilder(noOfItems * 16); +677 for (int i = startIndex; i < endIndex; i++) { +678 if (i > startIndex) { +679 buf.append(separator); +680 } +681 buf.append(array[i]); +682 } +683 return buf.toString(); +684 } +685 +686 /** +687 * <p> +688 * Joins the elements of the provided array into a single String containing the provided list of elements. +689 * </p> +690 * +691 * <p> +692 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +693 * by empty strings. +694 * </p> +695 * +696 * <pre> +697 * StringUtils.join(null, *) = null +698 * StringUtils.join([], *) = "" +699 * StringUtils.join([null], *) = "" +700 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +701 * StringUtils.join([1, 2, 3], null) = "123" +702 * </pre> +703 * +704 * @param array +705 * the array of values to join together, may be null +706 * @param separator +707 * the separator character to use +708 * @param startIndex +709 * the first index to start joining from. It is an error to pass in an end index past the end of the +710 * array +711 * @param endIndex +712 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +713 * the array +714 * @return the joined String, {@code null} if null array input +715 * @since 3.2 +716 */ +717 public static String join(final byte[] array, final char separator, final int startIndex, final int endIndex) { +718 if (array == null) { +719 return null; +720 } +721 final int noOfItems = endIndex - startIndex; +722 if (noOfItems <= 0) { +723 return EMPTY; +724 } +725 final StringBuilder buf = new StringBuilder(noOfItems * 16); +726 for (int i = startIndex; i < endIndex; i++) { +727 if (i > startIndex) { +728 buf.append(separator); +729 } +730 buf.append(array[i]); +731 } +732 return buf.toString(); +733 } +734 +735 /** +736 * <p> +737 * Joins the elements of the provided array into a single String containing the provided list of elements. +738 * </p> +739 * +740 * <p> +741 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +742 * by empty strings. +743 * </p> +744 * +745 * <pre> +746 * StringUtils.join(null, *) = null +747 * StringUtils.join([], *) = "" +748 * StringUtils.join([null], *) = "" +749 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +750 * StringUtils.join([1, 2, 3], null) = "123" +751 * </pre> +752 * +753 * @param array +754 * the array of values to join together, may be null +755 * @param separator +756 * the separator character to use +757 * @param startIndex +758 * the first index to start joining from. It is an error to pass in an end index past the end of the +759 * array +760 * @param endIndex +761 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +762 * the array +763 * @return the joined String, {@code null} if null array input +764 * @since 3.2 +765 */ +766 public static String join(final short[] array, final char separator, final int startIndex, final int endIndex) { +767 if (array == null) { +768 return null; +769 } +770 final int noOfItems = endIndex - startIndex; +771 if (noOfItems <= 0) { +772 return EMPTY; +773 } +774 final StringBuilder buf = new StringBuilder(noOfItems * 16); +775 for (int i = startIndex; i < endIndex; i++) { +776 if (i > startIndex) { +777 buf.append(separator); +778 } +779 buf.append(array[i]); +780 } +781 return buf.toString(); +782 } +783 +784 /** +785 * <p> +786 * Joins the elements of the provided array into a single String containing the provided list of elements. +787 * </p> +788 * +789 * <p> +790 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +791 * by empty strings. +792 * </p> +793 * +794 * <pre> +795 * StringUtils.join(null, *) = null +796 * StringUtils.join([], *) = "" +797 * StringUtils.join([null], *) = "" +798 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +799 * StringUtils.join([1, 2, 3], null) = "123" +800 * </pre> +801 * +802 * @param array +803 * the array of values to join together, may be null +804 * @param separator +805 * the separator character to use +806 * @param startIndex +807 * the first index to start joining from. It is an error to pass in an end index past the end of the +808 * array +809 * @param endIndex +810 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +811 * the array +812 * @return the joined String, {@code null} if null array input +813 * @since 3.2 +814 */ +815 public static String join(final char[] array, final char separator, final int startIndex, final int endIndex) { +816 if (array == null) { +817 return null; +818 } +819 final int noOfItems = endIndex - startIndex; +820 if (noOfItems <= 0) { +821 return EMPTY; +822 } +823 final StringBuilder buf = new StringBuilder(noOfItems * 16); +824 for (int i = startIndex; i < endIndex; i++) { +825 if (i > startIndex) { +826 buf.append(separator); +827 } +828 buf.append(array[i]); +829 } +830 return buf.toString(); +831 } +832 +833 /** +834 * <p> +835 * Joins the elements of the provided array into a single String containing the provided list of elements. +836 * </p> +837 * +838 * <p> +839 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +840 * by empty strings. +841 * </p> +842 * +843 * <pre> +844 * StringUtils.join(null, *) = null +845 * StringUtils.join([], *) = "" +846 * StringUtils.join([null], *) = "" +847 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +848 * StringUtils.join([1, 2, 3], null) = "123" +849 * </pre> +850 * +851 * @param array +852 * the array of values to join together, may be null +853 * @param separator +854 * the separator character to use +855 * @param startIndex +856 * the first index to start joining from. It is an error to pass in an end index past the end of the +857 * array +858 * @param endIndex +859 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +860 * the array +861 * @return the joined String, {@code null} if null array input +862 * @since 3.2 +863 */ +864 public static String join(final double[] array, final char separator, final int startIndex, final int endIndex) { +865 if (array == null) { +866 return null; +867 } +868 final int noOfItems = endIndex - startIndex; +869 if (noOfItems <= 0) { +870 return EMPTY; +871 } +872 final StringBuilder buf = new StringBuilder(noOfItems * 16); +873 for (int i = startIndex; i < endIndex; i++) { +874 if (i > startIndex) { +875 buf.append(separator); +876 } +877 buf.append(array[i]); +878 } +879 return buf.toString(); +880 } +881 +882 /** +883 * <p> +884 * Joins the elements of the provided array into a single String containing the provided list of elements. +885 * </p> +886 * +887 * <p> +888 * No delimiter is added before or after the list. Null objects or empty strings within the array are represented +889 * by empty strings. +890 * </p> +891 * +892 * <pre> +893 * StringUtils.join(null, *) = null +894 * StringUtils.join([], *) = "" +895 * StringUtils.join([null], *) = "" +896 * StringUtils.join([1, 2, 3], ';') = "1;2;3" +897 * StringUtils.join([1, 2, 3], null) = "123" +898 * </pre> +899 * +900 * @param array +901 * the array of values to join together, may be null +902 * @param separator +903 * the separator character to use +904 * @param startIndex +905 * the first index to start joining from. It is an error to pass in an end index past the end of the +906 * array +907 * @param endIndex +908 * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of +909 * the array +910 * @return the joined String, {@code null} if null array input +911 * @since 3.2 +912 */ +913 public static String join(final float[] array, final char separator, final int startIndex, final int endIndex) { +914 if (array == null) { +915 return null; +916 } +917 final int noOfItems = endIndex - startIndex; +918 if (noOfItems <= 0) { +919 return EMPTY; +920 } +921 final StringBuilder buf = new StringBuilder(noOfItems * 16); +922 for (int i = startIndex; i < endIndex; i++) { +923 if (i > startIndex) { +924 buf.append(separator); +925 } +926 buf.append(array[i]); +927 } +928 return buf.toString(); +929 } +930 +931 +932 /** +933 * <p>Joins the elements of the provided array into a single String +934 * containing the provided list of elements.</p> +935 * +936 * <p>No delimiter is added before or after the list. +937 * A {@code null} separator is the same as an empty String (""). +938 * Null objects or empty strings within the array are represented by +939 * empty strings.</p> +940 * +941 * <pre> +942 * StringUtils.join(null, *) = null +943 * StringUtils.join([], *) = "" +944 * StringUtils.join([null], *) = "" +945 * StringUtils.join(["a", "b", "c"], "--") = "a--b--c" +946 * StringUtils.join(["a", "b", "c"], null) = "abc" +947 * StringUtils.join(["a", "b", "c"], "") = "abc" +948 * StringUtils.join([null, "", "a"], ',') = ",,a" +949 * </pre> +950 * +951 * @param array the array of values to join together, may be null +952 * @param separator the separator character to use, null treated as "" +953 * @return the joined String, {@code null} if null array input +954 */ +955 public static String join(final Object[] array, final String separator) { +956 if (array == null) { +957 return null; +958 } +959 return join(array, separator, 0, array.length); +960 } +961 +962 /** +963 * <p>Joins the elements of the provided array into a single String +964 * containing the provided list of elements.</p> +965 * +966 * <p>No delimiter is added before or after the list. +967 * A {@code null} separator is the same as an empty String (""). +968 * Null objects or empty strings within the array are represented by +969 * empty strings.</p> +970 * +971 * <pre> +972 * StringUtils.join(null, *, *, *) = null +973 * StringUtils.join([], *, *, *) = "" +974 * StringUtils.join([null], *, *, *) = "" +975 * StringUtils.join(["a", "b", "c"], "--", 0, 3) = "a--b--c" +976 * StringUtils.join(["a", "b", "c"], "--", 1, 3) = "b--c" +977 * StringUtils.join(["a", "b", "c"], "--", 2, 3) = "c" +978 * StringUtils.join(["a", "b", "c"], "--", 2, 2) = "" +979 * StringUtils.join(["a", "b", "c"], null, 0, 3) = "abc" +980 * StringUtils.join(["a", "b", "c"], "", 0, 3) = "abc" +981 * StringUtils.join([null, "", "a"], ',', 0, 3) = ",,a" +982 * </pre> +983 * +984 * @param array the array of values to join together, may be null +985 * @param separator the separator character to use, null treated as "" +986 * @param startIndex the first index to start joining from. +987 * @param endIndex the index to stop joining from (exclusive). +988 * @return the joined String, {@code null} if null array input; or the empty string +989 * if {@code endIndex - startIndex <= 0}. The number of joined entries is given by +990 * {@code endIndex - startIndex} +991 * @throws ArrayIndexOutOfBoundsException ife<br> +992 * {@code startIndex < 0} or <br> +993 * {@code startIndex >= array.length()} or <br> +994 * {@code endIndex < 0} or <br> +995 * {@code endIndex > array.length()} +996 */ +997 public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) { +998 if (array == null) { +999 return null; +1000 } +1001 if (separator == null) { +1002 separator = EMPTY; +1003 } +1004 +1005 // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) +1006 // (Assuming that all Strings are roughly equally long) +1007 final int noOfItems = endIndex - startIndex; +1008 if (noOfItems <= 0) { +1009 return EMPTY; +1010 } +1011 +1012 final StringBuilder buf = new StringBuilder(noOfItems * 16); +1013 +1014 for (int i = startIndex; i < endIndex; i++) { +1015 if (i > startIndex) { +1016 buf.append(separator); +1017 } +1018 if (array[i] != null) { +1019 buf.append(array[i]); +1020 } +1021 } +1022 return buf.toString(); +1023 } +1024 +1025 /** +1026 * <p>Joins the elements of the provided {@code Iterator} into +1027 * a single String containing the provided elements.</p> +1028 * +1029 * <p>No delimiter is added before or after the list. Null objects or empty +1030 * strings within the iteration are represented by empty strings.</p> +1031 * +1032 * <p>See the examples here: {@link #join(Object[],char)}. </p> +1033 * +1034 * @param iterator the {@code Iterator} of values to join together, may be null +1035 * @param separator the separator character to use +1036 * @return the joined String, {@code null} if null iterator input +1037 * @since 2.0 +1038 */ +1039 public static String join(final Iterator<?> iterator, final char separator) { +1040 +1041 // handle null, zero and one elements before building a buffer +1042 if (iterator == null) { +1043 return null; +1044 } +1045 if (!iterator.hasNext()) { +1046 return EMPTY; +1047 } +1048 final Object first = iterator.next(); +1049 if (!iterator.hasNext()) { +1050 final String result = first != null ? first.toString() : ""; +1051 return result; +1052 } +1053 +1054 // two or more elements +1055 final StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small +1056 if (first != null) { +1057 buf.append(first); +1058 } +1059 +1060 while (iterator.hasNext()) { +1061 buf.append(separator); +1062 final Object obj = iterator.next(); +1063 if (obj != null) { +1064 buf.append(obj); +1065 } +1066 } +1067 +1068 return buf.toString(); +1069 } +1070 +1071 /** +1072 * <p>Joins the elements of the provided {@code Iterator} into +1073 * a single String containing the provided elements.</p> +1074 * +1075 * <p>No delimiter is added before or after the list. +1076 * A {@code null} separator is the same as an empty String ("").</p> +1077 * +1078 * <p>See the examples here: {@link #join(Object[],String)}. </p> +1079 * +1080 * @param iterator the {@code Iterator} of values to join together, may be null +1081 * @param separator the separator character to use, null treated as "" +1082 * @return the joined String, {@code null} if null iterator input +1083 */ +1084 public static String join(final Iterator<?> iterator, final String separator) { +1085 +1086 // handle null, zero and one elements before building a buffer +1087 if (iterator == null) { +1088 return null; +1089 } +1090 if (!iterator.hasNext()) { +1091 return EMPTY; +1092 } +1093 final Object first = iterator.next(); +1094 if (!iterator.hasNext()) { +1095 final String result = first != null ? first.toString() : ""; +1096 return result; +1097 } +1098 +1099 // two or more elements +1100 final StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small +1101 if (first != null) { +1102 buf.append(first); +1103 } +1104 +1105 while (iterator.hasNext()) { +1106 if (separator != null) { +1107 buf.append(separator); +1108 } +1109 final Object obj = iterator.next(); +1110 if (obj != null) { +1111 buf.append(obj); +1112 } +1113 } +1114 return buf.toString(); +1115 } +1116 +1117 /** +1118 * <p>Joins the elements of the provided {@code Iterable} into +1119 * a single String containing the provided elements.</p> +1120 * +1121 * <p>No delimiter is added before or after the list. Null objects or empty +1122 * strings within the iteration are represented by empty strings.</p> +1123 * +1124 * <p>See the examples here: {@link #join(Object[],char)}. </p> +1125 * +1126 * @param iterable the {@code Iterable} providing the values to join together, may be null +1127 * @param separator the separator character to use +1128 * @return the joined String, {@code null} if null iterator input +1129 * @since 2.3 +1130 */ +1131 public static String join(final Iterable<?> iterable, final char separator) { +1132 if (iterable == null) { +1133 return null; +1134 } +1135 return join(iterable.iterator(), separator); +1136 } +1137 +1138 /** +1139 * <p>Joins the elements of the provided {@code Iterable} into +1140 * a single String containing the provided elements.</p> +1141 * +1142 * <p>No delimiter is added before or after the list. +1143 * A {@code null} separator is the same as an empty String ("").</p> +1144 * +1145 * <p>See the examples here: {@link #join(Object[],String)}. </p> +1146 * +1147 * @param iterable the {@code Iterable} providing the values to join together, may be null +1148 * @param separator the separator character to use, null treated as "" +1149 * @return the joined String, {@code null} if null iterator input +1150 * @since 2.3 +1151 */ +1152 public static String join(final Iterable<?> iterable, final String separator) { +1153 if (iterable == null) { +1154 return null; +1155 } +1156 return join(iterable.iterator(), separator); +1157 } +1158 +1159 +1160 /** +1161 * <p>Checks if the CharSequence contains only Unicode digits. +1162 * A decimal point is not a Unicode digit and returns false.</p> +1163 * +1164 * <p>{@code null} will return {@code false}. +1165 * An empty CharSequence (length()=0) will return {@code false}.</p> +1166 * +1167 * <p>Note that the method does not allow for a leading sign, either positive or negative. +1168 * Also, if a String passes the numeric test, it may still generate a NumberFormatException +1169 * when parsed by Integer.parseInt or Long.parseLong, e.g. if the value is outside the range +1170 * for int or long respectively.</p> +1171 * +1172 * <pre> +1173 * StringUtils.isNumeric(null) = false +1174 * StringUtils.isNumeric("") = false +1175 * StringUtils.isNumeric(" ") = false +1176 * StringUtils.isNumeric("123") = true +1177 * StringUtils.isNumeric("\u0967\u0968\u0969") = true +1178 * StringUtils.isNumeric("12 3") = false +1179 * StringUtils.isNumeric("ab2c") = false +1180 * StringUtils.isNumeric("12-3") = false +1181 * StringUtils.isNumeric("12.3") = false +1182 * StringUtils.isNumeric("-123") = false +1183 * StringUtils.isNumeric("+123") = false +1184 * </pre> +1185 * +1186 * @param cs the CharSequence to check, may be null +1187 * @return {@code true} if only contains digits, and is non-null +1188 * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence) +1189 * @since 3.0 Changed "" to return false and not true +1190 */ +1191 public static boolean isNumeric(final CharSequence cs) { +1192 if (cs == null || cs.length() == 0) { +1193 return false; +1194 } +1195 final int sz = cs.length(); +1196 for (int i = 0; i < sz; i++) { +1197 if (!Character.isDigit(cs.charAt(i))) { +1198 return false; +1199 } +1200 } +1201 return true; +1202 } +1203 +1204 +1205 // startsWith +1206 //----------------------------------------------------------------------- +1207 +1208 /** +1209 * <p>Check if a CharSequence starts with a specified prefix.</p> +1210 * +1211 * <p>{@code null}s are handled without exceptions. Two {@code null} +1212 * references are considered to be equal. The comparison is case sensitive.</p> +1213 * +1214 * <pre> +1215 * StringUtils.startsWith(null, null) = true +1216 * StringUtils.startsWith(null, "abc") = false +1217 * StringUtils.startsWith("abcdef", null) = false +1218 * StringUtils.startsWith("abcdef", "abc") = true +1219 * StringUtils.startsWith("ABCDEF", "abc") = false +1220 * </pre> +1221 * +1222 * @see java.lang.String#startsWith(String) +1223 * @param str the CharSequence to check, may be null +1224 * @param prefix the prefix to find, may be null +1225 * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or +1226 * both {@code null} +1227 * @since 2.4 +1228 * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence) +1229 */ +1230 public static boolean startsWith(final CharSequence str, final CharSequence prefix) { +1231 return startsWith(str, prefix, false); +1232 } +1233 +1234 /** +1235 * <p>Case insensitive check if a CharSequence starts with a specified prefix.</p> +1236 * +1237 * <p>{@code null}s are handled without exceptions. Two {@code null} +1238 * references are considered to be equal. The comparison is case insensitive.</p> +1239 * +1240 * <pre> +1241 * StringUtils.startsWithIgnoreCase(null, null) = true +1242 * StringUtils.startsWithIgnoreCase(null, "abc") = false +1243 * StringUtils.startsWithIgnoreCase("abcdef", null) = false +1244 * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true +1245 * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true +1246 * </pre> +1247 * +1248 * @see java.lang.String#startsWith(String) +1249 * @param str the CharSequence to check, may be null +1250 * @param prefix the prefix to find, may be null +1251 * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or +1252 * both {@code null} +1253 * @since 2.4 +1254 * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence) +1255 */ +1256 public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix) { +1257 return startsWith(str, prefix, true); +1258 } +1259 +1260 /** +1261 * <p>Check if a CharSequence starts with a specified prefix (optionally case insensitive).</p> +1262 * +1263 * @see java.lang.String#startsWith(String) +1264 * @param str the CharSequence to check, may be null +1265 * @param prefix the prefix to find, may be null +1266 * @param ignoreCase indicates whether the compare should ignore case +1267 * (case insensitive) or not. +1268 * @return {@code true} if the CharSequence starts with the prefix or +1269 * both {@code null} +1270 */ +1271 private static boolean startsWith(final CharSequence str, final CharSequence prefix, final boolean ignoreCase) { +1272 if (str == null || prefix == null) { +1273 return str == null && prefix == null; +1274 } +1275 if (prefix.length() > str.length()) { +1276 return false; +1277 } +1278 return regionMatches(str, ignoreCase, 0, prefix, 0, prefix.length()); +1279 } +1280 +1281 /** +1282 * Green implementation of regionMatches. +1283 * +1284 * @param cs the {@code CharSequence} to be processed +1285 * @param ignoreCase whether or not to be case insensitive +1286 * @param thisStart the index to start on the {@code cs} CharSequence +1287 * @param substring the {@code CharSequence} to be looked for +1288 * @param start the index to start on the {@code substring} CharSequence +1289 * @param length character length of the region +1290 * @return whether the region matched +1291 */ +1292 static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, +1293 final CharSequence substring, final int start, final int length) { +1294 if (cs instanceof String && substring instanceof String) { +1295 return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); +1296 } +1297 int index1 = thisStart; +1298 int index2 = start; +1299 int tmpLen = length; +1300 +1301 // Extract these first so we detect NPEs the same as the java.lang.String version +1302 final int srcLen = cs.length() - thisStart; +1303 final int otherLen = substring.length() - start; +1304 +1305 // Check for invalid parameters +1306 if (thisStart < 0 || start < 0 || length < 0) { +1307 return false; +1308 } +1309 +1310 // Check that the regions are long enough +1311 if (srcLen < length || otherLen < length) { +1312 return false; +1313 } +1314 +1315 while (tmpLen-- > 0) { +1316 final char c1 = cs.charAt(index1++); +1317 final char c2 = substring.charAt(index2++); +1318 +1319 if (c1 == c2) { +1320 continue; +1321 } +1322 +1323 if (!ignoreCase) { +1324 return false; +1325 } +1326 +1327 // The same check as in String.regionMatches(): +1328 if (Character.toUpperCase(c1) != Character.toUpperCase(c2) +1329 && Character.toLowerCase(c1) != Character.toLowerCase(c2)) { +1330 return false; +1331 } +1332 } +1333 +1334 return true; +1335 } +1336 +1337 /** +1338 * The index value when an element is not found in a list or array: <code>-1</code>. +1339 * This value is returned by methods in this class and can also be used in comparisons with values returned by +1340 * various method from {@link java.util.List}. +1341 */ +1342 public static final int INDEX_NOT_FOUND = -1; +1343 +1344 // IndexOf search +1345 // ---------------------------------------------------------------------- +1346 +1347 // Object IndexOf +1348 //----------------------------------------------------------------------- +1349 /** +1350 * <p>Finds the index of the given object in the array.</p> +1351 * +1352 * <p>This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a <code>null</code> input array.</p> +1353 * +1354 * @param array the array to search through for the object, may be <code>null</code> +1355 * @param objectToFind the object to find, may be <code>null</code> +1356 * @return the index of the object within the array, +1357 * {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or <code>null</code> array input +1358 */ +1359 public static int indexOf(Object[] array, Object objectToFind) { +1360 return indexOf(array, objectToFind, 0); +1361 } +1362 +1363 /** +1364 * <p>Finds the index of the given object in the array starting at the given index.</p> +1365 * +1366 * <p>This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a <code>null</code> input array.</p> +1367 * +1368 * <p>A negative startIndex is treated as zero. A startIndex larger than the array +1369 * length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).</p> +1370 * +1371 * @param array the array to search through for the object, may be <code>null</code> +1372 * @param objectToFind the object to find, may be <code>null</code> +1373 * @param startIndex the index to start searching at +1374 * @return the index of the object within the array starting at the index, +1375 * {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or <code>null</code> array input +1376 */ +1377 public static int indexOf(Object[] array, Object objectToFind, int startIndex) { +1378 if (array == null) { +1379 return INDEX_NOT_FOUND; +1380 } +1381 if (startIndex < 0) { +1382 startIndex = 0; +1383 } +1384 if (objectToFind == null) { +1385 for (int i = startIndex; i < array.length; i++) { +1386 if (array[i] == null) { +1387 return i; +1388 } +1389 } +1390 } else { +1391 for (int i = startIndex; i < array.length; i++) { +1392 if (objectToFind.equals(array[i])) { +1393 return i; +1394 } +1395 } +1396 } +1397 return INDEX_NOT_FOUND; +1398 } +1399} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.contexts; +025 +026import co.aikar.commands.CommandExecutionContext; +027import co.aikar.commands.CommandIssuer; +028import co.aikar.commands.InvalidCommandArgument; +029 +030@FunctionalInterface +031public interface ContextResolver <T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> { +032 T getContext(C c) throws InvalidCommandArgument; +033} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.contexts; +025 +026import co.aikar.commands.CommandExecutionContext; +027import co.aikar.commands.CommandIssuer; +028 +029 +030public interface IssuerAwareContextResolver<T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> extends ContextResolver<T, C> {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.contexts; +025 +026import co.aikar.commands.CommandExecutionContext; +027import co.aikar.commands.CommandIssuer; +028 +029/** +030 * A context resolver that will never consume input, and only resolves using the context of the issuer of the command +031 * @param <T> +032 * @param <C> +033 */ +034public interface IssuerOnlyContextResolver<T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> extends ContextResolver <T, C> {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.contexts; +025 +026import co.aikar.commands.CommandExecutionContext; +027import co.aikar.commands.CommandIssuer; +028 +029/** +030 * Context Resolver that can accept null input +031 */ +032public interface OptionalContextResolver <T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> extends ContextResolver <T, C> {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++
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.contexts; +025 +026import co.aikar.commands.CommandExecutionContext; +027import co.aikar.commands.CommandIssuer; +028 +029/** +030 * Wrapper for IssuerAwareContextResolver +031 * +032 * @deprecated Please use {@link IssuerAwareContextResolver} +033 * @see IssuerAwareContextResolver +034 */ +035@Deprecated +036public interface SenderAwareContextResolver<T, C extends CommandExecutionContext<?, ? extends CommandIssuer>> extends IssuerAwareContextResolver <T, C> {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ++