StringUtils.java
/*
* 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 com.cronutils.utils;
import java.util.StringJoiner;
/**
* <p>Operations on {@link java.lang.String} that are
* {@code null} safe.</p>
*
* <ul>
* <li><b>IsEmpty</b>
* - checks if a String contains text</li>
* <li><b>IndexOfAny</b>
* - index-of any of a set of Strings</li>
* </ul>
*
* <p>The {@code StringUtils} class defines certain words related to
* String handling.</p>
*
* <ul>
* <li>null - {@code null}</li>
* <li>empty - a zero-length string ({@code ""})</li>
* <li>space - the space character ({@code ' '}, char 32)</li>
* <li>whitespace - the characters defined by {@link Character#isWhitespace(char)}</li>
* <li>trim - the characters <= 32 as in {@link String#trim()}</li>
* </ul>
*
* <p>{@code StringUtils} handles {@code null} input Strings quietly.
* That is to say that a {@code null} input will return {@code null}.
* Where a {@code boolean} or {@code int} is being returned
* details vary by method.</p>
*
* <p>A side effect of the {@code null} handling is that a
* {@code NullPointerException} should be considered a bug in
* {@code StringUtils}.</p>
*
* <p>Methods in this class give sample code to explain their operation.
* The symbol {@code *} is used to indicate any input including {@code null}.</p>
*
* <p>#ThreadSafe#</p>
*
* @version $Id: StringUtils.java 1648067 2014-12-27 16:45:42Z britter $
* @see java.lang.String
* @since 1.0
*/
//@Immutable
public class StringUtils {
/**
* The empty String {@code ""}.
* @since 2.0
*/
public static final String EMPTY = "";
private StringUtils() {
}
// Empty checks
//-----------------------------------------------------------------------
/**
* <p>Checks if a CharSequence is empty ("") or null.</p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>NOTE: This method changed in Lang version 2.0.
* It no longer trims the CharSequence.
* That functionality is available in isBlank().</p>
*
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is empty or null
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
*/
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
// ContainsAny
//-----------------------------------------------------------------------
/**
* <p>Checks if the CharSequence contains any character in the given.
* set of characters.</p>
*
* <p>A {@code null} CharSequence will return {@code false}.
* A {@code null} or zero length search array will return {@code false}.</p>
*
* <pre>
* StringUtils.containsAny(null, *) = false
* StringUtils.containsAny("", *) = false
* StringUtils.containsAny(*, null) = false
* StringUtils.containsAny(*, []) = false
* StringUtils.containsAny("zzabyycdxx",['z','a']) = true
* StringUtils.containsAny("zzabyycdxx",['b','y']) = true
* StringUtils.containsAny("aba", ['z']) = false
* </pre>
*
* @param cs the CharSequence to check, may be null
* @param searchChars the chars to search for, may be null
* @return the {@code true} if any of the chars are found,
* {@code false} if no match or null input
* @since 2.4
* @since 3.0 Changed signature from containsAny(String, char[]) to containsAny(CharSequence, char...)
*/
public static boolean containsAny(final CharSequence cs, final char... searchChars) {
if (isEmpty(cs) || searchChars == null || searchChars.length == 0) {
return false;
}
final int csLength = cs.length();
final int searchLength = searchChars.length;
final int csLast = csLength - 1;
final int searchLast = searchLength - 1;
for (int i = 0; i < csLength; i++) {
final char ch = cs.charAt(i);
for (int j = 0; j < searchLength; j++) {
if (searchChars[j] == ch) {
if (Character.isHighSurrogate(ch)) {
if (j == searchLast) {
// missing low surrogate, fine, like String.indexOf(String)
return true;
}
if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
return true;
}
} else {
// ch is in the Basic Multilingual Plane
return true;
}
}
}
}
return false;
}
/**
* <p>
* Checks if the CharSequence contains only Unicode digits. A decimal point is
* not a Unicode digit and returns false.
* </p>
*
* <p>
* {@code null} will return {@code false}. An empty CharSequence (length()=0)
* will return {@code false}.
* </p>
*
* <p>
* Note that the method does not allow for a leading sign, either positive or
* negative. Also, if a String passes the numeric test, it may still generate a
* NumberFormatException when parsed by Integer.parseInt or Long.parseLong, e.g.
* if the value is outside the range for int or long respectively.
* </p>
*
* <pre>
* StringUtils.isNumeric(null) = false
* StringUtils.isNumeric("") = false
* StringUtils.isNumeric(" ") = false
* StringUtils.isNumeric("123") = true
* StringUtils.isNumeric("\u0967\u0968\u0969") = true
* StringUtils.isNumeric("12 3") = false
* StringUtils.isNumeric("ab2c") = false
* StringUtils.isNumeric("12-3") = false
* StringUtils.isNumeric("12.3") = false
* StringUtils.isNumeric("-123") = false
* StringUtils.isNumeric("+123") = false
* </pre>
*
* @param cs the CharSequence to check, may be null
* @return {@code true} if only contains digits, and is non-null
* @since 3.0 Changed signature from isNumeric(String) to
* isNumeric(CharSequence)
* @since 3.0 Changed "" to return false and not true
*/
public static boolean isNumeric(final CharSequence cs) {
if (isEmpty(cs)) {
return false;
}
final int sz = cs.length();
for (int i = 0; i < sz; i++) {
if (!Character.isDigit(cs.charAt(i))) {
return false;
}
}
return true;
}
/**
* <p>
* Joins the elements of the provided array into a single String containing the
* provided list of elements.
* </p>
*
* <p>
* No delimiter is added before or after the list. A {@code null} separator is
* the same as an empty String (""). Null objects or empty strings within the
* array are represented by empty strings.
* </p>
*
* <pre>
* StringUtils.join(null, *) = null
* StringUtils.join([], *) = ""
* StringUtils.join([null], *) = "null"
* StringUtils.join(["a", "b", "c"], "--") = "a--b--c"
* StringUtils.join(["a", "b", "c"], null) = "abc"
* StringUtils.join(["a", "b", "c"], "") = "abc"
* StringUtils.join([null, "", "a"], ',') = "null,,a"
* </pre>
*
* @param parts - the array of values to join together, may be null
* @param separator - the separator character to use, null treated as ""
* @return the joined String, {@code null} if null array input
*/
public static String join(final Object[] parts, final String separator) {
if (parts == null) {
return null;
}
final StringJoiner joiner = new StringJoiner(separator == null ? EMPTY : separator);
for (final Object part : parts) {
joiner.add(String.valueOf(part));
}
return joiner.toString();
}
}