Java tutorial
/******************************************************************************* * Copyright 2012 Internet2 * * Licensed 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 org.openTwoFactor.server.util; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.sql.SQLException; import java.sql.Timestamp; import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javassist.util.proxy.ProxyObject; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.Nestable; import org.apache.commons.logging.Log; /** * utility methods for system that are safe to use in expression language. Generally these are static methods. * * @author mchyzer * */ public class TwoFactorServerUtilsElSafe { /** * null safe to upper method * @param input * @return upper */ public static String toUpperCase(String input) { return input == null ? null : input.toUpperCase(); } /** * null safe to lower method * @param input * @return lower */ public static String toLowerCase(String input) { return input == null ? null : input.toLowerCase(); } /** * take email addresses from a textarea and turn them into semi separated * @param emailAddresses can be whitespace, comma, or semi separated * @return the email addresses semi separated */ public static String normalizeEmailAddresses(String emailAddresses) { if (emailAddresses == null) { return null; } emailAddresses = StringUtils.replace(emailAddresses, ",", " "); emailAddresses = StringUtils.replace(emailAddresses, ";", " "); emailAddresses = StringUtils.replace(emailAddresses, "\n", " "); emailAddresses = StringUtils.replace(emailAddresses, "\t", " "); emailAddresses = StringUtils.replace(emailAddresses, "\r", " "); emailAddresses = TwoFactorServerUtilsElSafe.join(TwoFactorServerUtilsElSafe.splitTrim(emailAddresses, " "), ";"); return emailAddresses; } /** * pattern as simple validation for email. need text, @ sign, then text, dot, and text. * granted this could be better, but this is a first step */ private static Pattern emailPattern = Pattern.compile("^[^@]+@[^.]+\\..+$"); /** * * @param email * @return true if valid, false if not */ public static boolean validEmail(String email) { Matcher matcher = emailPattern.matcher(email); return matcher.matches(); } /** * shorten a set if it is too long * @param <T> * @param theSet * @param maxSize * @return the new set */ public static <T> Set<T> setShorten(Set<T> theSet, int maxSize) { if (length(theSet) < maxSize) { return theSet; } //truncate the list Set<T> newList = new LinkedHashSet<T>(); int i = 0; //TODO test this logic for (T t : theSet) { if (i >= maxSize) { break; } newList.add(t); i++; } return newList; } /** * * @param number e.g. 12345678 * @return the string, e.g. 12,345,678 */ public static String formatNumberWithCommas(Long number) { if (number == null) { return "null"; } DecimalFormat df = new DecimalFormat(); return df.format(number); } /** * compare null safe * @param first * @param second * @return 0 for equal, 1 for greater, -1 for less */ @SuppressWarnings("unchecked") public static int compare(Comparable first, Comparable second) { if (first == second) { return 0; } if (first == null) { return -1; } if (second == null) { return 1; } return first.compareTo(second); } /** * turn some strings into a map * @param strings * @return the map (never null) */ public static Map<String, String> toMap(String... strings) { Map<String, String> map = new LinkedHashMap<String, String>(); if (strings != null) { if (strings.length % 2 != 0) { throw new RuntimeException("Must pass in an even number of strings: " + strings.length); } for (int i = 0; i < strings.length; i += 2) { map.put(strings[i], strings[i + 1]); } } return map; } /** * turn some strings into a map * @param stringObjects is an array of String,Object,String,Object etc where the * Strings are the key, and the Object is the value * @return the map (never null) */ public static Map<String, Object> toStringObjectMap(Object... stringObjects) { Map<String, Object> map = new LinkedHashMap<String, Object>(); if (stringObjects != null) { if (stringObjects.length % 2 != 0) { throw new RuntimeException("Must pass in an even number of strings: " + stringObjects.length); } for (int i = 0; i < stringObjects.length; i += 2) { String key = (String) stringObjects[i]; map.put(key, stringObjects[i + 1]); } } return map; } /** * convert millis to friendly string * @param duration * @return the friendly string */ public static String convertMillisToFriendlyString(Integer duration) { if (duration == null) { return convertMillisToFriendlyString((Long) null); } return convertMillisToFriendlyString(new Long(duration.intValue())); } /** * convert millis to friendly string * @param duration * @return the friendly string */ public static String convertMillisToFriendlyString(Long duration) { if (duration == null) { return ""; } if (duration < 1000) { return duration + "ms"; } long ms = duration % 1000; duration = duration / 1000; long s = duration % 60; duration = duration / 60; if (duration == 0) { return s + "s, " + ms + "ms"; } long m = duration % 60; duration = duration / 60; if (duration == 0) { return m + "m, " + s + "s, " + ms + "ms"; } long h = duration % 24; duration = duration / 24; if (duration == 0) { return h + "h, " + m + "m, " + s + "s, " + ms + "ms"; } long d = duration; return d + "d, " + h + "h, " + m + "m, " + s + "s, " + ms + "ms"; } /** * return the arg after the argBefore, or null if not there, or exception * if argBefore is not found * @param args * @param argBefore * @return the arg */ public static String argAfter(String[] args, String argBefore) { if (length(args) <= 1) { return null; } int argBeforeIndex = -1; for (int i = 0; i < args.length; i++) { if (equals(args[i], argBefore)) { argBeforeIndex = i; break; } } if (argBeforeIndex == -1) { throw new RuntimeException("Cant find arg before"); } if (argBeforeIndex < args.length - 1) { return args[argBeforeIndex + 1]; } return null; } /** * append and maybe put a separator in there * @param result * @param separatorIfResultNotEmpty * @param stringToAppend */ public static void append(StringBuilder result, String separatorIfResultNotEmpty, String stringToAppend) { if (result.length() != 0) { result.append(separatorIfResultNotEmpty); } result.append(stringToAppend); } /** * */ public static final String LOG_ERROR = "Error trying to make parent dirs for logger or logging first statement, check to make " + "sure you have proper file permissions, and that your servlet container is giving " + "your app rights to access the log directory (e.g. for tomcat set TOMCAT5_SECURITY=no), g" + "oogle it for more info"; /** * The number of bytes in a kilobyte. */ public static final long ONE_KB = 1024; /** * The number of bytes in a megabyte. */ public static final long ONE_MB = ONE_KB * ONE_KB; /** * The number of bytes in a gigabyte. */ public static final long ONE_GB = ONE_KB * ONE_MB; /** * Returns a human-readable version of the file size (original is in * bytes). * * @param size The number of bytes. * @return A human-readable display value (includes units). * @todo need for I18N? */ public static String byteCountToDisplaySize(long size) { String displaySize; if (size / ONE_GB > 0) { displaySize = String.valueOf(size / ONE_GB) + " GB"; } else if (size / ONE_MB > 0) { displaySize = String.valueOf(size / ONE_MB) + " MB"; } else if (size / ONE_KB > 0) { displaySize = String.valueOf(size / ONE_KB) + " KB"; } else { displaySize = String.valueOf(size) + " bytes"; } return displaySize; } /** * return the suffix after a char. If the char doesnt exist, just return the string * @param input string * @param theChar char * @return new string */ public static String suffixAfterChar(String input, char theChar) { if (input == null) { return null; } //get the real type off the end int lastIndex = input.lastIndexOf(theChar); if (lastIndex > -1) { input = input.substring(lastIndex + 1, input.length()); } return input; } /** * sleep, if interrupted, throw runtime * @param millis */ public static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException ie) { throw new RuntimeException(ie); } } /** * * @param seconds */ public static void sleepWithStdoutCountdown(int seconds) { for (int i = seconds; i > 0; i--) { System.out.println("Sleeping: " + i); sleep(1000); } } /** * encrypt a message to SHA * @param plaintext * @return the hash */ public synchronized static String encryptSha(String plaintext) { MessageDigest md = null; try { md = MessageDigest.getInstance("SHA"); //step 2 } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } try { md.update(plaintext.getBytes("UTF-8")); //step 3 } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } byte raw[] = md.digest(); //step 4 byte[] encoded = Base64.encodeBase64(raw); //step 5 try { String hash = new String(encoded, "UTF-8"); //String hash = (new BASE64Encoder()).encode(raw); //step 5 return hash; //step 6 } catch (UnsupportedEncodingException uee) { throw new RuntimeException(uee); } } /** * get a unique string identifier based on the current time, * this is not globally unique, just unique for as long as this * server is running... * * @return String */ public static String uniqueId() { //this needs to be threadsafe since we are using a static field synchronized (TwoFactorServerUtilsElSafe.class) { lastId = incrementStringInt(lastId); } return String.valueOf(lastId); } /** * make sure a array is non null. If null, then return an empty array. * @param <T> * @param array * @param theClass to make array from * @return the list or empty list if null */ @SuppressWarnings({ "unchecked", "cast" }) public static <T> T[] nonNull(T[] array, Class<?> theClass) { if (int.class.equals(theClass)) { return (T[]) (Object) new int[0]; } if (float.class.equals(theClass)) { return (T[]) (Object) new float[0]; } if (double.class.equals(theClass)) { return (T[]) (Object) new double[0]; } if (short.class.equals(theClass)) { return (T[]) (Object) new short[0]; } if (long.class.equals(theClass)) { return (T[]) (Object) new long[0]; } if (byte.class.equals(theClass)) { return (T[]) (Object) new byte[0]; } if (boolean.class.equals(theClass)) { return (T[]) (Object) new boolean[0]; } if (char.class.equals(theClass)) { return (T[]) (Object) new char[0]; } return array == null ? ((T[]) Array.newInstance(theClass, 0)) : array; } /** * strip the suffix off * @param string * @param suffix * @return the string without the suffix */ public static String stripSuffix(String string, String suffix) { if (string == null || suffix == null) { return string; } if (string.endsWith(suffix)) { return string.substring(0, string.length() - suffix.length()); } return string; } /** * get the prefix or suffix of a string based on a separator * * @param startString * is the string to start with * @param separator * is the separator to split on * @param isPrefix * if thre prefix or suffix should be returned * * @return the prefix or suffix, if the separator isnt there, return the * original string */ public static String prefixOrSuffix(String startString, String separator, boolean isPrefix) { String prefixOrSuffix = null; //no nulls if (startString == null) { return startString; } //where is the separator int separatorIndex = startString.indexOf(separator); //if none exists, dont proceed if (separatorIndex == -1) { return startString; } //maybe the separator isnt on character int separatorLength = separator.length(); if (isPrefix) { prefixOrSuffix = startString.substring(0, separatorIndex); } else { prefixOrSuffix = startString.substring(separatorIndex + separatorLength, startString.length()); } return prefixOrSuffix; } /** * <pre> * this method will indent xml or json. * this is for logging or documentations purposes only and should * not be used for a production use (since it is not 100% tested * or compliant with all constructs like xml CDATA * * For xml, assumes elements either have text or sub elements, not both. * No cdata, nothing fancy. * * If the input is <a><b><c>hey</c><d><e>there</e></d></b></a> * It would output: * <a> * <b> * <c>hey</c> * <d> * <e>there</e> * </d> * </b> * </a> * * For , if the input is: {"a":{"b\"b":{"c\\":"d"},"e":"f","g":["h":"i"]}} * It would output: * { * "a":{ * "b\"b":{ * "c\\":"d" * }, * "e":"f", * "g":[ * "h":"i" * ] * } * } * * * <pre> * @param string * @param failIfTypeNotFound * @return the indented string, 2 spaces per line */ public static String indent(String string, boolean failIfTypeNotFound) { if (string == null) { return null; } string = trim(string); if (string.startsWith("<")) { //this is xml return new XmlIndenter(string).result(); } if (string.startsWith("{")) { return new JsonIndenter(string).result(); } if (!failIfTypeNotFound) { //just return if cant indent return string; } throw new RuntimeException("Cant find type of string: " + string); } /** * get the extension from name. if name is a:b:c, name is c * @param name * @return the name */ public static String extensionFromName(String name) { if (isBlank(name)) { return name; } int lastColonIndex = name.lastIndexOf(':'); if (lastColonIndex == -1) { return name; } String extension = name.substring(lastColonIndex + 1); return extension; } /** * get the parent stem name from name. if already a root stem * then just return null. e.g. if the name is a:b:c then * the return value is a:b * @param name * @return the parent stem name or null if none */ public static String parentStemNameFromName(String name) { return parentStemNameFromName(name, true); } /** * get the parent stem name from name. if already a root stem * then just return null. e.g. if the name is a:b:c then * the return value is a:b * @param name * @param nullForRoot null for root, otherwise colon * @return the parent stem name or null if none */ public static String parentStemNameFromName(String name, boolean nullForRoot) { //null safe if (TwoFactorServerUtilsElSafe.isBlank(name)) { return name; } int lastColonIndex = name.lastIndexOf(':'); if (lastColonIndex == -1) { if (nullForRoot) { return null; } return ":"; } String parentStemName = name.substring(0, lastColonIndex); return parentStemName; } /** * return the string or the other if the first is blank * @param string * @param defaultStringIfBlank * @return the string or the default one */ public static String defaultIfBlank(String string, String defaultStringIfBlank) { return isBlank(string) ? defaultStringIfBlank : string; } /** * genericized method to see if first is null, if so then return second, else first. * @param <T> * @param theValue first input * @param defaultIfTheValueIsNull second input * @return the first if not null, second if no */ public static <T> T defaultIfNull(T theValue, T defaultIfTheValueIsNull) { return theValue != null ? theValue : defaultIfTheValueIsNull; } /** * add each element of listToAdd if it is not already in list * @param <T> * @param list to add to * @param listToAdd each element will be added to list, or null if none */ public static <T> void addIfNotThere(Collection<T> list, Collection<T> listToAdd) { //maybe nothing to do if (listToAdd == null) { return; } for (T t : listToAdd) { if (!list.contains(t)) { list.add(t); } } } /** * print out various types of objects * * @param object * @param maxChars is where it should stop when figuring out object. note, result might be longer than max... * need to abbreviate when back * @param result is where to append to */ @SuppressWarnings("unchecked") private static void toStringForLogHelper(Object object, int maxChars, StringBuilder result) { try { if (object == null) { result.append("null"); } else if (object.getClass().isArray()) { // handle arrays int length = Array.getLength(object); if (length == 0) { result.append("Empty array"); } else { result.append("Array size: ").append(length).append(": "); for (int i = 0; i < length; i++) { result.append("[").append(i).append("]: ").append(Array.get(object, i)).append("\n"); if (maxChars != -1 && result.length() > maxChars) { return; } } } } else if (object instanceof Collection) { //give size and type if collection Collection<Object> collection = (Collection<Object>) object; int collectionSize = collection.size(); if (collectionSize == 0) { result.append("Empty ").append(object.getClass().getSimpleName()); } else { result.append(object.getClass().getSimpleName()).append(" size: ").append(collectionSize) .append(": "); int i = 0; for (Object collectionObject : collection) { result.append("[").append(i).append("]: ").append(collectionObject).append("\n"); if (maxChars != -1 && result.length() > maxChars) { return; } i++; } } } else { result.append(object.toString()); } } catch (Exception e) { result.append("<<exception>> ").append(object.getClass()).append(":\n").append(getFullStackTrace(e)) .append("\n"); } } /** * convert a set to a string (comma separate) * @param collection * @return the String */ public static String collectionToString(Collection collection) { if (collection == null) { return "null"; } if (collection.size() == 0) { return "empty"; } StringBuilder result = new StringBuilder(); boolean first = true; for (Object object : collection) { if (!first) { result.append(", "); } first = false; result.append(object); } return result.toString(); } /** * convert a set to a string (comma separate) * @param set * @return the String */ public static String setToString(Set set) { return collectionToString(set); } /** * convert a set to a string (comma separate) * @param map * @return the String * @deprecated use mapToString(map) */ @Deprecated public static String MapToString(Map map) { return mapToString(map); } /** * convert a set to a string (comma separate) * @param map * @return the String */ public static String mapToString(Map map) { if (map == null) { return "null"; } if (map.size() == 0) { return "empty"; } StringBuilder result = new StringBuilder(); boolean first = true; for (Object object : map.keySet()) { if (!first) { result.append(", "); } first = false; result.append(object).append(": ").append(map.get(object)); } return result.toString(); } /** * print out various types of objects * * @param object * @return the string value */ public static String toStringForLog(Object object) { StringBuilder result = new StringBuilder(); toStringForLogHelper(object, -1, result); return result.toString(); } /** * print out various types of objects * * @param object * @param maxChars is the max chars that should be returned (abbreviate if longer), or -1 for any amount * @return the string value */ public static String toStringForLog(Object object, int maxChars) { StringBuilder result = new StringBuilder(); toStringForLogHelper(object, -1, result); String resultString = result.toString(); if (maxChars != -1) { return abbreviate(resultString, maxChars); } return resultString; } /** * If batching this is the number of batches * @param count is size of set * @param batchSize * @return the number of batches */ public static int batchNumberOfBatches(int count, int batchSize) { //not sure why this would be 0... if (batchSize == 0) { return 0; } int batches = 1 + ((count - 1) / batchSize); return batches; } /** * If batching this is the number of batches * @param collection * @param batchSize * @return the number of batches */ public static int batchNumberOfBatches(Collection<?> collection, int batchSize) { int arrraySize = length(collection); return batchNumberOfBatches(arrraySize, batchSize); } /** * retrieve a batch by 0 index. Will return an array of size batchSize or * the remainder. the array will be full of elements. Note, this requires an * ordered input (so use linkedhashset not hashset if doing sets) * @param <T> template type * @param collection * @param batchSize * @param batchIndex * @return the list * This never returns null, only empty list */ @SuppressWarnings("unchecked") public static <T> List<T> batchList(Collection<T> collection, int batchSize, int batchIndex) { int numberOfBatches = batchNumberOfBatches(collection, batchSize); int arraySize = length(collection); // short circuit if (arraySize == 0) { return new ArrayList<T>(); } List<T> theBatchObjects = new ArrayList<T>(); // lets get the type of the first element if possible // Object first = get(arrayOrCollection, 0); // // Class theType = first == null ? Object.class : first.getClass(); // if last batch if (batchIndex == numberOfBatches - 1) { // needs to work to 1-n //int thisBatchSize = 1 + ((arraySize - 1) % batchSize); int collectionIndex = 0; for (T t : collection) { if (collectionIndex++ < batchIndex * batchSize) { continue; } //just copy the rest //if (collectionIndex >= (batchIndex * batchSize) + arraySize) { // break; //} //we are in the copy mode theBatchObjects.add(t); } } else { // if non-last batch //int newIndex = 0; int collectionIndex = 0; for (T t : collection) { if (collectionIndex < batchIndex * batchSize) { collectionIndex++; continue; } //done with batch if (collectionIndex >= (batchIndex + 1) * batchSize) { break; } theBatchObjects.add(t); collectionIndex++; } } return theBatchObjects; } /** * split a string based on a separator into an array, and trim each entry (see * the Commons Util trim() for more details) * * @param input * is the delimited input to split and trim * @param separator * is what to split on * * @return the array of items after split and trimmed, or null if input is null. will be trimmed to empty */ public static String[] splitTrim(String input, String separator) { return splitTrim(input, separator, true); } /** * split a string based on a separator into an array, and trim each entry (see * the Commons Util trim() for more details) * * @param input * is the delimited input to split and trim * @param separator * is what to split on * * @return the list of items after split and trimmed, or null if input is null. will be trimmed to empty */ public static List<String> splitTrimToList(String input, String separator) { if (isBlank(input)) { return null; } String[] array = splitTrim(input, separator); return toList(array); } /** * split a string based on a separator into an array, and trim each entry (see * the Commons Util trim() for more details) * * @param input * is the delimited input to split and trim * @param separator * is what to split on * * @return the set of items after split and trimmed, or null if input is null. will be trimmed to empty */ public static Set<String> splitTrimToSet(String input, String separator) { if (isBlank(input)) { return null; } String[] array = splitTrim(input, separator); return toSet(array); } /** * split a string based on a separator into an array, and trim each entry (see * the Commons Util trim() for more details) * * @param input * is the delimited input to split and trim * @param separator * is what to split on * @param treatAdjacentSeparatorsAsOne * @return the array of items after split and trimmed, or null if input is null. will be trimmed to empty */ public static String[] splitTrim(String input, String separator, boolean treatAdjacentSeparatorsAsOne) { if (isBlank(input)) { return null; } //first split String[] items = treatAdjacentSeparatorsAsOne ? splitByWholeSeparator(input, separator) : split(input, separator); //then trim for (int i = 0; (items != null) && (i < items.length); i++) { items[i] = trim(items[i]); } //return the array return items; } /** * escape url chars (e.g. a # is %23) * @param string input * @return the encoded string */ public static String escapeUrlEncode(String string) { String result = null; try { result = URLEncoder.encode(string, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException("UTF-8 not supported", ex); } return result; } /** * unescape url chars (e.g. a space is %20) * @param string input * @return the encoded string */ public static String escapeUrlDecode(String string) { String result = null; try { result = URLDecoder.decode(string, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException("UTF-8 not supported", ex); } return result; } /** * make sure a list is non null. If null, then return an empty list * @param <T> * @param list * @return the list or empty list if null */ public static <T> List<T> nonNull(List<T> list) { return list == null ? new ArrayList<T>() : list; } /** * make sure a collection is non null. If null, then return an empty list * @param <T> * @param list * @return the list or empty list if null */ public static <T> Collection<T> nonNull(Collection<T> list) { return list == null ? new ArrayList<T>() : list; } /** * make sure a list is non null. If null, then return an empty set * @param <T> * @param set * @return the set or empty set if null */ public static <T> Set<T> nonNull(Set<T> set) { return set == null ? new HashSet<T>() : set; } /** * make sure it is non null, if null, then give new map * * @param <K> key of map * @param <V> value of map * @param map is map * @return set non null */ public static <K, V> Map<K, V> nonNull(Map<K, V> map) { return map == null ? new HashMap<K, V>() : map; } /** * return a list of objects from varargs. Though if there is one * object, and it is a list, return it. * * @param <T> * template type of the objects * @param objects * @return the list or null if objects is null */ @SuppressWarnings("unchecked") public static <T> List<T> toList(T... objects) { if (objects == null) { return null; } if (objects.length == 1 && objects[0] instanceof List) { return (List<T>) objects[0]; } List<T> result = new ArrayList<T>(); for (T object : objects) { result.add(object); } return result; } /** * return a list of objects from varargs. Though if there is one * object, and it is a list, return it. * * @param objects * @return the list or null if objects is null */ public static List<Object> toListObject(Object... objects) { if (objects == null) { return null; } List<Object> result = new ArrayList<Object>(); for (Object object : objects) { result.add(object); } return result; } /** * return a set of objects from varargs. * * @param <T> template type of the objects * @param objects * @return the set */ public static <T> Set<T> toSet(T... objects) { if (objects == null) { return null; } Set<T> result = new LinkedHashSet<T>(); for (T object : objects) { result.add(object); } return result; } /** * return a set of string * * @param <T> template type of the objects * @param object * @return the set */ public static <T> Set<T> toSetObject(T object) { if (object == null) { return null; } Set<T> result = new LinkedHashSet<T>(); result.add(object); return result; } /** * string format of dates */ public static final String DATE_FORMAT = "yyyyMMdd"; /** * string format of dates for file names */ public static final String TIMESTAMP_FILE_FORMAT = "yyyy_MM_dd__HH_mm_ss_SSS"; /** * timestamp format, make sure to synchronize */ final static SimpleDateFormat timestampFileFormat = new SimpleDateFormat(TIMESTAMP_FILE_FORMAT); /** * string format of dates */ public static final String DATE_FORMAT2 = "yyyy/MM/dd"; /** * format including minutes and seconds: yyyy/MM/dd HH:mm:ss */ public static final String DATE_MINUTES_SECONDS_FORMAT = "yyyy/MM/dd HH:mm:ss"; /** * format including minutes and seconds: yyyyMMdd HH:mm:ss */ public static final String DATE_MINUTES_SECONDS_NO_SLASH_FORMAT = "yyyyMMdd HH:mm:ss"; /** * format on screen of config for milestone: yyyy/MM/dd HH:mm:ss.SSS */ public static final String TIMESTAMP_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS"; /** * format on screen of config for milestone: yyyyMMdd HH:mm:ss.SSS */ public static final String TIMESTAMP_NO_SLASH_FORMAT = "yyyyMMdd HH:mm:ss.SSS"; /** * date format, make sure to synchronize */ final static SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); /** * date format, make sure to synchronize */ final static SimpleDateFormat dateFormat2 = new SimpleDateFormat(DATE_FORMAT2); /** * synchronize code that uses this standard formatter for dates with minutes and seconds */ final static SimpleDateFormat dateMinutesSecondsFormat = new SimpleDateFormat(DATE_MINUTES_SECONDS_FORMAT); /** * synchronize code that uses this standard formatter for dates with minutes and seconds */ final static SimpleDateFormat dateMinutesSecondsNoSlashFormat = new SimpleDateFormat( DATE_MINUTES_SECONDS_NO_SLASH_FORMAT); /** * <pre> format: yyyy/MM/dd HH:mm:ss.SSS synchronize code that uses this standard formatter for timestamps </pre> */ final static SimpleDateFormat timestampFormat = new SimpleDateFormat(TIMESTAMP_FORMAT); /** * synchronize code that uses this standard formatter for timestamps */ final static SimpleDateFormat timestampNoSlashFormat = new SimpleDateFormat(TIMESTAMP_NO_SLASH_FORMAT); /** * If false, throw an assertException, and give a reason * * @param isTrue * @param reason */ public static void assertion(boolean isTrue, String reason) { if (!isTrue) { throw new RuntimeException(reason); } } /** * Field lastId. */ private static char[] lastId = convertLongToStringSmall(new Date().getTime()).toCharArray(); /** * null safe iterator getter if the type if collection * * @param collection * @return the iterator */ public static Iterator iterator(Object collection) { if (collection == null) { return null; } // array list doesnt need an iterator if (collection instanceof Collection && !(collection instanceof ArrayList)) { return ((Collection) collection).iterator(); } return null; } /** * Null safe array length or map * * @param arrayOrCollection * @return the length of the array (0 for null) */ public static int length(Object arrayOrCollection) { if (arrayOrCollection == null) { return 0; } if (arrayOrCollection.getClass().isArray()) { return Array.getLength(arrayOrCollection); } if (arrayOrCollection instanceof Collection) { return ((Collection) arrayOrCollection).size(); } if (arrayOrCollection instanceof Map) { return ((Map) arrayOrCollection).size(); } // simple non array non collection object return 1; } /** * If array, get the element based on index, if Collection, get it based on * iterator. * * @param arrayOrCollection * @param iterator * @param index * @return the object */ public static Object next(Object arrayOrCollection, Iterator iterator, int index) { if (arrayOrCollection.getClass().isArray()) { return Array.get(arrayOrCollection, index); } if (arrayOrCollection instanceof ArrayList) { return ((ArrayList) arrayOrCollection).get(index); } if (arrayOrCollection instanceof Collection) { return iterator.next(); } // simple object if (0 == index) { return arrayOrCollection; } throw new RuntimeException("Invalid class type: " + arrayOrCollection.getClass().getName()); } /** * Remove the iterator or index * * @param arrayOrCollection * @param index * @return the object list or new array */ public static Object remove(Object arrayOrCollection, int index) { return remove(arrayOrCollection, null, index); } /** * Remove the iterator or index * * @param arrayOrCollection * @param iterator * @param index * @return the object list or new array */ public static Object remove(Object arrayOrCollection, Iterator iterator, int index) { //if theres an iterator, just use that if (iterator != null) { iterator.remove(); return arrayOrCollection; } if (arrayOrCollection.getClass().isArray()) { int newLength = Array.getLength(arrayOrCollection) - 1; Object newArray = Array.newInstance(arrayOrCollection.getClass().getComponentType(), newLength); if (newLength == 0) { return newArray; } if (index > 0) { System.arraycopy(arrayOrCollection, 0, newArray, 0, index); } if (index < newLength) { System.arraycopy(arrayOrCollection, index + 1, newArray, index, newLength - index); } return newArray; } if (arrayOrCollection instanceof List) { ((List) arrayOrCollection).remove(index); return arrayOrCollection; } else if (arrayOrCollection instanceof Collection) { //this should work unless there are duplicates or something weird ((Collection) arrayOrCollection).remove(get(arrayOrCollection, index)); return arrayOrCollection; } throw new RuntimeException("Invalid class type: " + arrayOrCollection.getClass().getName()); } /** * print the simple names of a list of classes * @param object * @return the simple names */ public static String classesString(Object object) { StringBuilder result = new StringBuilder(); if (object.getClass().isArray()) { int length = Array.getLength(object); for (int i = 0; i < length; i++) { result.append(((Class) Array.get(object, i)).getSimpleName()); if (i < length - 1) { result.append(", "); } } return result.toString(); } throw new RuntimeException("Not implemented: " + className(object)); } /** * null safe classname method, max out at 20 * * @param object * @return the classname */ public static String classNameCollection(Object object) { if (object == null) { return null; } StringBuffer result = new StringBuffer(); Iterator iterator = iterator(object); int length = length(object); for (int i = 0; i < length && i < 20; i++) { result.append(className(next(object, iterator, i))); if (i != length - 1) { result.append(", "); } } return result.toString(); } /** * null safe classname method, gets the unenhanced name * * @param object * @return the classname */ public static String className(Object object) { return object == null ? null : unenhanceClass(object.getClass()).getName(); } /** * Convert a list to an array with the type of the first element e.g. if it * is a list of Person objects, then the array is Person[] * * @param objectOrArrayOrCollection * is a list * @return the array of objects with type of the first element in the list */ public static Object toArray(Object objectOrArrayOrCollection) { // do this before length since if array with null in it, we want ti get // it back if (objectOrArrayOrCollection != null && objectOrArrayOrCollection.getClass().isArray()) { return objectOrArrayOrCollection; } int length = length(objectOrArrayOrCollection); if (length == 0) { return null; } if (objectOrArrayOrCollection instanceof Collection) { Collection collection = (Collection) objectOrArrayOrCollection; Object first = collection.iterator().next(); return toArray(collection, first == null ? Object.class : first.getClass()); } // make an array of the type of object passed in, size one Object array = Array.newInstance(objectOrArrayOrCollection.getClass(), 1); Array.set(array, 0, objectOrArrayOrCollection); return array; } /** * convert a list into an array of type of theClass * @param <T> is the type of the array * @param collection list to convert * @param theClass type of array to return * @return array of type theClass[] filled with the objects from list */ @SuppressWarnings("unchecked") public static <T> T[] toArray(Collection collection, Class<T> theClass) { if (collection == null || collection.size() == 0) { return null; } return (T[]) collection.toArray((Object[]) Array.newInstance(theClass, collection.size())); } /** * replace a string or strings from a string, and put the output in a string * buffer. This does not recurse * * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with * @return the string */ public static String replace(String text, Object searchFor, Object replaceWith) { return replace(null, null, text, searchFor, replaceWith, false, 0, false); } /** * replace a string or strings from a string, and put the output in a string * buffer * * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with * @param recurse * if true then do multiple replaces (on the replacements) * @return the string */ public static String replace(String text, Object searchFor, Object replaceWith, boolean recurse) { return replace(null, null, text, searchFor, replaceWith, recurse, recurse ? length(searchFor) : 0, false); } /** * replace a string or strings from a string, and put the output in a string * buffer * * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with * @param recurse * if true then do multiple replaces (on the replacements) * @param removeIfFound * true if removing from searchFor and replaceWith if found * @return the string */ public static String replace(String text, Object searchFor, Object replaceWith, boolean recurse, boolean removeIfFound) { return replace(null, null, text, searchFor, replaceWith, recurse, recurse ? length(searchFor) : 0, removeIfFound); } /** * <p> * Replaces all occurrences of a String within another String. * </p> * * <p> * A <code>null</code> reference passed to this method is a no-op. * </p> * * <pre> * replace(null, *, *) = null * replace("", *, *) = "" * replace("any", null, *) = "any" * replace("any", *, null) = "any" * replace("any", "", *) = "any" * replace("aba", "a", null) = "aba" * replace("aba", "a", "") = "b" * replace("aba", "a", "z") = "zbz" * </pre> * * @see #replace(String text, String repl, String with, int max) * @param text * text to search and replace in, may be null * @param repl * the String to search for, may be null * @param with * the String to replace with, may be null * @return the text with any replacements processed, <code>null</code> if * null String input */ public static String replace(String text, String repl, String with) { return replace(text, repl, with, -1); } /** * <p> * Replaces a String with another String inside a larger String, for the * first <code>max</code> values of the search String. * </p> * * <p> * A <code>null</code> reference passed to this method is a no-op. * </p> * * <pre> * replace(null, *, *, *) = null * replace("", *, *, *) = "" * replace("any", null, *, *) = "any" * replace("any", *, null, *) = "any" * replace("any", "", *, *) = "any" * replace("any", *, *, 0) = "any" * replace("abaa", "a", null, -1) = "abaa" * replace("abaa", "a", "", -1) = "b" * replace("abaa", "a", "z", 0) = "abaa" * replace("abaa", "a", "z", 1) = "zbaa" * replace("abaa", "a", "z", 2) = "zbza" * replace("abaa", "a", "z", -1) = "zbzz" * </pre> * * @param text * text to search and replace in, may be null * @param repl * the String to search for, may be null * @param with * the String to replace with, may be null * @param max * maximum number of values to replace, or <code>-1</code> if * no maximum * @return the text with any replacements processed, <code>null</code> if * null String input */ public static String replace(String text, String repl, String with, int max) { if (text == null || isEmpty(repl) || with == null || max == 0) { return text; } StringBuffer buf = new StringBuffer(text.length()); int start = 0, end = 0; while ((end = text.indexOf(repl, start)) != -1) { buf.append(text.substring(start, end)).append(with); start = end + repl.length(); if (--max == 0) { break; } } buf.append(text.substring(start)); return buf.toString(); } /** * <p> * Checks if a String is empty ("") or null. * </p> * * <pre> * isEmpty(null) = true * isEmpty("") = true * isEmpty(" ") = false * isEmpty("bob") = false * isEmpty(" bob ") = false * </pre> * * <p> * NOTE: This method changed in Lang version 2.0. It no longer trims the * String. That functionality is available in isBlank(). * </p> * * @param str * the String to check, may be null * @return <code>true</code> if the String is empty or null */ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } /** * replace a string or strings from a string, and put the output in a string * buffer. This does not recurse * * @param outBuffer * stringbuffer to write to * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with */ public static void replace(StringBuffer outBuffer, String text, Object searchFor, Object replaceWith) { replace(outBuffer, null, text, searchFor, replaceWith, false, 0, false); } /** * replace a string or strings from a string, and put the output in a string * buffer * * @param outBuffer * stringbuffer to write to * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with * @param recurse * if true then do multiple replaces (on the replacements) */ public static void replace(StringBuffer outBuffer, String text, Object searchFor, Object replaceWith, boolean recurse) { replace(outBuffer, null, text, searchFor, replaceWith, recurse, recurse ? length(searchFor) : 0, false); } /** * replace a string with other strings, and either write to outWriter, or * StringBuffer, and if StringBuffer potentially return a string. If * outBuffer and outWriter are null, then return the String * * @param outBuffer * stringbuffer to write to, or null to not * @param outWriter * Writer to write to, or null to not. * @param text * string to look in * @param searchFor * string array to search for, or string, or list * @param replaceWith * string array to replace with, or string, or list * @param recurse * if true then do multiple replaces (on the replacements) * @param timeToLive * if recursing, prevent endless loops * @param removeIfFound * true if removing from searchFor and replaceWith if found * @return the String if outBuffer and outWriter are null * @throws IndexOutOfBoundsException * if the lengths of the arrays are not the same (null is ok, * and/or size 0) * @throws IllegalArgumentException * if the search is recursive and there is an endless loop due * to outputs of one being inputs to another */ private static String replace(StringBuffer outBuffer, Writer outWriter, String text, Object searchFor, Object replaceWith, boolean recurse, int timeToLive, boolean removeIfFound) { // if recursing, we need to get the string, then print to buffer (since // we need multiple passes) if (!recurse) { return replaceHelper(outBuffer, outWriter, text, searchFor, replaceWith, recurse, timeToLive, removeIfFound); } // get the string String result = replaceHelper(null, null, text, searchFor, replaceWith, recurse, timeToLive, removeIfFound); if (outBuffer != null) { outBuffer.append(result); return null; } if (outWriter != null) { try { outWriter.write(result); } catch (IOException ioe) { throw new RuntimeException(ioe); } return null; } return result; } /** * replace a string or strings from a string, and put the output in a string * buffer. This does not recurse * * @param outWriter * writer to write to * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with */ public static void replace(Writer outWriter, String text, Object searchFor, Object replaceWith) { replace(null, outWriter, text, searchFor, replaceWith, false, 0, false); } /** * replace a string or strings from a string, and put the output in a string * buffer * * @param outWriter * writer to write to * @param text * string to look in * @param searchFor * string array to search for * @param replaceWith * string array to replace with * @param recurse * if true then do multiple replaces (on the replacements) */ public static void replace(Writer outWriter, String text, Object searchFor, Object replaceWith, boolean recurse) { replace(null, outWriter, text, searchFor, replaceWith, recurse, recurse ? length(searchFor) : 0, false); } /** * replace a string with other strings, and either write to outWriter, or * StringBuffer, and if StringBuffer potentially return a string. If * outBuffer and outWriter are null, then return the String * * @param outBuffer * stringbuffer to write to, or null to not * @param outWriter * Writer to write to, or null to not. * @param text * string to look in * @param searchFor * string array to search for, or string, or list * @param replaceWith * string array to replace with, or string, or list * @param recurse * if true then do multiple replaces (on the replacements) * @param timeToLive * if recursing, prevent endless loops * @param removeIfFound * true if removing from searchFor and replaceWith if found * @return the String if outBuffer and outWriter are null * @throws IllegalArgumentException * if the search is recursive and there is an endless loop due * to outputs of one being inputs to another * @throws IndexOutOfBoundsException * if the lengths of the arrays are not the same (null is ok, * and/or size 0) */ private static String replaceHelper(StringBuffer outBuffer, Writer outWriter, String text, Object searchFor, Object replaceWith, boolean recurse, int timeToLive, boolean removeIfFound) { try { // if recursing, this shouldnt be less than 0 if (timeToLive < 0) { throw new IllegalArgumentException("TimeToLive under 0: " + timeToLive + ", " + text); } int searchForLength = length(searchFor); boolean done = false; // no need to do anything if (isEmpty(text)) { return text; } // need to write the input to output, later if (searchForLength == 0) { done = true; } boolean[] noMoreMatchesForReplIndex = null; int inputIndex = -1; int replaceIndex = -1; long resultPacked = -1; if (!done) { // make sure lengths are ok, these need to be equal if (searchForLength != length(replaceWith)) { throw new IndexOutOfBoundsException( "Lengths dont match: " + searchForLength + ", " + length(replaceWith)); } // keep track of which still have matches noMoreMatchesForReplIndex = new boolean[searchForLength]; // index of replace array that will replace the search string // found resultPacked = findNextIndexHelper(searchForLength, searchFor, replaceWith, noMoreMatchesForReplIndex, text, 0); inputIndex = unpackInt(resultPacked, true); replaceIndex = unpackInt(resultPacked, false); } // get a good guess on the size of the result buffer so it doesnt // have to double if it // goes over a bit boolean writeToWriter = outWriter != null; // no search strings found, we are done if (done || inputIndex == -1) { if (writeToWriter) { outWriter.write(text, 0, text.length()); return null; } if (outBuffer != null) { appendSubstring(outBuffer, text, 0, text.length()); return null; } return text; } // no buffer if writing to writer StringBuffer bufferToWriteTo = outBuffer != null ? outBuffer : (writeToWriter ? null : new StringBuffer( text.length() + replaceStringsBufferIncrease(text, searchFor, replaceWith))); String searchString = null; String replaceString = null; int start = 0; while (inputIndex != -1) { searchString = (String) get(searchFor, replaceIndex); replaceString = (String) get(replaceWith, replaceIndex); if (writeToWriter) { outWriter.write(text, start, inputIndex - start); outWriter.write(replaceString); } else { appendSubstring(bufferToWriteTo, text, start, inputIndex).append(replaceString); } if (removeIfFound) { // better be an iterator based find replace searchFor = remove(searchFor, replaceIndex); replaceWith = remove(replaceWith, replaceIndex); noMoreMatchesForReplIndex = (boolean[]) remove(noMoreMatchesForReplIndex, replaceIndex); // we now have a lesser size if we removed one searchForLength--; } start = inputIndex + searchString.length(); resultPacked = findNextIndexHelper(searchForLength, searchFor, replaceWith, noMoreMatchesForReplIndex, text, start); inputIndex = unpackInt(resultPacked, true); replaceIndex = unpackInt(resultPacked, false); } if (writeToWriter) { outWriter.write(text, start, text.length() - start); } else { appendSubstring(bufferToWriteTo, text, start, text.length()); } // no need to convert to string if incoming buffer or writer if (writeToWriter || outBuffer != null) { if (recurse) { throw new IllegalArgumentException("Cannot recurse and write to existing buffer or writer!"); } return null; } String resultString = bufferToWriteTo.toString(); if (recurse) { return replaceHelper(outBuffer, outWriter, resultString, searchFor, replaceWith, recurse, timeToLive - 1, false); } // this might be null for writer return resultString; } catch (IOException ioe) { throw new RuntimeException(ioe); } } /** * give a best guess on buffer increase for String[] replace get a good * guess on the size of the result buffer so it doesnt have to double if it * goes over a bit * * @param text * @param repl * @param with * @return the increase, with 20% cap */ static int replaceStringsBufferIncrease(String text, Object repl, Object with) { // count the greaters int increase = 0; Iterator iteratorReplace = iterator(repl); Iterator iteratorWith = iterator(with); int replLength = length(repl); String currentRepl = null; String currentWith = null; for (int i = 0; i < replLength; i++) { currentRepl = (String) next(repl, iteratorReplace, i); currentWith = (String) next(with, iteratorWith, i); if (currentRepl == null || currentWith == null) { throw new NullPointerException( "Replace string is null: " + text + ", " + currentRepl + ", " + currentWith); } int greater = currentWith.length() - currentRepl.length(); increase += greater > 0 ? 3 * greater : 0; // assume 3 matches } // have upper-bound at 20% increase, then let Java take over increase = Math.min(increase, text.length() / 5); return increase; } /** * Helper method to find the next match in an array of strings replace * * @param searchForLength * @param searchFor * @param replaceWith * @param noMoreMatchesForReplIndex * @param input * @param start * is where to start looking * @return result packed into a long, inputIndex first, then replaceIndex */ private static long findNextIndexHelper(int searchForLength, Object searchFor, Object replaceWith, boolean[] noMoreMatchesForReplIndex, String input, int start) { int inputIndex = -1; int replaceIndex = -1; Iterator iteratorSearchFor = iterator(searchFor); Iterator iteratorReplaceWith = iterator(replaceWith); String currentSearchFor = null; String currentReplaceWith = null; int tempIndex = -1; for (int i = 0; i < searchForLength; i++) { currentSearchFor = (String) next(searchFor, iteratorSearchFor, i); currentReplaceWith = (String) next(replaceWith, iteratorReplaceWith, i); if (noMoreMatchesForReplIndex[i] || isEmpty(currentSearchFor) || currentReplaceWith == null) { continue; } tempIndex = input.indexOf(currentSearchFor, start); // see if we need to keep searching for this noMoreMatchesForReplIndex[i] = tempIndex == -1; if (tempIndex != -1 && (inputIndex == -1 || tempIndex < inputIndex)) { inputIndex = tempIndex; replaceIndex = i; } } // dont create an array, no more objects long resultPacked = packInts(inputIndex, replaceIndex); return resultPacked; } /** * pack two ints into a long. Note: the first is held in the left bits, the * second is held in the right bits * * @param first * is first int * @param second * is second int * @return the long which has two ints in there */ public static long packInts(int first, int second) { long result = first; result <<= 32; result |= second; return result; } /** * take a long * * @param theLong * to unpack * @param isFirst * true for first, false for second * @return one of the packed ints, first or second */ public static int unpackInt(long theLong, boolean isFirst) { int result = 0; // put this in the position of the second one if (isFirst) { theLong >>= 32; } // only look at right part result = (int) (theLong & 0xffffffff); return result; } /** * append a substring to a stringbuffer. removes dependency on substring * which creates objects * * @param buf * stringbuffer * @param string * source string * @param start * start index of source string * @param end * end index of source string * @return the string buffer for chaining */ private static StringBuffer appendSubstring(StringBuffer buf, String string, int start, int end) { for (int i = start; i < end; i++) { buf.append(string.charAt(i)); } return buf; } /** * fail safe toString for Exception blocks, and include the stack * if there is a problem with toString() * @param object * @return the toStringSafe string */ @SuppressWarnings("unchecked") public static String toStringSafe(Object object) { if (object == null) { return null; } try { //give size and type if collection if (object instanceof Collection) { Collection<Object> collection = (Collection<Object>) object; int collectionSize = collection.size(); if (collectionSize == 0) { return "Empty " + object.getClass().getSimpleName(); } Object first = collection.iterator().next(); return object.getClass().getSimpleName() + " of size " + collectionSize + " with first type: " + (first == null ? null : first.getClass()); } return object.toString(); } catch (Exception e) { return "<<exception>> " + object.getClass() + ":\n" + getFullStackTrace(e) + "\n"; } } /** * get the boolean value for an object, cant be null or blank * * @param object * @return the boolean */ public static boolean booleanValue(Object object) { // first handle blanks if (nullOrBlank(object)) { throw new RuntimeException( "Expecting something which can be converted to boolean, but is null or blank: '" + object + "'"); } // its not blank, just convert if (object instanceof Boolean) { return (Boolean) object; } if (object instanceof String) { String string = (String) object; if (equalsIgnoreCase(string, "true") || equalsIgnoreCase(string, "t") || equalsIgnoreCase(string, "yes") || equalsIgnoreCase(string, "y")) { return true; } if (equalsIgnoreCase(string, "false") || equalsIgnoreCase(string, "f") || equalsIgnoreCase(string, "no") || equalsIgnoreCase(string, "n")) { return false; } throw new RuntimeException("Invalid string to boolean conversion: '" + string + "' expecting true|false or t|f or yes|no or y|n case insensitive"); } throw new RuntimeException("Cant convert object to boolean: " + object.getClass()); } /** * get the boolean value for an object * * @param object * @param defaultBoolean * if object is null or empty * @return the boolean */ public static boolean booleanValue(Object object, boolean defaultBoolean) { if (nullOrBlank(object)) { return defaultBoolean; } return booleanValue(object); } /** * get the Boolean value for an object * * @param object * @return the Boolean or null if null or empty */ public static Boolean booleanObjectValue(Object object) { if (nullOrBlank(object)) { return null; } return booleanValue(object); } /** * is an object null or blank * * @param object * @return true if null or blank */ public static boolean nullOrBlank(Object object) { // first handle blanks and nulls if (object == null) { return true; } if (object instanceof String && isBlank(((String) object))) { return true; } return false; } /** * convert a string date into a long date (e.g. for xml export) * @param date * @return the long or null if the date was null or blank */ public static Long dateLongValue(String date) { if (isBlank(date)) { return null; } Date dateObject = dateValue(date); return dateObject.getTime(); } /** * web service format string */ private static final String TIMESTAMP_XML_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS"; /** * date object to a string: * @param date * @return the long or null if the date was null or blank */ public static String dateStringValue(Date date) { if (date == null) { return null; } SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TIMESTAMP_XML_FORMAT); return simpleDateFormat.format(date); } /** * date object to a string: * @param theDate * @return the long or null if the date was null or blank */ public static String dateStringValue(Long theDate) { if (theDate == null) { return null; } return dateStringValue(new Date(theDate)); } /** * <pre> * Convert an object to a java.util.Date. allows, dates, null, blank, * yyyymmdd or yyyymmdd hh24:mm:ss * or yyyy/MM/dd HH:mm:ss.SSS * </pre> * @param inputObject * is the String or Date to convert * * @return the Date */ public static Date dateValue(Object inputObject) { if (inputObject == null) { return null; } if (inputObject instanceof java.util.Date) { return (Date) inputObject; } if (inputObject instanceof String) { String input = (String) inputObject; //trim and handle null and empty if (isBlank(input)) { return null; } try { if (input.length() == 8) { return dateFormat().parse(input); } if (!contains(input, '.')) { if (contains(input, '/')) { return dateMinutesSecondsFormat.parse(input); } //else no slash return dateMinutesSecondsNoSlashFormat.parse(input); } if (contains(input, '/')) { //see if the period is 6 back int lastDotIndex = input.lastIndexOf('.'); if (lastDotIndex == input.length() - 7) { String nonNanoInput = input.substring(0, input.length() - 3); Date date = timestampFormat.parse(nonNanoInput); //get the last 3 String lastThree = input.substring(input.length() - 3, input.length()); int lastThreeInt = Integer.parseInt(lastThree); Timestamp timestamp = new Timestamp(date.getTime()); timestamp.setNanos(timestamp.getNanos() + (lastThreeInt * 1000)); return timestamp; } return timestampFormat.parse(input); } //else no slash return timestampNoSlashFormat.parse(input); } catch (ParseException pe) { throw new RuntimeException(errorStart + toStringForLog(input)); } } throw new RuntimeException("Cannot convert Object to date : " + toStringForLog(inputObject)); } /** * match regex pattern yyyy-mm-dd or yyyy/mm/dd */ private static Pattern datePattern_yyyy_mm_dd = Pattern.compile("^(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})$"); /** * match regex pattern dd-mon-yyyy or dd/mon/yyyy */ private static Pattern datePattern_dd_mon_yyyy = Pattern .compile("^(\\d{1,2})[^\\d]+([a-zA-Z]{3,15})[^\\d]+(\\d{4})$"); /** * match regex pattern yyyy-mm-dd hh:mm:ss or yyyy/mm/dd hh:mm:ss */ private static Pattern datePattern_yyyy_mm_dd_hhmmss = Pattern.compile( "^(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})$"); /** * match regex pattern dd-mon-yyyy hh:mm:ss or dd/mon/yyyy hh:mm:ss */ private static Pattern datePattern_dd_mon_yyyy_hhmmss = Pattern.compile( "^(\\d{1,2})[^\\d]+([a-zA-Z]{3,15})[^\\d]+(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})$"); /** * match regex pattern yyyy-mm-dd hh:mm:ss.SSS or yyyy/mm/dd hh:mm:ss.SSS */ private static Pattern datePattern_yyyy_mm_dd_hhmmss_SSS = Pattern.compile( "^(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,3})$"); /** * match regex pattern dd-mon-yyyy hh:mm:ss.SSS or dd/mon/yyyy hh:mm:ss.SSS */ private static Pattern datePattern_dd_mon_yyyy_hhmmss_SSS = Pattern.compile( "^(\\d{1,2})[^\\d]+([a-zA-Z]{3,15})[^\\d]+(\\d{4})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,2})[^\\d]+(\\d{1,3})$"); /** * take as input: * yyyy/mm/dd * yyyy-mm-dd * dd-mon-yyyy * yyyy/mm/dd hh:mm:ss * dd-mon-yyyy hh:mm:ss * yyyy/mm/dd hh:mm:ss.SSS * dd-mon-yyyy hh:mm:ss.SSS * @param input * @return the date */ public static Date stringToDate2(String input) { if (isBlank(input)) { return null; } input = input.trim(); Matcher matcher = null; int month = 0; int day = 0; int year = 0; int hour = 0; int minute = 0; int second = 0; int milli = 0; boolean foundMatch = false; //yyyy/mm/dd if (!foundMatch) { matcher = datePattern_yyyy_mm_dd.matcher(input); if (matcher.matches()) { year = intValue(matcher.group(1)); month = intValue(matcher.group(2)); day = intValue(matcher.group(3)); foundMatch = true; } } //dd-mon-yyyy if (!foundMatch) { matcher = datePattern_dd_mon_yyyy.matcher(input); if (matcher.matches()) { day = intValue(matcher.group(1)); month = monthInt(matcher.group(2)); year = intValue(matcher.group(3)); foundMatch = true; } } //yyyy/mm/dd hh:mm:ss if (!foundMatch) { matcher = datePattern_yyyy_mm_dd_hhmmss.matcher(input); if (matcher.matches()) { year = intValue(matcher.group(1)); month = intValue(matcher.group(2)); day = intValue(matcher.group(3)); hour = intValue(matcher.group(4)); minute = intValue(matcher.group(5)); second = intValue(matcher.group(6)); foundMatch = true; } } //dd-mon-yyyy hh:mm:ss if (!foundMatch) { matcher = datePattern_dd_mon_yyyy_hhmmss.matcher(input); if (matcher.matches()) { day = intValue(matcher.group(1)); month = monthInt(matcher.group(2)); year = intValue(matcher.group(3)); hour = intValue(matcher.group(4)); minute = intValue(matcher.group(5)); second = intValue(matcher.group(6)); foundMatch = true; } } //yyyy/mm/dd hh:mm:ss.SSS if (!foundMatch) { matcher = datePattern_yyyy_mm_dd_hhmmss_SSS.matcher(input); if (matcher.matches()) { year = intValue(matcher.group(1)); month = intValue(matcher.group(2)); day = intValue(matcher.group(3)); hour = intValue(matcher.group(4)); minute = intValue(matcher.group(5)); second = intValue(matcher.group(6)); milli = intValue(matcher.group(7)); foundMatch = true; } } //dd-mon-yyyy hh:mm:ss.SSS if (!foundMatch) { matcher = datePattern_dd_mon_yyyy_hhmmss_SSS.matcher(input); if (matcher.matches()) { day = intValue(matcher.group(1)); month = monthInt(matcher.group(2)); year = intValue(matcher.group(3)); hour = intValue(matcher.group(4)); minute = intValue(matcher.group(5)); second = intValue(matcher.group(6)); milli = intValue(matcher.group(7)); foundMatch = true; } } Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.YEAR, year); calendar.set(Calendar.MONTH, month - 1); calendar.set(Calendar.DAY_OF_MONTH, day); calendar.set(Calendar.HOUR_OF_DAY, hour); calendar.set(Calendar.MINUTE, minute); calendar.set(Calendar.SECOND, second); calendar.set(Calendar.MILLISECOND, milli); return calendar.getTime(); } /** * convert a month string to an int (1 indexed). * e.g. if input is feb or Feb or february or February return 2 * @param mon * @return the month */ public static int monthInt(String mon) { if (!isBlank(mon)) { mon = mon.toLowerCase(); if (equals(mon, "jan") || equals(mon, "january")) { return 1; } if (equals(mon, "feb") || equals(mon, "february")) { return 2; } if (equals(mon, "mar") || equals(mon, "march")) { return 3; } if (equals(mon, "apr") || equals(mon, "april")) { return 4; } if (equals(mon, "may")) { return 5; } if (equals(mon, "jun") || equals(mon, "june")) { return 6; } if (equals(mon, "jul") || equals(mon, "july")) { return 7; } if (equals(mon, "aug") || equals(mon, "august")) { return 8; } if (equals(mon, "sep") || equals(mon, "september")) { return 9; } if (equals(mon, "oct") || equals(mon, "october")) { return 10; } if (equals(mon, "nov") || equals(mon, "november")) { return 11; } if (equals(mon, "dec") || equals(mon, "december")) { return 12; } } throw new RuntimeException("Invalid month: " + mon); } /** * See if the input is null or if string, if it is empty or blank (whitespace) * @param input * @return true if blank */ public static boolean isBlank(Object input) { if (null == input) { return true; } return (input instanceof String && isBlank((String) input)); } /** * see if a class is a scalar (not bean, not array or list, etc) * @param type * @return true if scalar */ public static boolean isScalar(Class<?> type) { if (type.isArray()) { return false; } //definitely all primitives if (type.isPrimitive()) { return true; } //Integer, Float, etc if (Number.class.isAssignableFrom(type)) { return true; } //Date, Timestamp if (Date.class.isAssignableFrom(type)) { return true; } if (Character.class.equals(type)) { return true; } //handles strings and string builders if (CharSequence.class.equals(type) || CharSequence.class.isAssignableFrom(type)) { return true; } if (Class.class == type || Boolean.class == type || type.isEnum()) { return true; } //appears not to be a scalar return false; } /** * <pre> * Convert a string or object to a timestamp (could be string, date, timestamp, etc) * yyyymmdd * or * yyyy/MM/dd * or * yyyy/MM/dd HH:mm:ss * or * yyyy/MM/dd HH:mm:ss.SSS * or * yyyy/MM/dd HH:mm:ss.SSSSSS * * </pre> * * @param input * @return the timestamp * @throws RuntimeException if invalid format */ public static Timestamp toTimestamp(Object input) { if (null == input) { return null; } else if (input instanceof java.sql.Timestamp) { return (Timestamp) input; } else if (input instanceof String) { return stringToTimestamp((String) input); } else if (input instanceof Date) { return new Timestamp(((Date) input).getTime()); } else if (input instanceof java.sql.Date) { return new Timestamp(((java.sql.Date) input).getTime()); } else { throw new RuntimeException("Cannot convert Object to timestamp : " + input); } } /** * convert an object to a string * * @param input * is the object to convert * * @return the String conversion of the object */ public static String stringValue(Object input) { //this isnt needed if (input == null) { return (String) input; } if (input instanceof Timestamp) { //convert to yyyy/MM/dd HH:mm:ss.SSS return timestampToString((Timestamp) input); } if (input instanceof Date) { //convert to yyyymmdd return stringValue((Date) input); } if (input instanceof Number) { DecimalFormat decimalFormat = new DecimalFormat("###################.###############"); return decimalFormat.format(((Number) input).doubleValue()); } return input.toString(); } /** * Convert a timestamp into a string: yyyy/MM/dd HH:mm:ss.SSS * @param timestamp * @return the string representation */ public synchronized static String timestampToString(Date timestamp) { if (timestamp == null) { return null; } return timestampFormat.format(timestamp); } /** * Convert a timestamp into a string: yyyy/MM/dd HH:mm:ss.SSS * @param timestamp * @return the string representation */ public synchronized static String timestampToFileString(Date timestamp) { if (timestamp == null) { return null; } return timestampFileFormat.format(timestamp); } /** * get the timestamp format for this thread * if you call this make sure to synchronize on FastDateUtils.class * @return the timestamp format */ synchronized static SimpleDateFormat dateFormat() { return dateFormat; } /** * get the timestamp format for this thread * if you call this make sure to synchronize on FastDateUtils.class * @return the timestamp format */ synchronized static SimpleDateFormat dateFormat2() { return dateFormat2; } /** * convert a date to the standard string yyyymmdd * @param date * @return the string value */ public static String stringValue(java.util.Date date) { synchronized (TwoFactorServerUtilsElSafe.class) { if (date == null) { return null; } String theString = dateFormat().format(date); return theString; } } /** * <pre>convert a string to timestamp based on the following formats: * yyyyMMdd * yyyy/MM/dd * yyyy/MM/dd HH:mm:ss * yyyy/MM/dd HH:mm:ss.SSS * yyyy/MM/dd HH:mm:ss.SSSSSS * </pre> * @param input * @return the timestamp object */ public static Timestamp stringToTimestamp(String input) { Date date = stringToTimestampHelper(input); if (date == null) { return null; } //maybe already a timestamp if (date instanceof Timestamp) { return (Timestamp) date; } return new Timestamp(date.getTime()); } /** * return a date based on input, null safe. Allow any of the three * formats: * yyyyMMdd * yyyy/MM/dd * yyyy/MM/dd HH:mm:ss * yyyy/MM/dd HH:mm:ss.SSS * yyyy/MM/dd HH:mm:ss.SSSSSS * * @param input * @return the millis, -1 for null */ synchronized static Date stringToTimestampHelper(String input) { //trim and handle null and empty if (isBlank(input)) { return null; } input = input.trim(); try { //convert mainframe if (equals("99999999", input) || equals("999999", input)) { input = "20991231"; } if (input.length() == 8) { return dateFormat().parse(input); } if (input.length() == 10) { return dateFormat2().parse(input); } if (!contains(input, '.')) { if (contains(input, '/')) { return dateMinutesSecondsFormat.parse(input); } //else no slash return dateMinutesSecondsNoSlashFormat.parse(input); } if (contains(input, '/')) { //see if the period is 6 back int lastDotIndex = input.lastIndexOf('.'); if (lastDotIndex == input.length() - 7) { String nonNanoInput = input.substring(0, input.length() - 3); Date date = timestampFormat.parse(nonNanoInput); //get the last 3 String lastThree = input.substring(input.length() - 3, input.length()); int lastThreeInt = Integer.parseInt(lastThree); Timestamp timestamp = new Timestamp(date.getTime()); timestamp.setNanos(timestamp.getNanos() + (lastThreeInt * 1000)); return timestamp; } return timestampFormat.parse(input); } //else no slash return timestampNoSlashFormat.parse(input); } catch (ParseException pe) { throw new RuntimeException(errorStart + input); } } /** * start of error parsing messages */ private static final String errorStart = "Invalid timestamp, please use any of the formats: " + DATE_FORMAT + ", " + TIMESTAMP_FORMAT + ", " + DATE_MINUTES_SECONDS_FORMAT + ": "; /** * Convert an object to a byte, allow nulls * @param input * @return the boolean object value */ public static BigDecimal bigDecimalObjectValue(Object input) { if (input instanceof BigDecimal) { return (BigDecimal) input; } if (isBlank(input)) { return null; } return BigDecimal.valueOf(doubleValue(input)); } /** * Convert an object to a byte, allow nulls * @param input * @return the boolean object value */ public static Byte byteObjectValue(Object input) { if (input instanceof Byte) { return (Byte) input; } if (isBlank(input)) { return null; } return Byte.valueOf(byteValue(input)); } /** * convert an object to a byte * @param input * @return the byte */ public static byte byteValue(Object input) { if (input instanceof String) { String string = (String) input; return Byte.parseByte(string); } if (input instanceof Number) { return ((Number) input).byteValue(); } throw new RuntimeException("Cannot convert to byte: " + className(input)); } /** * get the Double value of an object * * @param input * is a number or String * @param allowNullBlank used to default to false, if true, return null if nul inputted * * @return the Double equivalent */ public static Double doubleObjectValue(Object input, boolean allowNullBlank) { if (input instanceof Double) { return (Double) input; } if (allowNullBlank && isBlank(input)) { return null; } return Double.valueOf(doubleValue(input)); } /** * get the double value of an object * * @param input * is a number or String * * @return the double equivalent */ public static double doubleValue(Object input) { if (input instanceof String) { String string = (String) input; return Double.parseDouble(string); } if (input instanceof Number) { return ((Number) input).doubleValue(); } throw new RuntimeException("Cannot convert to double: " + className(input)); } /** * get the double value of an object, do not throw an * exception if there is an * error * * @param input * is a number or String * * @return the double equivalent */ public static double doubleValueNoError(Object input) { if (input == null || (input instanceof String && isBlank((String) input))) { return NOT_FOUND; } try { return doubleValue(input); } catch (Exception e) { //no need to log here } return NOT_FOUND; } /** * get the Float value of an object * * @param input * is a number or String * @param allowNullBlank true if allow null or blank * * @return the Float equivalent */ public static Float floatObjectValue(Object input, boolean allowNullBlank) { if (input instanceof Float) { return (Float) input; } if (allowNullBlank && isBlank(input)) { return null; } return Float.valueOf(floatValue(input)); } /** * get the float value of an object * * @param input * is a number or String * * @return the float equivalent */ public static float floatValue(Object input) { if (input instanceof String) { String string = (String) input; return Float.parseFloat(string); } if (input instanceof Number) { return ((Number) input).floatValue(); } throw new RuntimeException("Cannot convert to float: " + className(input)); } /** * get the float value of an object, do not throw an exception if there is an * error * * @param input * is a number or String * * @return the float equivalent */ public static float floatValueNoError(Object input) { if (input == null || (input instanceof String && isBlank((String) input))) { return NOT_FOUND; } try { return floatValue(input); } catch (Exception e) { LOG.error(e); } return NOT_FOUND; } /** * get the Integer value of an object * * @param input * is a number or String * @param allowNullBlank true if convert null or blank to null * * @return the Integer equivalent */ public static Integer intObjectValue(Object input, boolean allowNullBlank) { if (input instanceof Integer) { return (Integer) input; } if (allowNullBlank && isBlank(input)) { return null; } return Integer.valueOf(intValue(input)); } /** * convert an object to a int * @param input * @return the number */ public static int intValue(Object input) { if (input instanceof String) { String string = (String) input; return Integer.parseInt(string); } if (input instanceof Number) { return ((Number) input).intValue(); } throw new RuntimeException("Cannot convert to int: " + className(input)); } /** * convert an object to a int * @param input * @param valueIfNull is if the input is null or empty, return this value * @return the number */ public static int intValue(Object input, int valueIfNull) { if (input == null || "".equals(input)) { return valueIfNull; } return intObjectValue(input, false); } /** * get the int value of an object, do not throw an exception if there is an * error * * @param input * is a number or String * * @return the int equivalent */ public static int intValueNoError(Object input) { if (input == null || (input instanceof String && isBlank((String) input))) { return NOT_FOUND; } try { return intValue(input); } catch (Exception e) { //no need to log here } return NOT_FOUND; } /** special number when a number is not found */ public static final int NOT_FOUND = -999999999; /** * logger */ private static final Log LOG = TwoFactorServerUtils.getLog(TwoFactorServerUtilsElSafe.class); /** * The name says it all. */ public static final int DEFAULT_BUFFER_SIZE = 1024 * 4; /** * get the Long value of an object * * @param input * is a number or String * @param allowNullBlank true if null or blank converts to null * * @return the Long equivalent */ public static Long longObjectValue(Object input, boolean allowNullBlank) { if (input instanceof Long) { return (Long) input; } if (allowNullBlank && isBlank(input)) { return null; } return Long.valueOf(longValue(input)); } /** * convert an object to a long * @param input * @return the number */ public static long longValue(Object input) { if (input instanceof String) { String string = (String) input; return Long.parseLong(string); } if (input instanceof Number) { return ((Number) input).longValue(); } throw new RuntimeException("Cannot convert to long: " + className(input)); } /** * convert an object to a long * @param input * @param valueIfNull is if the input is null or empty, return this value * @return the number */ public static long longValue(Object input, long valueIfNull) { if (input == null || "".equals(input)) { return valueIfNull; } return longObjectValue(input, false); } /** * get the long value of an object, do not throw an exception if there is an * error * * @param input * is a number or String * * @return the long equivalent */ public static long longValueNoError(Object input) { if (input == null || (input instanceof String && isBlank((String) input))) { return NOT_FOUND; } try { return longValue(input); } catch (Exception e) { //no need to log here } return NOT_FOUND; } /** * get the Short value of an object. converts null or blank to null * * @param input * is a number or String * * @return the Long equivalent */ public static Short shortObjectValue(Object input) { if (input instanceof Short) { return (Short) input; } if (isBlank(input)) { return null; } return Short.valueOf(shortValue(input)); } /** * convert an object to a short * @param input * @return the number */ public static short shortValue(Object input) { if (input instanceof String) { String string = (String) input; return Short.parseShort(string); } if (input instanceof Number) { return ((Number) input).shortValue(); } throw new RuntimeException("Cannot convert to short: " + className(input)); } /** * get the Character wrapper value for the input * @param input allow null, return null * @return the Character object wrapper */ public static Character charObjectValue(Object input) { if (input instanceof Character) { return (Character) input; } if (isBlank(input)) { return null; } return new Character(charValue(input)); } /** * convert an object to a char * @param input * @return the number */ public static char charValue(Object input) { if (input instanceof Character) { return ((Character) input).charValue(); } //if string length 1, thats ok if (input instanceof String) { String inputString = (String) input; if (inputString.length() == 1) { return inputString.charAt(0); } } throw new RuntimeException( "Cannot convert to char: " + (input == null ? null : (input.getClass() + ", " + input))); } /** * replace all whitespace with space * @param input * @return the string */ public static String replaceWhitespaceWithSpace(String input) { if (input == null) { return input; } return input.replaceAll("\\s+", " "); } /** * this method takes a long (less than 62) and converts it to a 1 character * string (a-z, A-Z, 0-9) * * @param theLong * is the long (less than 62) to convert to a 1 character string * * @return a one character string */ public static String convertLongToChar(long theLong) { if ((theLong < 0) || (theLong >= 62)) { throw new RuntimeException("convertLongToChar() " + " invalid input (not >=0 && <62: " + theLong); } else if (theLong < 26) { return "" + (char) ('a' + theLong); } else if (theLong < 52) { return "" + (char) ('A' + (theLong - 26)); } else { return "" + (char) ('0' + (theLong - 52)); } } /** * this method takes a long (less than 36) and converts it to a 1 character * string (A-Z, 0-9) * * @param theLong * is the long (less than 36) to convert to a 1 character string * * @return a one character string */ public static String convertLongToCharSmall(long theLong) { if ((theLong < 0) || (theLong >= 36)) { throw new RuntimeException("convertLongToCharSmall() " + " invalid input (not >=0 && <36: " + theLong); } else if (theLong < 26) { return "" + (char) ('A' + theLong); } else { return "" + (char) ('0' + (theLong - 26)); } } /** * convert a long to a string by converting it to base 62 (26 lower, 26 upper, * 10 digits) * * @param theLong * is the long to convert * * @return the String conversion of this */ public static String convertLongToString(long theLong) { long quotient = theLong / 62; long remainder = theLong % 62; if (quotient == 0) { return convertLongToChar(remainder); } StringBuffer result = new StringBuffer(); result.append(convertLongToString(quotient)); result.append(convertLongToChar(remainder)); return result.toString(); } /** * convert a long to a string by converting it to base 36 (26 upper, 10 * digits) * * @param theLong * is the long to convert * * @return the String conversion of this */ public static String convertLongToStringSmall(long theLong) { long quotient = theLong / 36; long remainder = theLong % 36; if (quotient == 0) { return convertLongToCharSmall(remainder); } StringBuffer result = new StringBuffer(); result.append(convertLongToStringSmall(quotient)); result.append(convertLongToCharSmall(remainder)); return result.toString(); } /** * increment a character (A-Z then 0-9) * * @param theChar * * @return the value */ public static char incrementChar(char theChar) { if (theChar == 'Z') { return '0'; } if (theChar == '9') { return 'A'; } return ++theChar; } /** * Increment a string with A-Z and 0-9 (no lower case so case insensitive apps * like windows IE will still work) * * @param string * * @return the value */ public static char[] incrementStringInt(char[] string) { if (string == null) { return string; } //loop through the string backwards int i = 0; for (i = string.length - 1; i >= 0; i--) { char inc = string[i]; inc = incrementChar(inc); string[i] = inc; if (inc != 'A') { break; } } //if we are at 0, then it means we hit AAAAAAA (or more) if (i < 0) { return ("A" + new String(string)).toCharArray(); } return string; } /** * is ascii char * @param input * @return true if ascii */ public static boolean isAscii(char input) { return input < 128; } /** * find the length of ascii chars (non ascii are counted as two) * @param input * @return the length of ascii chars */ public static int lengthAscii(String input) { if (input == null) { return 0; } //see what real length is int utfLength = input.length(); //count how many non asciis int extras = 0; for (int i = 0; i < utfLength; i++) { //keep count of non ascii chars if (!isAscii(input.charAt(i))) { extras++; } } return utfLength + extras; } /** * string length * @param string * @return string length */ public static int stringLength(String string) { return string == null ? 0 : string.length(); } /** * find the length of ascii chars (non ascii are counted as two) * @param input is the string to operate on * @param requiredLength length we need the string to be * @return the length of ascii chars */ public static String truncateAscii(String input, int requiredLength) { if (input == null) { return input; } //see what real length is int utfLength = input.length(); //see if not worth checking if (utfLength * 2 < requiredLength) { return input; } //count how many non asciis int asciiLength = 0; for (int i = 0; i < utfLength; i++) { asciiLength++; //keep count of non ascii chars if (!isAscii(input.charAt(i))) { asciiLength++; } //see if we are over if (asciiLength > requiredLength) { //do not include the current char return input.substring(0, i); } } //must have fit return input; } /** * null safe string compare * @param first * @param second * @return true if equal */ public static boolean equals(String first, String second) { if (first == second) { return true; } if (first == null || second == null) { return false; } return first.equals(second); } /** * <p>Checks if a String is whitespace, empty ("") or null.</p> * * <pre> * isBlank(null) = true * isBlank("") = true * isBlank(" ") = true * isBlank("bob") = false * isBlank(" bob ") = false * </pre> * * @param str the String to check, may be null * @return <code>true</code> if the String is null, empty or whitespace * @since 2.0 */ public static boolean isBlank(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if ((Character.isWhitespace(str.charAt(i)) == false)) { return false; } } return true; } /** * * @param str * @return true if not blank */ public static boolean isNotBlank(String str) { return !isBlank(str); } /** * trim whitespace from string * @param str * @return trimmed string */ public static String trim(String str) { return str == null ? null : str.trim(); } /** * equalsignorecase * @param str1 * @param str2 * @return true if the strings are equal ignore case */ public static boolean equalsIgnoreCase(String str1, String str2) { return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2); } /** * trim to empty, convert null to empty * @param str * @return trimmed */ public static String trimToEmpty(String str) { return str == null ? "" : str.trim(); } /** * <p>Abbreviates a String using ellipses. This will turn * "Now is the time for all good men" into "Now is the time for..."</p> * * <p>Specifically: * <ul> * <li>If <code>str</code> is less than <code>maxWidth</code> characters * long, return it.</li> * <li>Else abbreviate it to <code>(substring(str, 0, max-3) + "...")</code>.</li> * <li>If <code>maxWidth</code> is less than <code>4</code>, throw an * <code>IllegalArgumentException</code>.</li> * <li>In no case will it return a String of length greater than * <code>maxWidth</code>.</li> * </ul> * </p> * * <pre> * StringUtils.abbreviate(null, *) = null * StringUtils.abbreviate("", 4) = "" * StringUtils.abbreviate("abcdefg", 6) = "abc..." * StringUtils.abbreviate("abcdefg", 7) = "abcdefg" * StringUtils.abbreviate("abcdefg", 8) = "abcdefg" * StringUtils.abbreviate("abcdefg", 4) = "a..." * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException * </pre> * * @param str the String to check, may be null * @param maxWidth maximum length of result String, must be at least 4 * @return abbreviated String, <code>null</code> if null String input * @throws IllegalArgumentException if the width is too small * @since 2.0 */ public static String abbreviate(String str, int maxWidth) { return abbreviate(str, 0, maxWidth); } /** * <p>Abbreviates a String using ellipses. This will turn * "Now is the time for all good men" into "...is the time for..."</p> * * <p>Works like <code>abbreviate(String, int)</code>, but allows you to specify * a "left edge" offset. Note that this left edge is not necessarily going to * be the leftmost character in the result, or the first character following the * ellipses, but it will appear somewhere in the result. * * <p>In no case will it return a String of length greater than * <code>maxWidth</code>.</p> * * <pre> * StringUtils.abbreviate(null, *, *) = null * StringUtils.abbreviate("", 0, 4) = "" * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..." * StringUtils.abbreviate("abcdefghijklmno", 0, 10) = "abcdefg..." * StringUtils.abbreviate("abcdefghijklmno", 1, 10) = "abcdefg..." * StringUtils.abbreviate("abcdefghijklmno", 4, 10) = "abcdefg..." * StringUtils.abbreviate("abcdefghijklmno", 5, 10) = "...fghi..." * StringUtils.abbreviate("abcdefghijklmno", 6, 10) = "...ghij..." * StringUtils.abbreviate("abcdefghijklmno", 8, 10) = "...ijklmno" * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno" * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno" * StringUtils.abbreviate("abcdefghij", 0, 3) = IllegalArgumentException * StringUtils.abbreviate("abcdefghij", 5, 6) = IllegalArgumentException * </pre> * * @param str the String to check, may be null * @param offset left edge of source String * @param maxWidth maximum length of result String, must be at least 4 * @return abbreviated String, <code>null</code> if null String input * @throws IllegalArgumentException if the width is too small * @since 2.0 */ public static String abbreviate(String str, int offset, int maxWidth) { if (str == null) { return null; } if (maxWidth < 4) { throw new IllegalArgumentException("Minimum abbreviation width is 4"); } if (str.length() <= maxWidth) { return str; } if (offset > str.length()) { offset = str.length(); } if ((str.length() - offset) < (maxWidth - 3)) { offset = str.length() - (maxWidth - 3); } if (offset <= 4) { return str.substring(0, maxWidth - 3) + "..."; } if (maxWidth < 7) { throw new IllegalArgumentException("Minimum abbreviation width with offset is 7"); } if ((offset + (maxWidth - 3)) < str.length()) { return "..." + abbreviate(str.substring(offset), maxWidth - 3); } return "..." + str.substring(str.length() - (maxWidth - 3)); } // Splitting //----------------------------------------------------------------------- /** * <p>Splits the provided text into an array, using whitespace as the * separator. * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as one separator. * For more control over the split use the StrTokenizer class.</p> * * <p>A <code>null</code> input String returns <code>null</code>.</p> * * <pre> * StringUtils.split(null) = null * StringUtils.split("") = [] * StringUtils.split("abc def") = ["abc", "def"] * StringUtils.split("abc def") = ["abc", "def"] * StringUtils.split(" abc ") = ["abc"] * </pre> * * @param str the String to parse, may be null * @return an array of parsed Strings, <code>null</code> if null String input */ public static String[] split(String str) { return split(str, null, -1); } /** * <p>Splits the provided text into an array, separator specified. * This is an alternative to using StringTokenizer.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as one separator. * For more control over the split use the StrTokenizer class.</p> * * <p>A <code>null</code> input String returns <code>null</code>.</p> * * <pre> * StringUtils.split(null, *) = null * StringUtils.split("", *) = [] * StringUtils.split("a.b.c", '.') = ["a", "b", "c"] * StringUtils.split("a..b.c", '.') = ["a", "b", "c"] * StringUtils.split("a:b:c", '.') = ["a:b:c"] * StringUtils.split("a\tb\nc", null) = ["a", "b", "c"] * StringUtils.split("a b c", ' ') = ["a", "b", "c"] * </pre> * * @param str the String to parse, may be null * @param separatorChar the character used as the delimiter, * <code>null</code> splits on whitespace * @return an array of parsed Strings, <code>null</code> if null String input * @since 2.0 */ public static String[] split(String str, char separatorChar) { return splitWorker(str, separatorChar, false); } /** * <p>Splits the provided text into an array, separators specified. * This is an alternative to using StringTokenizer.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as one separator. * For more control over the split use the StrTokenizer class.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * A <code>null</code> separatorChars splits on whitespace.</p> * * <pre> * StringUtils.split(null, *) = null * StringUtils.split("", *) = [] * StringUtils.split("abc def", null) = ["abc", "def"] * StringUtils.split("abc def", " ") = ["abc", "def"] * StringUtils.split("abc def", " ") = ["abc", "def"] * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"] * </pre> * * @param str the String to parse, may be null * @param separatorChars the characters used as the delimiters, * <code>null</code> splits on whitespace * @return an array of parsed Strings, <code>null</code> if null String input */ public static String[] split(String str, String separatorChars) { return splitWorker(str, separatorChars, -1, false); } /** * <p>Splits the provided text into an array with a maximum length, * separators specified.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as one separator.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * A <code>null</code> separatorChars splits on whitespace.</p> * * <p>If more than <code>max</code> delimited substrings are found, the last * returned string includes all characters after the first <code>max - 1</code> * returned strings (including separator characters).</p> * * <pre> * StringUtils.split(null, *, *) = null * StringUtils.split("", *, *) = [] * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"] * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"] * StringUtils.split("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"] * StringUtils.split("ab:cd:ef", ":", 2) = ["ab", "cd:ef"] * </pre> * * @param str the String to parse, may be null * @param separatorChars the characters used as the delimiters, * <code>null</code> splits on whitespace * @param max the maximum number of elements to include in the * array. A zero or negative value implies no limit * @return an array of parsed Strings, <code>null</code> if null String input */ public static String[] split(String str, String separatorChars, int max) { return splitWorker(str, separatorChars, max, false); } /** * <p>Splits the provided text into an array, separator string specified.</p> * * <p>The separator(s) will not be included in the returned String array. * Adjacent separators are treated as one separator.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * A <code>null</code> separator splits on whitespace.</p> * * <pre> * StringUtils.split(null, *) = null * StringUtils.split("", *) = [] * StringUtils.split("ab de fg", null) = ["ab", "de", "fg"] * StringUtils.split("ab de fg", null) = ["ab", "de", "fg"] * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"] * StringUtils.split("abstemiouslyaeiouyabstemiously", "aeiouy") = ["bst", "m", "sl", "bst", "m", "sl"] * StringUtils.split("abstemiouslyaeiouyabstemiously", "aeiouy") = ["abstemiously", "abstemiously"] * </pre> * * @param str the String to parse, may be null * @param separator String containing the String to be used as a delimiter, * <code>null</code> splits on whitespace * @return an array of parsed Strings, <code>null</code> if null String was input */ public static String[] splitByWholeSeparator(String str, String separator) { return splitByWholeSeparator(str, separator, -1); } /** * <p>Splits the provided text into an array, separator string specified. * Returns a maximum of <code>max</code> substrings.</p> * * <p>The separator(s) will not be included in the returned String array. * Adjacent separators are treated as one separator.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * A <code>null</code> separator splits on whitespace.</p> * * <pre> * StringUtils.splitByWholeSeparator(null, *, *) = null * StringUtils.splitByWholeSeparator("", *, *) = [] * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"] * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"] * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2) = ["ab", "cd"] * StringUtils.splitByWholeSeparator("abstemiouslyaeiouyabstemiously", "aeiouy", 2) = ["bst", "m"] * StringUtils.splitByWholeSeparator("abstemiouslyaeiouyabstemiously", "aeiouy", 2) = ["abstemiously", "abstemiously"] * </pre> * * @param str the String to parse, may be null * @param separator String containing the String to be used as a delimiter, * <code>null</code> splits on whitespace * @param max the maximum number of elements to include in the returned * array. A zero or negative value implies no limit. * @return an array of parsed Strings, <code>null</code> if null String was input */ @SuppressWarnings("unchecked") public static String[] splitByWholeSeparator(String str, String separator, int max) { if (str == null) { return null; } int len = str.length(); if (len == 0) { return EMPTY_STRING_ARRAY; } if ((separator == null) || ("".equals(separator))) { // Split on whitespace. return split(str, null, max); } int separatorLength = separator.length(); ArrayList substrings = new ArrayList(); int numberOfSubstrings = 0; int beg = 0; int end = 0; while (end < len) { end = str.indexOf(separator, beg); if (end > -1) { if (end > beg) { numberOfSubstrings += 1; if (numberOfSubstrings == max) { end = len; substrings.add(str.substring(beg)); } else { // The following is OK, because String.substring( beg, end ) excludes // the character at the position 'end'. substrings.add(str.substring(beg, end)); // Set the starting point for the next search. // The following is equivalent to beg = end + (separatorLength - 1) + 1, // which is the right calculation: beg = end + separatorLength; } } else { // We found a consecutive occurrence of the separator, so skip it. beg = end + separatorLength; } } else { // String.substring( beg ) goes from 'beg' to the end of the String. substrings.add(str.substring(beg)); end = len; } } return (String[]) substrings.toArray(new String[substrings.size()]); } //----------------------------------------------------------------------- /** * <p>Splits the provided text into an array, using whitespace as the * separator, preserving all tokens, including empty tokens created by * adjacent separators. This is an alternative to using StringTokenizer. * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as separators for empty tokens. * For more control over the split use the StrTokenizer class.</p> * * <p>A <code>null</code> input String returns <code>null</code>.</p> * * <pre> * StringUtils.splitPreserveAllTokens(null) = null * StringUtils.splitPreserveAllTokens("") = [] * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "def"] * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "", "def"] * StringUtils.splitPreserveAllTokens(" abc ") = ["", "abc", ""] * </pre> * * @param str the String to parse, may be <code>null</code> * @return an array of parsed Strings, <code>null</code> if null String input * @since 2.1 */ public static String[] splitPreserveAllTokens(String str) { return splitWorker(str, null, -1, true); } /** * <p>Splits the provided text into an array, separator specified, * preserving all tokens, including empty tokens created by adjacent * separators. This is an alternative to using StringTokenizer.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as separators for empty tokens. * For more control over the split use the StrTokenizer class.</p> * * <p>A <code>null</code> input String returns <code>null</code>.</p> * * <pre> * StringUtils.splitPreserveAllTokens(null, *) = null * StringUtils.splitPreserveAllTokens("", *) = [] * StringUtils.splitPreserveAllTokens("a.b.c", '.') = ["a", "b", "c"] * StringUtils.splitPreserveAllTokens("a..b.c", '.') = ["a", "b", "c"] * StringUtils.splitPreserveAllTokens("a:b:c", '.') = ["a:b:c"] * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"] * StringUtils.splitPreserveAllTokens("a b c", ' ') = ["a", "b", "c"] * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", ""] * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", "", ""] * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", a", "b", "c"] * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", "", a", "b", "c"] * StringUtils.splitPreserveAllTokens(" a b c ", ' ') = ["", a", "b", "c", ""] * </pre> * * @param str the String to parse, may be <code>null</code> * @param separatorChar the character used as the delimiter, * <code>null</code> splits on whitespace * @return an array of parsed Strings, <code>null</code> if null String input * @since 2.1 */ public static String[] splitPreserveAllTokens(String str, char separatorChar) { return splitWorker(str, separatorChar, true); } /** * Performs the logic for the <code>split</code> and * <code>splitPreserveAllTokens</code> methods that do not return a * maximum array length. * * @param str the String to parse, may be <code>null</code> * @param separatorChar the separate character * @param preserveAllTokens if <code>true</code>, adjacent separators are * treated as empty token separators; if <code>false</code>, adjacent * separators are treated as one separator. * @return an array of parsed Strings, <code>null</code> if null String input */ @SuppressWarnings("unchecked") private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) { // Performance tuned for 2.0 (JDK1.4) if (str == null) { return null; } int len = str.length(); if (len == 0) { return EMPTY_STRING_ARRAY; } List list = new ArrayList(); int i = 0, start = 0; boolean match = false; boolean lastMatch = false; while (i < len) { if (str.charAt(i) == separatorChar) { if (match || preserveAllTokens) { list.add(str.substring(start, i)); match = false; lastMatch = true; } start = ++i; continue; } lastMatch = false; match = true; i++; } if (match || (preserveAllTokens && lastMatch)) { list.add(str.substring(start, i)); } return (String[]) list.toArray(new String[list.size()]); } /** * <p>Splits the provided text into an array, separators specified, * preserving all tokens, including empty tokens created by adjacent * separators. This is an alternative to using StringTokenizer.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as separators for empty tokens. * For more control over the split use the StrTokenizer class.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * A <code>null</code> separatorChars splits on whitespace.</p> * * <pre> * StringUtils.splitPreserveAllTokens(null, *) = null * StringUtils.splitPreserveAllTokens("", *) = [] * StringUtils.splitPreserveAllTokens("abc def", null) = ["abc", "def"] * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "def"] * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "", def"] * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"] * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":") = ["ab", "cd", "ef", ""] * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""] * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":") = ["ab", "", cd", "ef"] * StringUtils.splitPreserveAllTokens(":cd:ef", ":") = ["", cd", "ef"] * StringUtils.splitPreserveAllTokens("::cd:ef", ":") = ["", "", cd", "ef"] * StringUtils.splitPreserveAllTokens(":cd:ef:", ":") = ["", cd", "ef", ""] * </pre> * * @param str the String to parse, may be <code>null</code> * @param separatorChars the characters used as the delimiters, * <code>null</code> splits on whitespace * @return an array of parsed Strings, <code>null</code> if null String input * @since 2.1 */ public static String[] splitPreserveAllTokens(String str, String separatorChars) { return splitWorker(str, separatorChars, -1, true); } /** * <p>Splits the provided text into an array with a maximum length, * separators specified, preserving all tokens, including empty tokens * created by adjacent separators.</p> * * <p>The separator is not included in the returned String array. * Adjacent separators are treated as separators for empty tokens. * Adjacent separators are treated as one separator.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * A <code>null</code> separatorChars splits on whitespace.</p> * * <p>If more than <code>max</code> delimited substrings are found, the last * returned string includes all characters after the first <code>max - 1</code> * returned strings (including separator characters).</p> * * <pre> * StringUtils.splitPreserveAllTokens(null, *, *) = null * StringUtils.splitPreserveAllTokens("", *, *) = [] * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"] * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"] * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"] * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2) = ["ab", "cd:ef"] * StringUtils.splitPreserveAllTokens("ab de fg", null, 2) = ["ab", " de fg"] * StringUtils.splitPreserveAllTokens("ab de fg", null, 3) = ["ab", "", " de fg"] * StringUtils.splitPreserveAllTokens("ab de fg", null, 4) = ["ab", "", "", "de fg"] * </pre> * * @param str the String to parse, may be <code>null</code> * @param separatorChars the characters used as the delimiters, * <code>null</code> splits on whitespace * @param max the maximum number of elements to include in the * array. A zero or negative value implies no limit * @return an array of parsed Strings, <code>null</code> if null String input * @since 2.1 */ public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) { return splitWorker(str, separatorChars, max, true); } /** * Performs the logic for the <code>split</code> and * <code>splitPreserveAllTokens</code> methods that return a maximum array * length. * * @param str the String to parse, may be <code>null</code> * @param separatorChars the separate character * @param max the maximum number of elements to include in the * array. A zero or negative value implies no limit. * @param preserveAllTokens if <code>true</code>, adjacent separators are * treated as empty token separators; if <code>false</code>, adjacent * separators are treated as one separator. * @return an array of parsed Strings, <code>null</code> if null String input */ @SuppressWarnings("unchecked") private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) { // Performance tuned for 2.0 (JDK1.4) // Direct code is quicker than StringTokenizer. // Also, StringTokenizer uses isSpace() not isWhitespace() if (str == null) { return null; } int len = str.length(); if (len == 0) { return EMPTY_STRING_ARRAY; } List list = new ArrayList(); int sizePlus1 = 1; int i = 0, start = 0; boolean match = false; boolean lastMatch = false; if (separatorChars == null) { // Null separator means use whitespace while (i < len) { if (Character.isWhitespace(str.charAt(i))) { if (match || preserveAllTokens) { lastMatch = true; if (sizePlus1++ == max) { i = len; lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } lastMatch = false; match = true; i++; } } else if (separatorChars.length() == 1) { // Optimise 1 character case char sep = separatorChars.charAt(0); while (i < len) { if (str.charAt(i) == sep) { if (match || preserveAllTokens) { lastMatch = true; if (sizePlus1++ == max) { i = len; lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } lastMatch = false; match = true; i++; } } else { // standard case while (i < len) { if (separatorChars.indexOf(str.charAt(i)) >= 0) { if (match || preserveAllTokens) { lastMatch = true; if (sizePlus1++ == max) { i = len; lastMatch = false; } list.add(str.substring(start, i)); match = false; } start = ++i; continue; } lastMatch = false; match = true; i++; } } if (match || (preserveAllTokens && lastMatch)) { list.add(str.substring(start, i)); } return (String[]) list.toArray(new String[list.size()]); } /** * <p>Joins the elements of the provided array into a single String * containing the provided list of elements.</p> * * <p>No separator is added to the joined String. * Null objects or empty strings within the array are represented by * empty strings.</p> * * <pre> * StringUtils.join(null) = null * StringUtils.join([]) = "" * StringUtils.join([null]) = "" * StringUtils.join(["a", "b", "c"]) = "abc" * StringUtils.join([null, "", "a"]) = "a" * </pre> * * @param array the array of values to join together, may be null * @return the joined String, <code>null</code> if null array input * @since 2.0 */ public static String join(Object[] array) { return join(array, null); } /** * <p>Joins the elements of the provided array into a single String * containing the provided list of elements.</p> * * <p>No delimiter is added before or after the list. * Null objects or empty strings within the array are represented by * empty strings.</p> * * <pre> * 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" * </pre> * * @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</code> if null array input * @since 2.0 */ public static String join(Object[] array, char separator) { if (array == null) { return null; } int arraySize = array.length; int bufSize = (arraySize == 0 ? 0 : ((array[0] == null ? 16 : array[0].toString().length()) + 1) * arraySize); StringBuffer buf = new StringBuffer(bufSize); for (int i = 0; i < arraySize; i++) { if (i > 0) { buf.append(separator); } if (array[i] != null) { buf.append(array[i]); } } return buf.toString(); } /** * <p>Joins the elements of the provided array into a single String * containing the provided list of elements.</p> * * <p>No delimiter is added before or after the list. * A <code>null</code> separator is the same as an empty String (""). * Null objects or empty strings within the array are represented by * empty strings.</p> * * <pre> * 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" * </pre> * * @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</code> if null array input */ public static String join(Object[] array, String separator) { if (array == null) { return null; } if (separator == null) { separator = ""; } int arraySize = array.length; // ArraySize == 0: Len = 0 // ArraySize > 0: Len = NofStrings *(len(firstString) + len(separator)) // (Assuming that all Strings are roughly equally long) int bufSize = ((arraySize == 0) ? 0 : arraySize * ((array[0] == null ? 16 : array[0].toString().length()) + separator.length())); StringBuffer buf = new StringBuffer(bufSize); for (int i = 0; i < arraySize; i++) { if (i > 0) { buf.append(separator); } if (array[i] != null) { buf.append(array[i]); } } return buf.toString(); } /** * <p>Joins the elements of the provided <code>Iterator</code> into * a single String containing the provided elements.</p> * * <p>No delimiter is added before or after the list. Null objects or empty * strings within the iteration are represented by empty strings.</p> * * <p>See the examples here: {@link #join(Object[],char)}. </p> * * @param iterator the <code>Iterator</code> of values to join together, may be null * @param separator the separator character to use * @return the joined String, <code>null</code> if null iterator input * @since 2.0 */ public static String join(Iterator iterator, char separator) { if (iterator == null) { return null; } StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small while (iterator.hasNext()) { Object obj = iterator.next(); if (obj != null) { buf.append(obj); } if (iterator.hasNext()) { buf.append(separator); } } return buf.toString(); } /** * <p>Joins the elements of the provided <code>Iterator</code> into * a single String containing the provided elements.</p> * * <p>No delimiter is added before or after the list. * A <code>null</code> separator is the same as an empty String ("").</p> * * <p>See the examples here: {@link #join(Object[],String)}. </p> * * @param iterator the <code>Iterator</code> of values to join together, may be null * @param separator the separator character to use, null treated as "" * @return the joined String, <code>null</code> if null iterator input */ public static String join(Iterator iterator, String separator) { if (iterator == null) { return null; } StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small while (iterator.hasNext()) { Object obj = iterator.next(); if (obj != null) { buf.append(obj); } if ((separator != null) && iterator.hasNext()) { buf.append(separator); } } return buf.toString(); } /** * <p>Returns either the passed in String, * or if the String is <code>null</code>, an empty String ("").</p> * * <pre> * StringUtils.defaultString(null) = "" * StringUtils.defaultString("") = "" * StringUtils.defaultString("bat") = "bat" * </pre> * * @see String#valueOf(Object) * @param str the String to check, may be null * @return the passed in String, or the empty String if it * was <code>null</code> */ public static String defaultString(String str) { return str == null ? "" : str; } /** * <p>Returns either the passed in String, or if the String is * <code>null</code>, the value of <code>defaultStr</code>.</p> * * <pre> * StringUtils.defaultString(null, "NULL") = "NULL" * StringUtils.defaultString("", "NULL") = "" * StringUtils.defaultString("bat", "NULL") = "bat" * </pre> * * @see String#valueOf(Object) * @param str the String to check, may be null * @param defaultStr the default String to return * if the input is <code>null</code>, may be null * @return the passed in String, or the default if it was <code>null</code> */ public static String defaultString(String str, String defaultStr) { return str == null ? defaultStr : str; } /** * <p>Returns either the passed in String, or if the String is * empty or <code>null</code>, the value of <code>defaultStr</code>.</p> * * <pre> * StringUtils.defaultIfEmpty(null, "NULL") = "NULL" * StringUtils.defaultIfEmpty("", "NULL") = "NULL" * StringUtils.defaultIfEmpty("bat", "NULL") = "bat" * </pre> * * @param str the String to check, may be null * @param defaultStr the default String to return * if the input is empty ("") or <code>null</code>, may be null * @return the passed in String, or the default */ public static String defaultIfEmpty(String str, String defaultStr) { return isEmpty(str) ? defaultStr : str; } /** * <p>Capitalizes a String changing the first letter to title case as * per {@link Character#toTitleCase(char)}. No other letters are changed.</p> * * A <code>null</code> input String returns <code>null</code>.</p> * * <pre> * StringUtils.capitalize(null) = null * StringUtils.capitalize("") = "" * StringUtils.capitalize("cat") = "Cat" * StringUtils.capitalize("cAt") = "CAt" * </pre> * * @param str the String to capitalize, may be null * @return the capitalized String, <code>null</code> if null String input * @since 2.0 */ public static String capitalize(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } return new StringBuffer(strLen).append(Character.toTitleCase(str.charAt(0))).append(str.substring(1)) .toString(); } /** * <p>Checks if String contains a search character, handling <code>null</code>. * This method uses {@link String#indexOf(int)}.</p> * * <p>A <code>null</code> or empty ("") String will return <code>false</code>.</p> * * <pre> * StringUtils.contains(null, *) = false * StringUtils.contains("", *) = false * StringUtils.contains("abc", 'a') = true * StringUtils.contains("abc", 'z') = false * </pre> * * @param str the String to check, may be null * @param searchChar the character to find * @return true if the String contains the search character, * false if not or <code>null</code> string input * @since 2.0 */ public static boolean contains(String str, char searchChar) { if (isEmpty(str)) { return false; } return str.indexOf(searchChar) >= 0; } /** * <p>Checks if String contains a search String, handling <code>null</code>. * This method uses {@link String#indexOf(int)}.</p> * * <p>A <code>null</code> String will return <code>false</code>.</p> * * <pre> * StringUtils.contains(null, *) = false * StringUtils.contains(*, null) = false * StringUtils.contains("", "") = true * StringUtils.contains("abc", "") = true * StringUtils.contains("abc", "a") = true * StringUtils.contains("abc", "z") = false * </pre> * * @param str the String to check, may be null * @param searchStr the String to find, may be null * @return true if the String contains the search String, * false if not or <code>null</code> string input * @since 2.0 */ public static boolean contains(String str, String searchStr) { if (str == null || searchStr == null) { return false; } return str.indexOf(searchStr) >= 0; } /** * An empty immutable <code>String</code> array. */ public static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * <p>Compares two objects for equality, where either one or both * objects may be <code>null</code>.</p> * * <pre> * ObjectUtils.equals(null, null) = true * ObjectUtils.equals(null, "") = false * ObjectUtils.equals("", null) = false * ObjectUtils.equals("", "") = true * ObjectUtils.equals(Boolean.TRUE, null) = false * ObjectUtils.equals(Boolean.TRUE, "true") = false * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE) = true * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false * </pre> * * @param object1 the first object, may be <code>null</code> * @param object2 the second object, may be <code>null</code> * @return <code>true</code> if the values of both objects are the same */ public static boolean equals(Object object1, Object object2) { if (object1 == object2) { return true; } if ((object1 == null) || (object2 == null)) { return false; } return object1.equals(object2); } /** * An empty immutable <code>Object</code> array. */ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; /** * strip the last slash (/ or \) from a string if it exists * * @param input * * @return input - the last / or \ */ public static String stripLastSlashIfExists(String input) { if ((input == null) || (input.length() == 0)) { return null; } char lastChar = input.charAt(input.length() - 1); if ((lastChar == '\\') || (lastChar == '/')) { return input.substring(0, input.length() - 1); } return input; } /** * <p>Strips any of a set of characters from the start of a String.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * An empty string ("") input returns the empty string.</p> * * <p>If the stripChars String is <code>null</code>, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.</p> * * <pre> * StringUtils.stripStart(null, *) = null * StringUtils.stripStart("", *) = "" * StringUtils.stripStart("abc", "") = "abc" * StringUtils.stripStart("abc", null) = "abc" * StringUtils.stripStart(" abc", null) = "abc" * StringUtils.stripStart("abc ", null) = "abc " * StringUtils.stripStart(" abc ", null) = "abc " * StringUtils.stripStart("yxabc ", "xyz") = "abc " * </pre> * * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, <code>null</code> if null String input */ public static String stripStart(String str, String stripChars) { int strLen; if (str == null || (strLen = str.length()) == 0) { return str; } int start = 0; if (stripChars == null) { while ((start != strLen) && Character.isWhitespace(str.charAt(start))) { start++; } } else if (stripChars.length() == 0) { return str; } else { while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != -1)) { start++; } } return str.substring(start); } /** * <p>Strips any of a set of characters from the end of a String.</p> * * <p>A <code>null</code> input String returns <code>null</code>. * An empty string ("") input returns the empty string.</p> * * <p>If the stripChars String is <code>null</code>, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.</p> * * <pre> * StringUtils.stripEnd(null, *) = null * StringUtils.stripEnd("", *) = "" * StringUtils.stripEnd("abc", "") = "abc" * StringUtils.stripEnd("abc", null) = "abc" * StringUtils.stripEnd(" abc", null) = " abc" * StringUtils.stripEnd("abc ", null) = "abc" * StringUtils.stripEnd(" abc ", null) = " abc" * StringUtils.stripEnd(" abcyx", "xyz") = " abc" * </pre> * * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, <code>null</code> if null String input */ public static String stripEnd(String str, String stripChars) { int end; if (str == null || (end = str.length()) == 0) { return str; } if (stripChars == null) { while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) { end--; } } else if (stripChars.length() == 0) { return str; } else { while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != -1)) { end--; } } return str.substring(0, end); } /** * The empty String <code>""</code>. * @since 2.0 */ public static final String EMPTY = ""; /** * Represents a failed index search. * @since 2.1 */ public static final int INDEX_NOT_FOUND = -1; /** * <p>The maximum size to which the padding constant(s) can expand.</p> */ private static final int PAD_LIMIT = 8192; /** * <p>An array of <code>String</code>s used for padding.</p> * * <p>Used for efficient space padding. The length of each String expands as needed.</p> */ private static final String[] PADDING = new String[Character.MAX_VALUE]; static { // space padding is most common, start with 64 chars PADDING[32] = " "; } /** * <p>Repeat a String <code>repeat</code> times to form a * new String.</p> * * <pre> * StringUtils.repeat(null, 2) = null * StringUtils.repeat("", 0) = "" * StringUtils.repeat("", 2) = "" * StringUtils.repeat("a", 3) = "aaa" * StringUtils.repeat("ab", 2) = "abab" * StringUtils.repeat("a", -2) = "" * </pre> * * @param str the String to repeat, may be null * @param repeat number of times to repeat str, negative treated as zero * @return a new String consisting of the original String repeated, * <code>null</code> if null String input */ public static String repeat(String str, int repeat) { // Performance tuned for 2.0 (JDK1.4) if (str == null) { return null; } if (repeat <= 0) { return EMPTY; } int inputLength = str.length(); if (repeat == 1 || inputLength == 0) { return str; } if (inputLength == 1 && repeat <= PAD_LIMIT) { return padding(repeat, str.charAt(0)); } int outputLength = inputLength * repeat; switch (inputLength) { case 1: char ch = str.charAt(0); char[] output1 = new char[outputLength]; for (int i = repeat - 1; i >= 0; i--) { output1[i] = ch; } return new String(output1); case 2: char ch0 = str.charAt(0); char ch1 = str.charAt(1); char[] output2 = new char[outputLength]; for (int i = repeat * 2 - 2; i >= 0; i--, i--) { output2[i] = ch0; output2[i + 1] = ch1; } return new String(output2); default: StringBuffer buf = new StringBuffer(outputLength); for (int i = 0; i < repeat; i++) { buf.append(str); } return buf.toString(); } } /** * <p>Returns padding using the specified delimiter repeated * to a given length.</p> * * <pre> * StringUtils.padding(0, 'e') = "" * StringUtils.padding(3, 'e') = "eee" * StringUtils.padding(-2, 'e') = IndexOutOfBoundsException * </pre> * * @param repeat number of times to repeat delim * @param padChar character to repeat * @return String with repeated character * @throws IndexOutOfBoundsException if <code>repeat < 0</code> */ private static String padding(int repeat, char padChar) { // be careful of synchronization in this method // we are assuming that get and set from an array index is atomic String pad = PADDING[padChar]; if (pad == null) { pad = String.valueOf(padChar); } while (pad.length() < repeat) { pad = pad.concat(pad); } PADDING[padChar] = pad; return pad.substring(0, repeat); } /** * <p>Right pad a String with spaces (' ').</p> * * <p>The String is padded to the size of <code>size</code>.</p> * * <pre> * StringUtils.rightPad(null, *) = null * StringUtils.rightPad("", 3) = " " * StringUtils.rightPad("bat", 3) = "bat" * StringUtils.rightPad("bat", 5) = "bat " * StringUtils.rightPad("bat", 1) = "bat" * StringUtils.rightPad("bat", -1) = "bat" * </pre> * * @param str the String to pad out, may be null * @param size the size to pad to * @return right padded String or original String if no padding is necessary, * <code>null</code> if null String input */ public static String rightPad(String str, int size) { return rightPad(str, size, ' '); } /** * <p>Right pad a String with a specified character.</p> * * <p>The String is padded to the size of <code>size</code>.</p> * * <pre> * StringUtils.rightPad(null, *, *) = null * StringUtils.rightPad("", 3, 'z') = "zzz" * StringUtils.rightPad("bat", 3, 'z') = "bat" * StringUtils.rightPad("bat", 5, 'z') = "batzz" * StringUtils.rightPad("bat", 1, 'z') = "bat" * StringUtils.rightPad("bat", -1, 'z') = "bat" * </pre> * * @param str the String to pad out, may be null * @param size the size to pad to * @param padChar the character to pad with * @return right padded String or original String if no padding is necessary, * <code>null</code> if null String input * @since 2.0 */ public static String rightPad(String str, int size, char padChar) { if (str == null) { return null; } int pads = size - str.length(); if (pads <= 0) { return str; // returns original String when possible } if (pads > PAD_LIMIT) { return rightPad(str, size, String.valueOf(padChar)); } return str.concat(padding(pads, padChar)); } /** * <p>Right pad a String with a specified String.</p> * * <p>The String is padded to the size of <code>size</code>.</p> * * <pre> * StringUtils.rightPad(null, *, *) = null * StringUtils.rightPad("", 3, "z") = "zzz" * StringUtils.rightPad("bat", 3, "yz") = "bat" * StringUtils.rightPad("bat", 5, "yz") = "batyz" * StringUtils.rightPad("bat", 8, "yz") = "batyzyzy" * StringUtils.rightPad("bat", 1, "yz") = "bat" * StringUtils.rightPad("bat", -1, "yz") = "bat" * StringUtils.rightPad("bat", 5, null) = "bat " * StringUtils.rightPad("bat", 5, "") = "bat " * </pre> * * @param str the String to pad out, may be null * @param size the size to pad to * @param padStr the String to pad with, null or empty treated as single space * @return right padded String or original String if no padding is necessary, * <code>null</code> if null String input */ public static String rightPad(String str, int size, String padStr) { if (str == null) { return null; } if (isEmpty(padStr)) { padStr = " "; } int padLen = padStr.length(); int strLen = str.length(); int pads = size - strLen; if (pads <= 0) { return str; // returns original String when possible } if (padLen == 1 && pads <= PAD_LIMIT) { return rightPad(str, size, padStr.charAt(0)); } if (pads == padLen) { return str.concat(padStr); } else if (pads < padLen) { return str.concat(padStr.substring(0, pads)); } else { char[] padding = new char[pads]; char[] padChars = padStr.toCharArray(); for (int i = 0; i < pads; i++) { padding[i] = padChars[i % padLen]; } return str.concat(new String(padding)); } } /** * <p>Left pad a String with spaces (' ').</p> * * <p>The String is padded to the size of <code>size<code>.</p> * * <pre> * StringUtils.leftPad(null, *) = null * StringUtils.leftPad("", 3) = " " * StringUtils.leftPad("bat", 3) = "bat" * StringUtils.leftPad("bat", 5) = " bat" * StringUtils.leftPad("bat", 1) = "bat" * StringUtils.leftPad("bat", -1) = "bat" * </pre> * * @param str the String to pad out, may be null * @param size the size to pad to * @return left padded String or original String if no padding is necessary, * <code>null</code> if null String input */ public static String leftPad(String str, int size) { return leftPad(str, size, ' '); } /** * <p>Left pad a String with a specified character.</p> * * <p>Pad to a size of <code>size</code>.</p> * * <pre> * StringUtils.leftPad(null, *, *) = null * StringUtils.leftPad("", 3, 'z') = "zzz" * StringUtils.leftPad("bat", 3, 'z') = "bat" * StringUtils.leftPad("bat", 5, 'z') = "zzbat" * StringUtils.leftPad("bat", 1, 'z') = "bat" * StringUtils.leftPad("bat", -1, 'z') = "bat" * </pre> * * @param str the String to pad out, may be null * @param size the size to pad to * @param padChar the character to pad with * @return left padded String or original String if no padding is necessary, * <code>null</code> if null String input * @since 2.0 */ public static String leftPad(String str, int size, char padChar) { if (str == null) { return null; } int pads = size - str.length(); if (pads <= 0) { return str; // returns original String when possible } if (pads > PAD_LIMIT) { return leftPad(str, size, String.valueOf(padChar)); } return padding(pads, padChar).concat(str); } /** * <p>Left pad a String with a specified String.</p> * * <p>Pad to a size of <code>size</code>.</p> * * <pre> * StringUtils.leftPad(null, *, *) = null * StringUtils.leftPad("", 3, "z") = "zzz" * StringUtils.leftPad("bat", 3, "yz") = "bat" * StringUtils.leftPad("bat", 5, "yz") = "yzbat" * StringUtils.leftPad("bat", 8, "yz") = "yzyzybat" * StringUtils.leftPad("bat", 1, "yz") = "bat" * StringUtils.leftPad("bat", -1, "yz") = "bat" * StringUtils.leftPad("bat", 5, null) = " bat" * StringUtils.leftPad("bat", 5, "") = " bat" * </pre> * * @param str the String to pad out, may be null * @param size the size to pad to * @param padStr the String to pad with, null or empty treated as single space * @return left padded String or original String if no padding is necessary, * <code>null</code> if null String input */ public static String leftPad(String str, int size, String padStr) { if (str == null) { return null; } if (isEmpty(padStr)) { padStr = " "; } int padLen = padStr.length(); int strLen = str.length(); int pads = size - strLen; if (pads <= 0) { return str; // returns original String when possible } if (padLen == 1 && pads <= PAD_LIMIT) { return leftPad(str, size, padStr.charAt(0)); } if (pads == padLen) { return padStr.concat(str); } else if (pads < padLen) { return padStr.substring(0, pads).concat(str); } else { char[] padding = new char[pads]; char[] padChars = padStr.toCharArray(); for (int i = 0; i < pads; i++) { padding[i] = padChars[i % padLen]; } return new String(padding).concat(str); } } /** * <p>Gets the substring before the first occurrence of a separator. * The separator is not returned.</p> * * <p>A <code>null</code> string input will return <code>null</code>. * An empty ("") string input will return the empty string. * A <code>null</code> separator will return the input string.</p> * * <pre> * StringUtils.substringBefore(null, *) = null * StringUtils.substringBefore("", *) = "" * StringUtils.substringBefore("abc", "a") = "" * StringUtils.substringBefore("abcba", "b") = "a" * StringUtils.substringBefore("abc", "c") = "ab" * StringUtils.substringBefore("abc", "d") = "abc" * StringUtils.substringBefore("abc", "") = "" * StringUtils.substringBefore("abc", null) = "abc" * </pre> * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring before the first occurrence of the separator, * <code>null</code> if null String input * @since 2.0 */ public static String substringBefore(String str, String separator) { if (isEmpty(str) || separator == null) { return str; } if (separator.length() == 0) { return EMPTY; } int pos = str.indexOf(separator); if (pos == -1) { return str; } return str.substring(0, pos); } /** * <p>Gets the substring after the first occurrence of a separator. * The separator is not returned.</p> * * <p>A <code>null</code> string input will return <code>null</code>. * An empty ("") string input will return the empty string. * A <code>null</code> separator will return the empty string if the * input string is not <code>null</code>.</p> * * <pre> * StringUtils.substringAfter(null, *) = null * StringUtils.substringAfter("", *) = "" * StringUtils.substringAfter(*, null) = "" * StringUtils.substringAfter("abc", "a") = "bc" * StringUtils.substringAfter("abcba", "b") = "cba" * StringUtils.substringAfter("abc", "c") = "" * StringUtils.substringAfter("abc", "d") = "" * StringUtils.substringAfter("abc", "") = "abc" * </pre> * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring after the first occurrence of the separator, * <code>null</code> if null String input * @since 2.0 */ public static String substringAfter(String str, String separator) { if (isEmpty(str)) { return str; } if (separator == null) { return EMPTY; } int pos = str.indexOf(separator); if (pos == -1) { return EMPTY; } return str.substring(pos + separator.length()); } /** * <p>Gets the substring before the last occurrence of a separator. * The separator is not returned.</p> * * <p>A <code>null</code> string input will return <code>null</code>. * An empty ("") string input will return the empty string. * An empty or <code>null</code> separator will return the input string.</p> * * <pre> * StringUtils.substringBeforeLast(null, *) = null * StringUtils.substringBeforeLast("", *) = "" * StringUtils.substringBeforeLast("abcba", "b") = "abc" * StringUtils.substringBeforeLast("abc", "c") = "ab" * StringUtils.substringBeforeLast("a", "a") = "" * StringUtils.substringBeforeLast("a", "z") = "a" * StringUtils.substringBeforeLast("a", null) = "a" * StringUtils.substringBeforeLast("a", "") = "a" * </pre> * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring before the last occurrence of the separator, * <code>null</code> if null String input * @since 2.0 */ public static String substringBeforeLast(String str, String separator) { if (isEmpty(str) || isEmpty(separator)) { return str; } int pos = str.lastIndexOf(separator); if (pos == -1) { return str; } return str.substring(0, pos); } /** * <p>Gets the substring after the last occurrence of a separator. * The separator is not returned.</p> * * <p>A <code>null</code> string input will return <code>null</code>. * An empty ("") string input will return the empty string. * An empty or <code>null</code> separator will return the empty string if * the input string is not <code>null</code>.</p> * * <pre> * StringUtils.substringAfterLast(null, *) = null * StringUtils.substringAfterLast("", *) = "" * StringUtils.substringAfterLast(*, "") = "" * StringUtils.substringAfterLast(*, null) = "" * StringUtils.substringAfterLast("abc", "a") = "bc" * StringUtils.substringAfterLast("abcba", "b") = "a" * StringUtils.substringAfterLast("abc", "c") = "" * StringUtils.substringAfterLast("a", "a") = "" * StringUtils.substringAfterLast("a", "z") = "" * </pre> * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring after the last occurrence of the separator, * <code>null</code> if null String input * @since 2.0 */ public static String substringAfterLast(String str, String separator) { if (isEmpty(str)) { return str; } if (isEmpty(separator)) { return EMPTY; } int pos = str.lastIndexOf(separator); if (pos == -1 || pos == (str.length() - separator.length())) { return EMPTY; } return str.substring(pos + separator.length()); } /** * Return the zero element of the list, if it exists, null if the list is empty. * If there is more than one element in the list, an exception is thrown. * @param <T> * @param list is the container of objects to get the first of. * @return the first object, null, or exception. */ public static <T> T listPopOne(List<T> list) { int size = length(list); if (size == 1) { return list.get(0); } else if (size == 0) { return null; } throw new RuntimeException("More than one object of type " + className(list.get(0)) + " was returned when only one was expected. (size:" + size + ")"); } /** * Return the zero element of the set, if it exists, null if the list is empty. * If there is more than one element in the list, an exception is thrown. * @param <T> * @param set is the container of objects to get the first of. * @return the first object, null, or exception. */ public static <T> T setPopOne(Set<T> set) { int size = length(set); if (size == 1) { return set.iterator().next(); } else if (size == 0) { return null; } throw new RuntimeException("More than one object of type " + className(set.iterator().next()) + " was returned when only one was expected. (size:" + size + ")"); } /** * Return the zero element of the list, if it exists, null if the list is empty. * If there is more than one element in the list, an exception is thrown. * @param <T> * @param collection is the container of objects to get the first of. * @param exceptionIfMoreThanOne will throw exception if there is more than one item in list * @return the first object, null, or exception. */ public static <T> T collectionPopOne(Collection<T> collection, boolean exceptionIfMoreThanOne) { int size = length(collection); if (size > 1 && exceptionIfMoreThanOne) { throw new RuntimeException("More than one object of type " + className(get(collection, 0)) + " was returned when only one was expected. (size:" + size + ")"); } if (size == 0) { return null; } return collection.iterator().next(); } /** * Convert an XML string to HTML to display on the screen * * @param input * is the XML to convert * @param isEscape true to escape chars, false to unescape * * @return the HTML converted string */ public static String xmlEscape(String input, boolean isEscape) { if (isEscape) { return replace(input, XML_SEARCH_NO_SINGLE, XML_REPLACE_NO_SINGLE); } return replace(input, XML_REPLACE_NO_SINGLE, XML_SEARCH_NO_SINGLE); } /** * if theString is not blank, apppend it to the result, and if appending, * @param result to append to * add a prefix and suffix (if not null) * @param theStringOrArrayOrList is a string, array, list, or set of strings * @return true if something appended, false if not */ public static boolean appendIfNotBlank(StringBuilder result, Object theStringOrArrayOrList) { return appendIfNotBlank(result, null, theStringOrArrayOrList, null); } /** * <pre> * append a string to another string if both not blank, with separator. trim to empty everything * </pre> * @param string * @param separator * @param suffix * @return the resulting string or blank if nothing */ public static String appendIfNotBlankString(String string, String separator, String suffix) { string = StringUtils.trimToEmpty(string); suffix = StringUtils.trimToEmpty(suffix); boolean stringIsBlank = StringUtils.isBlank(string); boolean suffixIsBlank = StringUtils.isBlank(suffix); if (stringIsBlank && suffixIsBlank) { return ""; } if (stringIsBlank) { return suffix; } if (suffixIsBlank) { return string; } return string + separator + suffix; } /** * <pre> * append a prefix to another string if both not blank. trim to empty everything except separator * i.e. appendPrefixIfStringNotBlank("[]", " - ", "a") returns [] - a * i.e. appendPrefixIfStringNotBlank("", " - ", "a") returns a * i.e. appendPrefixIfStringNotBlank("[]", " - ", "") returns "" * i.e. appendPrefixIfStringNotBlank("", " - ", "") returns "" * </pre> * @param prefix * @param separator * @param string * @return the resulting string or blank if nothing */ public static String appendPrefixIfStringNotBlank(String prefix, String separator, String string) { string = StringUtils.trimToEmpty(string); prefix = StringUtils.trimToEmpty(prefix); boolean stringIsBlank = StringUtils.isBlank(string); boolean prefixIsBlank = StringUtils.isBlank(prefix); if (stringIsBlank) { return ""; } if (prefixIsBlank) { return string; } return prefix + separator + string; } /** * if theString is not Blank, apppend it to the result, and if appending, * add a prefix (if not null) * @param result to append to * @param prefix * @param theStringOrArrayOrList is a string, array, list, or set of strings * @return true if something appended, false if not */ public static boolean appendIfNotBlank(StringBuilder result, String prefix, Object theStringOrArrayOrList) { return appendIfNotBlank(result, prefix, theStringOrArrayOrList, null); } /** * if theString is not Blank, apppend it to the result, and if appending, * add a prefix and suffix (if not null) * @param result to append to, assumed to be not null * @param prefix * @param theStringOrArrayOrList is a string, array, list, or set of strings * @param suffix * @return true if anything appended, false if not */ public static boolean appendIfNotBlank(StringBuilder result, String prefix, Object theStringOrArrayOrList, String suffix) { return appendIfNotBlank(result, prefix, null, theStringOrArrayOrList, suffix); } /** * if theString is not Blank, apppend it to the result, and if appending, * add a prefix and suffix (if not null) * @param result to append to, assumed to be not null * @param prefix prepend this prefix always (when a result not empty). Will be after the other prefix * @param prefixIfNotBlank prepend this prefix if the result is not empty * @param theStringOrArrayOrList is a string, array, list, or set of strings * @param suffix * @return true if anything appended, false if not */ public static boolean appendIfNotBlank(StringBuilder result, String prefix, String prefixIfNotBlank, Object theStringOrArrayOrList, String suffix) { int length = length(theStringOrArrayOrList); Iterator iterator = iterator(theStringOrArrayOrList); boolean appendedAnything = false; //these could be appending spaces, so only check to see if they are empty boolean hasPrefix = !StringUtils.isEmpty(prefix); boolean hasPrefixIfNotBlank = !StringUtils.isEmpty(prefixIfNotBlank); boolean hasSuffix = !StringUtils.isEmpty(suffix); for (int i = 0; i < length; i++) { String current = (String) next(theStringOrArrayOrList, iterator, i); //only append if not empty if (!StringUtils.isBlank(current)) { //keeping track if anything changed appendedAnything = true; if (hasPrefix) { result.append(prefix); } if (hasPrefixIfNotBlank && result.length() > 0) { result.append(prefixIfNotBlank); } result.append(current); if (hasSuffix) { result.append(suffix); } } } return appendedAnything; } /** * if theString is not empty, apppend it to the result, and if appending, * @param result to append to * add a prefix and suffix (if not null) * @param theStringOrArrayOrList is a string, array, list, or set of strings * @return true if something appended, false if not */ public static boolean appendIfNotEmpty(StringBuilder result, Object theStringOrArrayOrList) { return appendIfNotEmpty(result, null, theStringOrArrayOrList, null); } /** * if theString is not empty, apppend it to the result, and if appending, * add a prefix (if not null) * @param result to append to * @param prefix * @param theStringOrArrayOrList is a string, array, list, or set of strings * @return true if something appended, false if not */ public static boolean appendIfNotEmpty(StringBuilder result, String prefix, Object theStringOrArrayOrList) { return appendIfNotEmpty(result, prefix, theStringOrArrayOrList, null); } /** * if theString is not empty, apppend it to the result, and if appending, * add a prefix and suffix (if not null) * @param result to append to, assumed to be not null * @param prefix * @param theStringOrArrayOrList is a string, array, list, or set of strings * @param suffix * @return true if anything appended, false if not */ public static boolean appendIfNotEmpty(StringBuilder result, String prefix, Object theStringOrArrayOrList, String suffix) { return appendIfNotEmpty(result, prefix, null, theStringOrArrayOrList, suffix); } /** * if theString is not empty, apppend it to the result, and if appending, * add a prefix and suffix (if not null) * @param result to append to, assumed to be not null * @param prefix prepend this prefix always (when a result not empty). Will be after the other prefix * @param prefixIfNotEmpty prepend this prefix if the result is not empty * @param theStringOrArrayOrList is a string, array, list, or set of strings * @param suffix * @return true if anything appended, false if not */ public static boolean appendIfNotEmpty(StringBuilder result, String prefix, String prefixIfNotEmpty, Object theStringOrArrayOrList, String suffix) { int length = length(theStringOrArrayOrList); Iterator iterator = iterator(theStringOrArrayOrList); boolean appendedAnything = false; boolean hasPrefix = !StringUtils.isEmpty(prefix); boolean hasPrefixIfNotEmpty = !StringUtils.isEmpty(prefixIfNotEmpty); boolean hasSuffix = !StringUtils.isEmpty(suffix); for (int i = 0; i < length; i++) { String current = (String) next(theStringOrArrayOrList, iterator, i); //only append if not empty if (!StringUtils.isEmpty(current)) { //keeping track if anything changed appendedAnything = true; if (hasPrefix) { result.append(prefix); } if (hasPrefixIfNotEmpty && result.length() > 0) { result.append(prefixIfNotEmpty); } result.append(current); if (hasSuffix) { result.append(suffix); } } } return appendedAnything; } /** * <p>Find the index of the given object in the array.</p> * * <p>This method returns <code>-1</code> if <code>null</code> array input.</p> * * @param array the array to search through for the object, may be <code>null</code> * @param objectToFind the object to find, may be <code>null</code> * @return the index of the object within the array, * <code>-1</code> if not found or <code>null</code> array input */ public static int indexOf(Object[] array, Object objectToFind) { return indexOf(array, objectToFind, 0); } /** * <p>Checks if the object is in the given array.</p> * * <p>The method returns <code>false</code> if a <code>null</code> array is passed in.</p> * * @param array the array to search through * @param objectToFind the object to find * @return <code>true</code> if the array contains the object */ public static boolean contains(Object[] array, Object objectToFind) { return indexOf(array, objectToFind) != -1; } /** * <p>Find the index of the given object in the array starting at the given index.</p> * * <p>This method returns <code>-1</code> if <code>null</code> array input.</p> * * <p>A negative startIndex is treated as zero. A startIndex larger than the array * length will return <code>-1</code>.</p> * * @param array the array to search through for the object, may be <code>null</code> * @param objectToFind the object to find, may be <code>null</code> * @param startIndex the index to start searching at * @return the index of the object within the array starting at the index, * <code>-1</code> if not found or <code>null</code> array input */ public static int indexOf(Object[] array, Object objectToFind, int startIndex) { if (array == null) { return -1; } 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 -1; } /** * Return the zero element of the array, if it exists, null if the array is empty. * If there is more than one element in the list, an exception is thrown. * @param <T> * @param array is the container of objects to get the first of. * @return the first object, null, or exception. */ public static <T> T arrayPopOne(T[] array) { int size = length(array); if (size == 1) { return array[0]; } else if (size == 0) { return null; } throw new RuntimeException("More than one object of type " + className(array[0]) + " was returned when only one was expected. (size:" + size + ")"); } /** * Note, this is * web service format string */ private static final String WS_DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS"; /** * Note, this is * web service format string */ private static final String WS_DATE_FORMAT2 = "yyyy/MM/dd_HH:mm:ss.SSS"; /** * convert a date to a string using the standard web service pattern * yyyy/MM/dd HH:mm:ss.SSS Note that HH is 0-23 * * @param date * @return the string, or null if the date is null */ public static String dateToString(Date date) { if (date == null) { return null; } SimpleDateFormat simpleDateFormat = new SimpleDateFormat(WS_DATE_FORMAT); return simpleDateFormat.format(date); } /** * convert a string to a date using the standard web service pattern Note * that HH is 0-23 * * @param dateString * @return the string, or null if the date was null */ public static Date stringToDate(String dateString) { if (isBlank(dateString)) { return null; } SimpleDateFormat simpleDateFormat = new SimpleDateFormat(WS_DATE_FORMAT); try { return simpleDateFormat.parse(dateString); } catch (ParseException e) { SimpleDateFormat simpleDateFormat2 = new SimpleDateFormat(WS_DATE_FORMAT2); try { return simpleDateFormat2.parse(dateString); } catch (ParseException e2) { throw new RuntimeException( "Cannot convert '" + dateString + "' to a date based on format: " + WS_DATE_FORMAT, e); } } } /** * @param values * @return the max long in the list of args */ public static Long getMaxLongValue(Long... values) { if (values == null || values.length == 0) { return null; } Long maxValue = null; for (int i = 0; i < values.length; i++) { if (values[i] != null) { if (maxValue == null || maxValue.compareTo(values[i]) < 0) { maxValue = new Long(values[i]); } } } return maxValue; } /** * @param values * @return the min long in the list of args */ public static Long getMinLongValue(Long... values) { if (values == null || values.length == 0) { return null; } Long minValue = null; for (int i = 0; i < values.length; i++) { if (values[i] != null) { if (minValue == null || minValue.compareTo(values[i]) > 0) { minValue = new Long(values[i]); } } } return minValue; } /** * see if an ip address is on a network * * @param ipString * is the ip address to check * @param networkIpString * is the ip address of the network * @param mask * is the length of the mask (0-32) * @return boolean */ public static boolean ipOnNetwork(String ipString, String networkIpString, int mask) { //this allows all if (mask == 0) { return true; } int ip = ipInt(ipString); int networkIp = ipInt(networkIpString); ip = ipReadyForAnd(ip, mask); networkIp = ipReadyForAnd(networkIp, mask); return ip == networkIp; } /** * see if an ip address is on a network * * @param ipString * is the ip address to check * @param networkIpStrings * are the ip addresses of the networks, e.g. 1.2.3.4/12, 2.3.4.5/24 * @return boolean */ public static boolean ipOnNetworks(String ipString, String networkIpStrings) { String[] networkIpStringsArray = splitTrim(networkIpStrings, ","); //check each one for (String networkIpString : networkIpStringsArray) { if (!contains(networkIpString, "/")) { throw new RuntimeException("String must contain slash and CIDR network bits, e.g. 1.2.3.4/14"); } //get network part: String network = prefixOrSuffix(networkIpString, "/", true); network = trim(network); String mask = prefixOrSuffix(networkIpString, "/", false); mask = trim(mask); int maskInt = -1; maskInt = Integer.parseInt(mask); //if on the network, then all good if (ipOnNetwork(ipString, network, maskInt)) { return true; } } return false; } /** * get the ip address after putting 1's where the subnet mask is not * @param ip int * @param maskLength int * @return int */ public static int ipReadyForAnd(int ip, int maskLength) { int mask = -1 + (int) Math.pow(2, 32 - maskLength); return ip | mask; } /** * get the ip addres integer from a string ip address * @param ip String * @return int */ public static int ipInt(String ip) { int block1; int block2; int block3; int block4; try { int periodIndex = ip.indexOf('.'); String blockString = ip.substring(0, periodIndex); block1 = Integer.parseInt(blockString); //split it up for 2^24 since it does the math wrong if you dont int mathPow = (int) Math.pow(2, 24); block1 *= mathPow; int oldPeriodIndex = periodIndex; periodIndex = ip.indexOf('.', periodIndex + 1); blockString = ip.substring(oldPeriodIndex + 1, periodIndex); block2 = Integer.parseInt(blockString); block2 *= Math.pow(2, 16); oldPeriodIndex = periodIndex; periodIndex = ip.indexOf('.', periodIndex + 1); blockString = ip.substring(oldPeriodIndex + 1, periodIndex); block3 = Integer.parseInt(blockString); block3 *= Math.pow(2, 8); blockString = ip.substring(periodIndex + 1, ip.length()); block4 = Integer.parseInt(blockString); } catch (NumberFormatException nfe) { throw new RuntimeException("Could not parse the ipaddress: " + ip); } return block1 + block2 + block3 + block4; } /** array for converting HTML to string */ private static final String[] XML_REPLACE_NO_SINGLE = new String[] { "&", "<", ">", """ }; /** array for converting HTML to string */ private static final String[] XML_SEARCH_NO_SINGLE = new String[] { "&", "<", ">", "\"" }; /** * <p>A way to get the entire nested stack-trace of an throwable.</p> * * @param throwable the <code>Throwable</code> 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(); } /** * Get a specific index of an array or collection (note for collections and * iterating, it is more efficient to get an iterator and iterate * * @param arrayOrCollection * @param index * @return the object at that index */ public static Object get(Object arrayOrCollection, int index) { if (arrayOrCollection == null) { if (index == 0) { return null; } throw new RuntimeException("Trying to access index " + index + " of null"); } // no need to iterator on list (e.g. FastProxyList has no iterator if (arrayOrCollection instanceof List) { return ((List) arrayOrCollection).get(index); } if (arrayOrCollection instanceof Collection) { Iterator iterator = iterator(arrayOrCollection); for (int i = 0; i < index; i++) { next(arrayOrCollection, iterator, i); } return next(arrayOrCollection, iterator, index); } if (arrayOrCollection.getClass().isArray()) { return Array.get(arrayOrCollection, index); } if (index == 0) { return arrayOrCollection; } throw new RuntimeException("Trying to access index " + index + " of and object: " + arrayOrCollection); } /** * if a class is enhanced, get the unenhanced version * * @param theClass * @return the unenhanced version */ public static Class unenhanceClass(Class theClass) { try { //this was cglib // while (Enhancer.isEnhanced(theClass)) { // theClass = theClass.getSuperclass(); // } while (ProxyObject.class.isAssignableFrom(theClass)) { theClass = theClass.getSuperclass(); } return theClass; } catch (Exception e) { throw new RuntimeException("Problem unenhancing " + theClass, e); } } /** * <p>Returns the list of <code>Throwable</code> objects in the * exception chain.</p> * * <p>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 <code>null</code> throwable will return an array size zero.</p> * * @param throwable the throwable to inspect, may be null * @return the array of throwables, never null */ @SuppressWarnings("unchecked") public static Throwable[] getThrowables(Throwable throwable) { List list = new ArrayList(); while (throwable != null) { list.add(throwable); throwable = getCause(throwable); } return (Throwable[]) list.toArray(new Throwable[list.size()]); } /** * <p>Checks if the Throwable class has a <code>getCause</code> method.</p> * * <p>This is true for JDK 1.4 and above.</p> * * @return true if Throwable is nestable * @since 2.0 */ public static boolean isThrowableNested() { return THROWABLE_CAUSE_METHOD != null; } /** * <p>Checks whether this <code>Throwable</code> class can store a cause.</p> * * <p>This method does <b>not</b> check whether it actually does store a cause.<p> * * @param throwable the <code>Throwable</code> to examine, may be null * @return boolean <code>true</code> if nested otherwise <code>false</code> * @since 2.0 */ @SuppressWarnings("unchecked") 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], (Class[]) null); if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { return true; } } catch (NoSuchMethodException ignored) { } catch (SecurityException ignored) { } } try { Field field = cls.getField("detail"); if (field != null) { return true; } } catch (NoSuchFieldException ignored) { } catch (SecurityException ignored) { } return false; } /** * <p>The Method object for JDK1.4 getCause.</p> */ private static final Method THROWABLE_CAUSE_METHOD; static { Method getCauseMethod; try { getCauseMethod = Throwable.class.getMethod("getCause", (Class[]) null); } catch (Exception e) { getCauseMethod = null; } THROWABLE_CAUSE_METHOD = getCauseMethod; } /** * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> * * <p>The method searches for methods with specific names that return a * <code>Throwable</code> object. This will pick up most wrapping exceptions, * including those from JDK 1.4, and * {@link org.apache.commons.lang.exception.NestableException NestableException}.</p> * * <p>The default list searched for are:</p> * <ul> * <li><code>getCause()</code></li> * <li><code>getNextException()</code></li> * <li><code>getTargetException()</code></li> * <li><code>getException()</code></li> * <li><code>getSourceException()</code></li> * <li><code>getRootCause()</code></li> * <li><code>getCausedByException()</code></li> * <li><code>getNested()</code></li> * </ul> * * <p>In the absence of any such method, the object is inspected for a * <code>detail</code> field assignable to a <code>Throwable</code>.</p> * * <p>If none of the above is found, returns <code>null</code>.</p> * * @param throwable the throwable to introspect for a cause, may be null * @return the cause of the <code>Throwable</code>, * <code>null</code> if none found or null throwable input * @since 1.0 */ public static Throwable getCause(Throwable throwable) { return getCause(throwable, CAUSE_METHOD_NAMES); } /** * <p>The names of methods commonly used to access a wrapped exception.</p> */ private static String[] CAUSE_METHOD_NAMES = { "getCause", "getNextException", "getTargetException", "getException", "getSourceException", "getRootCause", "getCausedByException", "getNested", "getLinkedException", "getNestedException", "getLinkedCause", "getThrowable", }; /** * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> * * <ol> * <li>Try known exception types.</li> * <li>Try the supplied array of method names.</li> * <li>Try the field 'detail'.</li> * </ol> * * <p>A <code>null</code> set of method names means use the default set. * A <code>null</code> in the set of method names will be ignored.</p> * * @param throwable the throwable to introspect for a cause, may be null * @param methodNames the method names, null treated as default set * @return the cause of the <code>Throwable</code>, * <code>null</code> 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; } /** * <p>Finds a <code>Throwable</code> by field name.</p> * * @param throwable the exception to examine * @param fieldName the name of the attribute to examine * @return the wrapped exception, or <code>null</code> if not found */ private static Throwable getCauseUsingFieldName(Throwable throwable, String fieldName) { Field field = null; try { field = throwable.getClass().getField(fieldName); } catch (NoSuchFieldException ignored) { } catch (SecurityException ignored) { } if (field != null && Throwable.class.isAssignableFrom(field.getType())) { try { return (Throwable) field.get(throwable); } catch (IllegalAccessException ignored) { } catch (IllegalArgumentException ignored) { } } return null; } /** * <p>Finds a <code>Throwable</code> by method name.</p> * * @param throwable the exception to examine * @param methodName the name of the method to find and invoke * @return the wrapped exception, or <code>null</code> if not found */ private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { Method method = null; try { method = throwable.getClass().getMethod(methodName, (Class[]) null); } catch (NoSuchMethodException ignored) { } catch (SecurityException ignored) { } if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { try { return (Throwable) method.invoke(throwable, EMPTY_OBJECT_ARRAY); } catch (IllegalAccessException ignored) { } catch (IllegalArgumentException ignored) { } catch (InvocationTargetException ignored) { } } return null; } /** * <p>Finds a <code>Throwable</code> for known types.</p> * * <p>Uses <code>instanceof</code> checks to examine the exception, * looking for well known types which could contain chained or * wrapped exceptions.</p> * * @param throwable the exception to examine * @return the wrapped exception, or <code>null</code> 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; } } }