一、前言
关于字符串工具类StringUtils实现字符串是否包含isChinese中文、是否包含isMessyCode乱码、常规数据类型转换、替换/删除/判空、两字符串数组mergeStringArrays合并、删除trimArrayElements字符串数组指定数组元素、字符串去重removeDuplicateStrings处理、字符串集合转换等相关处理。
二、代码示例
import com.alibaba.fastjson.JSONObject;@b@@b@import org.apache.commons.lang.WordUtils;@b@import org.springframework.util.CollectionUtils;@b@import org.springframework.util.ObjectUtils;@b@@b@import java.nio.charset.Charset;@b@import java.text.DateFormat;@b@import java.text.SimpleDateFormat;@b@import java.util.ArrayList;@b@import java.util.Arrays;@b@import java.util.Collection;@b@import java.util.Collections;@b@import java.util.Date;@b@import java.util.Enumeration;@b@import java.util.Iterator;@b@import java.util.LinkedList;@b@import java.util.List;@b@import java.util.Locale;@b@import java.util.Properties;@b@import java.util.Random;@b@import java.util.Set;@b@import java.util.StringTokenizer;@b@import java.util.TimeZone;@b@import java.util.TreeSet;@b@import java.util.regex.Matcher;@b@import java.util.regex.Pattern;@b@@b@public class StringUtils {@b@ @b@ public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");@b@ @b@ private static DateFormat date_format = new SimpleDateFormat("yyyy/M/d", Locale.ENGLISH);@b@ private static DateFormat time_format = new SimpleDateFormat("yyyy/M/d hh:mm:ss", Locale.ENGLISH);@b@ @b@ private static final String FOLDER_SEPARATOR = "/";@b@@b@ private static final String WINDOWS_FOLDER_SEPARATOR = "\\";@b@@b@ private static final String TOP_PATH = "..";@b@@b@ private static final String CURRENT_PATH = ".";@b@@b@ private static final char EXTENSION_SEPARATOR = '.';@b@ @b@ public static String defFormatDate(Date date) {@b@ return date_format.format(date);@b@ }@b@ @b@ public static String defFormatTime(Date date) {@b@ return time_format.format(date);@b@ }@b@@b@ public static String firstUpcase(String text) {@b@ if (text != null && text.length() > 0) {@b@ if (text.length() == 1) {@b@ return text.toUpperCase();@b@ }@b@ return text.substring(0, 1).toUpperCase() + text.substring(1); @b@ }@b@ return text;@b@ }@b@@b@ public static String firstLowercase(String text) {@b@ if (text != null && text.length() > 0) {@b@ if (text.length() == 1) {@b@ return text.toLowerCase();@b@ }@b@ return text.substring(0, 1).toLowerCase() + text.substring(1);@b@ }@b@ return text;@b@ }@b@ @b@ public static String getter(String field) {@b@ return "get" + firstUpcase(field);@b@ }@b@ @b@ public static String setter(String field) {@b@ return "set" + firstUpcase(field);@b@ }@b@ @b@ public static String indent(int idx) {@b@ StringBuffer result = new StringBuffer(idx*4 + 1);@b@ for (int i = 0; i < idx; i ++) {@b@ result.append(" ");@b@ }@b@ return result.toString();@b@ }@b@ @b@ /**@b@ * Parse1@b@ */@b@ @b@ public static Boolean parseBoolean(String s) {@b@ try {@b@ return Boolean.parseBoolean(s);@b@ } catch (Exception e) {@b@ }@b@ return null;@b@ }@b@ @b@ public static Integer parseInt(String s) {@b@ try {@b@ return Integer.parseInt(s);@b@ } catch (Exception e) {@b@ }@b@ return null;@b@ }@b@ @b@ public static Long parseLong(String s) {@b@ try {@b@ return Long.parseLong(s);@b@ } catch (Exception e) {@b@ }@b@ return null;@b@ }@b@ @b@ public static Float parseFloat(String s) {@b@ try {@b@ return Float.parseFloat(s);@b@ } catch (Exception e) {@b@ }@b@ return null;@b@ }@b@ @b@ public static Double parseDouble(String s) {@b@ try {@b@ return Double.parseDouble(s);@b@ } catch (Exception e) {@b@ }@b@ return null;@b@ }@b@ @b@ /**@b@ * Parse2@b@ */@b@ @b@ public static boolean parseBoolean(String s, boolean def) {@b@ try {@b@ return Boolean.parseBoolean(s);@b@ } catch (Exception e) {@b@ }@b@ return def;@b@ }@b@ @b@ public static int parseInt(String s, int def) {@b@ try {@b@ return Integer.parseInt(s);@b@ } catch (Exception e) {@b@ }@b@ return def;@b@ }@b@ @b@ public static long parseLong(String s, long def) {@b@ try {@b@ return Long.parseLong(s);@b@ } catch (Exception e) {@b@ }@b@ return def;@b@ }@b@ @b@ public static float parseFloat(String s, float def) {@b@ try {@b@ return Float.parseFloat(s);@b@ } catch (Exception e) {@b@ }@b@ return def;@b@ }@b@ @b@ public static double parseDouble(String s, double def) {@b@ try {@b@ return Double.parseDouble(s);@b@ } catch (Exception e) {@b@ }@b@ return def;@b@ }@b@ @b@ public static int[] parseVersion(String version) {@b@ int[] ret = {0,0,0};@b@ if (version != null) {@b@ String[] nums = version.replace('.', '-').split("-");@b@ for (int i = 0; i < nums.length && i < ret.length; i ++) {@b@ ret[i] = parseInt(nums[i], 0);@b@ }@b@ }@b@ return ret;@b@ }@b@ @b@ public static String toString(Object o) {@b@ return JSONObject.toJSONString(o);@b@ }@b@@b@ public static String underline2Camel(String name) {@b@ return@b@ org.apache.commons.lang.StringUtils.uncapitalize(@b@ org.apache.commons.lang.StringUtils.remove@b@ (WordUtils.capitalizeFully(name, new char[]{'_'}), "_")@b@ );@b@ }@b@@b@ public static String camel2underline(String name) {@b@ return name.replaceAll("(.)(\\p{Upper})", "$1_$2").toLowerCase();@b@ }@b@@b@@b@ public static String itemCode() {@b@ Random rand = new Random();@b@ return String.format("%010d%02d",@b@ Math.abs(org.apache.commons.lang.StringUtils.reverse((System.currentTimeMillis() + "")).hashCode()),@b@ rand.nextInt(99));@b@ }@b@ @b@ /**@b@ * 判断字符是否是中文@b@ *@b@ * @param c 字符@b@ * @return 是否是中文@b@ */@b@ private static boolean isChinese(char c) { @b@ Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); @b@ if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS @b@ || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS @b@ || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A @b@ || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION @b@ || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION @b@ || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { @b@ return true; @b@ } @b@ return false; @b@ } @b@@b@ /**@b@ * 判断字符串是否是乱码@b@ *@b@ * @param strName 字符串@b@ * @return 是否是乱码@b@ */@b@ public static boolean isMessyCode(String strName) { @b@ Pattern p = Pattern.compile("\\s*|\t*|\r*|\n*"); @b@ Matcher m = p.matcher(strName); @b@ String after = m.replaceAll(""); @b@ String temp = after.replaceAll("\\p{P}", ""); @b@ char[] ch = temp.trim().toCharArray(); @b@ float chLength = 0 ; @b@ float count = 0; @b@ for (int i = 0; i < ch.length; i++) { @b@ char c = ch[i]; @b@ if (!Character.isLetterOrDigit(c)) { @b@ if (!isChinese(c)) { @b@ count = count + 1; @b@ } @b@ chLength++; @b@ } @b@ } @b@ float result = count / chLength ; @b@ if (result > 0.4) { @b@ return true; @b@ } else { @b@ return false; @b@ } @b@ } @b@ @b@ //---------------------------------------------------------------------@b@ // General convenience methods for working with Strings@b@ //---------------------------------------------------------------------@b@@b@ /**@b@ * Check whether the given String is empty.@b@ * <p>This method accepts any Object as an argument, comparing it to@b@ * {@code null} and the empty String. As a consequence, this method@b@ * will never return {@code true} for a non-null non-String object.@b@ * <p>The Object signature is useful for general attribute handling code@b@ * that commonly deals with Strings but generally has to iterate over@b@ * Objects since attributes may e.g. be primitive value objects as well.@b@ * @param str the candidate String@b@ * @since 3.2.1@b@ */@b@ public static boolean isEmpty(Object str) {@b@ return (str == null || "".equals(str));@b@ }@b@ @b@ public static boolean hasEmpty(Object...strs) {@b@ for (Object str : strs) {@b@ if (str == null || "".equals(str)) {@b@ return true;@b@ }@b@ }@b@ return false;@b@ }@b@@b@ /**@b@ * Check that the given CharSequence is neither {@code null} nor of length 0.@b@ * Note: Will return {@code true} for a CharSequence that purely consists of whitespace.@b@ * <p><pre class="code">@b@ * StringUtils.hasLength(null) = false@b@ * StringUtils.hasLength("") = false@b@ * StringUtils.hasLength(" ") = true@b@ * StringUtils.hasLength("Hello") = true@b@ * </pre>@b@ * @param str the CharSequence to check (may be {@code null})@b@ * @return {@code true} if the CharSequence is not null and has length@b@ * @see #hasText(String)@b@ */@b@ public static boolean hasLength(CharSequence str) {@b@ return (str != null && str.length() > 0);@b@ }@b@@b@ /**@b@ * Check that the given String is neither {@code null} nor of length 0.@b@ * Note: Will return {@code true} for a String that purely consists of whitespace.@b@ * @param str the String to check (may be {@code null})@b@ * @return {@code true} if the String is not null and has length@b@ * @see #hasLength(CharSequence)@b@ */@b@ public static boolean hasLength(String str) {@b@ return hasLength((CharSequence) str);@b@ }@b@@b@ /**@b@ * Check whether the given CharSequence has actual text.@b@ * More specifically, returns {@code true} if the string not {@code null},@b@ * its length is greater than 0, and it contains at least one non-whitespace character.@b@ * <p><pre class="code">@b@ * StringUtils.hasText(null) = false@b@ * StringUtils.hasText("") = false@b@ * StringUtils.hasText(" ") = false@b@ * StringUtils.hasText("12345") = true@b@ * StringUtils.hasText(" 12345 ") = true@b@ * </pre>@b@ * @param str the CharSequence to check (may be {@code null})@b@ * @return {@code true} if the CharSequence is not {@code null},@b@ * its length is greater than 0, and it does not contain whitespace only@b@ * @see Character#isWhitespace@b@ */@b@ public static boolean hasText(CharSequence str) {@b@ if (!hasLength(str)) {@b@ return false;@b@ }@b@ int strLen = str.length();@b@ for (int i = 0; i < strLen; i++) {@b@ if (!Character.isWhitespace(str.charAt(i))) {@b@ return true;@b@ }@b@ }@b@ return false;@b@ }@b@@b@ /**@b@ * Check whether the given String has actual text.@b@ * More specifically, returns {@code true} if the string not {@code null},@b@ * its length is greater than 0, and it contains at least one non-whitespace character.@b@ * @param str the String to check (may be {@code null})@b@ * @return {@code true} if the String is not {@code null}, its length is@b@ * greater than 0, and it does not contain whitespace only@b@ * @see #hasText(CharSequence)@b@ */@b@ public static boolean hasText(String str) {@b@ return hasText((CharSequence) str);@b@ }@b@@b@ /**@b@ * Check whether the given CharSequence contains any whitespace characters.@b@ * @param str the CharSequence to check (may be {@code null})@b@ * @return {@code true} if the CharSequence is not empty and@b@ * contains at least 1 whitespace character@b@ * @see Character#isWhitespace@b@ */@b@ public static boolean containsWhitespace(CharSequence str) {@b@ if (!hasLength(str)) {@b@ return false;@b@ }@b@ int strLen = str.length();@b@ for (int i = 0; i < strLen; i++) {@b@ if (Character.isWhitespace(str.charAt(i))) {@b@ return true;@b@ }@b@ }@b@ return false;@b@ }@b@@b@ /**@b@ * Check whether the given String contains any whitespace characters.@b@ * @param str the String to check (may be {@code null})@b@ * @return {@code true} if the String is not empty and@b@ * contains at least 1 whitespace character@b@ * @see #containsWhitespace(CharSequence)@b@ */@b@ public static boolean containsWhitespace(String str) {@b@ return containsWhitespace((CharSequence) str);@b@ }@b@@b@ /**@b@ * Trim leading and trailing whitespace from the given String.@b@ * @param str the String to check@b@ * @return the trimmed String@b@ * @see java.lang.Character#isWhitespace@b@ */@b@ public static String trimWhitespace(String str) {@b@ if (!hasLength(str)) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str);@b@ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {@b@ sb.deleteCharAt(0);@b@ }@b@ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {@b@ sb.deleteCharAt(sb.length() - 1);@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Trim <i>all</i> whitespace from the given String:@b@ * leading, trailing, and in between characters.@b@ * @param str the String to check@b@ * @return the trimmed String@b@ * @see java.lang.Character#isWhitespace@b@ */@b@ public static String trimAllWhitespace(String str) {@b@ if (!hasLength(str)) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str);@b@ int index = 0;@b@ while (sb.length() > index) {@b@ if (Character.isWhitespace(sb.charAt(index))) {@b@ sb.deleteCharAt(index);@b@ }@b@ else {@b@ index++;@b@ }@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Trim leading whitespace from the given String.@b@ * @param str the String to check@b@ * @return the trimmed String@b@ * @see java.lang.Character#isWhitespace@b@ */@b@ public static String trimLeadingWhitespace(String str) {@b@ if (!hasLength(str)) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str);@b@ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {@b@ sb.deleteCharAt(0);@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Trim trailing whitespace from the given String.@b@ * @param str the String to check@b@ * @return the trimmed String@b@ * @see java.lang.Character#isWhitespace@b@ */@b@ public static String trimTrailingWhitespace(String str) {@b@ if (!hasLength(str)) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str);@b@ while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {@b@ sb.deleteCharAt(sb.length() - 1);@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Trim all occurrences of the supplied leading character from the given String.@b@ * @param str the String to check@b@ * @param leadingCharacter the leading character to be trimmed@b@ * @return the trimmed String@b@ */@b@ public static String trimLeadingCharacter(String str, char leadingCharacter) {@b@ if (!hasLength(str)) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str);@b@ while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {@b@ sb.deleteCharAt(0);@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Trim all occurrences of the supplied trailing character from the given String.@b@ * @param str the String to check@b@ * @param trailingCharacter the trailing character to be trimmed@b@ * @return the trimmed String@b@ */@b@ public static String trimTrailingCharacter(String str, char trailingCharacter) {@b@ if (!hasLength(str)) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str);@b@ while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) {@b@ sb.deleteCharAt(sb.length() - 1);@b@ }@b@ return sb.toString();@b@ }@b@@b@@b@ /**@b@ * Test if the given String starts with the specified prefix,@b@ * ignoring upper/lower case.@b@ * @param str the String to check@b@ * @param prefix the prefix to look for@b@ * @see java.lang.String#startsWith@b@ */@b@ public static boolean startsWithIgnoreCase(String str, String prefix) {@b@ if (str == null || prefix == null) {@b@ return false;@b@ }@b@ if (str.startsWith(prefix)) {@b@ return true;@b@ }@b@ if (str.length() < prefix.length()) {@b@ return false;@b@ }@b@ String lcStr = str.substring(0, prefix.length()).toLowerCase();@b@ String lcPrefix = prefix.toLowerCase();@b@ return lcStr.equals(lcPrefix);@b@ }@b@@b@ /**@b@ * Test if the given String ends with the specified suffix,@b@ * ignoring upper/lower case.@b@ * @param str the String to check@b@ * @param suffix the suffix to look for@b@ * @see java.lang.String#endsWith@b@ */@b@ public static boolean endsWithIgnoreCase(String str, String suffix) {@b@ if (str == null || suffix == null) {@b@ return false;@b@ }@b@ if (str.endsWith(suffix)) {@b@ return true;@b@ }@b@ if (str.length() < suffix.length()) {@b@ return false;@b@ }@b@@b@ String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();@b@ String lcSuffix = suffix.toLowerCase();@b@ return lcStr.equals(lcSuffix);@b@ }@b@@b@ /**@b@ * Test whether the given string matches the given substring@b@ * at the given index.@b@ * @param str the original string (or StringBuilder)@b@ * @param index the index in the original string to start matching against@b@ * @param substring the substring to match at the given index@b@ */@b@ public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {@b@ for (int j = 0; j < substring.length(); j++) {@b@ int i = index + j;@b@ if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {@b@ return false;@b@ }@b@ }@b@ return true;@b@ }@b@@b@ /**@b@ * Count the occurrences of the substring in string s.@b@ * @param str string to search in. Return 0 if this is null.@b@ * @param sub string to search for. Return 0 if this is null.@b@ */@b@ public static int countOccurrencesOf(String str, String sub) {@b@ if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {@b@ return 0;@b@ }@b@ int count = 0;@b@ int pos = 0;@b@ int idx;@b@ while ((idx = str.indexOf(sub, pos)) != -1) {@b@ ++count;@b@ pos = idx + sub.length();@b@ }@b@ return count;@b@ }@b@@b@ /**@b@ * Replace all occurrences of a substring within a string with@b@ * another string.@b@ * @param inString String to examine@b@ * @param oldPattern String to replace@b@ * @param newPattern String to insert@b@ * @return a String with the replacements@b@ */@b@ public static String replace(String inString, String oldPattern, String newPattern) {@b@ if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {@b@ return inString;@b@ }@b@ StringBuilder sb = new StringBuilder();@b@ int pos = 0; // our position in the old string@b@ int index = inString.indexOf(oldPattern);@b@ // the index of an occurrence we've found, or -1@b@ int patLen = oldPattern.length();@b@ while (index >= 0) {@b@ sb.append(inString.substring(pos, index));@b@ sb.append(newPattern);@b@ pos = index + patLen;@b@ index = inString.indexOf(oldPattern, pos);@b@ }@b@ sb.append(inString.substring(pos));@b@ // remember to append any characters to the right of a match@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Delete all occurrences of the given substring.@b@ * @param inString the original String@b@ * @param pattern the pattern to delete all occurrences of@b@ * @return the resulting String@b@ */@b@ public static String delete(String inString, String pattern) {@b@ return replace(inString, pattern, "");@b@ }@b@@b@ /**@b@ * Delete any character in a given String.@b@ * @param inString the original String@b@ * @param charsToDelete a set of characters to delete.@b@ * E.g. "az\n" will delete 'a's, 'z's and new lines.@b@ * @return the resulting String@b@ */@b@ public static String deleteAny(String inString, String charsToDelete) {@b@ if (!hasLength(inString) || !hasLength(charsToDelete)) {@b@ return inString;@b@ }@b@ StringBuilder sb = new StringBuilder();@b@ for (int i = 0; i < inString.length(); i++) {@b@ char c = inString.charAt(i);@b@ if (charsToDelete.indexOf(c) == -1) {@b@ sb.append(c);@b@ }@b@ }@b@ return sb.toString();@b@ }@b@@b@@b@ //---------------------------------------------------------------------@b@ // Convenience methods for working with formatted Strings@b@ //---------------------------------------------------------------------@b@@b@ /**@b@ * Quote the given String with single quotes.@b@ * @param str the input String (e.g. "myString")@b@ * @return the quoted String (e.g. "'myString'"),@b@ * or {@code null} if the input was {@code null}@b@ */@b@ public static String quote(String str) {@b@ return (str != null ? "'" + str + "'" : null);@b@ }@b@@b@ /**@b@ * Turn the given Object into a String with single quotes@b@ * if it is a String; keeping the Object as-is else.@b@ * @param obj the input Object (e.g. "myString")@b@ * @return the quoted String (e.g. "'myString'"),@b@ * or the input object as-is if not a String@b@ */@b@ public static Object quoteIfString(Object obj) {@b@ return (obj instanceof String ? quote((String) obj) : obj);@b@ }@b@@b@ /**@b@ * Unqualify a string qualified by a '.' dot character. For example,@b@ * "this.name.is.qualified", returns "qualified".@b@ * @param qualifiedName the qualified name@b@ */@b@ public static String unqualify(String qualifiedName) {@b@ return unqualify(qualifiedName, '.');@b@ }@b@@b@ /**@b@ * Unqualify a string qualified by a separator character. For example,@b@ * "this:name:is:qualified" returns "qualified" if using a ':' separator.@b@ * @param qualifiedName the qualified name@b@ * @param separator the separator@b@ */@b@ public static String unqualify(String qualifiedName, char separator) {@b@ return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);@b@ }@b@@b@ /**@b@ * Capitalize a {@code String}, changing the first letter to@b@ * upper case as per {@link Character#toUpperCase(char)}.@b@ * No other letters are changed.@b@ * @param str the String to capitalize, may be {@code null}@b@ * @return the capitalized String, {@code null} if null@b@ */@b@ public static String capitalize(String str) {@b@ return changeFirstCharacterCase(str, true);@b@ }@b@@b@ /**@b@ * Uncapitalize a {@code String}, changing the first letter to@b@ * lower case as per {@link Character#toLowerCase(char)}.@b@ * No other letters are changed.@b@ * @param str the String to uncapitalize, may be {@code null}@b@ * @return the uncapitalized String, {@code null} if null@b@ */@b@ public static String uncapitalize(String str) {@b@ return changeFirstCharacterCase(str, false);@b@ }@b@@b@ private static String changeFirstCharacterCase(String str, boolean capitalize) {@b@ if (str == null || str.length() == 0) {@b@ return str;@b@ }@b@ StringBuilder sb = new StringBuilder(str.length());@b@ if (capitalize) {@b@ sb.append(Character.toUpperCase(str.charAt(0)));@b@ }@b@ else {@b@ sb.append(Character.toLowerCase(str.charAt(0)));@b@ }@b@ sb.append(str.substring(1));@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Extract the filename from the given path,@b@ * e.g. "mypath/myfile.txt" -> "myfile.txt".@b@ * @param path the file path (may be {@code null})@b@ * @return the extracted filename, or {@code null} if none@b@ */@b@ public static String getFilename(String path) {@b@ if (path == null) {@b@ return null;@b@ }@b@ int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);@b@ return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);@b@ }@b@@b@ /**@b@ * Extract the filename extension from the given path,@b@ * e.g. "mypath/myfile.txt" -> "txt".@b@ * @param path the file path (may be {@code null})@b@ * @return the extracted filename extension, or {@code null} if none@b@ */@b@ public static String getFilenameExtension(String path) {@b@ if (path == null) {@b@ return null;@b@ }@b@ int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);@b@ if (extIndex == -1) {@b@ return null;@b@ }@b@ int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);@b@ if (folderIndex > extIndex) {@b@ return null;@b@ }@b@ return path.substring(extIndex + 1);@b@ }@b@@b@ /**@b@ * Strip the filename extension from the given path,@b@ * e.g. "mypath/myfile.txt" -> "mypath/myfile".@b@ * @param path the file path (may be {@code null})@b@ * @return the path with stripped filename extension,@b@ * or {@code null} if none@b@ */@b@ public static String stripFilenameExtension(String path) {@b@ if (path == null) {@b@ return null;@b@ }@b@ int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);@b@ if (extIndex == -1) {@b@ return path;@b@ }@b@ int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);@b@ if (folderIndex > extIndex) {@b@ return path;@b@ }@b@ return path.substring(0, extIndex);@b@ }@b@@b@ /**@b@ * Apply the given relative path to the given path,@b@ * assuming standard Java folder separation (i.e. "/" separators).@b@ * @param path the path to start from (usually a full file path)@b@ * @param relativePath the relative path to apply@b@ * (relative to the full file path above)@b@ * @return the full file path that results from applying the relative path@b@ */@b@ public static String applyRelativePath(String path, String relativePath) {@b@ int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);@b@ if (separatorIndex != -1) {@b@ String newPath = path.substring(0, separatorIndex);@b@ if (!relativePath.startsWith(FOLDER_SEPARATOR)) {@b@ newPath += FOLDER_SEPARATOR;@b@ }@b@ return newPath + relativePath;@b@ }@b@ else {@b@ return relativePath;@b@ }@b@ }@b@@b@ /**@b@ * Normalize the path by suppressing sequences like "path/.." and@b@ * inner simple dots.@b@ * <p>The result is convenient for path comparison. For other uses,@b@ * notice that Windows separators ("\") are replaced by simple slashes.@b@ * @param path the original path@b@ * @return the normalized path@b@ */@b@ public static String cleanPath(String path) {@b@ if (path == null) {@b@ return null;@b@ }@b@ String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);@b@@b@ // Strip prefix from path to analyze, to not treat it as part of the@b@ // first path element. This is necessary to correctly parse paths like@b@ // "file:core/../core/io/Resource.class", where the ".." should just@b@ // strip the first "core" directory while keeping the "file:" prefix.@b@ int prefixIndex = pathToUse.indexOf(":");@b@ String prefix = "";@b@ if (prefixIndex != -1) {@b@ prefix = pathToUse.substring(0, prefixIndex + 1);@b@ pathToUse = pathToUse.substring(prefixIndex + 1);@b@ }@b@ if (pathToUse.startsWith(FOLDER_SEPARATOR)) {@b@ prefix = prefix + FOLDER_SEPARATOR;@b@ pathToUse = pathToUse.substring(1);@b@ }@b@@b@ String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);@b@ List<String> pathElements = new LinkedList<String>();@b@ int tops = 0;@b@@b@ for (int i = pathArray.length - 1; i >= 0; i--) {@b@ String element = pathArray[i];@b@ if (CURRENT_PATH.equals(element)) {@b@ // Points to current directory - drop it.@b@ }@b@ else if (TOP_PATH.equals(element)) {@b@ // Registering top path found.@b@ tops++;@b@ }@b@ else {@b@ if (tops > 0) {@b@ // Merging path element with element corresponding to top path.@b@ tops--;@b@ }@b@ else {@b@ // Normal path element found.@b@ pathElements.add(0, element);@b@ }@b@ }@b@ }@b@@b@ // Remaining top paths need to be retained.@b@ for (int i = 0; i < tops; i++) {@b@ pathElements.add(0, TOP_PATH);@b@ }@b@@b@ return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);@b@ }@b@@b@ /**@b@ * Compare two paths after normalization of them.@b@ * @param path1 first path for comparison@b@ * @param path2 second path for comparison@b@ * @return whether the two paths are equivalent after normalization@b@ */@b@ public static boolean pathEquals(String path1, String path2) {@b@ return cleanPath(path1).equals(cleanPath(path2));@b@ }@b@@b@ /**@b@ * Parse the given {@code localeString} value into a {@link Locale}.@b@ * <p>This is the inverse operation of {@link Locale#toString Locale's toString}.@b@ * @param localeString the locale String, following {@code Locale's}@b@ * {@code toString()} format ("en", "en_UK", etc);@b@ * also accepts spaces as separators, as an alternative to underscores@b@ * @return a corresponding {@code Locale} instance@b@ * @throws IllegalArgumentException in case of an invalid locale specification@b@ */@b@ public static Locale parseLocaleString(String localeString) {@b@ String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);@b@ String language = (parts.length > 0 ? parts[0] : "");@b@ String country = (parts.length > 1 ? parts[1] : "");@b@ validateLocalePart(language);@b@ validateLocalePart(country);@b@ String variant = "";@b@ if (parts.length > 2) {@b@ // There is definitely a variant, and it is everything after the country@b@ // code sans the separator between the country code and the variant.@b@ int endIndexOfCountryCode = localeString.lastIndexOf(country) + country.length();@b@ // Strip off any leading '_' and whitespace, what's left is the variant.@b@ variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));@b@ if (variant.startsWith("_")) {@b@ variant = trimLeadingCharacter(variant, '_');@b@ }@b@ }@b@ return (language.length() > 0 ? new Locale(language, country, variant) : null);@b@ }@b@@b@ private static void validateLocalePart(String localePart) {@b@ for (int i = 0; i < localePart.length(); i++) {@b@ char ch = localePart.charAt(i);@b@ if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) {@b@ throw new IllegalArgumentException(@b@ "Locale part \"" + localePart + "\" contains invalid characters");@b@ }@b@ }@b@ }@b@@b@ /**@b@ * Determine the RFC 3066 compliant language tag,@b@ * as used for the HTTP "Accept-Language" header.@b@ * @param locale the Locale to transform to a language tag@b@ * @return the RFC 3066 compliant language tag as String@b@ */@b@ public static String toLanguageTag(Locale locale) {@b@ return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");@b@ }@b@@b@ /**@b@ * Parse the given {@code timeZoneString} value into a {@link TimeZone}.@b@ * @param timeZoneString the time zone String, following {@link TimeZone#getTimeZone(String)}@b@ * but throwing {@link IllegalArgumentException} in case of an invalid time zone specification@b@ * @return a corresponding {@link TimeZone} instance@b@ * @throws IllegalArgumentException in case of an invalid time zone specification@b@ */@b@ public static TimeZone parseTimeZoneString(String timeZoneString) {@b@ TimeZone timeZone = TimeZone.getTimeZone(timeZoneString);@b@ if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) {@b@ // We don't want that GMT fallback...@b@ throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'");@b@ }@b@ return timeZone;@b@ }@b@@b@@b@ //---------------------------------------------------------------------@b@ // Convenience methods for working with String arrays@b@ //---------------------------------------------------------------------@b@@b@ /**@b@ * Append the given String to the given String array, returning a new array@b@ * consisting of the input array contents plus the given String.@b@ * @param array the array to append to (can be {@code null})@b@ * @param str the String to append@b@ * @return the new array (never {@code null})@b@ */@b@ public static String[] addStringToArray(String[] array, String str) {@b@ if (ObjectUtils.isEmpty(array)) {@b@ return new String[] {str};@b@ }@b@ String[] newArr = new String[array.length + 1];@b@ System.arraycopy(array, 0, newArr, 0, array.length);@b@ newArr[array.length] = str;@b@ return newArr;@b@ }@b@@b@ /**@b@ * Concatenate the given String arrays into one,@b@ * with overlapping array elements included twice.@b@ * <p>The order of elements in the original arrays is preserved.@b@ * @param array1 the first array (can be {@code null})@b@ * @param array2 the second array (can be {@code null})@b@ * @return the new array ({@code null} if both given arrays were {@code null})@b@ */@b@ public static String[] concatenateStringArrays(String[] array1, String[] array2) {@b@ if (ObjectUtils.isEmpty(array1)) {@b@ return array2;@b@ }@b@ if (ObjectUtils.isEmpty(array2)) {@b@ return array1;@b@ }@b@ String[] newArr = new String[array1.length + array2.length];@b@ System.arraycopy(array1, 0, newArr, 0, array1.length);@b@ System.arraycopy(array2, 0, newArr, array1.length, array2.length);@b@ return newArr;@b@ }@b@@b@ /**@b@ * Merge the given String arrays into one, with overlapping@b@ * array elements only included once.@b@ * <p>The order of elements in the original arrays is preserved@b@ * (with the exception of overlapping elements, which are only@b@ * included on their first occurrence).@b@ * @param array1 the first array (can be {@code null})@b@ * @param array2 the second array (can be {@code null})@b@ * @return the new array ({@code null} if both given arrays were {@code null})@b@ */@b@ public static String[] mergeStringArrays(String[] array1, String[] array2) {@b@ if (ObjectUtils.isEmpty(array1)) {@b@ return array2;@b@ }@b@ if (ObjectUtils.isEmpty(array2)) {@b@ return array1;@b@ }@b@ List<String> result = new ArrayList<String>();@b@ result.addAll(Arrays.asList(array1));@b@ for (String str : array2) {@b@ if (!result.contains(str)) {@b@ result.add(str);@b@ }@b@ }@b@ return toStringArray(result);@b@ }@b@@b@ /**@b@ * Turn given source String array into sorted array.@b@ * @param array the source array@b@ * @return the sorted array (never {@code null})@b@ */@b@ public static String[] sortStringArray(String[] array) {@b@ if (ObjectUtils.isEmpty(array)) {@b@ return new String[0];@b@ }@b@ Arrays.sort(array);@b@ return array;@b@ }@b@@b@ /**@b@ * Copy the given Collection into a String array.@b@ * The Collection must contain String elements only.@b@ * @param collection the Collection to copy@b@ * @return the String array ({@code null} if the passed-in@b@ * Collection was {@code null})@b@ */@b@ public static String[] toStringArray(Collection<String> collection) {@b@ if (collection == null) {@b@ return null;@b@ }@b@ return collection.toArray(new String[collection.size()]);@b@ }@b@@b@ /**@b@ * Copy the given Enumeration into a String array.@b@ * The Enumeration must contain String elements only.@b@ * @param enumeration the Enumeration to copy@b@ * @return the String array ({@code null} if the passed-in@b@ * Enumeration was {@code null})@b@ */@b@ public static String[] toStringArray(Enumeration<String> enumeration) {@b@ if (enumeration == null) {@b@ return null;@b@ }@b@ List<String> list = Collections.list(enumeration);@b@ return list.toArray(new String[list.size()]);@b@ }@b@@b@ /**@b@ * Trim the elements of the given String array,@b@ * calling {@code String.trim()} on each of them.@b@ * @param array the original String array@b@ * @return the resulting array (of the same size) with trimmed elements@b@ */@b@ public static String[] trimArrayElements(String[] array) {@b@ if (ObjectUtils.isEmpty(array)) {@b@ return new String[0];@b@ }@b@ String[] result = new String[array.length];@b@ for (int i = 0; i < array.length; i++) {@b@ String element = array[i];@b@ result[i] = (element != null ? element.trim() : null);@b@ }@b@ return result;@b@ }@b@@b@ /**@b@ * Remove duplicate Strings from the given array.@b@ * Also sorts the array, as it uses a TreeSet.@b@ * @param array the String array@b@ * @return an array without duplicates, in natural sort order@b@ */@b@ public static String[] removeDuplicateStrings(String[] array) {@b@ if (ObjectUtils.isEmpty(array)) {@b@ return array;@b@ }@b@ Set<String> set = new TreeSet<String>();@b@ for (String element : array) {@b@ set.add(element);@b@ }@b@ return toStringArray(set);@b@ }@b@@b@ /**@b@ * Split a String at the first occurrence of the delimiter.@b@ * Does not include the delimiter in the result.@b@ * @param toSplit the string to split@b@ * @param delimiter to split the string up with@b@ * @return a two element array with index 0 being before the delimiter, and@b@ * index 1 being after the delimiter (neither element includes the delimiter);@b@ * or {@code null} if the delimiter wasn't found in the given input String@b@ */@b@ public static String[] split(String toSplit, String delimiter) {@b@ if (!hasLength(toSplit) || !hasLength(delimiter)) {@b@ return null;@b@ }@b@ int offset = toSplit.indexOf(delimiter);@b@ if (offset < 0) {@b@ return null;@b@ }@b@ String beforeDelimiter = toSplit.substring(0, offset);@b@ String afterDelimiter = toSplit.substring(offset + delimiter.length());@b@ return new String[] {beforeDelimiter, afterDelimiter};@b@ }@b@@b@ /**@b@ * Take an array Strings and split each element based on the given delimiter.@b@ * A {@code Properties} instance is then generated, with the left of the@b@ * delimiter providing the key, and the right of the delimiter providing the value.@b@ * <p>Will trim both the key and value before adding them to the@b@ * {@code Properties} instance.@b@ * @param array the array to process@b@ * @param delimiter to split each element using (typically the equals symbol)@b@ * @return a {@code Properties} instance representing the array contents,@b@ * or {@code null} if the array to process was null or empty@b@ */@b@ public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {@b@ return splitArrayElementsIntoProperties(array, delimiter, null);@b@ }@b@@b@ /**@b@ * Take an array Strings and split each element based on the given delimiter.@b@ * A {@code Properties} instance is then generated, with the left of the@b@ * delimiter providing the key, and the right of the delimiter providing the value.@b@ * <p>Will trim both the key and value before adding them to the@b@ * {@code Properties} instance.@b@ * @param array the array to process@b@ * @param delimiter to split each element using (typically the equals symbol)@b@ * @param charsToDelete one or more characters to remove from each element@b@ * prior to attempting the split operation (typically the quotation mark@b@ * symbol), or {@code null} if no removal should occur@b@ * @return a {@code Properties} instance representing the array contents,@b@ * or {@code null} if the array to process was {@code null} or empty@b@ */@b@ public static Properties splitArrayElementsIntoProperties(@b@ String[] array, String delimiter, String charsToDelete) {@b@@b@ if (ObjectUtils.isEmpty(array)) {@b@ return null;@b@ }@b@ Properties result = new Properties();@b@ for (String element : array) {@b@ if (charsToDelete != null) {@b@ element = deleteAny(element, charsToDelete);@b@ }@b@ String[] splittedElement = split(element, delimiter);@b@ if (splittedElement == null) {@b@ continue;@b@ }@b@ result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());@b@ }@b@ return result;@b@ }@b@@b@ /**@b@ * Tokenize the given String into a String array via a StringTokenizer.@b@ * Trims tokens and omits empty tokens.@b@ * <p>The given delimiters string is supposed to consist of any number of@b@ * delimiter characters. Each of those characters can be used to separate@b@ * tokens. A delimiter is always a single character; for multi-character@b@ * delimiters, consider using {@code delimitedListToStringArray}@b@ * @param str the String to tokenize@b@ * @param delimiters the delimiter characters, assembled as String@b@ * (each of those characters is individually considered as delimiter).@b@ * @return an array of the tokens@b@ * @see java.util.StringTokenizer@b@ * @see String#trim()@b@ * @see #delimitedListToStringArray@b@ */@b@ public static String[] tokenizeToStringArray(String str, String delimiters) {@b@ return tokenizeToStringArray(str, delimiters, true, true);@b@ }@b@@b@ /**@b@ * Tokenize the given String into a String array via a StringTokenizer.@b@ * <p>The given delimiters string is supposed to consist of any number of@b@ * delimiter characters. Each of those characters can be used to separate@b@ * tokens. A delimiter is always a single character; for multi-character@b@ * delimiters, consider using {@code delimitedListToStringArray}@b@ * @param str the String to tokenize@b@ * @param delimiters the delimiter characters, assembled as String@b@ * (each of those characters is individually considered as delimiter)@b@ * @param trimTokens trim the tokens via String's {@code trim}@b@ * @param ignoreEmptyTokens omit empty tokens from the result array@b@ * (only applies to tokens that are empty after trimming; StringTokenizer@b@ * will not consider subsequent delimiters as token in the first place).@b@ * @return an array of the tokens ({@code null} if the input String@b@ * was {@code null})@b@ * @see java.util.StringTokenizer@b@ * @see String#trim()@b@ * @see #delimitedListToStringArray@b@ */@b@ public static String[] tokenizeToStringArray(@b@ String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {@b@@b@ if (str == null) {@b@ return null;@b@ }@b@ StringTokenizer st = new StringTokenizer(str, delimiters);@b@ List<String> tokens = new ArrayList<String>();@b@ while (st.hasMoreTokens()) {@b@ String token = st.nextToken();@b@ if (trimTokens) {@b@ token = token.trim();@b@ }@b@ if (!ignoreEmptyTokens || token.length() > 0) {@b@ tokens.add(token);@b@ }@b@ }@b@ return toStringArray(tokens);@b@ }@b@@b@ /**@b@ * Take a String which is a delimited list and convert it to a String array.@b@ * <p>A single delimiter can consists of more than one character: It will still@b@ * be considered as single delimiter string, rather than as bunch of potential@b@ * delimiter characters - in contrast to {@code tokenizeToStringArray}.@b@ * @param str the input String@b@ * @param delimiter the delimiter between elements (this is a single delimiter,@b@ * rather than a bunch individual delimiter characters)@b@ * @return an array of the tokens in the list@b@ * @see #tokenizeToStringArray@b@ */@b@ public static String[] delimitedListToStringArray(String str, String delimiter) {@b@ return delimitedListToStringArray(str, delimiter, null);@b@ }@b@@b@ /**@b@ * Take a String which is a delimited list and convert it to a String array.@b@ * <p>A single delimiter can consists of more than one character: It will still@b@ * be considered as single delimiter string, rather than as bunch of potential@b@ * delimiter characters - in contrast to {@code tokenizeToStringArray}.@b@ * @param str the input String@b@ * @param delimiter the delimiter between elements (this is a single delimiter,@b@ * rather than a bunch individual delimiter characters)@b@ * @param charsToDelete a set of characters to delete. Useful for deleting unwanted@b@ * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.@b@ * @return an array of the tokens in the list@b@ * @see #tokenizeToStringArray@b@ */@b@ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {@b@ if (str == null) {@b@ return new String[0];@b@ }@b@ if (delimiter == null) {@b@ return new String[] {str};@b@ }@b@ List<String> result = new ArrayList<String>();@b@ if ("".equals(delimiter)) {@b@ for (int i = 0; i < str.length(); i++) {@b@ result.add(deleteAny(str.substring(i, i + 1), charsToDelete));@b@ }@b@ }@b@ else {@b@ int pos = 0;@b@ int delPos;@b@ while ((delPos = str.indexOf(delimiter, pos)) != -1) {@b@ result.add(deleteAny(str.substring(pos, delPos), charsToDelete));@b@ pos = delPos + delimiter.length();@b@ }@b@ if (str.length() > 0 && pos <= str.length()) {@b@ // Add rest of String, but not in case of empty input.@b@ result.add(deleteAny(str.substring(pos), charsToDelete));@b@ }@b@ }@b@ return toStringArray(result);@b@ }@b@@b@ /**@b@ * Convert a CSV list into an array of Strings.@b@ * @param str the input String@b@ * @return an array of Strings, or the empty array in case of empty input@b@ */@b@ public static String[] commaDelimitedListToStringArray(String str) {@b@ return delimitedListToStringArray(str, ",");@b@ }@b@@b@ /**@b@ * Convenience method to convert a CSV string list to a set.@b@ * Note that this will suppress duplicates.@b@ * @param str the input String@b@ * @return a Set of String entries in the list@b@ */@b@ public static Set<String> commaDelimitedListToSet(String str) {@b@ Set<String> set = new TreeSet<String>();@b@ String[] tokens = commaDelimitedListToStringArray(str);@b@ for (String token : tokens) {@b@ set.add(token);@b@ }@b@ return set;@b@ }@b@@b@ /**@b@ * Convenience method to return a Collection as a delimited (e.g. CSV)@b@ * String. E.g. useful for {@code toString()} implementations.@b@ * @param coll the Collection to display@b@ * @param delim the delimiter to use (probably a ",")@b@ * @param prefix the String to start each element with@b@ * @param suffix the String to end each element with@b@ * @return the delimited String@b@ */@b@ public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {@b@ if (CollectionUtils.isEmpty(coll)) {@b@ return "";@b@ }@b@ StringBuilder sb = new StringBuilder();@b@ Iterator<?> it = coll.iterator();@b@ while (it.hasNext()) {@b@ sb.append(prefix).append(it.next()).append(suffix);@b@ if (it.hasNext()) {@b@ sb.append(delim);@b@ }@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Convenience method to return a Collection as a delimited (e.g. CSV)@b@ * String. E.g. useful for {@code toString()} implementations.@b@ * @param coll the Collection to display@b@ * @param delim the delimiter to use (probably a ",")@b@ * @return the delimited String@b@ */@b@ public static String collectionToDelimitedString(Collection<?> coll, String delim) {@b@ return collectionToDelimitedString(coll, delim, "", "");@b@ }@b@@b@ /**@b@ * Convenience method to return a Collection as a CSV String.@b@ * E.g. useful for {@code toString()} implementations.@b@ * @param coll the Collection to display@b@ * @return the delimited String@b@ */@b@ public static String collectionToCommaDelimitedString(Collection<?> coll) {@b@ return collectionToDelimitedString(coll, ",");@b@ }@b@@b@ /**@b@ * Convenience method to return a String array as a delimited (e.g. CSV)@b@ * String. E.g. useful for {@code toString()} implementations.@b@ * @param arr the array to display@b@ * @param delim the delimiter to use (probably a ",")@b@ * @return the delimited String@b@ */@b@ public static String arrayToDelimitedString(Object[] arr, String delim) {@b@ if (ObjectUtils.isEmpty(arr)) {@b@ return "";@b@ }@b@ if (arr.length == 1) {@b@ return ObjectUtils.nullSafeToString(arr[0]);@b@ }@b@ StringBuilder sb = new StringBuilder();@b@ for (int i = 0; i < arr.length; i++) {@b@ if (i > 0) {@b@ sb.append(delim);@b@ }@b@ sb.append(arr[i]);@b@ }@b@ return sb.toString();@b@ }@b@@b@ /**@b@ * Convenience method to return a String array as a CSV String.@b@ * E.g. useful for {@code toString()} implementations.@b@ * @param arr the array to display@b@ * @return the delimited String@b@ */@b@ public static String arrayToCommaDelimitedString(Object[] arr) {@b@ return arrayToDelimitedString(arr, ",");@b@ }@b@}