Java tutorial
/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.util; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Locale; import org.apache.commons.lang3.StringUtils; /** * A number of String-specific utility methods for use by PMD or its IDE * plugins. * * @author BrianRemedios */ public final class StringUtil { private static final String[] EMPTY_STRINGS = new String[0]; private StringUtil() { } /** * Formats a double to a percentage, keeping {@code numDecimal} decimal places. * * @param val a double value between 0 and 1 * @param numDecimals The number of decimal places to keep * * @return A formatted string * * @throws IllegalArgumentException if the double to format is not between 0 and 1 */ public static String percentageString(double val, int numDecimals) { if (val < 0 || val > 1) { throw new IllegalArgumentException("Expected a number between 0 and 1"); } return String.format(Locale.ROOT, "%." + numDecimals + "f%%", 100 * val); } /** * Return whether the non-null text arg starts with any of the prefix * values. * * @param text * @param prefixes * * @return boolean * @deprecated {@link StringUtils#startsWithAny(CharSequence, CharSequence...)} */ @Deprecated public static boolean startsWithAny(String text, String... prefixes) { for (String prefix : prefixes) { if (text.startsWith(prefix)) { return true; } } return false; } /** * Returns whether the non-null text arg matches any of the test values. * * @param text * @param tests * * @return boolean */ public static boolean isAnyOf(String text, String... tests) { for (String test : tests) { if (text.equals(test)) { return true; } } return false; } /** * Checks for the existence of any of the listed prefixes on the non-null * text and removes them. * * @param text * @param prefixes * * @return String */ public static String withoutPrefixes(String text, String... prefixes) { for (String prefix : prefixes) { if (text.startsWith(prefix)) { return text.substring(prefix.length()); } } return text; } /** * @param value String * * @return boolean * @deprecated {@link StringUtils#isNotBlank(CharSequence)} */ @Deprecated public static boolean isNotEmpty(String value) { return !isEmpty(value); } /** * Returns true if the value arg is either null, empty, or full of * whitespace characters. More efficient that calling * (string).trim().length() == 0. * * @param value String to test * * @return <code>true</code> if the value is empty, <code>false</code> otherwise. * @deprecated {@link StringUtils#isBlank(CharSequence)} */ @Deprecated public static boolean isEmpty(String value) { return StringUtils.isBlank(value); } /** * Returns true if the argument is null or the empty string. * * @param value String to test * * @return True if the argument is null or the empty string * @deprecated {@link StringUtils#isEmpty(CharSequence)} */ @Deprecated public static boolean isMissing(String value) { return StringUtils.isEmpty(value); } /** * Returns true if both strings are effectively null or whitespace, returns * false otherwise if they have actual text that differs. * * @param a * @param b * * @return boolean */ @Deprecated public static boolean areSemanticEquals(String a, String b) { if (a == null) { return isEmpty(b); } if (b == null) { return isEmpty(a); } return a.equals(b); } /** * @param original String * @param oldString String * @param newString String * * @return String * @deprecated {@link StringUtils#replace(String, String, String)} */ @Deprecated public static String replaceString(final String original, final String oldString, final String newString) { int index = original.indexOf(oldString); if (index < 0) { return original; } else { final String replace = newString == null ? "" : newString; final StringBuilder buf = new StringBuilder(Math.max(16, original.length() + replace.length())); int last = 0; while (index != -1) { buf.append(original.substring(last, index)); buf.append(replace); last = index + oldString.length(); index = original.indexOf(oldString, last); } buf.append(original.substring(last)); return buf.toString(); } } /** * @param buf * @param src * @param supportUTF8 override the default setting, whether special characters should be replaced with entities ( * <code>false</code>) or should be included as is ( <code>true</code>). * */ public static void appendXmlEscaped(StringBuilder buf, String src, boolean supportUTF8) { char c; int i = 0; while (i < src.length()) { c = src.charAt(i++); if (c > '~') { // 126 if (!supportUTF8) { int codepoint = c; // surrogate characters are not allowed in XML if (Character.isHighSurrogate(c)) { char low = src.charAt(i++); codepoint = Character.toCodePoint(c, low); } buf.append("&#x").append(Integer.toHexString(codepoint)).append(';'); } else { buf.append(c); } } else if (c == '&') { buf.append("&"); } else if (c == '"') { buf.append("""); } else if (c == '<') { buf.append("<"); } else if (c == '>') { buf.append(">"); } else { buf.append(c); } } } /** * Replace some whitespace characters so they are visually apparent. * * @param o * * @return String */ public static String escapeWhitespace(Object o) { if (o == null) { return null; } String s = String.valueOf(o); s = s.replace("\n", "\\n"); s = s.replace("\r", "\\r"); s = s.replace("\t", "\\t"); return s; } /** * @param original String * @param oldChar char * @param newString String * * @return String * @deprecated {@link StringUtils#replace(String, String, String)} or {@link StringUtils#replaceChars(String, char, char)} */ @Deprecated public static String replaceString(final String original, char oldChar, final String newString) { int index = original.indexOf(oldChar); if (index < 0) { return original; } else { final String replace = newString == null ? "" : newString; final StringBuilder buf = new StringBuilder(Math.max(16, original.length() + replace.length())); int last = 0; while (index != -1) { buf.append(original.substring(last, index)); buf.append(replace); last = index + 1; index = original.indexOf(oldChar, last); } buf.append(original.substring(last)); return buf.toString(); } } /** * Parses the input source using the delimiter specified. This method is * much faster than using the StringTokenizer or String.split(char) approach * and serves as a replacement for String.split() for JDK1.3 that doesn't * have it. * * @param source String * @param delimiter char * * @return String[] * @deprecated {@link StringUtils#split(String, char)} */ @Deprecated public static String[] substringsOf(String source, char delimiter) { if (source == null || source.length() == 0) { return EMPTY_STRINGS; } int delimiterCount = 0; int length = source.length(); char[] chars = source.toCharArray(); for (int i = 0; i < length; i++) { if (chars[i] == delimiter) { delimiterCount++; } } if (delimiterCount == 0) { return new String[] { source }; } String[] results = new String[delimiterCount + 1]; int i = 0; int offset = 0; while (offset <= length) { int pos = source.indexOf(delimiter, offset); if (pos < 0) { pos = length; } results[i++] = pos == offset ? "" : source.substring(offset, pos); offset = pos + 1; } return results; } /** * Much more efficient than StringTokenizer. * * @param str String * @param separator char * * @return String[] * @deprecated {@link StringUtils#split(String, String)} */ @Deprecated public static String[] substringsOf(String str, String separator) { if (str == null || str.length() == 0) { return EMPTY_STRINGS; } int index = str.indexOf(separator); if (index == -1) { return new String[] { str }; } List<String> list = new ArrayList<>(); int currPos = 0; int len = separator.length(); while (index != -1) { list.add(str.substring(currPos, index)); currPos = index + len; index = str.indexOf(separator, currPos); } list.add(str.substring(currPos)); return list.toArray(new String[0]); } /** * Copies the elements returned by the iterator onto the string buffer each * delimited by the separator. * * @param sb StringBuffer * @param iter Iterator * @param separator String * @deprecated {@link StringUtils#join(Iterator, String)} */ @Deprecated public static void asStringOn(StringBuffer sb, Iterator<?> iter, String separator) { if (!iter.hasNext()) { return; } sb.append(iter.next()); while (iter.hasNext()) { sb.append(separator); sb.append(iter.next()); } } /** * Copies the array items onto the string builder each delimited by the * separator. Does nothing if the array is null or empty. * * @param sb StringBuilder * @param items Object[] * @param separator String * @deprecated {@link StringUtils#join(Iterable, String)} */ @Deprecated public static void asStringOn(StringBuilder sb, Object[] items, String separator) { if (items == null || items.length == 0) { return; } sb.append(items[0]); for (int i = 1; i < items.length; i++) { sb.append(separator); sb.append(items[i]); } } /** * Determine the maximum number of common leading whitespace characters the * strings share in the same sequence. Useful for determining how many * leading characters can be removed to shift all the text in the strings to * the left without misaligning them. * * @param strings String[] * * @return int */ public static int maxCommonLeadingWhitespaceForAll(String[] strings) { int shortest = lengthOfShortestIn(strings); if (shortest == 0) { return 0; } char[] matches = new char[shortest]; String str; for (int m = 0; m < matches.length; m++) { matches[m] = strings[0].charAt(m); if (!Character.isWhitespace(matches[m])) { return m; } for (int i = 0; i < strings.length; i++) { str = strings[i]; if (str.charAt(m) != matches[m]) { return m; } } } return shortest; } /** * Return the length of the shortest string in the array. If the collection * is empty or any one of them is null then it returns 0. * * @param strings String[] * * @return int */ public static int lengthOfShortestIn(String[] strings) { if (CollectionUtil.isEmpty(strings)) { return 0; } int minLength = Integer.MAX_VALUE; for (int i = 0; i < strings.length; i++) { if (strings[i] == null) { return 0; } minLength = Math.min(minLength, strings[i].length()); } return minLength; } /** * Trims off the leading characters off the strings up to the trimDepth * specified. Returns the same strings if trimDepth = 0 * * @param strings * @param trimDepth * * @return String[] */ public static String[] trimStartOn(String[] strings, int trimDepth) { if (trimDepth == 0) { return strings; } String[] results = new String[strings.length]; for (int i = 0; i < strings.length; i++) { results[i] = strings[i].substring(trimDepth); } return results; } /** * Left pads a string. * * @param s The String to pad * @param length The desired minimum length of the resulting padded String * * @return The resulting left padded String * @deprecated {@link StringUtils#leftPad(String, int)} */ @Deprecated public static String lpad(String s, int length) { String res = s; if (length - s.length() > 0) { char[] arr = new char[length - s.length()]; Arrays.fill(arr, ' '); res = new StringBuilder(length).append(arr).append(s).toString(); } return res; } /** * Are the two String values the same. The Strings can be optionally trimmed * before checking. The Strings can be optionally compared ignoring case. * The Strings can be have embedded whitespace standardized before * comparing. Two null values are treated as equal. * * @param s1 The first String. * @param s2 The second String. * @param trim Indicates if the Strings should be trimmed before comparison. * @param ignoreCase Indicates if the case of the Strings should ignored during comparison. * @param standardizeWhitespace Indicates if the embedded whitespace should be standardized before comparison. * * @return <code>true</code> if the Strings are the same, <code>false</code> otherwise. */ public static boolean isSame(String s1, String s2, boolean trim, boolean ignoreCase, boolean standardizeWhitespace) { if (s1 == null && s2 == null) { return true; } else if (s1 == null || s2 == null) { return false; } else { if (trim) { s1 = s1.trim(); s2 = s2.trim(); } if (standardizeWhitespace) { // Replace all whitespace with a standard single space // character. s1 = s1.replaceAll("\\s+", " "); s2 = s2.replaceAll("\\s+", " "); } return ignoreCase ? s1.equalsIgnoreCase(s2) : s1.equals(s2); } } /** * Formats all items onto a string with separators if more than one exists, * return an empty string if the items are null or empty. * * @param items Object[] * @param separator String * * @return String */ public static String asString(Object[] items, String separator) { if (items == null || items.length == 0) { return ""; } if (items.length == 1) { return items[0].toString(); } StringBuilder sb = new StringBuilder(items[0].toString()); for (int i = 1; i < items.length; i++) { sb.append(separator).append(items[i]); } return sb.toString(); } /** * Returns an empty array of string * * @return String */ public static String[] getEmptyStrings() { return EMPTY_STRINGS; } /** * Converts the given string to Camel case, * that is, removing all spaces, and capitalising * the first letter of each word except the first. * * <p>If the first word starts with an uppercase * letter, it's kept as is. This method can thus * be used for Pascal case too. * * @param name The string to convert * * @return The string converted to Camel case */ public static String toCamelCase(String name) { return toCamelCase(name, false); } /** * Converts the given string to Camel case, * that is, removing all spaces, and capitalising * the first letter of each word except the first. * * <p>The second parameter can be used to force the * words to be converted to lowercase before capitalising. * This can be useful if eg the first word contains * several uppercase letters. * * @param name The string to convert * @param forceLowerCase Whether to force removal of all upper * case letters except on word start * * @return The string converted to Camel case */ public static String toCamelCase(String name, boolean forceLowerCase) { StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (String word : name.trim().split("\\s++")) { String pretreated = forceLowerCase ? word.toLowerCase(Locale.ROOT) : word; if (isFirst) { sb.append(pretreated); isFirst = false; } else { sb.append(StringUtils.capitalize(pretreated)); } } return sb.toString(); } }