Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:29 + * @since 0.0.1 + */ @SpringBootApplication public class AosFileIdentifyApplication { + /** + * Main + * + * @param args args + * @since 0.0.1 + */ public static void main(String[] args) { SpringApplication.run(AosFileIdentifyApplication.class, args); } diff --git a/src/main/java/com/aos/fileidentify/config/Knife4jConfiguration.java b/src/main/java/com/aos/fileidentify/config/Knife4jConfiguration.java index e5cacf7..6af32d1 100644 --- a/src/main/java/com/aos/fileidentify/config/Knife4jConfiguration.java +++ b/src/main/java/com/aos/fileidentify/config/Knife4jConfiguration.java @@ -24,6 +24,12 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2; @EnableSwagger2 public class Knife4jConfiguration { + /** + * Default api 2 + * + * @return the docket + * @since 0.0.1 + */ @Bean(value = "defaultApi2") public Docket defaultApi2() { Docket docket = new Docket(DocumentationType.SWAGGER_2) diff --git a/src/main/java/com/aos/fileidentify/controller/FileIdentifyController.java b/src/main/java/com/aos/fileidentify/controller/FileIdentifyController.java index 9ed2a64..7006450 100644 --- a/src/main/java/com/aos/fileidentify/controller/FileIdentifyController.java +++ b/src/main/java/com/aos/fileidentify/controller/FileIdentifyController.java @@ -26,6 +26,7 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class FileIdentifyController { + /** File identify service */ private final FileIdentifyService fileIdentifyService; diff --git a/src/main/java/com/aos/fileidentify/converter/SerializableStringConverter.java b/src/main/java/com/aos/fileidentify/converter/SerializableStringConverter.java new file mode 100644 index 0000000..bc13fb5 --- /dev/null +++ b/src/main/java/com/aos/fileidentify/converter/SerializableStringConverter.java @@ -0,0 +1,60 @@ +package com.aos.fileidentify.converter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import java.io.Serializable; + +/** + *Company: 成都返空汇网络技术有限公司
+ *Description: 用于EsayExel中转换实现{@link Serializable}的字段,将{@link Serializable#toString()}输出到excel中。 + *
+ * + * @author xianzhiping + * @version 2.3.0 + * @email "mailto:xianzhiping@fkhwl.com" + * @date 2022.01.19 16:49 + * @since 2.3.0 + */ +public class SerializableStringConverter implements ConverterCompany: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:29 + * @since 0.0.1 + */ @Data public class BaseEntity { + /** Id */ private String id; + /** Deleted */ private String deleted; + /** Create time */ private Date create_time; + /** Update time */ private Date update_time; } diff --git a/src/main/java/com/aos/fileidentify/entity/EmployeeApproval.java b/src/main/java/com/aos/fileidentify/entity/EmployeeApproval.java index bb141c6..b7fdae7 100644 --- a/src/main/java/com/aos/fileidentify/entity/EmployeeApproval.java +++ b/src/main/java/com/aos/fileidentify/entity/EmployeeApproval.java @@ -5,55 +5,89 @@ import lombok.Data; import javax.validation.constraints.NotNull; +/** + *Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:29 + * @since 0.0.1 + */ @Data public class EmployeeApproval extends BaseEntity { + /** Employee key */ private String employeeKey; + /** Name */ @ExcelProperty("姓名") @NotNull private String name; + /** Age */ @ExcelProperty("年龄") private String age; + /** Department */ @ExcelProperty("部门") private String department; + /** Project */ @ExcelProperty("项目") private String project; + /** Branch */ @ExcelProperty("单位") private String branch; + /** Current position */ @ExcelProperty("现任职务") private String currentPosition; + /** Gender */ @ExcelProperty("性别") private String gender; + /** Ethnic */ @ExcelProperty("民族") private String ethnic; + /** Birth */ @ExcelProperty("出生年月") private String birth; + /** Highest education */ @ExcelProperty("最高学历") private String highestEducation; + /** Full time edu qualifications */ @ExcelProperty("全日制教育学历学位") private String fullTimeEduQualifications; + /** Full time edu institutions */ @ExcelProperty("全日制教育毕业院校及专业") private String fullTimeEduInstitutions; + /** On job edu qualifications */ @ExcelProperty("在职教育学历学位") private String onJobEduQualifications; + /** On job edu institutions */ @ExcelProperty("在职教育毕业院校及专业") private String onJobEduInstitutions; + /** Join party time */ @ExcelProperty("入党时间") private String joinPartyTime; + /** Join work time */ @ExcelProperty("参加工作时间") private String joinWorkTime; + /** Current position time */ @ExcelProperty("任现职时间") private String currentPositionTime; + /** Current rank time */ @ExcelProperty("任现职级时间") private String currentRankTime; + /** Managers type */ @ExcelProperty("经理人性质") private String managersType; + /** Is concurrently */ @ExcelProperty("兼职") private String isConcurrently; + /** Appointment documents */ @ExcelProperty("任命文件") private String appointmentDocuments; + /** Post type */ @ExcelProperty("岗位类型") private String postType; + /** Remark */ private String remark; diff --git a/src/main/java/com/aos/fileidentify/entity/EmployeeChange.java b/src/main/java/com/aos/fileidentify/entity/EmployeeChange.java index 28defde..0c308b4 100644 --- a/src/main/java/com/aos/fileidentify/entity/EmployeeChange.java +++ b/src/main/java/com/aos/fileidentify/entity/EmployeeChange.java @@ -5,13 +5,30 @@ import lombok.Data; import java.util.Date; +/** + *Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:29 + * @since 0.0.1 + */ @Data public class EmployeeChange extends BaseEntity { + /** Employee key */ private String employee_key; + /** Change type */ private String change_type; + /** Source branch */ private String source_branch; + /** Transfer branch */ private String transfer_branch; + /** Change time */ private Date change_time; + /** Change branch */ private String change_branch; + /** Change position */ private String change_position; } diff --git a/src/main/java/com/aos/fileidentify/entity/dto/EmployeeChangeDTO.java b/src/main/java/com/aos/fileidentify/entity/dto/EmployeeChangeDTO.java index 7d4f05f..f8e1bba 100644 --- a/src/main/java/com/aos/fileidentify/entity/dto/EmployeeChangeDTO.java +++ b/src/main/java/com/aos/fileidentify/entity/dto/EmployeeChangeDTO.java @@ -6,51 +6,83 @@ import lombok.Data; import javax.validation.constraints.NotNull; import java.util.Date; +/** + *Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:29 + * @since 0.0.1 + */ @Data public class EmployeeChangeDTO { + /** Employee key */ private String employee_key; + /** Change type */ @ExcelProperty("异动类型") private String change_type; + /** Change time */ @ExcelProperty("异动时间") private Date change_time; + /** Change branch */ @ExcelProperty("拟任免部门") private String change_branch; + /** Change position */ @ExcelProperty("拟任免职务") private String change_position; + /** Name */ @ExcelProperty("姓名") @NotNull private String name; + /** Branch */ @ExcelProperty("单位") private String branch; + /** Current position */ @ExcelProperty("原职务") private String currentPosition; + /** Gender */ @ExcelProperty("性别") private String gender; + /** Ethnic */ @ExcelProperty("民族") private String ethnic; + /** Birth */ @ExcelProperty("出生年月") private String birth; + /** Full time edu qualifications */ @ExcelProperty("全日制教育学历学位") private String fullTimeEduQualifications; + /** Full time edu institutions */ @ExcelProperty("全日制教育毕业院校及专业") private String fullTimeEduInstitutions; + /** On job edu qualifications */ @ExcelProperty("在职教育学历学位") private String onJobEduQualifications; + /** On job edu institutions */ @ExcelProperty("在职教育毕业院校及专业") private String onJobEduInstitutions; + /** Age */ @ExcelProperty("年龄") private String age; + /** Join party time */ @ExcelProperty("入党时间") private String joinPartyTime; + /** Join work time */ @ExcelProperty("参加工作时间") private String joinWorkTime; + /** Managers type */ @ExcelProperty("经理人性质") private String managersType; + /** Is concurrently */ @ExcelProperty("兼职") private String isConcurrently; + /** Appointment documents */ @ExcelProperty("任命文件") private String appointmentDocuments; + /** Post type */ @ExcelProperty("岗位类型") private String postType; diff --git a/src/main/java/com/aos/fileidentify/service/impl/FileIdentifyServiceImpl.java b/src/main/java/com/aos/fileidentify/service/impl/FileIdentifyServiceImpl.java index 5a28e25..9bfc6e7 100644 --- a/src/main/java/com/aos/fileidentify/service/impl/FileIdentifyServiceImpl.java +++ b/src/main/java/com/aos/fileidentify/service/impl/FileIdentifyServiceImpl.java @@ -69,6 +69,12 @@ public class FileIdentifyServiceImpl implements FileIdentifyService { } + /** + * Process excel + * + * @param file file + * @since 0.0.1 + */ @Override public void processExcel(MultipartFile file) { Assert.notNull(file, "上传数据为空"); diff --git a/src/main/java/com/aos/fileidentify/util/ExcelUtils.java b/src/main/java/com/aos/fileidentify/util/ExcelUtils.java new file mode 100644 index 0000000..e89c2a7 --- /dev/null +++ b/src/main/java/com/aos/fileidentify/util/ExcelUtils.java @@ -0,0 +1,306 @@ +package com.aos.fileidentify.util; + +import com.google.common.collect.Lists; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.aos.fileidentify.converter.SerializableStringConverter; +import com.aos.fileidentify.util.function.DataSupplier; +import com.baomidou.mybatisplus.core.metadata.IPage; + +import com.baomidou.mybatisplus.core.toolkit.StringPool; + + +import org.springframework.http.HttpHeaders; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import javax.servlet.http.HttpServletResponse; + +import lombok.SneakyThrows; +import lombok.experimental.UtilityClass; + + +/** + *Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:29 + * @since 0.0.1 + */ +@UtilityClass +public class ExcelUtils { + /** + * excel导出content-type + */ + public static final String EXCEL_CONTENT_TYPE = "application/vnd.ms-excel"; + /** + * 每个工作表默认行数 + */ + private static final int DEFAULT_EXCEL_SHEET_SIZE = 200000; + /** + * 默认查询数据sql每页条数 + */ + private static final int DEFAULT_PAGE_LIMIT = 20000; + + /** + * 默认一次导出最大条数 + */ + private static final int DEFAULT_MAX_EXPORT_SIZE = 20000; + + /** + * 查询数据sql每页条数 + */ + private static int pageLimit = DEFAULT_PAGE_LIMIT; + + /** + * 是否限制excel导出条数 + */ + private static boolean limitExportSize = true; + /** + * excel最大导出条数 + */ + private static int maxExportSize = DEFAULT_MAX_EXPORT_SIZE; + + /** + * 初始化 + * + * @param pageLimit page limit + * @param limitExportSize 是否限制导出条数 + * @param maxExportSize max size + * @since 2.3.0 + */ + public static void init(Integer pageLimit, boolean limitExportSize, Integer maxExportSize) { + ExcelUtils.limitExportSize = limitExportSize; + ExcelUtils.pageLimit = pageLimit != null ? pageLimit : DEFAULT_PAGE_LIMIT; + ExcelUtils.maxExportSize = maxExportSize != null ? maxExportSize : DEFAULT_MAX_EXPORT_SIZE; + Assert.isTrue(ExcelUtils.pageLimit > 0, "查询数据sql每页条数必须大于0"); + Assert.isTrue(ExcelUtils.maxExportSize > 0, "excel最大导出条数必须大于0"); + } + + /** + * 导出excel数据到HttpServletResponse + * + * @paramCompany: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:13 + * @since 1.0.0 + */ +@Slf4j +@UtilityClass +public class UrlUtils extends org.springframework.web.util.UriUtils { + + /** + * Build url params by map + * + * @param map map + * @return the string + * @since 1.7.0 + */ + public static String buildUrlParamsByMap(MapCompany: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:16 + * @since 1.0.0 + */ +@UtilityClass +public class Charsets { + + /** 字符集ISO-8859-1 */ + public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; + /** The constant ISO_8859_1_NAME. */ + public static final String ISO_8859_1_NAME = ISO_8859_1.name(); + /** 字符集GBK */ + public static final Charset GBK = of("GBK"); + /** The constant GBK_NAME. */ + public static final String GBK_NAME = GBK.name(); + /** 字符集utf-8 */ + public static final Charset UTF_8 = StandardCharsets.UTF_8; + /** The constant UTF_8_NAME. */ + public static final String UTF_8_NAME = UTF_8.name(); + + /** + * 转换为Charset对象 + * + * @param charsetName 字符集,为空则返回默认字符集 + * @return Charsets charset + * @throws UnsupportedCharsetException 编码不支持 + * @since 1.0.0 + */ + public static Charset of(String charsetName) throws UnsupportedCharsetException { + return StringUtils.hasText(charsetName) ? Charset.forName(charsetName) : Charset.defaultCharset(); + } + + +} diff --git a/src/main/java/com/aos/fileidentify/util/basic/NumberUtils.java b/src/main/java/com/aos/fileidentify/util/basic/NumberUtils.java new file mode 100644 index 0000000..bb1aebb --- /dev/null +++ b/src/main/java/com/aos/fileidentify/util/basic/NumberUtils.java @@ -0,0 +1,223 @@ +package com.aos.fileidentify.util.basic; + +import com.sun.istack.internal.NotNull; + +import org.jetbrains.annotations.Contract; + +import javax.annotation.Nullable; + +import cn.hutool.core.util.NumberUtil; +import lombok.experimental.UtilityClass; + +/** + *Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:27 + * @since 1.0.0 + */ +@UtilityClass +public class NumberUtils extends org.springframework.util.NumberUtils { + + /** + * All possible chars for representing a number as a String + */ + public static final char[] DIGITS = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', + 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z' + }; + + /** + *Convert a String to an int, returning
+ * zero if the conversion fails.
If the string is null, zero is returned.
+ * NumberUtil.toInt(null) = 0
+ * NumberUtil.toInt("") = 0
+ * NumberUtil.toInt("1") = 1
+ *
+ *
+ * @param str the string to convert, may be null
+ * @return the int represented by the string, or zero if conversion fails
+ * @since 1.0.0
+ */
+ public static int toInt(String str) {
+ return toInt(str, -1);
+ }
+
+ /**
+ * Is numer boolean
+ *
+ * @param str str
+ * @return the boolean
+ * @since 1.0.0
+ */
+ @Contract("null -> false")
+ public static boolean isNumer(String str) {
+ return NumberUtil.isNumber(str);
+ }
+
+ /**
+ * Convert a String to an int, returning a
+ * default value if the conversion fails.
If the string is null, the default value is returned.
+ * NumberUtil.toInt(null, 1) = 1
+ * NumberUtil.toInt("", 1) = 1
+ * NumberUtil.toInt("1", 0) = 1
+ *
+ *
+ * @param str the string to convert, may be null
+ * @param defaultValue the default value
+ * @return the int represented by the string, or the default if conversion fails
+ * @since 1.0.0
+ */
+ @Contract("null, _ -> param2")
+ public static int toInt(@Nullable String str, int defaultValue) {
+ if (str == null) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException nfe) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Convert a String to a long, returning
+ * zero if the conversion fails.
If the string is null, zero is returned.
+ * NumberUtil.toLong(null) = 0L
+ * NumberUtil.toLong("") = 0L
+ * NumberUtil.toLong("1") = 1L
+ *
+ *
+ * @param str the string to convert, may be null
+ * @return the long represented by the string, or 0 if conversion fails
+ * @since 1.0.0
+ */
+ public static long toLong(String str) {
+ return toLong(str, 0L);
+ }
+
+ /**
+ * Convert a String to a long, returning a
+ * default value if the conversion fails.
If the string is null, the default value is returned.
+ * NumberUtil.toLong(null, 1L) = 1L
+ * NumberUtil.toLong("", 1L) = 1L
+ * NumberUtil.toLong("1", 0L) = 1L
+ *
+ *
+ * @param str the string to convert, may be null
+ * @param defaultValue the default value
+ * @return the long represented by the string, or the default if conversion fails
+ * @since 1.0.0
+ */
+ @Contract("null, _ -> param2")
+ public static long toLong(@Nullable String str, long defaultValue) {
+ if (str == null) {
+ return defaultValue;
+ }
+ try {
+ return Long.parseLong(str);
+ } catch (NumberFormatException nfe) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Convert a String to a Double
+ *
+ * @param value value
+ * @return double value
+ * @since 1.0.0
+ */
+ @Contract("!null -> !null")
+ public static Double toDouble(String value) {
+ return toDouble(value, null);
+ }
+
+ /**
+ *
Convert a String to a Double
+ *
+ * @param value value
+ * @param defaultValue 默认值
+ * @return double value
+ * @since 1.0.0
+ */
+ @Contract("!null, _ -> !null; null, _ -> param2")
+ public static Double toDouble(@Nullable String value, Double defaultValue) {
+ if (value != null) {
+ return Double.valueOf(value.trim());
+ }
+ return defaultValue;
+ }
+
+ /**
+ *
Convert a String to a Double
+ *
+ * @param value value
+ * @return double value
+ * @since 1.0.0
+ */
+ @Contract("!null -> !null")
+ public static Float toFloat(String value) {
+ return toFloat(value, null);
+ }
+
+ /**
+ *
Convert a String to a Double
+ *
+ * @param value value
+ * @param defaultValue 默认值
+ * @return double value
+ * @since 1.0.0
+ */
+ @Contract("!null, _ -> !null; null, _ -> param2")
+ public static Float toFloat(@Nullable String value, Float defaultValue) {
+ if (value != null) {
+ return Float.valueOf(value.trim());
+ }
+ return defaultValue;
+ }
+
+ /**
+ * 将 long 转短字符串 为 62 进制
+ *
+ * @param i 数字
+ * @return 短字符串 string
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ public static String to62String(long i) {
+ int radix = DIGITS.length;
+ char[] buf = new char[65];
+ int charPos = 64;
+ i = -i;
+ while (i <= -radix) {
+ buf[charPos--] = DIGITS[(int) (-(i % radix))];
+ i = i / radix;
+ }
+ buf[charPos] = DIGITS[(int) (-i)];
+
+ return new String(buf, charPos, (65 - charPos));
+ }
+
+}
diff --git a/src/main/java/com/aos/fileidentify/util/basic/StringUtils.java b/src/main/java/com/aos/fileidentify/util/basic/StringUtils.java
new file mode 100644
index 0000000..9befe06
--- /dev/null
+++ b/src/main/java/com/aos/fileidentify/util/basic/StringUtils.java
@@ -0,0 +1,1626 @@
+package com.aos.fileidentify.util.basic;
+
+import com.aos.fileidentify.util.pools.CharPool;
+import com.aos.fileidentify.util.pools.StringPool;
+
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.util.HtmlUtils;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import cn.hutool.core.text.StrSplitter;
+
+/**
+ *
Company: 成都返空汇网络技术有限公司
+ *Description:
+ * + * @author yangjiyu + * @version 1.0.0 + * @email "mailto:yangjiyu@fkhwl.com" + * @date 2022.07.05 14:24 + * @since 1.0.0 + */ +public abstract class StringUtils extends org.springframework.util.StringUtils { + /** INDEX_NOT_FOUND */ + public static final int INDEX_NOT_FOUND = -1; + /** 特殊字符正则,sql特殊字符和空白符 */ + public static final Pattern SPECIAL_CHARS_REGEX = Pattern.compile("[`'\"|/,;()-+*%#·•� \\s]"); + /** BLANK_SYMBOL */ + public static final String BLANK_SYMBOL = "\\s*|\t|\r|\n"; + /** 随机字符串因子 */ + public static final String INT_STR = "0123456789"; + /** STR_STR */ + public static final String STR_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + /** ALL_STR */ + public static final String ALL_STR = INT_STR + STR_STR; + + /** + * 有 任意 一个 Blank + * + * @param css CharSequence + * @return boolean boolean + * @since 1.0.0 + */ + public static boolean isAnyBlank(CharSequence... css) { + if (ObjectUtils.isEmpty(css)) { + return true; + } + return Stream.of(css).anyMatch(StringUtils::isBlank); + } + + /** + * Check whether the given {@code CharSequence} contains actual text. + *More specifically, this method returns {@code true} if the + * {@code CharSequence} is not {@code null}, its length is greater than + * 0, and it contains at least one non-whitespace character. + *
+ * StringUtil.isBlank(null) = true
+ * StringUtil.isBlank("") = true
+ * StringUtil.isBlank(" ") = true
+ * StringUtil.isBlank("12345") = false
+ * StringUtil.isBlank(" 12345 ") = false
+ *
+ *
+ * @param cs the {@code CharSequence} to check (may be {@code null})
+ * @return {@code true} if the {@code CharSequence} is not {@code null}, its length is greater than 0, and it does not contain
+ * whitespace only
+ * @see Character#isWhitespace
+ * @since 1.0.0
+ */
+ @Contract("null -> true")
+ public static boolean isBlank(CharSequence cs) {
+ return !StringUtils.hasText(cs);
+ }
+
+ /**
+ * 是否全部都不是 blank
+ *
+ * @param css CharSequence
+ * @return boolean boolean
+ * @since 1.0.0
+ */
+ public static boolean isNoneBlank(CharSequence... css) {
+ if (ObjectUtils.isEmpty(css)) {
+ return false;
+ }
+ return Stream.of(css).allMatch(StringUtils::isNotBlank);
+ }
+
+ /**
+ * 是否全部都是 blank
+ *
+ * @param css css
+ * @return the boolean
+ * @since 1.0.0
+ */
+ public static boolean isAllBlank(CharSequence... css) {
+ if (ObjectUtils.isEmpty(css)) {
+ return true;
+ }
+ return Stream.of(css).allMatch(StringUtils::isBlank);
+ }
+
+ /**
+ * Checks if a CharSequence is not empty (""), not null and not whitespace only.
+ *
+ * StringUtil.isNotBlank(null) = false
+ * StringUtil.isNotBlank("") = false
+ * StringUtil.isNotBlank(" ") = false
+ * StringUtil.isNotBlank("bob") = true
+ * StringUtil.isNotBlank(" bob ") = true
+ *
+ *
+ * @param cs the CharSequence to check, may be null
+ * @return {@code true} if the CharSequence is not empty and not null and not whitespace
+ * @see Character#isWhitespace
+ * @since 1.0.0
+ */
+ @Contract("null -> false")
+ public static boolean isNotBlank(CharSequence cs) {
+ return StringUtils.hasText(cs);
+ }
+
+ /**
+ * 判断一个字符串是否是数字
+ *
+ * @param cs the CharSequence to check, may be null
+ * @return {boolean}
+ * @since 1.0.0
+ */
+ public static boolean isNumeric(CharSequence cs) {
+ if (isBlank(cs)) {
+ return false;
+ }
+ for (int i = cs.length(); --i >= 0; ) {
+ int chr = cs.charAt(i);
+ if (chr < 48 || chr > 57) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Convert a {@code Collection} into a delimited {@code String} (e.g., CSV).
+ * Useful for {@code toString()} implementations. + * + * @param coll the {@code Collection} to convert + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + public static String join(Collection> coll) { + return StringUtils.collectionToCommaDelimitedString(coll); + } + + /** + * Convert a {@code Collection} into a delimited {@code String} (e.g. CSV). + *
Useful for {@code toString()} implementations. + * + * @param coll the {@code Collection} to convert + * @param delim the delimiter to use (typically a ",") + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + public static String join(Collection> coll, String delim) { + return StringUtils.collectionToDelimitedString(coll, delim); + } + + /** + * Convert a {@code String} array into a comma delimited {@code String} + * (i.e., CSV). + *
Useful for {@code toString()} implementations. + * + * @param arr the array to display + * @return the delimited {@code String} + * @since 1.0.0 + */ + @NotNull + @Contract(pure = true) + public static String join(Object[] arr) { + return StringUtils.arrayToCommaDelimitedString(arr); + } + + /** + * Convert a {@code String} array into a delimited {@code String} (e.g. CSV). + *
Useful for {@code toString()} implementations.
+ *
+ * @param arr the array to display
+ * @param delim the delimiter to use (typically a ",")
+ * @return the delimited {@code String}
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static String join(Object[] arr, String delim) {
+ return StringUtils.arrayToDelimitedString(arr, delim);
+ }
+
+ /**
+ * 生成uuid
+ *
+ * @return UUID string
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String randomUid() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ return new UUID(random.nextLong(), random.nextLong()).toString().replace(StringPool.DASH, StringPool.EMPTY);
+ }
+
+ /**
+ * 转义HTML用于安全过滤
+ *
+ * @param html html
+ * @return {String}
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String escapeHtml(String html) {
+ return HtmlUtils.htmlEscape(html);
+ }
+
+ /**
+ * 清理字符串,清理出某些不可见字符
+ *
+ * @param txt 字符串
+ * @return {String}
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(pure = true)
+ public static String cleanChars(@NotNull String txt) {
+ return txt.replaceAll("[ `·•�\\f\\t\\v\\s]", "");
+ }
+
+
+ /**
+ * 有序的格式化文本,使用{number}做为占位符
+ * 栗子:
+ *
+ * 栗子:
+ *
+ * 栗子:
+ *
+ * 栗子:
+ *
+ * 栗子 (*代表任意字符) :
+ * Company: 成都返空汇网络技术有限公司 Description: Company: 成都返空汇网络技术有限公司 Description: Company: 成都返空汇网络技术有限公司 Description:
+ * 例:
+ * 通常使用: format("this is {0} for {1}", "a", "b") =》 this is a for b
+ *
+ * @param pattern 文本格式
+ * @param arguments 参数
+ * @return 格式化后的文本 string
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String indexedFormat(@NotNull CharSequence pattern, Object... arguments) {
+ return MessageFormat.format(pattern.toString(), arguments);
+ }
+
+ /**
+ * 切分字符串,不去除切分后每个元素两边的空白符,不去除空白项
+ *
+ * @param str 被切分的字符串
+ * @param separator 分隔符字符
+ * @param limit 限制分片数,-1不限制
+ * @return 切分后的集合 list
+ * @since 1.0.0
+ */
+ @Contract("null, _, _ -> new")
+ public static List
+ * 如果给定的字符串为空串 (null或"") 或者分隔字符串为null,返回原字符串
+ * 如果分隔字符串为空串"",则返回空串,如果分隔字符串未找到,返回原字符串
+ *
+ * StringUtil.subBefore(null, *) = null
+ * StringUtil.subBefore("", *) = ""
+ * StringUtil.subBefore("abc", "a") = ""
+ * StringUtil.subBefore("abcba", "b") = "a"
+ * StringUtil.subBefore("abc", "c") = "ab"
+ * StringUtil.subBefore("abc", "d") = "abc"
+ * StringUtil.subBefore("abc", "") = ""
+ * StringUtil.subBefore("abc", null) = "abc"
+ *
+ *
+ * @param string 被查找的字符串
+ * @param separator 分隔字符串 (不包括)
+ * @param isLastSeparator 是否查找最后一个分隔字符串 (多次出现分隔字符串时选取最后一个) ,true为选取最后一个
+ * @return 切割后的字符串 string
+ * @since 3.1.1
+ */
+ @Nullable
+ public static String subBefore(CharSequence string, CharSequence separator, boolean isLastSeparator) {
+ if (StringUtils.isEmpty(string) || separator == null) {
+ return null == string ? null : string.toString();
+ }
+
+ String str = string.toString();
+ String sep = separator.toString();
+ if (sep.isEmpty()) {
+ return StringPool.EMPTY;
+ }
+ int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
+ if (pos == INDEX_NOT_FOUND) {
+ return str;
+ }
+ return str.substring(0, pos);
+ }
+
+ /**
+ * 默认查找最后一个分隔符
+ *
+ * @param string string
+ * @param separator separator
+ * @return the string
+ * @since 1.0.0
+ */
+ public static String subBefore(CharSequence string, CharSequence separator) {
+ return subBefore(string, separator, true);
+ }
+
+ /**
+ * 截取分隔字符串之后的字符串,不包括分隔字符串
+ * 如果给定的字符串为空串 (null或"") ,返回原字符串
+ * 如果分隔字符串为空串 (null或"") ,则返回空串,如果分隔字符串未找到,返回空串
+ *
+ * StringUtil.subAfter(null, *) = null
+ * StringUtil.subAfter("", *) = ""
+ * StringUtil.subAfter(*, null) = ""
+ * StringUtil.subAfter("abc", "a") = "bc"
+ * StringUtil.subAfter("abcba", "b") = "cba"
+ * StringUtil.subAfter("abc", "c") = ""
+ * StringUtil.subAfter("abc", "d") = ""
+ * StringUtil.subAfter("abc", "") = "abc"
+ *
+ *
+ * @param string 被查找的字符串
+ * @param separator 分隔字符串 (不包括)
+ * @param isLastSeparator 是否查找最后一个分隔字符串 (多次出现分隔字符串时选取最后一个) ,true为选取最后一个
+ * @return 切割后的字符串 string
+ * @since 3.1.1
+ */
+ @Nullable
+ public static String subAfter(CharSequence string, CharSequence separator, boolean isLastSeparator) {
+ if (StringUtils.isEmpty(string)) {
+ return null == string ? null : string.toString();
+ }
+ if (separator == null) {
+ return StringPool.EMPTY;
+ }
+ String str = string.toString();
+ String sep = separator.toString();
+ int pos = isLastSeparator ? str.lastIndexOf(sep) : str.indexOf(sep);
+ if (pos == INDEX_NOT_FOUND) {
+ return StringPool.EMPTY;
+ }
+ return str.substring(pos + separator.length());
+ }
+
+ /**
+ * 默认查找第一个分隔符
+ *
+ * @param string string
+ * @param separator separator
+ * @return the string
+ * @since 1.0.0
+ */
+ public static String subAfter(CharSequence string, CharSequence separator) {
+ return subAfter(string, separator, false);
+ }
+
+ /**
+ * 截取指定字符串中间部分,不包括标识字符串
+ *
+ * StringUtil.subBetween(null, *) = null
+ * StringUtil.subBetween("", "") = ""
+ * StringUtil.subBetween("", "tag") = null
+ * StringUtil.subBetween("tagabctag", null) = null
+ * StringUtil.subBetween("tagabctag", "") = ""
+ * StringUtil.subBetween("tagabctag", "tag") = "abc"
+ *
+ *
+ * @param str 被切割的字符串
+ * @param beforeAndAfter 截取开始和结束的字符串标识
+ * @return 截取后的字符串 string
+ * @since 3.1.1
+ */
+ @Contract("null, _ -> null; !null, null -> null")
+ public static String subBetween(CharSequence str, CharSequence beforeAndAfter) {
+ return subBetween(str, beforeAndAfter, beforeAndAfter);
+ }
+
+ /**
+ * 截取指定字符串中间部分,不包括标识字符串
+ *
+ * StringUtil.subBetween("wx[b]yz", "[", "]") = "b"
+ * StringUtil.subBetween(null, *, *) = null
+ * StringUtil.subBetween(*, null, *) = null
+ * StringUtil.subBetween(*, *, null) = null
+ * StringUtil.subBetween("", "", "") = ""
+ * StringUtil.subBetween("", "", "]") = null
+ * StringUtil.subBetween("", "[", "]") = null
+ * StringUtil.subBetween("yabcz", "", "") = ""
+ * StringUtil.subBetween("yabcz", "y", "z") = "abc"
+ * StringUtil.subBetween("yabczyabcz", "y", "z") = "abc"
+ *
+ *
+ * @param str 被切割的字符串
+ * @param before 截取开始的字符串标识
+ * @param after 截取到的字符串标识
+ * @return 截取后的字符串 string
+ * @since 3.1.1
+ */
+ @Contract("null, _, _ -> null; !null, null, _ -> null; !null, !null, null -> null")
+ private static String subBetween(CharSequence str, CharSequence before, CharSequence after) {
+ if (str == null || before == null || after == null) {
+ return null;
+ }
+
+ String str2 = str.toString();
+ String before2 = before.toString();
+ String after2 = after.toString();
+
+ int start = str2.indexOf(before2);
+ if (start != INDEX_NOT_FOUND) {
+ int end = str2.indexOf(after2, start + before2.length());
+ if (end != INDEX_NOT_FOUND) {
+ return str2.substring(start + before2.length(), end);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 去掉指定前缀
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @return 切掉后的字符串 ,若前缀不是 preffix, 返回原字符串
+ * @since 1.0.0
+ */
+ public static String removePrefix(CharSequence str, CharSequence prefix) {
+ if (StringUtils.isEmpty(str) || StringUtils.isEmpty(prefix)) {
+ return StringPool.EMPTY;
+ }
+
+ String str2 = str.toString();
+ if (str2.startsWith(prefix.toString())) {
+ return subSuf(str2, prefix.length());
+ }
+ return str2;
+ }
+
+ /**
+ * 切割指定位置之后部分的字符串
+ *
+ * @param string 字符串
+ * @param fromIndex 切割开始的位置 (包括)
+ * @return 切割后后剩余的后半部分字符串 string
+ * @since 1.0.0
+ */
+ @Nullable
+ public static String subSuf(CharSequence string, int fromIndex) {
+ if (StringUtils.isEmpty(string)) {
+ return StringPool.EMPTY;
+ }
+ return sub(string, fromIndex, string.length());
+ }
+
+ /**
+ * 改进JDK subString
+ * index从0开始计算,最后一个字符为-1
+ * 如果from和to位置一样,返回 ""
+ * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length
+ * 如果经过修正的index中from大于to,则互换from和to example:
+ * abcdefgh 2 3 =》 c
+ * abcdefgh 2 -3 =》 cde
+ *
+ * @param str String
+ * @param fromIndex 开始的index (包括)
+ * @param toIndex 结束的index (不包括)
+ * @return 字串 string
+ * @since 1.0.0
+ */
+ public static String sub(CharSequence str, int fromIndex, int toIndex) {
+ if (StringUtils.isEmpty(str)) {
+ return StringPool.EMPTY;
+ }
+ int len = str.length();
+
+ if (fromIndex < 0) {
+ fromIndex = len + fromIndex;
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+ } else if (fromIndex > len) {
+ fromIndex = len;
+ }
+
+ if (toIndex < 0) {
+ toIndex = len + toIndex;
+ if (toIndex < 0) {
+ toIndex = len;
+ }
+ } else if (toIndex > len) {
+ toIndex = len;
+ }
+
+ if (toIndex < fromIndex) {
+ int tmp = fromIndex;
+ fromIndex = toIndex;
+ toIndex = tmp;
+ }
+
+ if (fromIndex == toIndex) {
+ return StringPool.EMPTY;
+ }
+
+ return str.toString().substring(fromIndex, toIndex);
+ }
+
+ /**
+ * 忽略大小写去掉指定前缀
+ *
+ * @param str 字符串
+ * @param prefix 前缀
+ * @return 切掉后的字符串 ,若前缀不是 prefix, 返回原字符串
+ * @since 1.0.0
+ */
+ public static String removePrefixIgnoreCase(CharSequence str, CharSequence prefix) {
+ if (StringUtils.isEmpty(str) || StringUtils.isEmpty(prefix)) {
+ return StringPool.EMPTY;
+ }
+
+ String str2 = str.toString();
+ if (str2.toLowerCase().startsWith(prefix.toString().toLowerCase())) {
+ return subSuf(str2, prefix.length());
+ }
+ return str2;
+ }
+
+ /**
+ * 去掉指定后缀,并小写首字母
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 切掉后的字符串 ,若后缀不是 suffix, 返回原字符串
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String removeSufAndLowerFirst(CharSequence str, CharSequence suffix) {
+ return firstCharToLower(removeSuffix(str, suffix));
+ }
+
+ /**
+ * 首字母变小写
+ *
+ * @param str 字符串
+ * @return {String}
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String firstCharToLower(@NotNull String str) {
+ char firstChar = str.charAt(0);
+ if (firstChar >= CharPool.UPPER_A && firstChar <= CharPool.UPPER_Z) {
+ char[] arr = str.toCharArray();
+ arr[0] += (CharPool.LOWER_A - CharPool.UPPER_A);
+ return new String(arr);
+ }
+ return str;
+ }
+
+ /**
+ * 去掉指定后缀
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 切掉后的字符串 ,若后缀不是 suffix, 返回原字符串
+ * @since 1.0.0
+ */
+ public static String removeSuffix(CharSequence str, CharSequence suffix) {
+ if (StringUtils.isEmpty(str) || StringUtils.isEmpty(suffix)) {
+ return StringPool.EMPTY;
+ }
+
+ String str2 = str.toString();
+ if (str2.endsWith(suffix.toString())) {
+ return subPre(str2, str2.length() - suffix.length());
+ }
+ return str2;
+ }
+
+ /**
+ * 切割指定位置之前部分的字符串
+ *
+ * @param string 字符串
+ * @param toIndex 切割到的位置 (不包括)
+ * @return 切割后的剩余的前半部分字符串 string
+ * @since 1.0.0
+ */
+ private static String subPre(CharSequence string, int toIndex) {
+ return sub(string, 0, toIndex);
+ }
+
+ /**
+ * 忽略大小写去掉指定后缀
+ *
+ * @param str 字符串
+ * @param suffix 后缀
+ * @return 切掉后的字符串 ,若后缀不是 suffix, 返回原字符串
+ * @since 1.0.0
+ */
+ public static String removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) {
+ if (StringUtils.isEmpty(str) || StringUtils.isEmpty(suffix)) {
+ return StringPool.EMPTY;
+ }
+
+ String str2 = str.toString();
+ if (str2.toLowerCase().endsWith(suffix.toString().toLowerCase())) {
+ return subPre(str2, str2.length() - suffix.length());
+ }
+ return str2;
+ }
+
+ /**
+ * 首字母变大写
+ *
+ * @param str 字符串
+ * @return {String}
+ * @since 1.0.0
+ */
+ public static String firstCharToUpper(String str) {
+ if (isBlank(str)) {
+ return str;
+ }
+ char firstChar = str.charAt(0);
+ if (firstChar >= CharPool.LOWER_A && firstChar <= CharPool.LOWER_Z) {
+ char[] arr = str.toCharArray();
+ arr[0] -= (CharPool.LOWER_A - CharPool.UPPER_A);
+ return new String(arr);
+ }
+ return str;
+ }
+
+ /**
+ * 指定范围内查找字符串,忽略大小写
+ *
+ * StringUtil.indexOfIgnoreCase(null, *, *) = -1
+ * StringUtil.indexOfIgnoreCase(*, null, *) = -1
+ * StringUtil.indexOfIgnoreCase("", "", 0) = 0
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
+ * StringUtil.indexOfIgnoreCase("abc", "", 9) = -1
+ *
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @return 位置 int
+ * @since 3.2.1
+ */
+ public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
+ return indexOfIgnoreCase(str, searchStr, 0);
+ }
+
+ /**
+ * 指定范围内查找字符串
+ *
+ * StringUtil.indexOfIgnoreCase(null, *, *) = -1
+ * StringUtil.indexOfIgnoreCase(*, null, *) = -1
+ * StringUtil.indexOfIgnoreCase("", "", 0) = 0
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+ * StringUtil.indexOfIgnoreCase("aabaabaa", "", 2) = 2
+ * StringUtil.indexOfIgnoreCase("abc", "", 9) = -1
+ *
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置
+ * @return 位置 int
+ * @since 3.2.1
+ */
+ private static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) {
+ return indexOf(str, searchStr, fromIndex, true);
+ }
+
+ /**
+ * 指定范围内反向查找字符串
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置
+ * @param ignoreCase 是否忽略大小写
+ * @return 位置 int
+ * @since 3.2.1
+ */
+ public static int indexOf(CharSequence str, CharSequence searchStr, int fromIndex, boolean ignoreCase) {
+ if (str == null || searchStr == null) {
+ return INDEX_NOT_FOUND;
+ }
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+
+ int endLimit = str.length() - searchStr.length() + 1;
+ if (fromIndex > endLimit) {
+ return INDEX_NOT_FOUND;
+ }
+ if (searchStr.length() == 0) {
+ return fromIndex;
+ }
+
+ if (!ignoreCase) {
+ // 不忽略大小写调用JDK方法
+ return str.toString().indexOf(searchStr.toString(), fromIndex);
+ }
+
+ for (int i = fromIndex; i < endLimit; i++) {
+ if (isSubEquals(str, i, searchStr, 0, searchStr.length(), true)) {
+ return i;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ /**
+ * 截取两个字符串的不同部分 (长度一致) ,判断截取的子串是否相同
+ * 任意一个字符串为null返回false
+ *
+ * @param str1 第一个字符串
+ * @param start1 第一个字符串开始的位置
+ * @param str2 第二个字符串
+ * @param start2 第二个字符串开始的位置
+ * @param length 截取长度
+ * @param ignoreCase 是否忽略大小写
+ * @return 子串是否相同 boolean
+ * @since 3.2.1
+ */
+ @SuppressWarnings("checkstyle:ParameterNumber")
+ @Contract("null, _, _, _, _, _ -> false; !null, _, null, _, _, _ -> false")
+ private static boolean isSubEquals(CharSequence str1,
+ int start1,
+ CharSequence str2,
+ int start2,
+ int length,
+ boolean ignoreCase) {
+ if (null == str1 || null == str2) {
+ return false;
+ }
+
+ return str1.toString().regionMatches(ignoreCase, start1, str2.toString(), start2, length);
+ }
+
+ /**
+ * 指定范围内查找字符串,忽略大小写
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @return 位置 int
+ * @since 3.2.1
+ */
+ public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
+ return lastIndexOfIgnoreCase(str, searchStr, str.length());
+ }
+
+ /**
+ * 指定范围内查找字符串,忽略大小写
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置,从后往前计数
+ * @return 位置 int
+ * @since 3.2.1
+ */
+ private static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int fromIndex) {
+ return lastIndexOf(str, searchStr, fromIndex, true);
+ }
+
+ /**
+ * 指定范围内查找字符串
+ *
+ * @param str 字符串
+ * @param searchStr 需要查找位置的字符串
+ * @param fromIndex 起始位置,从后往前计数
+ * @param ignoreCase 是否忽略大小写
+ * @return 位置 int
+ * @since 3.2.1
+ */
+ private static int lastIndexOf(CharSequence str, CharSequence searchStr, int fromIndex, boolean ignoreCase) {
+ if (str == null || searchStr == null) {
+ return INDEX_NOT_FOUND;
+ }
+ if (fromIndex < 0) {
+ fromIndex = 0;
+ }
+ fromIndex = Math.min(fromIndex, str.length());
+
+ if (searchStr.length() == 0) {
+ return fromIndex;
+ }
+
+ if (!ignoreCase) {
+ // 不忽略大小写调用JDK方法
+ return str.toString().lastIndexOf(searchStr.toString(), fromIndex);
+ }
+
+ for (int i = fromIndex; i > 0; i--) {
+ if (isSubEquals(str, i, searchStr, 0, searchStr.length(), true)) {
+ return i;
+ }
+ }
+ return INDEX_NOT_FOUND;
+ }
+
+ /**
+ * 返回字符串 searchStr 在字符串 str 中第 ordinal 次出现的位置.
+ * 此方法来自: Apache-Commons-Lang
+ *
+ * StringUtil.ordinalIndexOf(null, *, *) = -1
+ * StringUtil.ordinalIndexOf(*, null, *) = -1
+ * StringUtil.ordinalIndexOf("", "", *) = 0
+ * StringUtil.ordinalIndexOf("aabaabaa", "a", 1) = 0
+ * StringUtil.ordinalIndexOf("aabaabaa", "a", 2) = 1
+ * StringUtil.ordinalIndexOf("aabaabaa", "b", 1) = 2
+ * StringUtil.ordinalIndexOf("aabaabaa", "b", 2) = 5
+ * StringUtil.ordinalIndexOf("aabaabaa", "ab", 1) = 1
+ * StringUtil.ordinalIndexOf("aabaabaa", "ab", 2) = 4
+ * StringUtil.ordinalIndexOf("aabaabaa", "", 1) = 0
+ * StringUtil.ordinalIndexOf("aabaabaa", "", 2) = 0
+ *
+ *
+ * @param str 被检查的字符串,可以为null
+ * @param searchStr 被查找的字符串,可以为null
+ * @param ordinal 第几次出现的位置
+ * @return 查找到的位置 int
+ * @since 3.2.3
+ */
+ public static int ordinalIndexOf(String str, String searchStr, int ordinal) {
+ if (str == null || searchStr == null || ordinal <= 0) {
+ return INDEX_NOT_FOUND;
+ }
+ if (searchStr.length() == 0) {
+ return 0;
+ }
+ int found = 0;
+ int index = INDEX_NOT_FOUND;
+ do {
+ index = str.indexOf(searchStr, index + 1);
+ if (index < 0) {
+ return index;
+ }
+ found++;
+ } while (found < ordinal);
+ return index;
+ }
+
+ /**
+ * 比较两个字符串 (大小写敏感) .
+ *
+ * equalsIgnoreCase(null, null) = true
+ * equalsIgnoreCase(null, "abc") = false
+ * equalsIgnoreCase("abc", null) = false
+ * equalsIgnoreCase("abc", "abc") = true
+ * equalsIgnoreCase("abc", "ABC") = true
+ *
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @return 如果两个字符串相同 ,或者都是null,则返回true
+ * @since 1.0.0
+ */
+ @Contract("null, null -> true; null, !null -> false; !null, null -> false")
+ public static boolean equals(CharSequence str1, CharSequence str2) {
+ return equals(str1, str2, false);
+ }
+
+ /**
+ * 比较两个字符串是否相等.
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @param ignoreCase 是否忽略大小写
+ * @return 如果两个字符串相同 ,或者都是null,则返回true
+ * @since 3.2.0
+ */
+ @Contract("null, null, _ -> true; null, !null, _ -> false; !null, null, _ -> false")
+ public static boolean equals(CharSequence str1, CharSequence str2, boolean ignoreCase) {
+ if (null == str1) {
+ // 只有两个都为null才判断相等
+ return str2 == null;
+ }
+ if (null == str2) {
+ // 字符串2空,字符串1非空,直接false
+ return false;
+ }
+
+ if (ignoreCase) {
+ return str1.toString().equalsIgnoreCase(str2.toString());
+ } else {
+ return str1.equals(str2);
+ }
+ }
+
+ /**
+ * 比较两个字符串 (大小写不敏感) .
+ *
+ * equalsIgnoreCase(null, null) = true
+ * equalsIgnoreCase(null, "abc") = false
+ * equalsIgnoreCase("abc", null) = false
+ * equalsIgnoreCase("abc", "abc") = true
+ * equalsIgnoreCase("abc", "ABC") = true
+ *
+ *
+ * @param str1 要比较的字符串1
+ * @param str2 要比较的字符串2
+ * @return 如果两个字符串相同 ,或者都是null,则返回true
+ * @since 1.0.0
+ */
+ @Contract("null, null -> true; null, !null -> false; !null, null -> false")
+ public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
+ return equals(str1, str2, true);
+ }
+
+ /**
+ * 创建StringBuilder对象
+ *
+ * @return StringBuilder对象 string builder
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(value = " -> new", pure = true)
+ public static StringBuilder builder() {
+ return new StringBuilder();
+ }
+
+ /**
+ * 创建StringBuilder对象
+ *
+ * @param capacity 初始大小
+ * @return StringBuilder对象 string builder
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(value = "_ -> new", pure = true)
+ public static StringBuilder builder(int capacity) {
+ return new StringBuilder(capacity);
+ }
+
+ /**
+ * 创建StringBuilder对象
+ *
+ * @param strs 初始字符串列表
+ * @return StringBuilder对象 string builder
+ * @since 1.0.0
+ */
+ public static StringBuilder builder(@NotNull CharSequence... strs) {
+ StringBuilder sb = new StringBuilder();
+ for (CharSequence str : strs) {
+ sb.append(str);
+ }
+ return sb;
+ }
+
+ /**
+ * 创建StringBuilder对象
+ *
+ * @param sb 初始StringBuilder
+ * @param strs 初始字符串列表
+ * @return StringBuilder对象 string builder
+ * @since 1.0.0
+ */
+ @Contract("_, _ -> param1")
+ public static StringBuilder appendBuilder(StringBuilder sb, @NotNull CharSequence... strs) {
+ for (CharSequence str : strs) {
+ sb.append(str);
+ }
+ return sb;
+ }
+
+ /**
+ * 获得StringReader
+ *
+ * @param str 字符串
+ * @return StringReader reader
+ * @since 1.0.0
+ */
+ @Contract("null -> null; !null -> new")
+ public static StringReader getReader(CharSequence str) {
+ if (null == str) {
+ return null;
+ }
+ return new StringReader(str.toString());
+ }
+
+ /**
+ * 获得StringWriter
+ *
+ * @return StringWriter writer
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(value = " -> new", pure = true)
+ public static StringWriter getWriter() {
+ return new StringWriter();
+ }
+
+
+ /**
+ * 统计指定内容中包含指定字符的数量
+ *
+ * @param content 内容
+ * @param charForSearch 被统计的字符
+ * @return 包含数量 int
+ * @since 1.0.0
+ */
+ public static int count(CharSequence content, char charForSearch) {
+ int count = 0;
+ if (StringUtils.isEmpty(content)) {
+ return 0;
+ }
+ int contentLength = content.length();
+ for (int i = 0; i < contentLength; i++) {
+ if (charForSearch == content.charAt(i)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * 下划线转驼峰
+ *
+ * @param para 字符串
+ * @return String string
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String underlineToHump(@NotNull String para) {
+ return toHump(para, "_");
+ }
+
+ /**
+ * 驼峰转下划线
+ *
+ * @param para 字符串
+ * @return String string
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String humpToUnderline(String para) {
+ return fromHump(para, "_");
+ }
+
+ /**
+ * 横线转驼峰
+ *
+ * @param para 字符串
+ * @return String string
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String lineToHump(@NotNull String para) {
+ return toHump(para, "-");
+ }
+
+ /**
+ * 驼峰转横线
+ *
+ * @param para 字符串
+ * @return String string
+ * @since 1.0.0
+ */
+ @NotNull
+ public static String humpToLine(String para) {
+ return fromHump(para, "-");
+ }
+
+ /**
+ * To hump string
+ *
+ * @param para para
+ * @param s2 s 2
+ * @return the string
+ * @since 1.0.0
+ */
+ @NotNull
+ private static String toHump(@NotNull String para, String s2) {
+ StringBuilder result = new StringBuilder();
+ String[] a = para.split(s2);
+ for (String s : a) {
+ if (result.length() == 0) {
+ result.append(s.toLowerCase());
+ } else {
+ result.append(s.substring(0, 1).toUpperCase());
+ result.append(s.substring(1).toLowerCase());
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * From hump string
+ *
+ * @param para para
+ * @param s s
+ * @return the string
+ * @since 1.0.0
+ */
+ @NotNull
+ private static String fromHump(String para, String s) {
+ para = firstCharToLower(para);
+ StringBuilder sb = new StringBuilder(para);
+ int temp = 0;
+ for (int i = 0; i < para.length(); i++) {
+ if (Character.isUpperCase(para.charAt(i))) {
+ sb.insert(i + temp, s);
+ temp += 1;
+ }
+ }
+ return sb.toString().toLowerCase();
+ }
+
+ /**
+ * 重复某个字符串到指定长度
+ *
+ * @param str 被重复的字符
+ * @param padLen 指定长度
+ * @return 重复字符字符串 string
+ * @since 4.3.2
+ */
+ @Contract("null, _ -> null")
+ private static String repeatByLength(CharSequence str, int padLen) {
+ if (null == str) {
+ return null;
+ }
+ if (padLen <= 0) {
+ return StringPool.EMPTY;
+ }
+ int strLen = str.length();
+ if (strLen == padLen) {
+ return str.toString();
+ } else if (strLen > padLen) {
+ return subPre(str, padLen);
+ }
+
+ // 重复,直到达到指定长度
+ char[] padding = new char[padLen];
+ for (int i = 0; i < padLen; i++) {
+ padding[i] = str.charAt(i % strLen);
+ }
+ return new String(padding);
+ }
+
+ /**
+ * 补充字符串以满足最小长度
+ *
+ * StrUtil.padPre(null, *, *);//null
+ * StrUtil.padPre("1", 3, "ABC");//"AB1"
+ * StrUtil.padPre("123", 2, "ABC");//"12"
+ *
+ *
+ * @param str 字符串
+ * @param minLength 最小长度
+ * @param padStr 补充的字符
+ * @return 补充后的字符串 string
+ * @since 1.0.0
+ */
+ @Contract("null, _, _ -> null")
+ private static String padPre(CharSequence str, int minLength, CharSequence padStr) {
+ if (null == str) {
+ return null;
+ }
+ int strLen = str.length();
+ if (strLen == minLength) {
+ return str.toString();
+ } else if (strLen > minLength) {
+ return subPre(str, minLength);
+ }
+
+ return repeatByLength(padStr, minLength - strLen).concat(str.toString());
+ }
+
+ /**
+ * 切割指定长度的后部分的字符串
+ *
+ * StrUtil.subSufByLength("abcde", 3) = "cde"
+ * StrUtil.subSufByLength("abcde", 0) = ""
+ * StrUtil.subSufByLength("abcde", -5) = ""
+ * StrUtil.subSufByLength("abcde", -1) = ""
+ * StrUtil.subSufByLength("abcde", 5) = "abcde"
+ * StrUtil.subSufByLength("abcde", 10) = "abcde"
+ * StrUtil.subSufByLength(null, 3) = null
+ *
+ *
+ * @param string 字符串
+ * @param length 切割长度
+ * @return 切割后后剩余的后半部分字符串 string
+ * @since 4.0.1
+ */
+ @Nullable
+ private static String subSufByLength(CharSequence string, int length) {
+ if (StringUtils.isEmpty(string)) {
+ return null;
+ }
+ if (length <= 0) {
+ return StringPool.EMPTY;
+ }
+ return sub(string, -length, string.length());
+ }
+
+ /**
+ * 补充字符串以满足最小长度
+ *
+ * StrUtil.padAfter(null, *, *);//null
+ * StrUtil.padAfter("1", 3, "ABC");//"1AB"
+ * StrUtil.padAfter("123", 2, "ABC");//"23"
+ *
+ *
+ * @param str 字符串,如果为null,按照空串处理
+ * @param minLength 最小长度
+ * @param padStr 补充的字符
+ * @return 补充后的字符串 string
+ * @since 4.3.2
+ */
+ @Contract("null, _, _ -> null")
+ public static String padAfter(CharSequence str, int minLength, CharSequence padStr) {
+ if (null == str) {
+ return null;
+ }
+ int strLen = str.length();
+ if (strLen == minLength) {
+ return str.toString();
+ } else if (strLen > minLength) {
+ return subSufByLength(str, minLength);
+ }
+
+ return str.toString().concat(repeatByLength(padStr, minLength - strLen));
+ }
+
+ /**
+ * {@link CharSequence} 转为字符串,null安全
+ *
+ * @param cs {@link CharSequence}
+ * @return 字符串 string
+ * @since 1.0.0
+ */
+ @Contract("null -> null; !null -> !null")
+ public static String str(CharSequence cs) {
+ return null == cs ? null : cs.toString();
+ }
+
+ /**
+ * 居中字符串,两边补充指定字符串,如果指定长度小于字符串,则返回原字符串
+ *
+ * StrUtil.center(null, *, *) = null
+ * StrUtil.center("", 4, " ") = " "
+ * StrUtil.center("ab", -1, " ") = "ab"
+ * StrUtil.center("ab", 4, " ") = " ab "
+ * StrUtil.center("abcd", 2, " ") = "abcd"
+ * StrUtil.center("a", 4, " ") = " a "
+ * StrUtil.center("a", 4, "yz") = "yayz"
+ * StrUtil.center("abc", 7, null) = " abc "
+ * StrUtil.center("abc", 7, "") = " abc "
+ *
+ *
+ * @param str 字符串
+ * @param size 指定长度
+ * @param padStr 两边补充的字符串
+ * @return 补充后的字符串 string
+ * @since 1.0.0
+ */
+ public static String center(CharSequence str, int size, CharSequence padStr) {
+ if (str == null || size <= 0) {
+ return str(str);
+ }
+ if (StringUtils.isEmpty(padStr)) {
+ padStr = StringPool.SPACE;
+ }
+ int strLen = str.length();
+ int pads = size - strLen;
+ if (pads <= 0) {
+ return str.toString();
+ }
+ str = padPre(str, strLen + pads / 2, padStr);
+ str = padAfter(str, size, padStr);
+ return str.toString();
+ }
+
+ /**
+ * 清理字符串,清理出某些不可见字符和一些sql特殊字符
+ *
+ * @param txt 文本
+ * @return {String}
+ * @since 1.0.0
+ */
+ @Contract("null -> null")
+ @Nullable
+ public static String cleanText(@Nullable String txt) {
+ if (txt == null) {
+ return null;
+ }
+ return SPECIAL_CHARS_REGEX.matcher(txt).replaceAll(StringPool.EMPTY);
+ }
+
+ /**
+ * 获取标识符,用于参数清理
+ *
+ * @param param 参数
+ * @return 清理后的标识符 string
+ * @since 1.0.0
+ */
+ @Contract("null -> null")
+ @Nullable
+ public static String cleanIdentifier(@Nullable String param) {
+ if (param == null) {
+ return null;
+ }
+ StringBuilder paramBuilder = new StringBuilder();
+ for (int i = 0; i < param.length(); i++) {
+ char c = param.charAt(i);
+ if (Character.isJavaIdentifierPart(c)) {
+ paramBuilder.append(c);
+ }
+ }
+ return paramBuilder.toString();
+ }
+
+ /**
+ * 删除空白符
+ *
+ * @param str the str
+ * @return the string
+ * @since 1.0.0
+ */
+ public static String replaceBlank(String str) {
+ String dest = "";
+ if (str != null) {
+ Pattern p = Pattern.compile(BLANK_SYMBOL);
+ Matcher m = p.matcher(str);
+ dest = m.replaceAll("");
+ }
+ return dest;
+ }
+
+ /**
+ * 生成uuid,采用 jdk 9 的形式,优化性能
+ *
+ * @return UUID uuid
+ * @since 1.0.0
+ */
+ @NotNull
+ @Contract(" -> new")
+ public static String getUid() {
+ ThreadLocalRandom random = ThreadLocalRandom.current();
+ long lsb = random.nextLong();
+ long msb = random.nextLong();
+ char[] buf = new char[32];
+ formatUnsignedLong(lsb, buf, 20, 12);
+ formatUnsignedLong(lsb >>> 48, buf, 16, 4);
+ formatUnsignedLong(msb, buf, 12, 4);
+ formatUnsignedLong(msb >>> 16, buf, 8, 4);
+ formatUnsignedLong(msb >>> 32, buf, 0, 8);
+ return new String(buf);
+ }
+
+ /**
+ * Format unsigned long *
+ *
+ * @param val val
+ * @param buf buf
+ * @param offset offset
+ * @param len len
+ * @since 1.0.0
+ */
+ private static void formatUnsignedLong(long val, @NotNull char[] buf, int offset, int len) {
+ int charPos = offset + len;
+ int radix = 1 << 4;
+ int mask = radix - 1;
+ do {
+ buf[--charPos] = NumberUtils.DIGITS[((int) val) & mask];
+ val >>>= 4;
+ } while (charPos > offset);
+ }
+
+}
+
+
+
diff --git a/src/main/java/com/aos/fileidentify/util/function/DataSupplier.java b/src/main/java/com/aos/fileidentify/util/function/DataSupplier.java
new file mode 100644
index 0000000..d6dcfb8
--- /dev/null
+++ b/src/main/java/com/aos/fileidentify/util/function/DataSupplier.java
@@ -0,0 +1,29 @@
+package com.aos.fileidentify.util.function;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+
+import javax.validation.constraints.NotNull;
+
+
+/**
+ *