/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.util; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.Collection; import java.util.Vector; import java.util.stream.Collector; import java.util.stream.Collectors; import org.apache.tools.ant.BuildException; /** * A set of helper methods related to string manipulation. * */ public final class StringUtils { private static final long KILOBYTE = 1024; private static final long MEGABYTE = KILOBYTE * 1024; private static final long GIGABYTE = MEGABYTE * 1024; private static final long TERABYTE = GIGABYTE * 1024; private static final long PETABYTE = TERABYTE * 1024; /** * constructor to stop anyone instantiating the class */ private StringUtils() { } /** the line separator for this OS */ public static final String LINE_SEP = System.getProperty("line.separator"); /** * Splits up a string into a list of lines. It is equivalent * to <tt>split(data, '\n')</tt>. * @param data the string to split up into lines. * @return the list of lines available in the string. */ public static Vector<String> lineSplit(String data) { return split(data, '\n'); } /** * Splits up a string where elements are separated by a specific * character and return all elements. * @param data the string to split up. * @param ch the separator character. * @return the list of elements. */ public static Vector<String> split(String data, int ch) { Vector<String> elems = new Vector<String>(); int pos = -1; int i = 0; while ((pos = data.indexOf(ch, i)) != -1) { String elem = data.substring(i, pos); elems.addElement(elem); i = pos + 1; } elems.addElement(data.substring(i)); return elems; } /** * Replace occurrences into a string. * @param data the string to replace occurrences into * @param from the occurrence to replace. * @param to the occurrence to be used as a replacement. * @return the new string with replaced occurrences. * @deprecated Use {@link String#replace(CharSequence, CharSequence)} now. */ public static String replace(String data, String from, String to) { return data.replace(from, to); } /** * Convenient method to retrieve the full stacktrace from a given exception. * @param t the exception to get the stacktrace from. * @return the stacktrace from the given exception. */ public static String getStackTrace(Throwable t) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw, true); t.printStackTrace(pw); //NOSONAR pw.flush(); pw.close(); return sw.toString(); } /** * Checks that a string buffer ends up with a given string. It may sound * trivial with the existing * JDK API but the various implementation among JDKs can make those * methods extremely resource intensive * and perform poorly due to massive memory allocation and copying. See * @param buffer the buffer to perform the check on * @param suffix the suffix * @return <code>true</code> if the character sequence represented by the * argument is a suffix of the character sequence represented by * the StringBuffer object; <code>false</code> otherwise. Note that the * result will be <code>true</code> if the argument is the * empty string. */ public static boolean endsWith(StringBuffer buffer, String suffix) { if (suffix.length() > buffer.length()) { return false; } // this loop is done on purpose to avoid memory allocation performance // problems on various JDKs // StringBuffer.lastIndexOf() was introduced in jdk 1.4 and // implementation is ok though does allocation/copying // StringBuffer.toString().endsWith() does massive memory // allocation/copying on JDK 1.5 // See http://issues.apache.org/bugzilla/show_bug.cgi?id=37169 int endIndex = suffix.length() - 1; int bufferIndex = buffer.length() - 1; while (endIndex >= 0) { if (buffer.charAt(bufferIndex) != suffix.charAt(endIndex)) { return false; } bufferIndex--; endIndex--; } return true; } /** * xml does not do "c" like interpretation of strings. * i.e. \n\r\t etc. * this method processes \n, \r, \t, \f, \\ * also subs \s -> " \n\r\t\f" * a trailing '\' will be ignored * * @param input raw string with possible embedded '\'s * @return converted string * @since Ant 1.7 */ public static String resolveBackSlash(String input) { StringBuilder b = new StringBuilder(); boolean backSlashSeen = false; for (int i = 0; i < input.length(); ++i) { char c = input.charAt(i); if (!backSlashSeen) { if (c == '\\') { backSlashSeen = true; } else { b.append(c); } } else { switch (c) { case '\\': b.append((char) '\\'); break; case 'n': b.append((char) '\n'); break; case 'r': b.append((char) '\r'); break; case 't': b.append((char) '\t'); break; case 'f': b.append((char) '\f'); break; case 's': b.append(" \t\n\r\f"); break; default: b.append(c); } backSlashSeen = false; } } return b.toString(); } /** * Takes a human readable size representation eg 10K * a long value. Doesn't support 1.1K or other rational values. * @param humanSize the amount as a human readable string. * @return a long value representation * @throws Exception if there is a problem. * @since Ant 1.7 */ public static long parseHumanSizes(String humanSize) throws Exception { //NOSONAR long factor = 1L; char s = humanSize.charAt(0); switch (s) { case '+': humanSize = humanSize.substring(1); break; case '-': factor = -1L; humanSize = humanSize.substring(1); break; default: break; } //last character isn't a digit char c = humanSize.charAt(humanSize.length() - 1); if (!Character.isDigit(c)) { int trim = 1; switch (c) { case 'K': factor *= KILOBYTE; break; case 'M': factor *= MEGABYTE; break; case 'G': factor *= GIGABYTE; break; case 'T': factor *= TERABYTE; break; case 'P': factor *= PETABYTE; break; default: trim = 0; } humanSize = humanSize.substring(0, humanSize.length() - trim); } try { return factor * Long.parseLong(humanSize); } catch (NumberFormatException e) { throw new BuildException("Failed to parse \"" + humanSize + "\"", e); } } /** * Removes the suffix from a given string, if the string contains * that suffix. * @param string String for check * @param suffix Suffix to remove * @return the <i>string</i> with the <i>suffix</i> */ public static String removeSuffix(String string, String suffix) { if (string.endsWith(suffix)) { return string.substring(0, string.length() - suffix.length()); } return string; } /** * Removes the prefix from a given string, if the string contains * that prefix. * @param string String for check * @param prefix Prefix to remove * @return the <i>string</i> with the <i>prefix</i> */ public static String removePrefix(String string, String prefix) { if (string.startsWith(prefix)) { return string.substring(prefix.length()); } return string; } /** * Joins the string representation of the elements of a collection to * a joined string with a given separator. * @param collection Collection of the data to be joined (may be null) * @param separator Separator between elements (may be null) * @return the joined string */ public static String join(Collection<?> collection, CharSequence separator) { if (collection == null) { return ""; } return collection.stream().map(String::valueOf) .collect(joining(separator)); } /** * Joins the string representation of the elements of an array to * a joined string with a given separator. * @param array Array of the data to be joined (may be null) * @param separator Separator between elements (may be null) * @return the joined string */ public static String join(Object[] array, CharSequence separator) { if (array == null) { return ""; } return join(Arrays.asList(array), separator); } private static Collector<CharSequence,?,String> joining(CharSequence separator) { return separator == null ? Collectors.joining() : Collectors.joining(separator); } /** * @param inputString String to trim * @return null if the input string is null or empty or contain only empty spaces. * It returns the input string without leading and trailing spaces otherwise. * */ public static String trimToNull(String inputString) { if (inputString == null) { return null; } String tmpString = inputString.trim(); return tmpString.isEmpty() ? null : tmpString; } }