spark.utils.StringUtils Maven / Gradle / Ivy
Show all versions of spark-core Show documentation
/*
* Copyright 2002-2013 the original author or authors.
*
* 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 spark.utils;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Miscellaneous {@link String} utility methods.
* Mainly for internal use within the framework; consider
* Jakarta's Commons Lang
* for a more comprehensive suite of String utilities.
*
This class delivers some simple functionality that should really
* be provided by the core Java {@code String} and {@link StringBuilder}
* classes, such as the ability to {@link #replace} all occurrences of a given
* substring in a target string. It also provides easy-to-use methods to convert
* between delimited strings, such as CSV strings, and collections and arrays.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Keith Donald
* @author Rob Harrop
* @author Rick Evans
* @author Arjen Poutsma
* Code copied from Spring source. Modifications made (mostly removal of methods) by Per Wendel.
*/
public abstract class StringUtils {
private static final String FOLDER_SEPARATOR = "/";
private static final String WINDOWS_FOLDER_SEPARATOR = "\\";
private static final String TOP_PATH = "..";
private static final String CURRENT_PATH = ".";
//---------------------------------------------------------------------
// General convenience methods for working with Strings
//---------------------------------------------------------------------
public static boolean isBlank(final CharSequence cs) {
int strLen;
if (cs == null || (strLen = cs.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(cs.charAt(i)) == false) {
return false;
}
}
return true;
}
public static boolean isNotBlank(final CharSequence cs) {
return !isBlank(cs);
}
/**
* Check whether the given String is empty.
*
This method accepts any Object as an argument, comparing it to
* {@code null} and the empty String. As a consequence, this method
* will never return {@code true} for a non-null non-String object.
*
The Object signature is useful for general attribute handling code
* that commonly deals with Strings but generally has to iterate over
* Objects since attributes may e.g. be primitive value objects as well.
*
* @param str the candidate String
* @return if the String is empty
* @since 3.2.1
*/
public static boolean isEmpty(Object str) {
return (str == null || "".equals(str));
}
/**
* Checks if the given String is not empty
*
* @param str the candidate String
* @return if the String is not empty
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
* Check that the given CharSequence is neither {@code null} nor of length 0.
*
* @param str the CharSequence to check (may be {@code null})
* @return {@code true} if the CharSequence is not null and has length
*/
public static boolean hasLength(CharSequence str) {
return (str != null && str.length() > 0);
}
/**
* Check that the given String is neither {@code null} nor of length 0.
* Note: Will return {@code true} for a String that purely consists of whitespace.
*
* @param str the String to check (may be {@code null})
* @return {@code true} if the String is not null and has length
* @see #hasLength(CharSequence)
*/
public static boolean hasLength(String str) {
return hasLength((CharSequence) str);
}
/**
* Replace all occurrences of a substring within a string with
* another string.
*
* @param inString String to examine
* @param oldPattern String to replace
* @param newPattern String to insert
* @return a String with the replacements
*/
public static String replace(String inString, String oldPattern, String newPattern) {
if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {
return inString;
}
StringBuilder sb = new StringBuilder();
int pos = 0; // our position in the old string
int index = inString.indexOf(oldPattern);
// the index of an occurrence we've found, or -1
int patLen = oldPattern.length();
while (index >= 0) {
sb.append(inString.substring(pos, index));
sb.append(newPattern);
pos = index + patLen;
index = inString.indexOf(oldPattern, pos);
}
sb.append(inString.substring(pos));
// remember to append any characters to the right of a match
return sb.toString();
}
/**
* Delete any character in a given String.
*
* @param inString the original String
* @param charsToDelete a set of characters to delete.
* E.g. "az\n" will delete 'a's, 'z's and new lines.
* @return the resulting String
*/
public static String deleteAny(String inString, String charsToDelete) {
if (!hasLength(inString) || !hasLength(charsToDelete)) {
return inString;
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
if (charsToDelete.indexOf(c) == -1) {
sb.append(c);
}
}
return sb.toString();
}
/**
* Extract the filename from the given path.
*
* @param path the file path (may be {@code null})
* @return the extracted filename, or {@code null} if none
*/
public static String getFilename(String path) {
if (path == null) {
return null;
}
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
}
/**
* Apply the given relative path to the given path,
* assuming standard Java folder separation (i.e. "/" separators).
*
* @param path the path to start from (usually a full file path)
* @param relativePath the relative path to apply
* (relative to the full file path above)
* @return the full file path that results from applying the relative path
*/
public static String applyRelativePath(String path, String relativePath) {
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
if (separatorIndex != -1) {
String newPath = path.substring(0, separatorIndex);
if (!relativePath.startsWith(FOLDER_SEPARATOR)) {
newPath += FOLDER_SEPARATOR;
}
return newPath + relativePath;
} else {
return relativePath;
}
}
/**
* Normalize the path by suppressing sequences like "path/.." and
* inner simple dots.
*
The result is convenient for path comparison. For other uses,
* notice that Windows separators ("\") are replaced by simple slashes.
*
* @param path the original path
* @return the normalized path
*/
public static String cleanPath(String path) {
if (path == null) {
return null;
}
String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);
// Strip prefix from path to analyze, to not treat it as part of the
// first path element. This is necessary to correctly parse paths like
// "file:core/../core/io/Resource.class", where the ".." should just
// strip the first "core" directory while keeping the "file:" prefix.
int prefixIndex = pathToUse.indexOf(":");
String prefix = "";
if (prefixIndex != -1) {
prefix = pathToUse.substring(0, prefixIndex + 1);
pathToUse = pathToUse.substring(prefixIndex + 1);
}
if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
prefix = prefix + FOLDER_SEPARATOR;
pathToUse = pathToUse.substring(1);
}
String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
List pathElements = new LinkedList<>();
int tops = 0;
for (int i = pathArray.length - 1; i >= 0; i--) {
String element = pathArray[i];
if (CURRENT_PATH.equals(element)) {
// Points to current directory - drop it.
} else if (TOP_PATH.equals(element)) {
// Registering top path found.
tops++;
} else {
if (tops > 0) {
// Merging path element with element corresponding to top path.
tops--;
} else {
// Normal path element found.
pathElements.add(0, element);
}
}
}
// Remaining top paths need to be retained.
for (int i = 0; i < tops; i++) {
pathElements.add(0, TOP_PATH);
}
return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
}
/**
* Copy the given Collection into a String array.
* The Collection must contain String elements only.
*
* @param collection the Collection to copy
* @return the String array ({@code null} if the passed-in
* Collection was {@code null})
*/
public static String[] toStringArray(Collection collection) {
if (collection == null) {
return null;
}
return collection.toArray(new String[collection.size()]);
}
/**
* Take a String which is a delimited list and convert it to a String array.
* A single delimiter can consists of more than one character: It will still
* be considered as single delimiter string, rather than as bunch of potential
* delimiter characters - in contrast to {@code tokenizeToStringArray}.
*
* @param str the input String
* @param delimiter the delimiter between elements (this is a single delimiter,
* rather than a bunch individual delimiter characters)
* @return an array of the tokens in the list
*/
public static String[] delimitedListToStringArray(String str, String delimiter) {
return delimitedListToStringArray(str, delimiter, null);
}
/**
* Take a String which is a delimited list and convert it to a String array.
*
A single delimiter can consists of more than one character: It will still
* be considered as single delimiter string, rather than as bunch of potential
* delimiter characters - in contrast to {@code tokenizeToStringArray}.
*
* @param str the input String
* @param delimiter the delimiter between elements (this is a single delimiter,
* rather than a bunch individual delimiter characters)
* @param charsToDelete a set of characters to delete. Useful for deleting unwanted
* line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.
* @return an array of the tokens in the list
*/
public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
if (str == null) {
return new String[0];
}
if (delimiter == null) {
return new String[] {str};
}
List result = new ArrayList<>();
if ("".equals(delimiter)) {
for (int i = 0; i < str.length(); i++) {
result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
}
} else {
int pos = 0;
int delPos;
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
pos = delPos + delimiter.length();
}
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
}
}
return toStringArray(result);
}
/**
* Convenience method to return a Collection as a delimited (e.g. CSV)
* String. E.g. useful for {@code toString()} implementations.
*
* @param coll the Collection to display
* @param delim the delimiter to use (probably a ",")
* @param prefix the String to start each element with
* @param suffix the String to end each element with
* @return the delimited String
*/
public static String collectionToDelimitedString(Collection> coll, String delim, String prefix, String suffix) {
if (CollectionUtils.isEmpty(coll)) {
return "";
}
StringBuilder sb = new StringBuilder();
Iterator> it = coll.iterator();
while (it.hasNext()) {
sb.append(prefix).append(it.next()).append(suffix);
if (it.hasNext()) {
sb.append(delim);
}
}
return sb.toString();
}
/**
* Convenience method to return a Collection as a delimited (e.g. CSV)
* String. E.g. useful for {@code toString()} implementations.
*
* @param coll the Collection to display
* @param delim the delimiter to use (probably a ",")
* @return the delimited String
*/
public static String collectionToDelimitedString(Collection> coll, String delim) {
return collectionToDelimitedString(coll, delim, "", "");
}
public static String toString(byte[] bytes, String encoding) {
String str;
if (encoding != null && Charset.isSupported(encoding)) {
try {
str = new String(bytes, encoding);
} catch (UnsupportedEncodingException e) {
// Uses same func as Charset.isSupported (cannot happen)
str = new String(bytes);
}
} else {
str = new String(bytes);
}
return str;
}
public static String removeLeadingAndTrailingSlashesFrom(String string) {
String trimmed = string;
if (trimmed.endsWith("/") || trimmed.endsWith("\\")) {
trimmed = trimmed.substring(0, trimmed.length() - 1);
}
if (trimmed.startsWith("/")) {
trimmed = trimmed.substring(1);
}
return trimmed;
}
}