Java tutorial
//Copyright (C) 2010 Novabit Informationssysteme GmbH // //This file is part of Nuclos. // //Nuclos is free software: you can redistribute it and/or modify //it under the terms of the GNU Affero General Public License as published by //the Free Software Foundation, either version 3 of the License, or //(at your option) any later version. // //Nuclos is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU Affero General Public License for more details. // //You should have received a copy of the GNU Affero General Public License //along with Nuclos. If not, see <http://www.gnu.org/licenses/>. package org.nuclos.common2; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.SortedMap; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringEscapeUtils; import org.apache.log4j.Logger; import org.apache.xml.security.utils.Base64; import org.nuclos.common.NuclosFatalException; import org.nuclos.common.collection.Pair; import org.nuclos.common.collection.Transformer; import org.nuclos.common2.exception.CommonFatalException; /** * Utility methods for Strings. * <br> * <br>Created by Novabit Informationssysteme GmbH * <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a> * * @author <a href="mailto:Christoph.Radig@novabit.de">Christoph.Radig</a> * @version 01.00.00 */ public class StringUtils { private static final Logger LOG = Logger.getLogger(StringUtils.class); /** * the Euro symbol in Unicode. */ public static final char EURO_SYMBOL = '\u20AC'; private static final Pattern PARAM_PATTERN = Pattern.compile("\\$\\{([^}]*)\\}"); private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("\\#\\{([^}]*)\\}"); private StringUtils() { } public static Pair<String, String> getDot(String s) { if (s == null) return null; final Pair<String, String> result; final int dot = s.lastIndexOf('.'); if (dot >= 1) { result = new Pair<String, String>(s.substring(0, dot), s.substring(dot + 1)); } else if (dot == 0) { result = new Pair<String, String>("", s.substring(dot + 1)); } else { result = new Pair<String, String>("", s); } return result; } /** * @param s * @return Is s <code>null</code> or empty? * @postcondition result <--> (s == null) || (s.length() == 0) */ public static boolean isNullOrEmpty(String s) { return (s == null || s.length() == 0); } /** * @param s * @return Does the given String look empty? Is it <code>null</code> or does it contain only whitespace? * @postcondition (s == null) || (s.length() == 0) --> result */ public static boolean looksEmpty(String s) { final boolean result = (s == null) || (org.apache.commons.lang.StringUtils.deleteWhitespace(s).length() == 0); assert !((s == null) || (s.length() == 0)) || result; return result; } /** * @param s * @return <code>null</code>, if <code>s</code> is empty. <code>s</code> otherwise * @postcondition !"".equals(result) * @postcondition ("".equals(s)) --> (result == null) * @postcondition (!"".equals(s)) --> (result == s) */ public static String nullIfEmpty(String s) { final String result = (s != null && s.length() > 0) ? s : null; assert !"".equals(result); assert !("".equals(s)) || (result == null); assert !(!"".equals(s)) || (result == s); return result; } /** * @return <code>""</code>, if <code>s == null</code>. <code>s</code> otherwise * @postcondition result != null * @postcondition (s == null) --> result.equals("") * @postcondition (s != null) --> result.equals(s) */ public static String emptyIfNull(String s) { final String result = (s == null) ? "" : s; assert result != null; assert !(s == null) || result.equals(""); assert !(s != null) || result.equals(s); return result; } /** * @return a substring of s (or null, if the input was null), * ending at the first appearance of one of the given delimiters (or at the first whitespace if no delimiters were given) * @param s the input string */ public static String getFirstSubString(String s, Set<String> delimiters) { if (s == null) return null; String sub = s.trim(); int endIndex = -1; if (delimiters != null) { Iterator<String> iter = delimiters.iterator(); while (iter.hasNext()) { String delimiter = iter.next(); if (delimiter != null && (endIndex < 0 || sub.indexOf(delimiter) < endIndex)) endIndex = sub.indexOf(delimiter); } } if (endIndex < 0) endIndex = sub.indexOf(" "); return endIndex < 0 ? s : sub.substring(0, endIndex); } /** * @return the parameterized exception message * @param resId the resource id (intid) of the exception message */ public static String getParameterizedExceptionMessage(String resId, Object... params) { if (resId == null) return null; StringBuilder sb = new StringBuilder(resId); if (params != null) { for (Object param : params) sb.append(" {").append(param == null ? "" : param.toString()).append("}"); } return sb.toString(); } public static String getParameterizedExceptionMessageWithIds(String resId, Integer... params) { return getParameterizedExceptionMessage(resId, (Object[]) params); } /** * @param s * @return a trimmed version of the given String - leading and trailing whitespace are omitted. * @postcondition (s == null) --> result == null * @postcondition (s != null) --> result.equals(s.trim()) * @see String#trim() */ public static String trim(String s) { return (s == null) ? null : s.trim(); } public static String getModuleFieldName(String s) { if (s.indexOf(".") < 0) { return s; } StringTokenizer st = new StringTokenizer(s, "."); st.nextToken(); return st.nextToken(); } /** * @param s * @return a copy of the given string without any white space character. * @postcondition (s == null) --> result == null */ public static String deleteWhitespace(String s) { final String result; if (s == null) { result = null; } else { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { final char c = s.charAt(i); if (!Character.isWhitespace(c)) { sb.append(c); } } result = sb.toString(); } assert (s != null) || result == null; return result; } /** * splits the given text into separate lines by inserting newline characters between words. * Doesn't separate words. Each line has approximately iColumns. * @param sText * @param iColumns * @return a String containing the given text, split into several lines. */ public static String splitIntoSeparateLines(String sText, int iColumns) { //NUCLEUSINT-977 final StringBuilder sb = new StringBuilder(); if (sText != null) { final String sOptionalWhitespaceFollowedByNonWhiteSpace = "\\s*\\S*"; final Pattern pattern = Pattern.compile(sOptionalWhitespaceFollowedByNonWhiteSpace); final Matcher matcher = pattern.matcher(sText); StringBuilder sbLine = new StringBuilder(); while (matcher.find()) { final String sWord = sText.substring(matcher.start(), matcher.end()); if ((sbLine.length() == 0) || (sbLine.length() + sWord.length() <= iColumns)) { sbLine.append(sWord); } else { if (sb.length() > 0) { sb.append("\n"); } sb.append(sbLine); sbLine = new StringBuilder(sWord); } } // while if (sbLine.length() > 0) { if (sb.length() > 0) { sb.append("\n"); } sb.append(sbLine); } } return sb.toString(); } /** * clears the given array by writing null bytes to it. Useful for clearing sensitive data like passwords from memory. * @param acPassword */ public static void clear(char[] acPassword) { if (acPassword != null) { for (int i = 0; i < acPassword.length; i++) { acPassword[i] = 0; } } } /** * @param s * @return s with the first character of s (if any) in upper case. * @postcondition isNullOrEmpty(s) --> LangUtils.equals(result, s) */ public static String capitalized(String s) { return isNullOrEmpty(s) ? s : s.substring(0, 1).toUpperCase() + s.substring(1); } public static String encrypt(String x) { MessageDigest digest = null; try { digest = MessageDigest.getInstance("SHA-1"); digest.reset(); digest.update(x.getBytes()); return new String(digest.digest()); } catch (NoSuchAlgorithmException e) { throw new CommonFatalException(e.getMessage(), e); } } /** * @deprecated */ public static String encryptBase64(String x) { try { // previously used jboss class uses ISO-8859-1 encoding return new String(Base64.encode(encrypt(x).getBytes("ISO-8859-1"))); } catch (UnsupportedEncodingException e) { throw new NuclosFatalException(); } } public static String encryptPw(String username, String password) { return StringUtils.encryptBase64(username.toLowerCase() + ((password == null) ? "" : password)); } public static String defaultIfNull(String x, String sDefault) { if (isNullOrEmpty(x)) { return sDefault; } return x; } public static int countChar(String s, char c) { int result = 0; char[] chars = s.toCharArray(); for (int i = 0; i < chars.length; i++) { if (chars[i] == c) { result++; } } return result; } public static double[] getDoubleArrayFromString(String sComplete, char cSeparator) throws NumberFormatException { String sValue; int index = 0; int i = 0; double[] result = new double[StringUtils.countChar(sComplete, cSeparator) + 1]; index = sComplete.indexOf(cSeparator); while (index != -1) { sValue = sComplete.substring(0, index); sComplete = sComplete.substring(index + 1); result[i] = Double.parseDouble(sValue); i++; index = sComplete.indexOf(cSeparator); } result[i] = Double.parseDouble(sComplete); return result; } public static String xmlEncode(String s) { if (s == null) return null; StringBuilder sb = new StringBuilder(s.length() + 16); for (int i = 0, n = s.length(); i < n; i++) { char c = s.charAt(i); switch (c) { case '&': sb.append("&"); break; case '<': sb.append("<"); break; case '\'': sb.append("'"); break; case '\"': sb.append("""); break; default: if (c >= 128) sb.append(String.format("&#x%04x;", (int) c)); else sb.append(c); break; } } return sb.toString(); } /** * get all required attribute names from the user defined pattern * @param sPattern * @return */ public static Set<String> getFieldsFromTreeViewPattern(String sPattern) { final Set<String> stAttributeNames = new HashSet<String>(); int sidx = 0; while ((sidx = sPattern.indexOf("${", sidx)) >= 0) { int eidx = sPattern.indexOf("}", sidx); String key = sPattern.substring(sidx + 2, eidx); int ci = key.indexOf(':'); if (ci >= 0) key = key.substring(0, ci); stAttributeNames.add(key); sidx = eidx; } return stAttributeNames; } /** * Get all parameters in placeholder format <code>${...}</code>. * * @param s the input string * @return list of parameters in input string */ public static List<String> getParameters(String s) { List<String> result = new ArrayList<String>(); Matcher m = PARAM_PATTERN.matcher(s); while (m.find()) { result.add(m.group(1)); } return result; } /** * Replaces all embedded parameters of the form <code>${...}</code>. * The replacement string is determined by calling a given transformer. * @param s the input string * @param t the transformer which maps a parameter to its replacement * @return the given string with all parameters replaced */ public static String replaceParameters(String s, Transformer<String, String> t) { if (s == null) { return null; } StringBuffer sb = new StringBuffer(); Matcher m = PARAM_PATTERN.matcher(s); while (m.find()) { String resId = m.group(1); String repString = t.transform(resId); m.appendReplacement(sb, Matcher.quoteReplacement(repString)); } m.appendTail(sb); return sb.toString(); } public static String replaceParametersRecursively(String text, final Properties props) { return replaceParameters(text, new Transformer<String, String>() { LinkedList<String> nesting = new LinkedList<String>(); @Override public String transform(String p) { if (nesting.contains(p)) throw new IllegalArgumentException("Recursive property definition for " + nesting.getFirst()); String rtext = props.getProperty(p); if (rtext == null) throw new IllegalArgumentException("Missing property " + p); nesting.push(p); String r = replaceParameters(rtext, this); nesting.pop(); return r; } }); } /** * Decode a String with "classic java" unicode escape sequences * @param s the input string * @return the decoded string (or null, if the input was null) */ public static String unicodeDecode(String s) { if (s == null) return null; StringBuffer res = new StringBuffer(); Pattern p = Pattern.compile("\\\\u\\p{XDigit}{4}"); Matcher m = p.matcher(s); while (m.find()) { int v = Integer.parseInt(m.group().substring(2), 16); m.appendReplacement(res, Character.toString((char) v)); } return m.appendTail(res).toString(); } /** * As unicode-decode, but also parsing "\n" as newlines, "\t" as tabs, and * "\"" as a simple quote * @param s the input string * @return the decoded string (or null, if the input was null) */ public static String unicodeDecodeWithNewlines(String s) { if (s == null) return null; s = unicodeDecode(s); return s.replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t").replaceAll("\\\\\"", "\""); } public static String wildcardToRegex(CharSequence s) { StringBuilder sb = new StringBuilder(); int len = s.length(); for (int i = 0; i < len; i++) { char ch = s.charAt(i); switch (ch) { case '*': sb.append(".*"); break; case '?': sb.append("."); break; case '[': case ']': case '(': case ')': case '{': case '}': case '|': case '+': case '-': case '^': case '$': case '\\': case '.': sb.append("\\").append(ch); break; default: sb.append(ch); break; } } return sb.toString(); } public static <T> String join(String sep, T... s) { return org.apache.commons.lang.StringUtils.join(s, sep); } public static String join(String sep, Iterable<?> iter) { return org.apache.commons.lang.StringUtils.join(iter.iterator(), sep); } @Deprecated /** Use {@link StringUtils#join instead. That's the standard name for * such this operation. */ public static String arrayToString(String[] a, String separator) { if (a == null || separator == null) { return null; } final StringBuilder result = new StringBuilder(); if (a.length > 0) { result.append(a[0]); for (int i = 1; i < a.length; i++) { result.append(separator); result.append(a[i]); } } return result.toString(); } /** * Locale-independent variant of {@link String#toLowerCase()}. * This method works on {@code char} level and hence uses a locale-independent * character mapping as defined by the Unicode standard. */ public static String toLowerCase(String s) { StringBuilder sb = new StringBuilder(s); for (int i = 0, len = sb.length(); i < len; i++) { char ch = sb.charAt(i); char ch2 = Character.toLowerCase(ch); if (ch != ch2) sb.setCharAt(i, ch2); } return sb.toString(); } /** * Locale-independent variant of {@link String#toUpperCase()}. * This method works on {@code char} level and hence uses a locale-independent * character mapping as defined by the Unicode standard. */ public static String toUpperCase(String s) { StringBuilder sb = new StringBuilder(s); for (int i = 0, len = sb.length(); i < len; i++) { char ch = sb.charAt(i); char ch2 = Character.toUpperCase(ch); if (ch != ch2) sb.setCharAt(i, ch2); } return sb.toString(); } public static Integer parseInt(String s, Integer def) { try { return Integer.parseInt(s); } catch (NumberFormatException e) { return def; } } public static String[] splitWithMatches(Pattern pattern, CharSequence input) { return splitWithMatches(pattern, 0, input); } /** * Splits the given input sequence around matches of the given pattern but * also include the matches in the result. * All strings at odd indices are matches of the given pattern, all strings * at even indices are the splitted text fragments. */ public static String[] splitWithMatches(Pattern pattern, int group, CharSequence input) { Matcher m = pattern.matcher(input); List<String> list = new ArrayList<String>(); while (m.find()) { StringBuffer sb = new StringBuffer(); m.appendReplacement(sb, ""); list.add(sb.toString()); list.add(m.group(group)); } StringBuffer sb = new StringBuffer(); m.appendTail(sb); list.add(sb.toString()); return list.toArray(new String[list.size()]); } /** * Returns a living view of the given map containing all strings that starts with the given * prefix (including the prefix string itself). The view is backed by the original map. * <p> * Note: This method uses {@link SortedMap#subMap(Object, Object)} internally and works * <em>only</em> for sorted string maps with natural (lexiographic) ordering! */ public static <V> SortedMap<String, V> submapWithPrefix(SortedMap<String, V> map, String prefix) { if (map.comparator() != null) throw new IllegalArgumentException("only natural (lexiographic) ordering supported"); int length = prefix.length(); if (length == 0) return map; // create a string lhs which is the _least higher string_ for the prefix, i.e. // there is no other string value between any prefixed string and lhs w.r.t ordering. StringBuilder lhs = new StringBuilder(prefix); char ch = lhs.charAt(length - 1); lhs.setCharAt(length - 1, (char) (ch + 1)); return map.subMap(prefix, lhs.toString()); } /** * Concatenate strings to one html string that starts with <code><html><body></code> * and ends with <code></html></body></code>. The parts will be separated by <code><br/></code>. * If a part is itself a html string (recognized if it starts with <code><html></code>), the start and end tags will be removed prior to concatenation. * * @param parts * @return */ public static String concatHtml(String... parts) { StringBuilder result = new StringBuilder(); result.append("<html><body>"); int i = 0; for (String part : parts) { if (StringUtils.isNullOrEmpty(part)) { continue; } if (i > 0) { result.append("<br/>"); } if (part.regionMatches(true, 0, "<html>", 0, "<html>".length())) { result.append(part.replaceAll("\\<\\/?(?i:html)\\>", "").replaceAll("\\<\\/?(?i:body)\\>", "")); } else { result.append(StringEscapeUtils.escapeHtml(part)); } i++; } return result.append("</body></html>").toString(); } public static String concat(Object... parts) { final StringBuffer result = new StringBuffer(); for (Object part : parts) { String s = null; if (part instanceof String) { s = (String) part; } else if (part != null) { s = part.toString(); } if (StringUtils.isNullOrEmpty(s)) { continue; } result.append(part); } return result.toString(); } public static String logFormat(String name, Object... pairs) { final StringBuffer result = new StringBuffer(); result.append(name); result.append('['); for (int i = 0; i < pairs.length; i++) { result.append(pairs[i]); if (i != pairs.length - 1) { result.append(i % 2 == 0 ? '=' : ','); } } result.append(']'); return result.toString(); } public static String trimInvalidCharactersInFilename(String sFilename) { // this is very ugly. regexp should be used here ! String invalidChars = "\\/:*?\"<>|(){}[]"; char ch[] = invalidChars.toCharArray(); for (int i = 0; i < ch.length; i++) { sFilename = sFilename.replace(ch[i], '_'); } sFilename = sFilename.replace(' ', '_'); return sFilename.trim(); } /** * * @param s1 * @param s2 * @return */ public static boolean equalsIgnoreCase(String s1, String s2) { return (s1 == null) ? (s2 == null) : s1.equalsIgnoreCase(s2); } public static int compareIgnoreCase(String s1, String s2) { return (s1 == null) ? ((s2 == null) ? 0 : 1) : s1.compareToIgnoreCase(s2); } public static boolean equals(String s1, String s2) { if (s1 == null) return s2 == null; else { return LangUtils.getDefaultCollator().equals(s1, s2); } } public static int compare(String s1, String s2) { if (s1 == null) { return (s2 == null) ? 0 : 1; } else { return (s2 == null) ? -1 : LangUtils.getDefaultCollator().compare(s1, s2); } } private static final int MAX_SQL_ID_LENGTH = 21; /** * Make a valid SQL identifier (e.g. for an SQL alias) from an arbitrary String. */ public static String makeSQLIdentifierFrom(String... strings) { if (strings == null || strings.length == 0 || strings.length >= MAX_SQL_ID_LENGTH) { throw new IllegalArgumentException(); } final StringBuilder result = new StringBuilder(); final int slen = strings.length; int i = slen; for (String s : strings) { int ilen = (MAX_SQL_ID_LENGTH - result.length()) / i; if (ilen < 1) ilen = 1; makeSQLIdentifierFrom(result, s, ilen); --i; } if (result.length() > MAX_SQL_ID_LENGTH) result.setLength(MAX_SQL_ID_LENGTH); return result.toString(); } private static void makeSQLIdentifierFrom(StringBuilder result, String s, int maxLen) { if (maxLen < 1) throw new IllegalArgumentException(); final int len = s.length(); final int max = result.length() + maxLen; for (int i = 0; i < len; ++i) { final boolean accept; int c = s.codePointAt(i); if (Character.isSupplementaryCodePoint(c)) { ++i; } if (c >= 'A' && c <= 'Z') { accept = true; } else if (c >= 'a' && c <= 'z') { accept = true; } else if (c >= '0' && c <= '9') { accept = true; } else { switch (c) { case '_': accept = true; break; case ' ': c = '_'; accept = true; break; // german umlaut support case '\u00e4': c = 'a'; accept = true; break; case '\u00f6': c = 'o'; accept = true; break; case '\u00fc': c = 'u'; accept = true; break; case '\u00df': c = 's'; accept = true; break; case '\u00c4': c = 'A'; accept = true; break; case '\u00d6': c = 'O'; accept = true; break; case '\u00dc': c = 'U'; accept = true; break; default: accept = false; } } if (accept) { result.append((char) c); } } if (result.length() > max) result.setLength(max); } public static String[] parseIdentifier(String identifier) { Matcher m = IDENTIFIER_PATTERN.matcher(identifier); if (m.matches()) { String s = identifier.substring(2, identifier.length() - 1); return s.split("\\."); } else { return null; } } private static final Pattern NEW_LINES = Pattern.compile("\\n|\\r\\n?"); public static int countLines(CharSequence cs) { int count = 0; Matcher matcher = NEW_LINES.matcher(cs); while (matcher.find()) count++; return count; } } // class StringUtils