/**
* Copyright (c) 2011-2014, hubin (jobob@qq.com).
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.baomidou.mybatisplus.toolkit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>
* String 工具类
* </p>
*
* @author D.Yang
* @Date 2016-08-18
*/
public class StringUtils {
/**
* 空字符
*/
public static final String EMPTY = "";
/**
* 下划线字符
*/
public static final char UNDERLINE = '_';
/**
* 占位符
*/
public static final String PLACE_HOLDER = "{%s}";
private StringUtils() {
}
/**
* <p>
* 判断字符串是否为空
* </p>
*
* @param cs 需要判断字符串
* @return 判断结果
*/
public static boolean isEmpty(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))) {
return false;
}
}
return true;
}
/**
* <p>
* 判断字符串是否不为空
* </p>
*
* @param cs 需要判断字符串
* @return 判断结果
*/
public static boolean isNotEmpty(final CharSequence cs) {
return !isEmpty(cs);
}
/**
* <p>
* 字符串驼峰转下划线格式
* </p>
*
* @param param 需要转换的字符串
* @return 转换好的字符串
*/
public static String camelToUnderline(String param) {
if (isEmpty(param)) {
return EMPTY;
}
int len = param.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = param.charAt(i);
if (Character.isUpperCase(c) && i > 0) {
sb.append(UNDERLINE);
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* <p>
* 字符串下划线转驼峰格式
* </p>
*
* @param param 需要转换的字符串
* @return 转换好的字符串
*/
public static String underlineToCamel(String param) {
if (isEmpty(param)) {
return EMPTY;
}
String temp = param.toLowerCase();
int len = temp.length();
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = temp.charAt(i);
if (c == UNDERLINE) {
if (++i < len) {
sb.append(Character.toUpperCase(temp.charAt(i)));
}
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* <p>
* 首字母转换小写
* </p>
*
* @param param 需要转换的字符串
* @return 转换好的字符串
*/
public static String firstToLowerCase(String param) {
if (isEmpty(param)) {
return EMPTY;
}
StringBuilder sb = new StringBuilder(param.length());
sb.append(param.substring(0, 1).toLowerCase());
sb.append(param.substring(1));
return sb.toString();
}
/**
* <p>
* 判断字符串是否为纯大写字母
* </p>
*
* @param str 要匹配的字符串
* @return
*/
public static boolean isUpperCase(String str) {
return match("^[A-Z]+$", str);
}
/**
* <p>
* 正则表达式匹配
* </p>
*
* @param regex 正则表达式字符串
* @param str 要匹配的字符串
* @return 如果str 符合 regex的正则表达式格式,返回true, 否则返回 false;
*/
public static boolean match(String regex, String str) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
return matcher.matches();
}
/**
* <p>
* SQL 参数填充
* </p>
*
* @param content 填充内容
* @param args 填充参数
* @return
*/
public static String sqlArgsFill(String content, Object... args) {
if (StringUtils.isEmpty(content)) {
return null;
}
if (args != null) {
int length = args.length;
if (length >= 1) {
for (int i = 0; i < length; i++) {
content = content.replace(String.format(PLACE_HOLDER, i), sqlParam(args[i]));
}
}
}
return content;
}
/**
* 获取SQL PARAMS字符串
*
* @param obj
* @return
*/
public static String sqlParam(Object obj) {
String repStr;
if (obj instanceof Collection) {
repStr = StringUtils.quotaMarkList((Collection<?>) obj);
} else {
repStr = StringUtils.quotaMark(obj);
}
return repStr;
}
/**
* <p>
* 使用单引号包含字符串
* </p>
*
* @param obj 原字符串
* @return 单引号包含的原字符串
*/
public static String quotaMark(Object obj) {
String srcStr = String.valueOf(obj);
if (obj instanceof CharSequence) {
// fix #79
return StringEscape.escapeString(srcStr);
}
return srcStr;
}
/**
* <p>
* 使用单引号包含字符串
* </p>
*
* @param coll 集合
* @return 单引号包含的原字符串的集合形式
*/
public static String quotaMarkList(Collection<?> coll) {
StringBuilder sqlBuild = new StringBuilder();
sqlBuild.append("(");
int _size = coll.size();
int i = 0;
Iterator<?> iterator = coll.iterator();
while (iterator.hasNext()) {
String tempVal = StringUtils.quotaMark(iterator.next());
sqlBuild.append(tempVal);
if (i + 1 < _size) {
sqlBuild.append(",");
}
i++;
}
sqlBuild.append(")");
return sqlBuild.toString();
}
/**
* <p>
* 拼接字符串第二个字符串第一个字母大写
* </p>
*
* @param concatStr
* @param str
* @return
*/
public static String concatCapitalize(String concatStr, final String str) {
if (isEmpty(concatStr)) {
concatStr = EMPTY;
}
int strLen;
if (str == null || (strLen = str.length()) == 0) {
return str;
}
final char firstChar = str.charAt(0);
if (Character.isTitleCase(firstChar)) {
// already capitalized
return str;
}
StringBuilder sb = new StringBuilder(strLen);
sb.append(concatStr);
sb.append(Character.toTitleCase(firstChar));
sb.append(str.substring(1));
return sb.toString();
}
/**
* <p>
* 字符串第一个字母大写
* </p>
*
* @param str
* @return
*/
public static String capitalize(final String str) {
return concatCapitalize(null, str);
}
/**
* <p>
* 判断对象是否为空
* </p>
*
* @param object
* @return
*/
public static boolean checkValNotNull(Object object) {
if (object instanceof CharSequence) {
return isNotEmpty((CharSequence) object);
}
return object != null;
}
/**
* <p>
* 判断对象是否为空
* </p>
*
* @param object
* @return
*/
public static boolean checkValNull(Object object) {
return !checkValNotNull(object);
}
/**
* <p>
* 包含大写字母
* </p>
*
* @param word 待判断字符串
* @return
*/
public static boolean containsUpperCase(String word) {
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (Character.isUpperCase(c)) {
return true;
}
}
return false;
}
/**
* <p>
* 是否为大写命名
* </p>
*
* @param word 待判断字符串
* @return
*/
public static boolean isCapitalMode(String word) {
return null != word && word.matches("^[0-9A-Z/_]+$");
}
/**
* <p>
* Check if a String ends with a specified suffix.
* </p>
* <p>
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code>
* references are considered to be equal. The comparison is case sensitive.
* </p>
* <p>
* <pre>
* StringUtils.endsWith(null, null) = true
* StringUtils.endsWith(null, "abcdef") = false
* StringUtils.endsWith("def", null) = false
* StringUtils.endsWith("def", "abcdef") = true
* StringUtils.endsWith("def", "ABCDEF") = false
* </pre>
*
* @param str the String to check, may be null
* @param suffix the suffix to find, may be null
* @return <code>true</code> if the String ends with the suffix, case
* sensitive, or both <code>null</code>
* @see java.lang.String#endsWith(String)
* @since 2.4
*/
public static boolean endsWith(String str, String suffix) {
return endsWith(str, suffix, false);
}
/**
* <p>
* Case insensitive check if a String ends with a specified suffix.
* </p>
* <p>
* <p>
* <code>null</code>s are handled without exceptions. Two <code>null</code>
* references are considered to be equal. The comparison is case
* insensitive.
* </p>
* <p>
* <pre>
* StringUtils.endsWithIgnoreCase(null, null) = true
* StringUtils.endsWithIgnoreCase(null, "abcdef") = false
* StringUtils.endsWithIgnoreCase("def", null) = false
* StringUtils.endsWithIgnoreCase("def", "abcdef") = true
* StringUtils.endsWithIgnoreCase("def", "ABCDEF") = false
* </pre>
*
* @param str the String to check, may be null
* @param suffix the suffix to find, may be null
* @return <code>true</code> if the String ends with the suffix, case
* insensitive, or both <code>null</code>
* @see java.lang.String#endsWith(String)
* @since 2.4
*/
public static boolean endsWithIgnoreCase(String str, String suffix) {
return endsWith(str, suffix, true);
}
/**
* <p>
* Check if a String ends with a specified suffix (optionally case
* insensitive).
* </p>
*
* @param str the String to check, may be null
* @param suffix the suffix to find, may be null
* @param ignoreCase inidicates whether the compare should ignore case (case
* insensitive) or not.
* @return <code>true</code> if the String starts with the prefix or both
* <code>null</code>
* @see java.lang.String#endsWith(String)
*/
private static boolean endsWith(String str, String suffix, boolean ignoreCase) {
if (str == null || suffix == null) {
return (str == null && suffix == null);
}
if (suffix.length() > str.length()) {
return false;
}
int strOffset = str.length() - suffix.length();
return str.regionMatches(ignoreCase, strOffset, suffix, 0, suffix.length());
}
/**
* <p>
* Splits the provided text into an array, separators specified. This is an
* alternative to using StringTokenizer.
* </p>
* <p>
* <p>
* The separator is not included in the returned String array. Adjacent
* separators are treated as one separator. For more control over the split
* use the StrTokenizer class.
* </p>
* <p>
* <p>
* A {@code null} input String returns {@code null}. A {@code null}
* separatorChars splits on whitespace.
* </p>
* <p>
* <pre>
* StringUtils.split(null, *) = null
* StringUtils.split("", *) = []
* StringUtils.split("abc def", null) = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("abc def", " ") = ["abc", "def"]
* StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
* </pre>
*
* @param str the String to parse, may be null
* @param separatorChars the characters used as the delimiters, {@code null} splits on
* whitespace
* @return an array of parsed Strings, {@code null} if null String input
*/
public static String[] split(final String str, final String separatorChars) {
List<String> strings = splitWorker(str, separatorChars, -1, false);
return strings.toArray(new String[strings.size()]);
}
/**
* Performs the logic for the {@code split} and
* {@code splitPreserveAllTokens} methods that return a maximum array
* length.
*
* @param str the String to parse, may be {@code null}
* @param separatorChars the separate character
* @param max the maximum number of elements to include in the array. A zero
* or negative value implies no limit.
* @param preserveAllTokens if {@code true}, adjacent separators are treated as empty
* token separators; if {@code false}, adjacent separators are
* treated as one separator.
* @return an array of parsed Strings, {@code null} if null String input
*/
public static List<String> splitWorker(final String str, final String separatorChars, final int max,
final boolean preserveAllTokens) {
// Performance tuned for 2.0 (JDK1.4)
// Direct code is quicker than StringTokenizer.
// Also, StringTokenizer uses isSpace() not isWhitespace()
if (str == null) {
return null;
}
final int len = str.length();
if (len == 0) {
return Collections.emptyList();
}
final List<String> list = new ArrayList<>();
int sizePlus1 = 1;
int i = 0, start = 0;
boolean match = false;
boolean lastMatch = false;
if (separatorChars == null) {
// Null separator means use whitespace
while (i < len) {
if (Character.isWhitespace(str.charAt(i))) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
} else if (separatorChars.length() == 1) {
// Optimise 1 character case
final char sep = separatorChars.charAt(0);
while (i < len) {
if (str.charAt(i) == sep) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
} else {
// standard case
while (i < len) {
if (separatorChars.indexOf(str.charAt(i)) >= 0) {
if (match || preserveAllTokens) {
lastMatch = true;
if (sizePlus1++ == max) {
i = len;
lastMatch = false;
}
list.add(str.substring(start, i));
match = false;
}
start = ++i;
continue;
}
lastMatch = false;
match = true;
i++;
}
}
if (match || preserveAllTokens && lastMatch) {
list.add(str.substring(start, i));
}
return list;
}
/**
* 是否为CharSequence类型
*
* @param cls
* @return
*/
public static Boolean isCharSequence(Class<?> cls) {
return cls != null && CharSequence.class.isAssignableFrom(cls);
}
/**
* 是否为CharSequence类型
*
* @param propertyType
* @return
*/
public static Boolean isCharSequence(String propertyType) {
Class<?> cls;
try {
cls = Class.forName(propertyType);
} catch (ClassNotFoundException e) {
return false;
}
return isCharSequence(cls);
}
}