From 55b092665b65d489ad92513923577eeae782bb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A6=82=E6=A2=A6=E6=8A=80=E6=9C=AF?= <596392912@qq.com> Date: Sat, 9 Mar 2024 15:45:00 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E5=AE=8C=E5=96=84=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../springblade/core/tool/utils/CharPool.java | 59 ++- .../core/tool/utils/DesensitizationUtil.java | 295 +++++++++++++ .../core/tool/utils/StringUtil.java | 396 ++++++++++++++++++ 3 files changed, 738 insertions(+), 12 deletions(-) create mode 100644 blade-core-tool/src/main/java/org/springblade/core/tool/utils/DesensitizationUtil.java diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java index c8ac124..437f9dd 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/CharPool.java @@ -21,16 +21,51 @@ package org.springblade.core.tool.utils; * @author L.cm */ public interface CharPool { - - char UPPER_A = 'A'; - char LOWER_A = 'a'; - char UPPER_Z = 'Z'; - char LOWER_Z = 'z'; - char DOT = '.'; - char AT = '@'; - char LEFT_BRACE = '{'; - char RIGHT_BRACE = '}'; - char LEFT_BRACKET = '('; - char RIGHT_BRACKET = ')'; - + // @formatter:off + char UPPER_A = 'A'; + char LOWER_A = 'a'; + char UPPER_Z = 'Z'; + char LOWER_Z = 'z'; + char DOT = '.'; + char AT = '@'; + char LEFT_BRACE = '{'; + char RIGHT_BRACE = '}'; + char LEFT_BRACKET = '('; + char RIGHT_BRACKET = ')'; + char DASH = '-'; + char PERCENT = '%'; + char PIPE = '|'; + char PLUS = '+'; + char QUESTION_MARK = '?'; + char EXCLAMATION_MARK = '!'; + char EQUALS = '='; + char AMPERSAND = '&'; + char ASTERISK = '*'; + char STAR = ASTERISK; + char BACK_SLASH = '\\'; + char COLON = ':'; + char COMMA = ','; + char DOLLAR = '$'; + char SLASH = '/'; + char HASH = '#'; + char HAT = '^'; + char LEFT_CHEV = '<'; + char NEWLINE = '\n'; + char N = 'n'; + char Y = 'y'; + char QUOTE = '\"'; + char RETURN = '\r'; + char TAB = '\t'; + char RIGHT_CHEV = '>'; + char SEMICOLON = ';'; + char SINGLE_QUOTE = '\''; + char BACKTICK = '`'; + char SPACE = ' '; + char TILDA = '~'; + char LEFT_SQ_BRACKET = '['; + char RIGHT_SQ_BRACKET = ']'; + char UNDERSCORE = '_'; + char ONE = '1'; + char ZERO = '0'; + // @formatter:on } diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DesensitizationUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DesensitizationUtil.java new file mode 100644 index 0000000..4c2ec68 --- /dev/null +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/DesensitizationUtil.java @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2018-2028, DreamLu 卢春梦 (qq596392912@gmail.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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.springblade.core.tool.utils; + + +import org.springframework.lang.Nullable; +import org.springframework.util.StringUtils; + +import java.util.Arrays; + +/** + * 脱敏工具类 + * + * @author Katrel(同事) + * @author L.cm + */ +public class DesensitizationUtil { + + /** + * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**> + * + * @param fullName 全名 + * @return 脱敏后的字符串 + */ + @Nullable + public static String chineseName(@Nullable final String fullName) { + return sensitive(fullName, 1, 0); + } + + /** + * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762> + * + * @param id 身份证号 + * @return 脱敏后的字符串 + */ + @Nullable + public static String idCardNum(@Nullable final String id) { + return sensitive(id, 0, 4); + } + + /** + * [固定电话] 后四位,其他隐藏<例子:****1234> + * + * @param num 固定电话号 + * @return 脱敏后的字符串 + */ + @Nullable + public static String phoneNo(@Nullable final String num) { + return sensitive(num, 0, 4); + } + + /** + * [手机号码] 前三位,后四位,其他隐藏<例子:138****1234> + * + * @param num 手机号 + * @return 脱敏后的字符串 + */ + @Nullable + public static String mobileNo(@Nullable final String num) { + return sensitive(num, 3, 4); + } + + /** + * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****> + * + * @param address 地区 + * @param sensitiveSize 敏感信息长度 + * @return 脱敏后的字符串 + */ + @Nullable + public static String address(@Nullable final String address, final int sensitiveSize) { + return sensitive(address, 0, sensitiveSize); + } + + /** + * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com> + * + * @param email 邮箱 + * @return 脱敏后的字符串 + */ + @Nullable + public static String email(@Nullable final String email) { + if (email == null) { + return null; + } + if (!StringUtils.hasText(email)) { + return StringPool.EMPTY; + } + final int index = email.indexOf(CharPool.AT); + if (index <= 1) { + return email; + } else { + return sensitive(email, 1, email.length() - index); + } + } + + /** + * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:622260***********1234> + * + * @param cardNum 银行卡号 + * @return 脱敏后的字符串 + */ + @Nullable + public static String bankCard(@Nullable final String cardNum) { + return sensitive(cardNum, 6, 4); + } + + /** + * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********> + * + * @param code 银行联行号 + * @return 脱敏后的字符串 + */ + @Nullable + public static String cnApsCode(@Nullable final String code) { + return sensitive(code, 2, 0); + } + + /** + * 右边脱敏 + * + * @param sensitiveStr 待脱敏的字符串 + * @return 脱敏后的字符串 + */ + @Nullable + public static String right(@Nullable final String sensitiveStr) { + if (sensitiveStr == null) { + return null; + } + if (!StringUtils.hasText(sensitiveStr)) { + return StringPool.EMPTY; + } + int length = sensitiveStr.length(); + return sensitive(sensitiveStr, length / 2, 0); + } + + /** + * 左边脱敏 + * + * @param sensitiveStr 待脱敏的字符串 + * @return 脱敏后的字符串 + */ + @Nullable + public static String left(@Nullable final String sensitiveStr) { + if (sensitiveStr == null) { + return null; + } + if (!StringUtils.hasText(sensitiveStr)) { + return StringPool.EMPTY; + } + int length = sensitiveStr.length(); + return sensitive(sensitiveStr, 0, length / 2); + } + + /** + * 中间脱敏,保留两端 + * + * @param sensitiveStr 待脱敏的字符串 + * @return 脱敏后的字符串 + */ + @Nullable + public static String middle(@Nullable final String sensitiveStr) { + if (sensitiveStr == null) { + return null; + } + if (!StringUtils.hasText(sensitiveStr)) { + return StringPool.EMPTY; + } + int length = sensitiveStr.length(); + if (length < 3) { + return StringUtil.leftPad(StringPool.EMPTY, length, CharPool.STAR); + } else if (length < 6) { + // 小于6个字符,脱敏中间 + char[] chars = new char[length]; + int last = length - 1; + Arrays.fill(chars, 1, last, CharPool.STAR); + chars[0] = sensitiveStr.charAt(0); + chars[last] = sensitiveStr.charAt(last); + return new String(chars); + } else { + // 大于6个字符 + int fromLastLen = length / 3; + return sensitive(sensitiveStr, fromLastLen, fromLastLen); + } + } + + /** + * 全部脱敏 + * + * @param sensitiveStr 待脱敏的字符串 + * @return 脱敏后的字符串 + */ + @Nullable + public static String all(@Nullable final String sensitiveStr) { + return sensitive(sensitiveStr, 0, 0); + } + + /** + * 文本脱敏 + * + * @param str 字符串 + * @param fromIndex 开始的索引 + * @param lastSize 尾部长度 + * @return 脱敏后的字符串 + */ + @Nullable + public static String sensitive(@Nullable String str, int fromIndex, int lastSize) { + return sensitive(str, fromIndex, lastSize, CharPool.STAR); + } + + /** + * 文本脱敏 + * + * @param str 字符串 + * @param fromIndex 开始的索引 + * @param lastSize 尾部长度 + * @param padSize 填充的长度 + * @return 脱敏后的字符串 + */ + @Nullable + public static String sensitive(@Nullable String str, int fromIndex, int lastSize, int padSize) { + return sensitive(str, fromIndex, lastSize, CharPool.STAR, padSize); + } + + /** + * 文本脱敏 + * + * @param str 字符串 + * @param fromIndex 开始的索引 + * @param lastSize 尾部长度 + * @param padChar 填充的字符 + * @return 脱敏后的字符串 + */ + @Nullable + public static String sensitive(@Nullable String str, int fromIndex, int lastSize, char padChar) { + return sensitive(str, fromIndex, lastSize, padChar, -1); + } + + /** + * 文本脱敏 + * + * @param str 字符串 + * @param fromIndex 开始的索引 + * @param lastSize 尾部长度 + * @param padChar 填充的字符 + * @param padSize 填充的长度 + * @return 脱敏后的字符串 + */ + @Nullable + public static String sensitive(@Nullable String str, int fromIndex, int lastSize, char padChar, int padSize) { + if (str == null) { + return null; + } + if (!StringUtils.hasText(str)) { + return StringPool.EMPTY; + } + int length = str.length(); + // 全部脱敏 + if (fromIndex == 0 && lastSize == 0) { + int padSiz = padSize > 0 ? padSize : length; + return StringUtil.repeat(CharPool.STAR, padSiz); + } + int toIndex = length - lastSize; + int padSiz = padSize > 0 ? padSize : toIndex - fromIndex; + // 头部脱敏 + if (fromIndex == 0) { + String tail = str.substring(toIndex); + return StringUtil.repeat(padChar, padSiz).concat(tail); + } + // 尾部脱敏 + if (toIndex == length) { + String head = str.substring(0, fromIndex); + return head.concat(StringUtil.repeat(padChar, padSiz)); + } + // 中部 + String head = str.substring(0, fromIndex); + String tail = str.substring(toIndex); + return head + StringUtil.repeat(padChar, padSiz) + tail; + } + +} diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java index 6a86ecd..b6f1946 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java +++ b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/StringUtil.java @@ -19,6 +19,7 @@ import org.springblade.core.tool.support.StrFormatter; import org.springblade.core.tool.support.StrSpliter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; import org.springframework.web.util.HtmlUtils; import java.io.StringReader; @@ -36,6 +37,10 @@ import java.util.stream.Stream; public class StringUtil extends org.springframework.util.StringUtils { public static final int INDEX_NOT_FOUND = -1; + /** + *

The maximum size to which the padding constant(s) can expand.

+ */ + private static final int PAD_LIMIT = 8192; /** * Check whether the given {@code CharSequence} contains actual text. @@ -1473,5 +1478,396 @@ public class StringUtil extends org.springframework.util.StringUtils { } + /** + * 参考自 commons lang 微调 + * + *

Returns padding using the specified delimiter repeated + * to a given length.

+ * + *
+	 * StringUtils.repeat('e', 0)  = ""
+	 * StringUtils.repeat('e', 3)  = "eee"
+	 * StringUtils.repeat('e', -2) = ""
+	 * 
+ * + *

Note: this method does not support padding with + * Unicode Supplementary Characters + * as they require a pair of {@code char}s to be represented. + *

+ * + * @param ch character to repeat + * @param repeat number of times to repeat char, negative treated as zero + * @return String with repeated character + */ + public static String repeat(final char ch, final int repeat) { + if (repeat <= 0) { + return StringPool.EMPTY; + } + final char[] buf = new char[repeat]; + Arrays.fill(buf, ch); + return new String(buf); + } + + /** + * 参考自 commons lang 微调 + * + *

Gets the leftmost {@code len} characters of a String.

+ * + *

If {@code len} characters are not available, or the + * String is {@code null}, the String will be returned without + * an exception. An empty String is returned if len is negative.

+ * + *
+	 * StringUtils.left(null, *)    = null
+	 * StringUtils.left(*, -ve)     = ""
+	 * StringUtils.left("", *)      = ""
+	 * StringUtils.left("abc", 0)   = ""
+	 * StringUtils.left("abc", 2)   = "ab"
+	 * StringUtils.left("abc", 4)   = "abc"
+	 * 
+ * + * @param str the CharSequence to get the leftmost characters from, may be null + * @param len the length of the required String + * @return the leftmost characters, {@code null} if null String input + */ + @Nullable + public static String left(@Nullable final String str, final int len) { + if (str == null) { + return null; + } + if (len < 0) { + return StringPool.EMPTY; + } + if (str.length() <= len) { + return str; + } + return str.substring(0, len); + } + + /** + * 参考自 commons lang 微调 + * + *

Gets the rightmost {@code len} characters of a String.

+ * + *

If {@code len} characters are not available, or the String + * is {@code null}, the String will be returned without an + * an exception. An empty String is returned if len is negative.

+ * + *
+	 * StringUtils.right(null, *)    = null
+	 * StringUtils.right(*, -ve)     = ""
+	 * StringUtils.right("", *)      = ""
+	 * StringUtils.right("abc", 0)   = ""
+	 * StringUtils.right("abc", 2)   = "bc"
+	 * StringUtils.right("abc", 4)   = "abc"
+	 * 
+ * + * @param str the String to get the rightmost characters from, may be null + * @param len the length of the required String + * @return the rightmost characters, {@code null} if null String input + */ + @Nullable + public static String right(@Nullable final String str, final int len) { + if (str == null) { + return null; + } + if (len < 0) { + return StringPool.EMPTY; + } + int length = str.length(); + if (length <= len) { + return str; + } + return str.substring(length - len); + } + + /** + * 参考自 commons lang 微调 + * + *

Right pad a String with spaces (' ').

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+	 * StringUtils.rightPad(null, *)   = null
+	 * StringUtils.rightPad("", 3)     = "   "
+	 * StringUtils.rightPad("bat", 3)  = "bat"
+	 * StringUtils.rightPad("bat", 5)  = "bat  "
+	 * StringUtils.rightPad("bat", 1)  = "bat"
+	 * StringUtils.rightPad("bat", -1) = "bat"
+	 * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + @Nullable + public static String rightPad(@Nullable final String str, final int size) { + return rightPad(str, size, CharPool.SPACE); + } + + /** + * 参考自 commons lang 微调 + * + *

Right pad a String with a specified character.

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+	 * StringUtils.rightPad(null, *, *)     = null
+	 * StringUtils.rightPad("", 3, 'z')     = "zzz"
+	 * StringUtils.rightPad("bat", 3, 'z')  = "bat"
+	 * StringUtils.rightPad("bat", 5, 'z')  = "batzz"
+	 * StringUtils.rightPad("bat", 1, 'z')  = "bat"
+	 * StringUtils.rightPad("bat", -1, 'z') = "bat"
+	 * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + @Nullable + public static String rightPad(@Nullable final String str, final int size, final char padChar) { + if (str == null) { + return null; + } + final int pads = size - str.length(); + if (pads <= 0) { + // returns original String when possible + return str; + } + if (pads > PAD_LIMIT) { + return rightPad(str, size, String.valueOf(padChar)); + } + return str.concat(repeat(padChar, pads)); + } + + /** + * 参考自 commons lang 微调 + * + *

Right pad a String with a specified String.

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+	 * StringUtils.rightPad(null, *, *)      = null
+	 * StringUtils.rightPad("", 3, "z")      = "zzz"
+	 * StringUtils.rightPad("bat", 3, "yz")  = "bat"
+	 * StringUtils.rightPad("bat", 5, "yz")  = "batyz"
+	 * StringUtils.rightPad("bat", 8, "yz")  = "batyzyzy"
+	 * StringUtils.rightPad("bat", 1, "yz")  = "bat"
+	 * StringUtils.rightPad("bat", -1, "yz") = "bat"
+	 * StringUtils.rightPad("bat", 5, null)  = "bat  "
+	 * StringUtils.rightPad("bat", 5, "")    = "bat  "
+	 * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + @Nullable + public static String rightPad(@Nullable final String str, final int size, String padStr) { + if (str == null) { + return null; + } + if (!StringUtils.hasLength(padStr)) { + padStr = StringPool.SPACE; + } + final int padLen = padStr.length(); + final int strLen = str.length(); + final int pads = size - strLen; + if (pads <= 0) { + // returns original String when possible + return str; + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return rightPad(str, size, padStr.charAt(0)); + } + if (pads == padLen) { + return str.concat(padStr); + } else if (pads < padLen) { + return str.concat(padStr.substring(0, pads)); + } else { + final char[] padding = new char[pads]; + final char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return str.concat(new String(padding)); + } + } + + /** + * 参考自 commons lang 微调 + * + *

Left pad a String with spaces (' ').

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+	 * StringUtils.leftPad(null, *)   = null
+	 * StringUtils.leftPad("", 3)     = "   "
+	 * StringUtils.leftPad("bat", 3)  = "bat"
+	 * StringUtils.leftPad("bat", 5)  = "  bat"
+	 * StringUtils.leftPad("bat", 1)  = "bat"
+	 * StringUtils.leftPad("bat", -1) = "bat"
+	 * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + @Nullable + public static String leftPad(@Nullable final String str, final int size) { + return leftPad(str, size, CharPool.SPACE); + } + + /** + * 参考自 commons lang 微调 + * + *

Left pad a String with a specified character.

+ * + *

Pad to a size of {@code size}.

+ * + *
+	 * StringUtils.leftPad(null, *, *)     = null
+	 * StringUtils.leftPad("", 3, 'z')     = "zzz"
+	 * StringUtils.leftPad("bat", 3, 'z')  = "bat"
+	 * StringUtils.leftPad("bat", 5, 'z')  = "zzbat"
+	 * StringUtils.leftPad("bat", 1, 'z')  = "bat"
+	 * StringUtils.leftPad("bat", -1, 'z') = "bat"
+	 * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + * @since 2.0 + */ + @Nullable + public static String leftPad(@Nullable final String str, final int size, final char padChar) { + if (str == null) { + return null; + } + final int pads = size - str.length(); + if (pads <= 0) { + // returns original String when possible + return str; + } + if (pads > PAD_LIMIT) { + return leftPad(str, size, String.valueOf(padChar)); + } + return repeat(padChar, pads).concat(str); + } + + /** + * 参考自 commons lang 微调 + * + *

Left pad a String with a specified String.

+ * + *

Pad to a size of {@code size}.

+ * + *
+	 * StringUtils.leftPad(null, *, *)      = null
+	 * StringUtils.leftPad("", 3, "z")      = "zzz"
+	 * StringUtils.leftPad("bat", 3, "yz")  = "bat"
+	 * StringUtils.leftPad("bat", 5, "yz")  = "yzbat"
+	 * StringUtils.leftPad("bat", 8, "yz")  = "yzyzybat"
+	 * StringUtils.leftPad("bat", 1, "yz")  = "bat"
+	 * StringUtils.leftPad("bat", -1, "yz") = "bat"
+	 * StringUtils.leftPad("bat", 5, null)  = "  bat"
+	 * StringUtils.leftPad("bat", 5, "")    = "  bat"
+	 * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + @Nullable + public static String leftPad(@Nullable final String str, final int size, String padStr) { + if (str == null) { + return null; + } + if (!StringUtils.hasLength(padStr)) { + padStr = StringPool.SPACE; + } + final int padLen = padStr.length(); + final int strLen = str.length(); + final int pads = size - strLen; + if (pads <= 0) { + // returns original String when possible + return str; + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return leftPad(str, size, padStr.charAt(0)); + } + if (pads == padLen) { + return padStr.concat(str); + } else if (pads < padLen) { + return padStr.substring(0, pads).concat(str); + } else { + final char[] padding = new char[pads]; + final char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return new String(padding).concat(str); + } + } + + /** + * 参考自 commons lang 微调 + * + *

Gets {@code len} characters from the middle of a String.

+ * + *

If {@code len} characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is {@code null}, {@code null} will be returned. + * An empty String is returned if len is negative or exceeds the + * length of {@code str}.

+ * + *
+	 * StringUtils.mid(null, *, *)    = null
+	 * StringUtils.mid(*, *, -ve)     = ""
+	 * StringUtils.mid("", 0, *)      = ""
+	 * StringUtils.mid("abc", 0, 2)   = "ab"
+	 * StringUtils.mid("abc", 0, 4)   = "abc"
+	 * StringUtils.mid("abc", 2, 4)   = "c"
+	 * StringUtils.mid("abc", 4, 2)   = ""
+	 * StringUtils.mid("abc", -2, 2)  = "ab"
+	 * 
+ * + * @param str the String to get the characters from, may be null + * @param pos the position to start from, negative treated as zero + * @param len the length of the required String + * @return the middle characters, {@code null} if null String input + */ + @Nullable + public static String mid(@Nullable final String str, int pos, final int len) { + if (str == null) { + return null; + } + int length = str.length(); + if (len < 0 || pos > length) { + return StringPool.EMPTY; + } + if (pos < 0) { + pos = 0; + } + if (length <= pos + len) { + return str.substring(pos); + } + return str.substring(pos, pos + len); + } + }