Provides utilities for manipulating and examining
+ * Throwable objects.
Used when printing stack frames to denote the start of a + * wrapped exception.
+ * + *Package private for accessibility by test suite.
+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *The names of methods commonly used to access a wrapped exception.
+ */ + private static String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *The Method object for Java 1.4 getCause.
+ */ + private static final Method THROWABLE_CAUSE_METHOD; + + /** + *The Method object for Java 1.4 initCause.
+ */ + private static final Method THROWABLE_INITCAUSE_METHOD; + + static { + Method causeMethod; + try { + causeMethod = Throwable.class.getMethod("getCause", null); + } catch (Exception e) { + causeMethod = null; + } + THROWABLE_CAUSE_METHOD = causeMethod; + try { + causeMethod = Throwable.class.getMethod("initCause", new Class[]{Throwable.class}); + } catch (Exception e) { + causeMethod = null; + } + THROWABLE_INITCAUSE_METHOD = causeMethod; + } + + /** + *
+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not
+ * normally necessary.
+ *
Adds to the list of method names used in the search for Throwable
+ * objects.
null
+ * and empty strings are ignored
+ * @since 2.0
+ */
+ public static void addCauseMethodName(String methodName) {
+ if (methodName != null && !methodName.isEmpty() && !isCauseMethodName(methodName)) {
+ List list = getCauseMethodNameList();
+ if (list.add(methodName)) {
+ CAUSE_METHOD_NAMES = toArray(list);
+ }
+ }
+ }
+
+ /**
+ * Removes from the list of method names used in the search for Throwable
+ * objects.
null
+ * and empty strings are ignored
+ * @since 2.1
+ */
+ public static void removeCauseMethodName(String methodName) {
+ if (methodName != null && !methodName.isEmpty()) {
+ List list = getCauseMethodNameList();
+ if (list.remove(methodName)) {
+ CAUSE_METHOD_NAMES = toArray(list);
+ }
+ }
+ }
+
+ /**
+ * 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);
+ * }
+ * }
+ *
+ *
+ * @param target the target Throwable
+ * @param cause the Throwable to set in the target
+ * @return a true if the target has been modified
+ * @since 2.2
+ */
+ public static boolean setCause(Throwable target, Throwable cause) {
+ if (target == null) {
+ throw new IllegalArgumentException("target");
+ }
+ Object[] causeArgs = new Object[]{cause};
+ boolean modifiedTarget = false;
+ if (THROWABLE_INITCAUSE_METHOD != null) {
+ try {
+ THROWABLE_INITCAUSE_METHOD.invoke(target, causeArgs);
+ modifiedTarget = true;
+ } catch (IllegalAccessException ignored) {
+ // Exception ignored.
+ } catch (InvocationTargetException ignored) {
+ // Exception ignored.
+ }
+ }
+ try {
+ Method setCauseMethod = target.getClass().getMethod("setCause", new Class[]{Throwable.class});
+ setCauseMethod.invoke(target, causeArgs);
+ modifiedTarget = true;
+ } catch (NoSuchMethodException ignored) {
+ // Exception ignored.
+ } catch (IllegalAccessException ignored) {
+ // Exception ignored.
+ } catch (InvocationTargetException ignored) {
+ // Exception ignored.
+ }
+ return modifiedTarget;
+ }
+
+ /**
+ * Returns the given list as a String[].
+ * @param list a list to transform.
+ * @return the given list as a String[].
+ */
+ private static String[] toArray(List list) {
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Returns {@link #CAUSE_METHOD_NAMES} as a List.
+ *
+ * @return {@link #CAUSE_METHOD_NAMES} as a List.
+ */
+ private static ArrayList getCauseMethodNameList() {
+ return new ArrayList(Arrays.asList(CAUSE_METHOD_NAMES));
+ }
+
+ /**
+ * Tests if the list of method names used in the search for Throwable
+ * objects include the given name.
Throwable
+ * objects include the given name.
+ * @since 2.1
+ */
+ public static boolean isCauseMethodName(String methodName) {
+ return ApacheCommonsLangUtil.indexOf(CAUSE_METHOD_NAMES, methodName) >= 0;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * 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 {@link #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,
+ * null if none found or null throwable input
+ * @since 1.0
+ */
+ public static Throwable getCause(Throwable throwable) {
+ return getCause(throwable, CAUSE_METHOD_NAMES);
+ }
+
+ /**
+ * 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,
+ * null if none found or null throwable input
+ * @since 1.0
+ */
+ public static Throwable getCause(Throwable throwable, String[] methodNames) {
+ if (throwable == null) {
+ return null;
+ }
+ Throwable cause = getCauseUsingWellKnownTypes(throwable);
+ if (cause == null) {
+ if (methodNames == null) {
+ methodNames = CAUSE_METHOD_NAMES;
+ }
+ for (int i = 0; i < methodNames.length; i++) {
+ String methodName = methodNames[i];
+ if (methodName != null) {
+ cause = getCauseUsingMethodName(throwable, methodName);
+ if (cause != null) {
+ break;
+ }
+ }
+ }
+
+ if (cause == null) {
+ cause = getCauseUsingFieldName(throwable, "detail");
+ }
+ }
+ return cause;
+ }
+
+ /**
+ * Introspects the Throwable to obtain the root cause.
This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #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.
+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of theThrowable,
+ * null if none found or null throwable input
+ */
+ public static Throwable getRootCause(Throwable throwable) {
+ List list = getThrowableList(throwable);
+ return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1));
+ }
+
+ /**
+ * Finds a Throwable for known types.
Uses instanceof checks to examine the exception,
+ * looking for well known types which could contain chained or
+ * wrapped exceptions.
null if not found
+ */
+ private static Throwable getCauseUsingWellKnownTypes(Throwable throwable) {
+ if (throwable instanceof Nestable) {
+ return ((Nestable) throwable).getCause();
+ } else if (throwable instanceof SQLException) {
+ return ((SQLException) throwable).getNextException();
+ } else if (throwable instanceof InvocationTargetException) {
+ return ((InvocationTargetException) throwable).getTargetException();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Finds a Throwable by method name.
null if not found
+ */
+ private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
+ Method method = null;
+ try {
+ method = throwable.getClass().getMethod(methodName, null);
+ } catch (NoSuchMethodException ignored) {
+ // exception ignored
+ } catch (SecurityException ignored) {
+ // exception ignored
+ }
+
+ if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
+ try {
+ return (Throwable) method.invoke(throwable);
+ } catch (IllegalAccessException ignored) {
+ // exception ignored
+ } catch (IllegalArgumentException ignored) {
+ // exception ignored
+ } catch (InvocationTargetException ignored) {
+ // exception ignored
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds a Throwable by field name.
null if not found
+ */
+ private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) {
+ Field field = null;
+ try {
+ field = throwable.getClass().getField(fieldName);
+ } catch (NoSuchFieldException ignored) {
+ // exception ignored
+ } catch (SecurityException ignored) {
+ // exception ignored
+ }
+
+ if (field != null && Throwable.class.isAssignableFrom(field.getType())) {
+ try {
+ return (Throwable) field.get(throwable);
+ } catch (IllegalAccessException ignored) {
+ // exception ignored
+ } catch (IllegalArgumentException ignored) {
+ // exception ignored
+ }
+ }
+ return null;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks if the Throwable class has a getCause method.
This is true for JDK 1.4 and above.
+ * + * @return true if Throwable is nestable + * @since 2.0 + */ + public static boolean isThrowableNested() { + return THROWABLE_CAUSE_METHOD != null; + } + + /** + *Checks whether this Throwable class can store a cause.
This method does not check whether it actually does store a cause.
+ *
+ * @param throwable the Throwable to examine, may be null
+ * @return boolean true if nested otherwise false
+ * @since 2.0
+ */
+ public static boolean isNestedThrowable(Throwable throwable) {
+ if (throwable == null) {
+ return false;
+ }
+
+ if (throwable instanceof Nestable) {
+ return true;
+ } else if (throwable instanceof SQLException) {
+ return true;
+ } else if (throwable instanceof InvocationTargetException) {
+ return true;
+ } else if (isThrowableNested()) {
+ return true;
+ }
+
+ Class cls = throwable.getClass();
+ for (int i = 0, isize = CAUSE_METHOD_NAMES.length; i < isize; i++) {
+ try {
+ Method method = cls.getMethod(CAUSE_METHOD_NAMES[i], null);
+ if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
+ return true;
+ }
+ } catch (NoSuchMethodException ignored) {
+ // exception ignored
+ } catch (SecurityException ignored) {
+ // exception ignored
+ }
+ }
+
+ try {
+ Field field = cls.getField("detail");
+ if (field != null) {
+ return true;
+ }
+ } catch (NoSuchFieldException ignored) {
+ // exception ignored
+ } catch (SecurityException ignored) {
+ // exception ignored
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ *
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.
+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *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.
+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(Throwable throwable) { + List list = getThrowableList(throwable); + return (Throwable[]) list.toArray(new Throwable[list.size()]); + } + + /** + *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.
+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *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
+ * {@link #indexOfType(Throwable, Class)} for the opposite.
A null throwable returns -1.
+ * A null type returns -1.
+ * No match in the chain returns -1.
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
+ * {@link #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.
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
+ * {@link #indexOfThrowable(Throwable, Class)} for the opposite.
A null throwable returns -1.
+ * A null type returns -1.
+ * No match in the chain returns -1.
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
+ * {@link #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.
Worker method for the indexOfType methods.
true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
+ * using references
+ * @return index of the type within throwables nested withing the specified throwable
+ */
+ private static int indexOf(Throwable throwable, Class type, int fromIndex, boolean subclass) {
+ if (throwable == null || type == null) {
+ return -1;
+ }
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+ Throwable[] throwables = getThrowables(throwable);
+ if (fromIndex >= throwables.length) {
+ return -1;
+ }
+ if (subclass) {
+ for (int i = fromIndex; i < throwables.length; i++) {
+ if (type.isAssignableFrom(throwables[i].getClass())) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = fromIndex; i < throwables.length; i++) {
+ if (type.equals(throwables[i].getClass())) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Removes common frames from the cause trace given the two stack traces.
+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(List causeFrames, List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + String causeFrame = (String) causeFrames.get(causeFrameIndex); + String wrapperFrame = (String) wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *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.
+ * + * @param throwable theThrowable to be examined
+ * @return the nested stack trace, with the root cause first
+ * @since 2.0
+ */
+ public static String getFullStackTrace(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ Throwable[] ts = getThrowables(throwable);
+ for (int i = 0; i < ts.length; i++) {
+ ts[i].printStackTrace(pw);
+ if (isNestedThrowable(ts[i])) {
+ break;
+ }
+ }
+ return sw.getBuffer().toString();
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the stack trace from a Throwable as a String.
+ * + *The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.
+ * + * @param throwable theThrowable to be examined
+ * @return the stack trace as generated by the exception's
+ * printStackTrace(PrintWriter) method
+ */
+ public static String getStackTrace(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw, true);
+ throwable.printStackTrace(pw);
+ return sw.getBuffer().toString();
+ }
+
+ /**
+ * Captures the stack trace associated with the specified
+ * Throwable object, decomposing it into a list of
+ * stack frames.
The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.
+ * + * @param throwable theThrowable to examine, may be null
+ * @return an array of strings describing each stack frame, never null
+ */
+// public static String[] getStackFrames(Throwable throwable) {
+// if (throwable == null) {
+// return ArrayUtils.EMPTY_STRING_ARRAY;
+// }
+// return getStackFrames(getStackTrace(throwable));
+// }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Returns an array where each element is a line from the argument.
+ * + *The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.
+ * + *Functionality shared between the
+ * getStackFrames(Throwable) methods of this and the
+ * {@link org.apache.commons.lang.exception.NestableDelegate} classes.
Produces a List of stack frames - the message
+ * is not included. Only the trace of the specified exception is
+ * returned, any caused by trace is stripped.
This works in most cases - it will only fail if the exception
+ * message contains a line that starts with:
+ * " at".
+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ +// public static String getMessage(Throwable th) { +// if (th == null) { +// return ""; +// } +// String clsName = ClassUtils.getShortClassName(th, null); +// String msg = th.getMessage(); +// return clsName + ": " + StringUtils.defaultString(msg); +// } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *
+ * The message returned is of the form
+ * {ClassNameWithoutPackage}: {ThrowableMessage}
+ *
+ * @param th the throwable to get a message for, null returns empty string
+ * @return the message, non-null
+ * @since Commons Lang 2.2
+ */
+// public static String getRootCauseMessage(Throwable th) {
+// Throwable root = ExceptionUtils.getRootCause(th);
+// root = (root == null ? th : root);
+// return getMessage(root);
+// }
+
+ /**
+ * An interface to be implemented by {@link java.lang.Throwable}
+ * extensions which would like to be able to nest root exceptions
+ * inside themselves.
+ *
+ * @author Daniel Rall
+ * @author Kasper Nielsen
+ * @author Steven Caswell
+ * @author Pete Gieser
+ * @since 1.0
+ * @version $Id$
+ */
+ public interface Nestable {
+
+ /**
+ * Returns the reference to the exception or error that caused the
+ * exception implementing the Nestable to be thrown.
+ *
+ * @return throwable that caused the original exception
+ */
+ public Throwable getCause();
+
+ /**
+ * Returns the error message of this and any nested
+ * Throwable.
+ *
+ * @return the error message
+ */
+ public String getMessage();
+
+ /**
+ * Returns the error message of the Throwable in the chain
+ * of Throwables at the specified index, numbered from 0.
+ *
+ * @param index the index of the Throwable in the chain of
+ * Throwables
+ * @return the error message, or null if the Throwable at the
+ * specified index in the chain does not contain a message
+ * @throws IndexOutOfBoundsException if the index argument is
+ * negative or not less than the count of Throwables in the
+ * chain
+ */
+ public String getMessage(int index);
+
+ /**
+ * Returns the error message of this and any nested 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 {@link #getThrowableCount()}
+ * operation.
+ *
+ * @return the error messages
+ */
+ public String[] getMessages();
+
+ /**
+ * Returns the Throwable in the chain of
+ * Throwables at the specified index, numbered from 0.
+ *
+ * @param index the index, numbered from 0, of the Throwable in
+ * the chain of Throwables
+ * @return the Throwable
+ * @throws IndexOutOfBoundsException if the index argument is
+ * negative or not less than the count of Throwables in the
+ * chain
+ */
+ public Throwable getThrowable(int index);
+
+ /**
+ * Returns the number of nested Throwables represented by
+ * this Nestable, including this Nestable.
+ *
+ * @return the throwable count
+ */
+ public int getThrowableCount();
+
+ /**
+ * Returns this Nestable and any nested Throwables
+ * in an array of Throwables, one element for each
+ * Throwable.
+ *
+ * @return the Throwables
+ */
+ public Throwable[] getThrowables();
+
+ /**
+ * Returns the index, numbered from 0, of the first occurrence of the
+ * specified type, or a subclass, in the chain of 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).
+ *
+ * @param type the type to find, subclasses match, null returns -1
+ * @return index of the first occurrence of the type in the chain, or -1 if
+ * the type is not found
+ */
+ public int indexOfThrowable(Class type);
+
+ /**
+ * 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.
+ * 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).
+ *
+ * @param type the type to find, subclasses match, null returns -1
+ * @param fromIndex the index, numbered from 0, of the starting position in
+ * the chain to be searched
+ * @return index of the first occurrence of the type in the chain, or -1 if
+ * the type is not found
+ * @throws IndexOutOfBoundsException if the fromIndex argument
+ * is negative or not less than the count of Throwables in the
+ * chain
+ */
+ public int indexOfThrowable(Class type, int fromIndex);
+
+ /**
+ * Prints the stack trace of this exception to the specified print
+ * writer. Includes information from the exception, if any,
+ * which caused this exception.
+ *
+ * @param out PrintWriter to use for output.
+ */
+ public void printStackTrace(PrintWriter out);
+
+ /**
+ * Prints the stack trace of this exception to the specified print
+ * stream. Includes information from the exception, if any,
+ * which caused this exception.
+ *
+ * @param out PrintStream to use for output.
+ */
+ public void printStackTrace(PrintStream out);
+
+ /**
+ * Prints the stack trace for this exception only--root cause not
+ * included--using the provided writer. Used by
+ * individual stack traces to a buffer. The implementation of
+ * this method should call
+ * super.printStackTrace(out); in most cases.
+ *
+ * @param out The writer to use.
+ */
+ public void printPartialStackTrace(PrintWriter out);
+
+ }
+}
diff --git a/core/src/main/java/co/aikar/commands/apachecommonslang/ApacheCommonsLangUtil.java b/core/src/main/java/co/aikar/commands/apachecommonslang/ApacheCommonsLangUtil.java
new file mode 100644
index 00000000..1a956938
--- /dev/null
+++ b/core/src/main/java/co/aikar/commands/apachecommonslang/ApacheCommonsLangUtil.java
@@ -0,0 +1,1399 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package co.aikar.commands.apachecommonslang;
+
+import java.lang.reflect.Array;
+import java.util.Iterator;
+
+/**
+ * Select methods copied from Apache Commons to avoid importing entire lib
+ * No changes to logic
+ */
+public class ApacheCommonsLangUtil {
+
+ /**
+ * The empty String {@code ""}.
+ * @since 2.0
+ */
+ public static final String EMPTY = "";
+ /**
+ *
Shallow clones an array returning a typecast result and handling + * {@code null}. + * + *
The objects in the array are not cloned, thus there is no special + * handling for multi-dimensional arrays. + * + *
This method returns {@code null} for a {@code null} input array.
+ *
+ * @param Adds all the elements of the given arrays into a new array.
+ * The new array contains all of the element of {@code array1} followed
+ * by all of the elements {@code array2}. When an array is returned, it is always
+ * a new array.
+ *
+ * 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 {@link Character#isWhitespace(char)}.
+ * A 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 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 {@link #capitalizeFully(String)}. Whitespace is defined by {@link Character#isWhitespace(char)}.
+ * A 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 {@link #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 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. 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.
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ * 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.
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ * 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 {@code null} separator is the same as an empty String ("").
+ * Null objects or empty strings within the array are represented by
+ * empty strings. 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 {@code null} separator is the same as an empty String ("").
+ * Null objects or empty strings within the array are represented by
+ * empty strings. Joins the elements of the provided {@code 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: {@link #join(Object[],char)}. Joins the elements of the provided {@code Iterator} into
+ * a single String containing the provided elements. No delimiter is added before or after the list.
+ * A {@code null} separator is the same as an empty String (""). See the examples here: {@link #join(Object[],String)}. Joins the elements of the provided {@code 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: {@link #join(Object[],char)}. Joins the elements of the provided {@code Iterable} into
+ * a single String containing the provided elements. No delimiter is added before or after the list.
+ * A {@code null} separator is the same as an empty String (""). See the examples here: {@link #join(Object[],String)}. Checks if the CharSequence contains only Unicode digits.
+ * A decimal point is not a Unicode digit and returns false. {@code null} will return {@code false}.
+ * An empty CharSequence (length()=0) will return {@code 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. Check if a CharSequence starts with a specified prefix. {@code null}s are handled without exceptions. Two {@code null}
+ * references are considered to be equal. The comparison is case sensitive. Case insensitive check if a CharSequence starts with a specified prefix. {@code null}s are handled without exceptions. Two {@code null}
+ * references are considered to be equal. The comparison is case insensitive. Check if a CharSequence starts with a specified prefix (optionally case insensitive). Finds the index of the given object in the array. This method returns {@link #INDEX_NOT_FOUND} ( Finds the index of the given object in the array starting at the given index. This method returns {@link #INDEX_NOT_FOUND} ( A negative startIndex is treated as zero. A startIndex larger than the array
+ * length will return {@link #INDEX_NOT_FOUND} (
+ * 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"]
+ *
+ *
+ * @param 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"
+ *
+ *
+ * @param str the String to capitalize, may be null
+ * @return capitalized String, null if null String input
+ */
+ public static String capitalizeFully(final String str) {
+ return capitalizeFully(str, null);
+ }
+
+ /**
+ * 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"
+ *
+ *
+ * @param str the String to capitalize, may be null
+ * @param delimiters set of characters to determine capitalization, null means whitespace
+ * @return capitalized String, null if null String input
+ * @since 2.1
+ */
+ public static String capitalizeFully(String str, final char... delimiters) {
+ final int delimLen = delimiters == null ? -1 : delimiters.length;
+ if (str == null || str.isEmpty() || delimLen == 0) {
+ return str;
+ }
+ str = str.toLowerCase();
+ return capitalize(str, delimiters);
+ }
+
+ // Capitalizing
+ //-----------------------------------------------------------------------
+ /**
+ * 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"
+ *
+ *
+ * @param str the String to capitalize, may be null
+ * @return capitalized String, null if null String input
+ * @see #capitalizeFully(String)
+ */
+ public static String capitalize(final String str) {
+ return capitalize(str, null);
+ }
+
+ /**
+ * 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"
+ *
+ *
+ * @param str the String to capitalize, may be null
+ * @param delimiters set of characters to determine capitalization, null means whitespace
+ * @return capitalized String, null if null String input
+ * @see #capitalizeFully(String)
+ * @since 2.1
+ */
+ public static String capitalize(final String str, final char... delimiters) {
+ final int delimLen = delimiters == null ? -1 : delimiters.length;
+ if (str == null || str.isEmpty() || delimLen == 0) {
+ return str;
+ }
+ final char[] buffer = str.toCharArray();
+ boolean capitalizeNext = true;
+ for (int i = 0; i < buffer.length; i++) {
+ final char ch = buffer[i];
+ if (isDelimiter(ch, delimiters)) {
+ capitalizeNext = true;
+ } else if (capitalizeNext) {
+ buffer[i] = Character.toTitleCase(ch);
+ capitalizeNext = false;
+ }
+ }
+ return new String(buffer);
+ }
+ //-----------------------------------------------------------------------
+ /**
+ * Is the character a delimiter.
+ *
+ * @param ch the character to check
+ * @param delimiters the delimiters
+ * @return true if it is a delimiter
+ */
+ public static boolean isDelimiter(final char ch, final char[] delimiters) {
+ if (delimiters == null) {
+ return Character.isWhitespace(ch);
+ }
+ for (final char delimiter : delimiters) {
+ if (ch == delimiter) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Joining
+ //-----------------------------------------------------------------------
+ /**
+ *
+ * StringUtils.join(null) = null
+ * StringUtils.join([]) = ""
+ * StringUtils.join([null]) = ""
+ * StringUtils.join(["a", "b", "c"]) = "abc"
+ * StringUtils.join([null, "", "a"]) = "a"
+ *
+ *
+ * @param
+ * 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"
+ *
+ *
+ * @param array the array of values to join together, may be null
+ * @param separator the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 2.0
+ */
+ public static String join(final Object[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final long[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final int[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final short[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final byte[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final char[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final float[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final double[] array, final char separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+
+ /**
+ *
+ * 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"
+ *
+ *
+ * @param array the array of values to join together, may be null
+ * @param separator the separator character to use
+ * @param startIndex the first index to start joining from. It is
+ * an error to pass in an end index past the end of the array
+ * @param endIndex the index to stop joining from (exclusive). It is
+ * an error to pass in an end index past the end of the array
+ * @return the joined String, {@code null} if null array input
+ * @since 2.0
+ */
+ public static String join(final Object[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ if (array[i] != null) {
+ buf.append(array[i]);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final long[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final int[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final byte[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final short[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final char[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final double[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.join(null, *) = null
+ * StringUtils.join([], *) = ""
+ * StringUtils.join([null], *) = ""
+ * StringUtils.join([1, 2, 3], ';') = "1;2;3"
+ * StringUtils.join([1, 2, 3], null) = "123"
+ *
+ *
+ * @param array
+ * the array of values to join together, may be null
+ * @param separator
+ * the separator character to use
+ * @param startIndex
+ * the first index to start joining from. It is an error to pass in an end index past the end of the
+ * array
+ * @param endIndex
+ * the index to stop joining from (exclusive). It is an error to pass in an end index past the end of
+ * the array
+ * @return the joined String, {@code null} if null array input
+ * @since 3.2
+ */
+ public static String join(final float[] array, final char separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ buf.append(array[i]);
+ }
+ return buf.toString();
+ }
+
+
+ /**
+ *
+ * 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"
+ *
+ *
+ * @param array the array of values to join together, may be null
+ * @param separator the separator character to use, null treated as ""
+ * @return the joined String, {@code null} if null array input
+ */
+ public static String join(final Object[] array, final String separator) {
+ if (array == null) {
+ return null;
+ }
+ return join(array, separator, 0, array.length);
+ }
+
+ /**
+ *
+ * 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"
+ *
+ *
+ * @param array the array of values to join together, may be null
+ * @param separator the separator character to use, null treated as ""
+ * @param startIndex the first index to start joining from.
+ * @param endIndex the index to stop joining from (exclusive).
+ * @return the joined String, {@code null} if null array input; or the empty string
+ * if {@code endIndex - startIndex <= 0}. The number of joined entries is given by
+ * {@code endIndex - startIndex}
+ * @throws ArrayIndexOutOfBoundsException ife
+ * {@code startIndex < 0} or
+ * {@code startIndex >= array.length()} or
+ * {@code endIndex < 0} or
+ * {@code endIndex > array.length()}
+ */
+ public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) {
+ if (array == null) {
+ return null;
+ }
+ if (separator == null) {
+ separator = EMPTY;
+ }
+
+ // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator))
+ // (Assuming that all Strings are roughly equally long)
+ final int noOfItems = endIndex - startIndex;
+ if (noOfItems <= 0) {
+ return EMPTY;
+ }
+
+ final StringBuilder buf = new StringBuilder(noOfItems * 16);
+
+ for (int i = startIndex; i < endIndex; i++) {
+ if (i > startIndex) {
+ buf.append(separator);
+ }
+ if (array[i] != null) {
+ buf.append(array[i]);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ *
+ * StringUtils.isNumeric(null) = false
+ * StringUtils.isNumeric("") = false
+ * StringUtils.isNumeric(" ") = false
+ * StringUtils.isNumeric("123") = true
+ * StringUtils.isNumeric("\u0967\u0968\u0969") = 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
+ *
+ *
+ * @param cs the CharSequence to check, may be null
+ * @return {@code true} if only contains digits, and is non-null
+ * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence)
+ * @since 3.0 Changed "" to return false and not true
+ */
+ public static boolean isNumeric(final CharSequence cs) {
+ if (cs == null || cs.length() == 0) {
+ return false;
+ }
+ final int sz = cs.length();
+ for (int i = 0; i < sz; i++) {
+ if (!Character.isDigit(cs.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ // startsWith
+ //-----------------------------------------------------------------------
+
+ /**
+ *
+ * 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
+ *
+ *
+ * @see java.lang.String#startsWith(String)
+ * @param str the CharSequence to check, may be null
+ * @param prefix the prefix to find, may be null
+ * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or
+ * both {@code null}
+ * @since 2.4
+ * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence)
+ */
+ public static boolean startsWith(final CharSequence str, final CharSequence prefix) {
+ return startsWith(str, prefix, false);
+ }
+
+ /**
+ *
+ * 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
+ *
+ *
+ * @see java.lang.String#startsWith(String)
+ * @param str the CharSequence to check, may be null
+ * @param prefix the prefix to find, may be null
+ * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or
+ * both {@code null}
+ * @since 2.4
+ * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence)
+ */
+ public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix) {
+ return startsWith(str, prefix, true);
+ }
+
+ /**
+ * -1.
+ * This value is returned by methods in this class and can also be used in comparisons with values returned by
+ * various method from {@link java.util.List}.
+ */
+ public static final int INDEX_NOT_FOUND = -1;
+
+ // IndexOf search
+ // ----------------------------------------------------------------------
+
+ // Object IndexOf
+ //-----------------------------------------------------------------------
+ /**
+ * -1) for a null input array.null
+ * @param objectToFind the object to find, may be null
+ * @return the index of the object within the array,
+ * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input
+ */
+ public static int indexOf(Object[] array, Object objectToFind) {
+ return indexOf(array, objectToFind, 0);
+ }
+
+ /**
+ * -1) for a null input array.-1).null
+ * @param objectToFind the object to find, may be null
+ * @param startIndex the index to start searching at
+ * @return the index of the object within the array starting at the index,
+ * {@link #INDEX_NOT_FOUND} (-1) if not found or null array input
+ */
+ public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
+ if (array == null) {
+ return INDEX_NOT_FOUND;
+ }
+ if (startIndex < 0) {
+ startIndex = 0;
+ }
+ if (objectToFind == null) {
+ for (int i = startIndex; i < array.length; i++) {
+ if (array[i] == null) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = startIndex; i < array.length; i++) {
+ if (objectToFind.equals(array[i])) {
+ return i;
+ }
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+}
diff --git a/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java b/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java
index 6887e581..9f194889 100644
--- a/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java
+++ b/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java
@@ -27,6 +27,6 @@ import co.aikar.commands.CommandExecutionContext;
import co.aikar.commands.InvalidCommandArgument;
@FunctionalInterface
-public interface ContextResolver