14 Commits

Author SHA1 Message Date
aa5e0b1798 docs: 完善 javadoc 2025-04-03 10:09:18 +08:00
7923046423 fix: 补充 ThrowingPredicate 缺失的 FunctionalInterface 注解 2025-04-03 09:59:44 +08:00
dc3d671ea7 style: ArrayTools 中删除已完成的 TODO 注释 2025-04-03 09:17:31 +08:00
d34634a18b perf: 限制 PagingAndSortingQueryParams 中用于排序的字段名称的长度,允许包含短横(-)。 2025-04-03 09:01:11 +08:00
615837ac27 perf: 抑制测试代码的一些警告 2025-04-02 15:21:32 +08:00
0b0036b62a perf: 重命名 ArrayTools 中的方法
indexOfWithPredicate -> indexOf
lastIndexOfWithPredicate -> lastIndexOf
2025-04-02 15:19:01 +08:00
de6b4a6d8d refactor: RegexTools 中使用 ArrayTools#isAllElementsNotNull 判断数组
NOTE: 数组为 null 时,不抛出 NullPointerException,而是 IllegalArgumentException。
2025-04-02 14:28:59 +08:00
55b459c131 perf: 重构 Chinese2ndGenIDCardNumber
不使用 ValidatableStringRecord,在工厂方法中进行参数校验。
2025-04-02 11:00:11 +08:00
2770614078 perf: 弃用 ValidatableStringRecord 2025-04-02 10:59:53 +08:00
0a9005668f perf: IDCardNumber#toDesensitizedString 使用 StringTools#desensitize 进行脱敏 2025-04-02 10:34:39 +08:00
4154fda36e feat: 新增字符串脱敏方法 StringTools#desensitize 2025-04-02 10:23:13 +08:00
aecf467a95 docs: 完善 javadoc 2025-04-02 09:38:39 +08:00
769feaf72e perf: 优化 JSR305 注解的使用 2025-04-01 17:51:35 +08:00
c0be49dd36 prepare 1.1.x 2025-04-01 17:30:02 +08:00
53 changed files with 2350 additions and 200 deletions

View File

@@ -6,7 +6,7 @@
<groupId>xyz.zhouxy.plusone</groupId> <groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-commons</artifactId> <artifactId>plusone-commons</artifactId>
<version>1.0.0</version> <version>1.1.0-SNAPSHOT</version>
<properties> <properties>
<!-- Basic properties --> <!-- Basic properties -->

View File

@@ -29,21 +29,50 @@ import javax.annotation.Nullable;
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/ */
public interface IWithCode<T> { public interface IWithCode<T> {
/**
* 获取码值
* @return 码值
*/
@Nonnull @Nonnull
T getCode(); T getCode();
/**
* 判断 {@code code} 与给定的值是否相等
*
* @param code 用于判断的值
* @return 判断结果
*/
default boolean isCodeEquals(@Nullable T code) { default boolean isCodeEquals(@Nullable T code) {
return Objects.equals(getCode(), code); return Objects.equals(getCode(), code);
} }
/**
* 判断是否与给定的 {@link IWithCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithCode<?> other) { default boolean isSameCodeAs(@Nullable IWithCode<?> other) {
return other != null && Objects.equals(getCode(), other.getCode()); return other != null && Objects.equals(getCode(), other.getCode());
} }
/**
* 判断是否与给定的 {@link IWithIntCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithIntCode other) { default boolean isSameCodeAs(@Nullable IWithIntCode other) {
return other != null && Objects.equals(getCode(), other.getCode()); return other != null && Objects.equals(getCode(), other.getCode());
} }
/**
* 判断是否与给定的 {@link IWithLongCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithLongCode other) { default boolean isSameCodeAs(@Nullable IWithLongCode other) {
return other != null && Objects.equals(getCode(), other.getCode()); return other != null && Objects.equals(getCode(), other.getCode());
} }

View File

@@ -28,20 +28,49 @@ import javax.annotation.Nullable;
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/ */
public interface IWithIntCode { public interface IWithIntCode {
/**
* 获取码值
* @return 码值
*/
int getCode(); int getCode();
/**
* 判断 {@code code} 与给定的值是否相等
*
* @param code 用于判断的值
* @return 判断结果
*/
default boolean isCodeEquals(int code) { default boolean isCodeEquals(int code) {
return getCode() == code; return getCode() == code;
} }
/**
* 判断是否与给定的 {@link IWithCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithCode<?> other) { default boolean isSameCodeAs(@Nullable IWithCode<?> other) {
return other != null && Objects.equals(getCode(), other.getCode()); return other != null && Objects.equals(getCode(), other.getCode());
} }
/**
* 判断是否与给定的 {@link IWithIntCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithIntCode other) { default boolean isSameCodeAs(@Nullable IWithIntCode other) {
return other != null && getCode() == other.getCode(); return other != null && getCode() == other.getCode();
} }
/**
* 判断是否与给定的 {@link IWithLongCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithLongCode other) { default boolean isSameCodeAs(@Nullable IWithLongCode other) {
return other != null && getCode() == other.getCode(); return other != null && getCode() == other.getCode();
} }

View File

@@ -28,20 +28,49 @@ import javax.annotation.Nullable;
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/ */
public interface IWithLongCode { public interface IWithLongCode {
/**
* 获取码值
* @return 码值
*/
long getCode(); long getCode();
/**
* 判断 {@code code} 与给定的值是否相等
*
* @param code 用于判断的值
* @return 判断结果
*/
default boolean isCodeEquals(long code) { default boolean isCodeEquals(long code) {
return getCode() == code; return getCode() == code;
} }
/**
* 判断是否与给定的 {@link IWithCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithCode<?> other) { default boolean isSameCodeAs(@Nullable IWithCode<?> other) {
return other != null && Objects.equals(getCode(), other.getCode()); return other != null && Objects.equals(getCode(), other.getCode());
} }
/**
* 判断是否与给定的 {@link IWithIntCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithIntCode other) { default boolean isSameCodeAs(@Nullable IWithIntCode other) {
return other != null && getCode() == other.getCode(); return other != null && getCode() == other.getCode();
} }
/**
* 判断是否与给定的 {@link IWithLongCode} 有着相等的 {@code code}
*
* @param other 用于比较的对象
* @return 判断结果
*/
default boolean isSameCodeAs(@Nullable IWithLongCode other) { default boolean isSameCodeAs(@Nullable IWithLongCode other) {
return other != null && getCode() == other.getCode(); return other != null && getCode() == other.getCode();
} }

View File

@@ -79,43 +79,100 @@ public final class Ref<T> {
this.value = value; this.value = value;
} }
/**
* 创建对象引用
*
* @param <T> 引用的类型
* @param value 引用的对象
* @return {@code Ref} 对象
*/
public static <T> Ref<T> of(@Nullable T value) { public static <T> Ref<T> of(@Nullable T value) {
return new Ref<>(value); return new Ref<>(value);
} }
/**
* 创建空引用
*
* @param <T> 引用的类型
* @return 空引用
*/
public static <T> Ref<T> empty() { public static <T> Ref<T> empty() {
return new Ref<>(null); return new Ref<>(null);
} }
/**
* 获取引用的对象
*
* @return 引用的对象
*/
@Nullable @Nullable
public T getValue() { public T getValue() {
return value; return value;
} }
/**
* 设置引用的对象
*
* @param value 要引用的对象
*/
public void setValue(@Nullable T value) { public void setValue(@Nullable T value) {
this.value = value; this.value = value;
} }
/**
* 使用 {@link UnaryOperator} 修改 {@code Ref} 内部引用的对象
*
* @param operator 修改逻辑
*/
public void transformValue(UnaryOperator<T> operator) { public void transformValue(UnaryOperator<T> operator) {
this.value = operator.apply(this.value); this.value = operator.apply(this.value);
} }
/**
* 使用 {@link Function} 修改所引用的对象,返回新的 {@code Ref}
*
* @param <R> 结果的引用类型
* @param function 修改逻辑
* @return 修改后的对象的引用
*/
public <R> Ref<R> transform(Function<? super T, R> function) { public <R> Ref<R> transform(Function<? super T, R> function) {
return Ref.of(function.apply(this.value)); return Ref.of(function.apply(this.value));
} }
/**
* 使用 {@link Predicate} 检查引用的对象
*
* @param predicate 判断逻辑
* @return 判断结果
*/
public boolean checkValue(Predicate<? super T> predicate) { public boolean checkValue(Predicate<? super T> predicate) {
return predicate.test(this.value); return predicate.test(this.value);
} }
/**
* 将引用的对象作为入参,执行 {@link Consumer} 的逻辑
*
* @param consumer 要执行的逻辑
*/
public void execute(Consumer<? super T> consumer) { public void execute(Consumer<? super T> consumer) {
consumer.accept(value); consumer.accept(value);
} }
/**
* 判断所引用的对象是否为 {@code null}
*
* @return 是否为 {@code null}
*/
public boolean isNull() { public boolean isNull() {
return this.value == null; return this.value == null;
} }
/**
* 判断所引用的对象是否不为 {@code null}
*
* @return 是否不为 {@code null}
*/
public boolean isNotNull() { public boolean isNotNull() {
return this.value != null; return this.value != null;
} }

View File

@@ -42,26 +42,62 @@ public class CollectionTools {
// #region - isEmpty // #region - isEmpty
// ================================ // ================================
/**
* 判断集合是否为空
*
* @param collection 集合
* @return 是否为空
*/
public static boolean isEmpty(@Nullable Collection<?> collection) { public static boolean isEmpty(@Nullable Collection<?> collection) {
return collection == null || collection.isEmpty(); return collection == null || collection.isEmpty();
} }
/**
* 判断集合是否为空
*
* @param map 集合
* @return 是否为空
*/
public static boolean isEmpty(@Nullable Map<?, ?> map) { public static boolean isEmpty(@Nullable Map<?, ?> map) {
return map == null || map.isEmpty(); return map == null || map.isEmpty();
} }
/**
* 判断集合是否为空
*
* @param table 集合
* @return 是否为空
*/
public static boolean isEmpty(@Nullable Table<?, ?, ?> table) { public static boolean isEmpty(@Nullable Table<?, ?, ?> table) {
return table == null || table.isEmpty(); return table == null || table.isEmpty();
} }
/**
* 判断集合是否为空
*
* @param map 集合
* @return 是否为空
*/
public static boolean isEmpty(@Nullable Multimap<?, ?> map) { public static boolean isEmpty(@Nullable Multimap<?, ?> map) {
return map == null || map.isEmpty(); return map == null || map.isEmpty();
} }
/**
* 判断集合是否为空
*
* @param set 集合
* @return 是否为空
*/
public static boolean isEmpty(@Nullable Multiset<?> set) { public static boolean isEmpty(@Nullable Multiset<?> set) {
return set == null || set.isEmpty(); return set == null || set.isEmpty();
} }
/**
* 判断集合是否为空
*
* @param set 集合
* @return 是否为空
*/
public static boolean isEmpty(@Nullable RangeSet<?> set) { public static boolean isEmpty(@Nullable RangeSet<?> set) {
return set == null || set.isEmpty(); return set == null || set.isEmpty();
} }
@@ -74,26 +110,62 @@ public class CollectionTools {
// #region - isNotEmpty // #region - isNotEmpty
// ================================ // ================================
/**
* 判断集合是否不为空
*
* @param collection 集合
* @return 是否不为空
*/
public static boolean isNotEmpty(@Nullable Collection<?> collection) { public static boolean isNotEmpty(@Nullable Collection<?> collection) {
return collection != null && !collection.isEmpty(); return collection != null && !collection.isEmpty();
} }
/**
* 判断集合是否不为空
*
* @param map 集合
* @return 是否不为空
*/
public static boolean isNotEmpty(@Nullable Map<?, ?> map) { public static boolean isNotEmpty(@Nullable Map<?, ?> map) {
return map != null && !map.isEmpty(); return map != null && !map.isEmpty();
} }
/**
* 判断集合是否不为空
*
* @param table 集合
* @return 是否不为空
*/
public static boolean isNotEmpty(@Nullable Table<?, ?, ?> table) { public static boolean isNotEmpty(@Nullable Table<?, ?, ?> table) {
return table != null && !table.isEmpty(); return table != null && !table.isEmpty();
} }
/**
* 判断集合是否不为空
*
* @param map 集合
* @return 是否不为空
*/
public static boolean isNotEmpty(@Nullable Multimap<?, ?> map) { public static boolean isNotEmpty(@Nullable Multimap<?, ?> map) {
return map != null && !map.isEmpty(); return map != null && !map.isEmpty();
} }
/**
* 判断集合是否不为空
*
* @param set 集合
* @return 是否不为空
*/
public static boolean isNotEmpty(@Nullable Multiset<?> set) { public static boolean isNotEmpty(@Nullable Multiset<?> set) {
return set != null && !set.isEmpty(); return set != null && !set.isEmpty();
} }
/**
* 判断集合是否不为空
*
* @param set 集合
* @return 是否不为空
*/
public static boolean isNotEmpty(@Nullable RangeSet<?> set) { public static boolean isNotEmpty(@Nullable RangeSet<?> set) {
return set != null && !set.isEmpty(); return set != null && !set.isEmpty();
} }
@@ -106,16 +178,41 @@ public class CollectionTools {
// #region - nullToEmpty // #region - nullToEmpty
// ================================ // ================================
/**
* 将 {@code null} 转为空 {@code List}
*
* @param <T> List 元素的类型
* @param list list
* @return 如果 {@code list} 为 {@code null},返回空列表;
* 如果 {@code list} 不为 {@code null},返回 {@code list} 本身
*/
@Nonnull @Nonnull
public static <T> List<T> nullToEmptyList(@Nullable List<T> list) { public static <T> List<T> nullToEmptyList(@Nullable List<T> list) {
return list == null ? Collections.emptyList() : list; return list == null ? Collections.emptyList() : list;
} }
/**
* 将 {@code null} 转为空 {@code Set}
*
* @param <T> Set 元素的类型
* @param set set
* @return 如果 {@code set} 为 {@code null},返回空集合;
* 如果 {@code set} 不为 {@code null},返回 {@code set} 本身
*/
@Nonnull @Nonnull
public static <T> Set<T> nullToEmptySet(@Nullable Set<T> set) { public static <T> Set<T> nullToEmptySet(@Nullable Set<T> set) {
return set == null ? Collections.emptySet() : set; return set == null ? Collections.emptySet() : set;
} }
/**
* 将 {@code null} 转为空 {@code Map}
*
* @param <K> Map 的键的类型
* @param <V> Map 的值的类型
* @param map map
* @return 如果 {@code map} 为 {@code null},返回空集合;
* 如果 {@code map} 不为 {@code null},返回 {@code map} 本身
*/
@Nonnull @Nonnull
public static <K, V> Map<K, V> nullToEmptyMap(@Nullable Map<K, V> map) { public static <K, V> Map<K, V> nullToEmptyMap(@Nullable Map<K, V> map) {
return map == null ? Collections.emptyMap() : map; return map == null ? Collections.emptyMap() : map;

View File

@@ -26,18 +26,40 @@ public final class DataNotExistsException extends Exception {
private static final long serialVersionUID = 6536955800679703111L; private static final long serialVersionUID = 6536955800679703111L;
/**
* 使用默认 message 构造新的 {@code DataNotExistsException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public DataNotExistsException() { public DataNotExistsException() {
super(); super();
} }
/**
* 使用指定的 {@code message} 构造新的 {@code DataNotExistsException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public DataNotExistsException(String message) { public DataNotExistsException(String message) {
super(message); super(message);
} }
/**
* 使用指定的 {@code cause} 构造新的 {@code DataNotExistsException}。
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public DataNotExistsException(Throwable cause) { public DataNotExistsException(Throwable cause) {
super(cause); super(cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 构造新的 {@code DataNotExistsException}。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public DataNotExistsException(String message, Throwable cause) { public DataNotExistsException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@@ -124,26 +124,66 @@ import xyz.zhouxy.plusone.commons.base.IWithCode;
*/ */
public interface MultiTypesException<E extends Exception, T extends MultiTypesException.ExceptionType<E>> { public interface MultiTypesException<E extends Exception, T extends MultiTypesException.ExceptionType<E>> {
/**
* 异常类型
*
* @return 异常类型。通常是实现了 {@link ExceptionType} 的枚举。
*/
@Nonnull @Nonnull
T getType(); T getType();
/**
* 获取异常类型编码
*
* @return 异常类型编码
*/
default @Nonnull String getTypeCode() { default @Nonnull String getTypeCode() {
return getType().getCode(); return getType().getCode();
} }
/**
* 异常类型
*/
public static interface ExceptionType<E extends Exception> extends IWithCode<String> { public static interface ExceptionType<E extends Exception> extends IWithCode<String> {
/**
* 默认异常信息
*/
String getDefaultMessage(); String getDefaultMessage();
/**
* 创建异常
*
* @return 异常对象
*/
@Nonnull @Nonnull
E create(); E create();
/**
* 使用指定 {@code message} 创建异常
*
* @param message 异常信息
* @return 异常对象
*/
@Nonnull @Nonnull
E create(String message); E create(String message);
/**
* 使用指定 {@code cause} 创建异常
*
* @param cause 包装的异常
* @return 异常对象
*/
@Nonnull @Nonnull
E create(Throwable cause); E create(Throwable cause);
/**
* 使用指定 {@code message} 和 {@code cause} 创建异常
*
* @param message 异常信息
* @param cause 包装的异常
* @return 异常对象
*/
@Nonnull @Nonnull
E create(String message, Throwable cause); E create(String message, Throwable cause);

View File

@@ -59,22 +59,56 @@ public final class ParsingFailureException
this.type = type; this.type = type;
} }
/**
* 创建默认类型的 {@code ParsingFailureException}。
* {@code type} 为 {@link Type#DEFAULT}
* {@code message} 为 {@link Type#DEFAULT} 的默认信息。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public ParsingFailureException() { public ParsingFailureException() {
this(Type.DEFAULT, Type.DEFAULT.getDefaultMessage()); this(Type.DEFAULT, Type.DEFAULT.getDefaultMessage());
} }
/**
* 使用指定 {@code message} 创建默认类型的 {@code ParsingFailureException}。
* {@code type} 为 {@link Type#DEFAULT}
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public ParsingFailureException(String message) { public ParsingFailureException(String message) {
this(Type.DEFAULT, message); this(Type.DEFAULT, message);
} }
/**
* 使用指定的 {@code cause} 创建默认类型的 {@code ParsingFailureException}。
* {@code type} 为 {@link Type#DEFAULT}
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public ParsingFailureException(Throwable cause) { public ParsingFailureException(Throwable cause) {
this(Type.DEFAULT, cause); this(Type.DEFAULT, cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 创建默认类型的 {@code ParsingFailureException}。
* {@code type} 为 {@link Type#DEFAULT}。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public ParsingFailureException(String message, Throwable cause) { public ParsingFailureException(String message, Throwable cause) {
this(Type.DEFAULT, message, cause); this(Type.DEFAULT, message, cause);
} }
/**
* 将 {@link DateTimeParseException} 包装为 {@link ParsingFailureException}。
* {@code type} 为 {@link Type#DATE_TIME_PARSING_FAILURE}。
*
* @param cause 包装的 {@code DateTimeParseException}
* @return ParsingFailureException
*/
public static ParsingFailureException of(DateTimeParseException cause) { public static ParsingFailureException of(DateTimeParseException cause) {
if (cause == null) { if (cause == null) {
return Type.DATE_TIME_PARSING_FAILURE.create(); return Type.DATE_TIME_PARSING_FAILURE.create();
@@ -82,10 +116,25 @@ public final class ParsingFailureException
return Type.DATE_TIME_PARSING_FAILURE.create(cause.getMessage(), cause); return Type.DATE_TIME_PARSING_FAILURE.create(cause.getMessage(), cause);
} }
/**
* 将 {@link DateTimeParseException} 包装为 {@link ParsingFailureException}。
* {@code type} 为 {@link Type#DATE_TIME_PARSING_FAILURE}。
*
* @param message 异常信息
* @param cause 包装的 {@code DateTimeParseException}
* @return ParsingFailureException
*/
public static ParsingFailureException of(String message, DateTimeParseException cause) { public static ParsingFailureException of(String message, DateTimeParseException cause) {
return Type.DATE_TIME_PARSING_FAILURE.create(message, cause); return Type.DATE_TIME_PARSING_FAILURE.create(message, cause);
} }
/**
* 将 {@link NumberFormatException} 包装为 {@link ParsingFailureException}。
* {@code type} 为 {@link Type#NUMBER_PARSING_FAILURE}。
*
* @param cause 包装的 {@code NumberFormatException}
* @return ParsingFailureException
*/
public static ParsingFailureException of(NumberFormatException cause) { public static ParsingFailureException of(NumberFormatException cause) {
if (cause == null) { if (cause == null) {
return Type.NUMBER_PARSING_FAILURE.create(); return Type.NUMBER_PARSING_FAILURE.create();
@@ -93,6 +142,14 @@ public final class ParsingFailureException
return Type.NUMBER_PARSING_FAILURE.create(cause.getMessage(), cause); return Type.NUMBER_PARSING_FAILURE.create(cause.getMessage(), cause);
} }
/**
* 将 {@link NumberFormatException} 包装为 {@link ParsingFailureException}。
* {@code type} 为 {@link Type#NUMBER_PARSING_FAILURE}。
*
* @param message 异常信息
* @param cause {@code NumberFormatException}
* @return ParsingFailureException
*/
public static ParsingFailureException of(String message, NumberFormatException cause) { public static ParsingFailureException of(String message, NumberFormatException cause) {
return Type.NUMBER_PARSING_FAILURE.create(message, cause); return Type.NUMBER_PARSING_FAILURE.create(message, cause);
} }
@@ -103,10 +160,15 @@ public final class ParsingFailureException
return type; return type;
} }
/** 默认类型 */
public static final Type DEFAULT = Type.DEFAULT; public static final Type DEFAULT = Type.DEFAULT;
/** 数字转换失败 */
public static final Type NUMBER_PARSING_FAILURE = Type.NUMBER_PARSING_FAILURE; public static final Type NUMBER_PARSING_FAILURE = Type.NUMBER_PARSING_FAILURE;
/** 时间解析失败 */
public static final Type DATE_TIME_PARSING_FAILURE = Type.DATE_TIME_PARSING_FAILURE; public static final Type DATE_TIME_PARSING_FAILURE = Type.DATE_TIME_PARSING_FAILURE;
/** JSON 解析失败 */
public static final Type JSON_PARSING_FAILURE = Type.JSON_PARSING_FAILURE; public static final Type JSON_PARSING_FAILURE = Type.JSON_PARSING_FAILURE;
/** XML 解析失败 */
public static final Type XML_PARSING_FAILURE = Type.XML_PARSING_FAILURE; public static final Type XML_PARSING_FAILURE = Type.XML_PARSING_FAILURE;
public enum Type implements ExceptionType<ParsingFailureException> { public enum Type implements ExceptionType<ParsingFailureException> {

View File

@@ -33,18 +33,40 @@ public class BizException extends RuntimeException {
private static final String DEFAULT_MSG = "业务异常"; private static final String DEFAULT_MSG = "业务异常";
/**
* 使用默认 message 构造新的业务异常。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public BizException() { public BizException() {
super(DEFAULT_MSG); super(DEFAULT_MSG);
} }
/**
* 使用指定的 {@code message} 构造新的业务异常。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public BizException(String message) { public BizException(String message) {
super(message); super(message);
} }
/**
* 使用指定的 {@code cause} 构造新的业务异常。
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public BizException(Throwable cause) { public BizException(Throwable cause) {
super(cause); super(cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 构造新的业务异常。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public BizException(String message, Throwable cause) { public BizException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@@ -60,18 +60,45 @@ public final class InvalidInputException
this.type = type; this.type = type;
} }
/**
* 创建默认类型的 {@code InvalidInputException}。
* {@code type} 为 {@link Type#DEFAULT}
* {@code message} 为 {@link Type#DEFAULT} 的默认信息。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public InvalidInputException() { public InvalidInputException() {
this(Type.DEFAULT); this(Type.DEFAULT);
} }
/**
* 使用指定 {@code message} 创建默认类型的 {@code InvalidInputException}。
* {@code type} 为 {@link Type#DEFAULT}
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public InvalidInputException(String message) { public InvalidInputException(String message) {
this(Type.DEFAULT, message); this(Type.DEFAULT, message);
} }
/**
* 使用指定的 {@code cause} 创建默认类型的 {@code InvalidInputException}。
* {@code type} 为 {@link Type#DEFAULT}
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public InvalidInputException(Throwable cause) { public InvalidInputException(Throwable cause) {
this(Type.DEFAULT, cause); this(Type.DEFAULT, cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 创建默认类型的 {@code InvalidInputException}。
* {@code type} 为 {@link Type#DEFAULT}。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public InvalidInputException(String message, Throwable cause) { public InvalidInputException(String message, Throwable cause) {
this(Type.DEFAULT, message, cause); this(Type.DEFAULT, message, cause);
} }

View File

@@ -30,18 +30,40 @@ public class RequestParamsException extends BizException {
private static final String DEFAULT_MSG = "用户请求参数错误"; private static final String DEFAULT_MSG = "用户请求参数错误";
/**
* 使用默认 message 构造新的 {@code RequestParamsException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public RequestParamsException() { public RequestParamsException() {
super(DEFAULT_MSG); super(DEFAULT_MSG);
} }
/**
* 使用指定的 {@code message} 构造新的 {@code RequestParamsException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public RequestParamsException(String message) { public RequestParamsException(String message) {
super(message); super(message);
} }
/**
* 使用指定的 {@code cause} 构造新的 {@code RequestParamsException}。
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public RequestParamsException(Throwable cause) { public RequestParamsException(Throwable cause) {
super(cause); super(cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 构造新的 {@code RequestParamsException}。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public RequestParamsException(String message, Throwable cause) { public RequestParamsException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@@ -35,18 +35,40 @@ public final class DataOperationResultException extends SysException {
private static final String DEFAULT_MSG = "数据操作的结果不符合预期"; private static final String DEFAULT_MSG = "数据操作的结果不符合预期";
/**
* 使用默认 message 构造新的 {@code DataOperationResultException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public DataOperationResultException() { public DataOperationResultException() {
super(DEFAULT_MSG); super(DEFAULT_MSG);
} }
/**
* 使用指定的 {@code message} 构造新的 {@code DataOperationResultException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public DataOperationResultException(String message) { public DataOperationResultException(String message) {
super(message); super(message);
} }
/**
* 使用指定的 {@code cause} 构造新的 {@code DataOperationResultException}。
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public DataOperationResultException(Throwable cause) { public DataOperationResultException(Throwable cause) {
super(cause); super(cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 构造新的 {@code DataOperationResultException}。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public DataOperationResultException(String message, Throwable cause) { public DataOperationResultException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@@ -29,18 +29,40 @@ package xyz.zhouxy.plusone.commons.exception.system;
public class NoAvailableMacFoundException extends SysException { public class NoAvailableMacFoundException extends SysException {
private static final long serialVersionUID = 152827098461071551L; private static final long serialVersionUID = 152827098461071551L;
/**
* 使用默认 message 构造新的 {@code NoAvailableMacFoundException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public NoAvailableMacFoundException() { public NoAvailableMacFoundException() {
super(); super();
} }
/**
* 使用指定的 {@code message} 构造新的 {@code NoAvailableMacFoundException}。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public NoAvailableMacFoundException(String message) { public NoAvailableMacFoundException(String message) {
super(message); super(message);
} }
/**
* 使用指定的 {@code cause} 构造新的 {@code NoAvailableMacFoundException}。
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public NoAvailableMacFoundException(Throwable cause) { public NoAvailableMacFoundException(Throwable cause) {
super(cause); super(cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 构造新的 {@code NoAvailableMacFoundException}。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public NoAvailableMacFoundException(String message, Throwable cause) { public NoAvailableMacFoundException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@@ -30,18 +30,40 @@ public class SysException extends RuntimeException {
private static final String DEFAULT_MSG = "系统异常"; private static final String DEFAULT_MSG = "系统异常";
/**
* 使用默认 message 构造新的系统异常。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*/
public SysException() { public SysException() {
super(DEFAULT_MSG); super(DEFAULT_MSG);
} }
/**
* 使用指定的 {@code message} 构造新的系统异常。
* {@code cause} 未初始化,后面可能会通过调用 {@link #initCause} 进行初始化。
*
* @param message 异常信息
*/
public SysException(String message) { public SysException(String message) {
super(message); super(message);
} }
/**
* 使用指定的 {@code cause} 构造新的系统异常。
* {@code message} 为 (cause==null ? null : cause.toString())。
*
* @param cause 包装的异常
*/
public SysException(Throwable cause) { public SysException(Throwable cause) {
super(cause); super(cause);
} }
/**
* 使用指定的 {@code message} 和 {@code cause} 构造新的系统异常。
*
* @param message 异常信息
* @param cause 包装的异常
*/
public SysException(String message, Throwable cause) { public SysException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }

View File

@@ -18,11 +18,35 @@ package xyz.zhouxy.plusone.commons.function;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
/**
* BoolUnaryOperator
*
* <p>
* 一个特殊的 {@link java.util.function.UnaryOperator}。
* 表示对 {@code boolean} 值的一元操作。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
* @see java.util.function.UnaryOperator
*/
@Beta @Beta
@FunctionalInterface @FunctionalInterface
public interface BoolUnaryOperator { public interface BoolUnaryOperator {
/**
* 将此函数应用于给定的 {@code boolean} 参数,返回一个 {@code boolean} 结果。
*
* @param operand 操作数
* @return 结果
*/
boolean applyAsBool(boolean operand); boolean applyAsBool(boolean operand);
/**
* 返回一个 {@code BoolUnaryOperator},该操作符将给定的操作数取反。
*
* @return {@code BoolUnaryOperator}
*/
static BoolUnaryOperator not() { static BoolUnaryOperator not() {
return b -> !b; return b -> !b;
} }

View File

@@ -18,8 +18,27 @@ package xyz.zhouxy.plusone.commons.function;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
/**
* CharUnaryOperator
*
* <p>
* 一个特殊的 {@link java.util.function.UnaryOperator}。
* 表示对 {@code char} 的一元操作。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
* @see java.util.function.UnaryOperator
*/
@Beta @Beta
@FunctionalInterface @FunctionalInterface
public interface CharUnaryOperator { public interface CharUnaryOperator {
/**
* 将此函数应用于给定的 {@code char} 参数,返回一个 {@code char} 结果。
*
* @param operand 操作数
* @return 结果
*/
char applyAsChar(char operand); char applyAsChar(char operand);
} }

View File

@@ -16,9 +16,26 @@
package xyz.zhouxy.plusone.commons.function; package xyz.zhouxy.plusone.commons.function;
/**
* Executable
*
* <p>
* 表示一个无入参无返回值的操作,可抛出异常。
* </p>
*
* @param <E> 可抛出的异常类型
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
*/
@FunctionalInterface @FunctionalInterface
public interface Executable<E extends Throwable> { public interface Executable<E extends Throwable> {
/**
* 执行
*
* @throws E 可抛出的异常
*/
void execute() throws E; void execute() throws E;
} }

View File

@@ -24,6 +24,7 @@ import java.util.function.Supplier;
* *
* <p> * <p>
* 返回 {@code Optional&lt;T&gt;} 对象。 * 返回 {@code Optional&lt;T&gt;} 对象。
* </p>
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0 * @since 1.0.0

View File

@@ -16,13 +16,24 @@
package xyz.zhouxy.plusone.commons.function; package xyz.zhouxy.plusone.commons.function;
/**
* ThrowingConsumer
*
* <p>
* 允许抛出异常的消费操作。是一个特殊的 {@link java.util.function.Consumer}。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
* @see java.util.function.Consumer
*/
@FunctionalInterface @FunctionalInterface
public interface ThrowingConsumer<T, E extends Throwable> { public interface ThrowingConsumer<T, E extends Throwable> {
/** /**
* Consume the supplied argument, potentially throwing an exception. * 消费给定的参数,允许抛出异常
* *
* @param t the argument to consume * @param t 要消费的参数
*/ */
void accept(T t) throws E; void accept(T t) throws E;

View File

@@ -15,14 +15,29 @@
*/ */
package xyz.zhouxy.plusone.commons.function; package xyz.zhouxy.plusone.commons.function;
/**
* ThrowingFunction
*
* <p>
* 接收一个参数,并返回一个结果,可以抛出异常。
* </p>
*
* @param <T> 入参类型
* @param <R> 返回结果类型
* @param <E> 异常类型
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0
* @see java.util.function.Function
*/
@FunctionalInterface @FunctionalInterface
public interface ThrowingFunction<T, R, E extends Throwable> { public interface ThrowingFunction<T, R, E extends Throwable> {
/** /**
* Applies this function to the given argument. * 接收一个参数,并返回一个结果,可以抛出异常。
* *
* @param t the function argument * @param t 入参
* @return the function result * @return 函数结果
*/ */
R apply(T t) throws E; R apply(T t) throws E;

View File

@@ -16,14 +16,25 @@
package xyz.zhouxy.plusone.commons.function; package xyz.zhouxy.plusone.commons.function;
/**
* ThrowingPredicate
*
* <p>
* 接收一个参数,返回一个布尔值,可抛出异常。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
* @see java.util.function.Predicate
*/
@FunctionalInterface
public interface ThrowingPredicate<T, E extends Throwable> { public interface ThrowingPredicate<T, E extends Throwable> {
/** /**
* Evaluates this predicate on the given argument. * 对给定的参数进行评估
* *
* @param t the input argument * @param t 入参
* @return {@code true} if the input argument matches the predicate, * @return 入参符合条件时返回 {@code true},否则返回 {@code false}
* otherwise {@code false}
*/ */
boolean test(T t) throws E; boolean test(T t) throws E;
} }

View File

@@ -16,13 +16,27 @@
package xyz.zhouxy.plusone.commons.function; package xyz.zhouxy.plusone.commons.function;
/**
* ThrowingSupplier
*
* <p>
* 允许抛出异常的 Supplier 接口。
* </p>
*
* @param <T> 结果类型
* @param <E> 异常类型
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
* @see java.util.function.Supplier
*/
@FunctionalInterface @FunctionalInterface
public interface ThrowingSupplier<T, E extends Throwable> { public interface ThrowingSupplier<T, E extends Throwable> {
/** /**
* Get a result, potentially throwing an exception. * 获取一个结果,允许抛出异常。
* *
* @return a result * @return 结果
*/ */
T get() throws E; T get() throws E;

View File

@@ -16,11 +16,15 @@
package xyz.zhouxy.plusone.commons.model; package xyz.zhouxy.plusone.commons.model;
import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import javax.annotation.Nullable;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.Immutable; import com.google.errorprone.annotations.Immutable;
@@ -45,8 +49,11 @@ import xyz.zhouxy.plusone.commons.util.StringTools;
@ValueObject @ValueObject
@Immutable @Immutable
public class Chinese2ndGenIDCardNumber public class Chinese2ndGenIDCardNumber
extends ValidatableStringRecord<Chinese2ndGenIDCardNumber> implements IDCardNumber, Comparable<Chinese2ndGenIDCardNumber>, Serializable {
implements IDCardNumber { private static final long serialVersionUID = 5655592250204184210L;
/** 身份证号码 */
private final String value;
/** 省份编码 */ /** 省份编码 */
private final String provinceCode; private final String provinceCode;
@@ -61,79 +68,129 @@ public class Chinese2ndGenIDCardNumber
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
private Chinese2ndGenIDCardNumber(String value) { private Chinese2ndGenIDCardNumber(String value,
super(value.toUpperCase(), PatternConsts.CHINESE_2ND_ID_CARD_NUMBER, () -> "二代居民身份证校验失败:" + value); String provinceCode, String cityCode, String countyCode,
Gender gender, LocalDate birthDate) {
final Matcher matcher = getMatcher(); this.value = value;
this.provinceCode = provinceCode;
final String provinceCodeValue = matcher.group("province"); this.cityCode = cityCode;
AssertTools.checkArgument(Chinese2ndGenIDCardNumber.PROVINCE_CODES.containsKey(provinceCodeValue)); this.countyCode = countyCode;
this.gender = gender;
final String cityCodeValue = matcher.group("city"); this.birthDate = birthDate;
final String countyCodeValue = matcher.group("county"); }
final Gender genderValue;
final LocalDate birthDateValue;
/**
* 根据身份证号码创建 {@code Chinese2ndGenIDCardNumber} 对象
*
* @param idCardNumber 身份证号码值
* @return {@code Chinese2ndGenIDCardNumber} 对象
*/
public static Chinese2ndGenIDCardNumber of(final String idCardNumber) {
try { try {
AssertTools.checkArgument(StringTools.isNotBlank(idCardNumber), "二代居民身份证校验失败:号码为空");
final String value = idCardNumber.toUpperCase();
final Matcher matcher = PatternConsts.CHINESE_2ND_ID_CARD_NUMBER.matcher(value);
AssertTools.checkArgument(matcher.matches(), () -> "二代居民身份证校验失败:" + value);
final String provinceCode = matcher.group("province");
AssertTools.checkArgument(Chinese2ndGenIDCardNumber.PROVINCE_CODES.containsKey(provinceCode));
final String cityCode = matcher.group("city");
final String countyCode = matcher.group("county");
// 出生日期 // 出生日期
final String birthDateStr = matcher.group("birthDate"); final String birthDateStr = matcher.group("birthDate");
birthDateValue = LocalDate.parse(birthDateStr, DATE_FORMATTER); final LocalDate birthDate = LocalDate.parse(birthDateStr, DATE_FORMATTER);
// 性别 // 性别
final int genderCode = Integer.parseInt(matcher.group("gender")); final int genderCode = Integer.parseInt(matcher.group("gender"));
genderValue = genderCode % 2 == 0 ? Gender.FEMALE : Gender.MALE; final Gender gender = genderCode % 2 == 0 ? Gender.FEMALE : Gender.MALE;
return new Chinese2ndGenIDCardNumber(value, provinceCode, cityCode, countyCode, gender, birthDate);
}
catch (IllegalArgumentException e) {
throw e;
} }
catch (Exception e) { catch (Exception e) {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }
this.provinceCode = provinceCodeValue;
this.cityCode = cityCodeValue;
this.countyCode = countyCodeValue;
this.gender = genderValue;
this.birthDate = birthDateValue;
}
public static Chinese2ndGenIDCardNumber of(final String value) {
AssertTools.checkArgument(StringTools.isNotBlank(value), "二代居民身份证校验失败:号码为空");
return new Chinese2ndGenIDCardNumber(value);
} }
// ================================ // ================================
// #region - reader methods // #region - reader methods
// ================================ // ================================
@Override
@ReaderMethod
public String value() {
return value;
}
/**
* 所属省份代码
*
* @return 所属省份代码
*/
@ReaderMethod @ReaderMethod
public String getProvinceCode() { public String getProvinceCode() {
return provinceCode; return provinceCode;
} }
/**
* 所属省份名称
*
* @return 所属省份名称
*/
@ReaderMethod @ReaderMethod
public String getProvinceName() { public String getProvinceName() {
return PROVINCE_CODES.get(this.provinceCode); return PROVINCE_CODES.get(this.provinceCode);
} }
/**
* 所属省份完整行政区划代码
*
* @return 所属省份完整行政区划代码
*/
@ReaderMethod @ReaderMethod
public String getFullProvinceCode() { public String getFullProvinceCode() {
return Strings.padEnd(this.provinceCode, 12, '0'); return Strings.padEnd(this.provinceCode, 12, '0');
} }
/**
* 所属市级代码
*
* @return 所属市级代码
*/
@ReaderMethod @ReaderMethod
public String getCityCode() { public String getCityCode() {
return cityCode; return cityCode;
} }
/**
* 所属市级完整行政区划代码
*
* @return 所属市级完整行政区划代码
*/
@ReaderMethod @ReaderMethod
public String getFullCityCode() { public String getFullCityCode() {
return Strings.padEnd(this.cityCode, 12, '0'); return Strings.padEnd(this.cityCode, 12, '0');
} }
/**
* 所属县级代码
*
* @return 所属县级代码
*/
@ReaderMethod @ReaderMethod
public String getCountyCode() { public String getCountyCode() {
return countyCode; return countyCode;
} }
/**
* 所属县级完整行政区划代码
*
* @return 所属县级完整行政区划代码
*/
@ReaderMethod @ReaderMethod
public String getFullCountyCode() { public String getFullCountyCode() {
return Strings.padEnd(this.countyCode, 12, '0'); return Strings.padEnd(this.countyCode, 12, '0');
@@ -155,59 +212,72 @@ public class Chinese2ndGenIDCardNumber
// #endregion - reader methods // #endregion - reader methods
// ================================ // ================================
/** /** 省份代码表 */
* 省份代码表 public static final Map<String, String> PROVINCE_CODES = ImmutableMap.<String, String>builder()
*/ .put("11", "北京")
public static final Map<String, String> PROVINCE_CODES; .put("12", "天津")
.put("13", "河北")
.put("14", "山西")
.put("15", "内蒙古")
.put("21", "辽宁")
.put("22", "吉林")
.put("23", "黑龙江")
.put("31", "上海")
.put("32", "江苏")
.put("33", "浙江")
.put("34", "安徽")
.put("35", "福建")
.put("36", "江西")
.put("37", "山东")
.put("41", "河南")
.put("42", "湖北")
.put("43", "湖南")
.put("44", "广东")
.put("45", "广西")
.put("46", "海南")
.put("50", "重庆")
.put("51", "四川")
.put("52", "贵州")
.put("53", "云南")
.put("54", "西藏")
.put("61", "陕西")
.put("62", "甘肃")
.put("63", "青海")
.put("64", "宁夏")
.put("65", "新疆")
.put("71", "台湾")
.put("81", "香港")
.put("82", "澳门")
.put("83", "台湾") // 台湾身份证号码以83开头但是行政区划为71
.put("91", "国外")
.build();
static { @SuppressWarnings("null")
PROVINCE_CODES = ImmutableMap.<String, String>builder() @Override
.put("11", "北京") public int compareTo(Chinese2ndGenIDCardNumber o) {
.put("12", "天津") return value.compareTo(o.value);
.put("13", "河北")
.put("14", "山西")
.put("15", "内蒙古")
.put("21", "辽宁")
.put("22", "吉林")
.put("23", "黑龙江")
.put("31", "上海")
.put("32", "江苏")
.put("33", "浙江")
.put("34", "安徽")
.put("35", "福建")
.put("36", "江西")
.put("37", "山东")
.put("41", "河南")
.put("42", "湖北")
.put("43", "湖南")
.put("44", "广东")
.put("45", "广西")
.put("46", "海南")
.put("50", "重庆")
.put("51", "四川")
.put("52", "贵州")
.put("53", "云南")
.put("54", "西藏")
.put("61", "陕西")
.put("62", "甘肃")
.put("63", "青海")
.put("64", "宁夏")
.put("65", "新疆")
.put("71", "台湾")
.put("81", "香港")
.put("82", "澳门")
.put("83", "台湾") // 台湾身份证号码以83开头但是行政区划为71
.put("91", "国外")
.build();
} }
@Override @Override
public int hashCode() { public int hashCode() {
return super.hashCode(); return Objects.hash(value);
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
return super.equals(obj); if (this == obj) {
return true;
}
if (!(obj instanceof Chinese2ndGenIDCardNumber)) {
return false;
}
Chinese2ndGenIDCardNumber other = (Chinese2ndGenIDCardNumber) obj;
return Objects.equals(value, other.value);
} }
@Override
public String toString() {
return value();
}
} }

View File

@@ -43,20 +43,41 @@ public enum Gender implements IWithIntCode {
this.displayNameZh = displayNameZh; this.displayNameZh = displayNameZh;
} }
/**
* 根据码值获取对应枚举
*
* @param value 码值
* @return 枚举值
*/
public static Gender of(int value) { public static Gender of(int value) {
AssertTools.checkCondition(0 <= value && value < VALUES.length, AssertTools.checkCondition(0 <= value && value < VALUES.length,
() -> new EnumConstantNotPresentException(Gender.class, String.valueOf(value))); () -> new EnumConstantNotPresentException(Gender.class, String.valueOf(value)));
return VALUES[value]; return VALUES[value];
} }
/**
* 获取枚举码值
*
* @return 码值
*/
public int getValue() { public int getValue() {
return value; return value;
} }
/**
* 枚举名称
*
* @return 枚举名称
*/
public String getDisplayName() { public String getDisplayName() {
return displayName; return displayName;
} }
/**
* 枚举中文名称
*
* @return 枚举中文名称
*/
public String getDisplayNameZh() { public String getDisplayNameZh() {
return displayNameZh; return displayNameZh;
} }

View File

@@ -19,7 +19,7 @@ package xyz.zhouxy.plusone.commons.model;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import xyz.zhouxy.plusone.commons.util.AssertTools; import xyz.zhouxy.plusone.commons.util.StringTools;
/** /**
* 身份证号 * 身份证号
@@ -30,19 +30,32 @@ public interface IDCardNumber {
static final int DEFAULT_DISPLAY_FRONT = 1; static final int DEFAULT_DISPLAY_FRONT = 1;
static final int DEFAULT_DISPLAY_END = 2; static final int DEFAULT_DISPLAY_END = 2;
/**
* 身份证号
*
* @return 身份证号
*/
String value(); String value();
/** /**
* 根据身份证号判断性别 * 根据身份证号判断性别
*
* @return {@link Gender} 对象
*/ */
Gender getGender(); Gender getGender();
/** /**
* 获取出生日期 * 获取出生日期
*
* @return 出生日期
*/ */
LocalDate getBirthDate(); LocalDate getBirthDate();
/** 计算年龄 */ /**
* 计算年龄
*
* @return 年龄
*/
default int getAge() { default int getAge() {
LocalDate now = LocalDate.now(); LocalDate now = LocalDate.now();
return Period.between(getBirthDate(), now).getYears(); return Period.between(getBirthDate(), now).getYears();
@@ -52,23 +65,36 @@ public interface IDCardNumber {
// #region - toString // #region - toString
// ================================ // ================================
/**
* 脱敏字符串(前面留 1 位,后面留 2 位)
*
* @return 脱敏字符串
*/
default String toDesensitizedString() { default String toDesensitizedString() {
return toDesensitizedString(DEFAULT_REPLACED_CHAR, DEFAULT_DISPLAY_FRONT, DEFAULT_DISPLAY_END); return StringTools.desensitize(value(), DEFAULT_REPLACED_CHAR, DEFAULT_DISPLAY_FRONT, DEFAULT_DISPLAY_END);
} }
/**
* 脱敏字符串
*
* @param front 前面保留的字符数
* @param end 后面保留的字符数
* @return 脱敏字符串
*/
default String toDesensitizedString(int front, int end) { default String toDesensitizedString(int front, int end) {
return toDesensitizedString(DEFAULT_REPLACED_CHAR, front, end); return StringTools.desensitize(value(), DEFAULT_REPLACED_CHAR, front, end);
} }
/**
* 脱敏字符串
*
* @param replacedChar 替换字符
* @param front 前面保留的字符数
* @param end 后面保留的字符数
* @return 脱敏字符串
*/
default String toDesensitizedString(char replacedChar, int front, int end) { default String toDesensitizedString(char replacedChar, int front, int end) {
final String value = value(); return StringTools.desensitize(value(), replacedChar, front, end);
AssertTools.checkArgument(front >= 0 && end >= 0);
AssertTools.checkArgument((front + end) <= value.length(), "需要截取的长度不能大于身份证号长度");
final char[] charArray = value.toCharArray();
for (int i = front; i < charArray.length - end; i++) {
charArray[i] = replacedChar;
}
return String.valueOf(charArray);
} }
// ================================ // ================================

View File

@@ -22,6 +22,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.commons.annotation.ReaderMethod; import xyz.zhouxy.plusone.commons.annotation.ReaderMethod;
import xyz.zhouxy.plusone.commons.util.AssertTools; import xyz.zhouxy.plusone.commons.util.AssertTools;
@@ -31,8 +32,11 @@ import xyz.zhouxy.plusone.commons.util.AssertTools;
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0 * @since 1.0.0
*
* @deprecated 弃用。使用工厂方法创建对象,并在其中进行校验即可。
*/ */
public abstract class ValidatableStringRecord<T extends ValidatableStringRecord<T>> @Deprecated
public abstract class ValidatableStringRecord<T extends ValidatableStringRecord<T>> // NOSONAR 暂不删除
implements Comparable<T> { implements Comparable<T> {
@Nonnull @Nonnull
@@ -40,17 +44,36 @@ public abstract class ValidatableStringRecord<T extends ValidatableStringRecord<
private final Matcher matcher; private final Matcher matcher;
protected ValidatableStringRecord(@Nonnull String value, @Nonnull Pattern pattern) { /**
* 构造字符串值对象
*
* @param value 字符串值
* @param pattern 正则
*/
protected ValidatableStringRecord(String value, Pattern pattern) {
this(value, pattern, "Invalid value"); this(value, pattern, "Invalid value");
} }
protected ValidatableStringRecord(@Nonnull String value, @Nonnull Pattern pattern, /**
@Nonnull Supplier<String> errorMessageSupplier) { * 构造字符串值对象
*
* @param value 字符串值
* @param pattern 正则
* @param errorMessageSupplier 正则不匹配时的错误信息
*/
protected ValidatableStringRecord(String value, Pattern pattern,
Supplier<String> errorMessageSupplier) {
this(value, pattern, errorMessageSupplier.get()); this(value, pattern, errorMessageSupplier.get());
} }
protected ValidatableStringRecord(@Nonnull String value, @Nonnull Pattern pattern, /**
@Nonnull String errorMessage) { * 构造字符串值对象
*
* @param value 字符串值
* @param pattern 正则
* @param errorMessage 正则不匹配时的错误信息
*/
protected ValidatableStringRecord(String value, Pattern pattern, String errorMessage) {
AssertTools.checkArgument(Objects.nonNull(value), "The value cannot be null."); AssertTools.checkArgument(Objects.nonNull(value), "The value cannot be null.");
AssertTools.checkArgument(Objects.nonNull(pattern), "The pattern cannot be null."); AssertTools.checkArgument(Objects.nonNull(pattern), "The pattern cannot be null.");
this.matcher = pattern.matcher(value); this.matcher = pattern.matcher(value);
@@ -69,7 +92,7 @@ public abstract class ValidatableStringRecord<T extends ValidatableStringRecord<
} }
@Override @Override
public int compareTo(T o) { public int compareTo(@SuppressWarnings("null") T o) {
return this.value.compareTo(o.value()); return this.value.compareTo(o.value());
} }
@@ -79,7 +102,7 @@ public abstract class ValidatableStringRecord<T extends ValidatableStringRecord<
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) if (this == obj)
return true; return true;
if (obj == null) if (obj == null)
@@ -96,6 +119,11 @@ public abstract class ValidatableStringRecord<T extends ValidatableStringRecord<
return this.value(); return this.value();
} }
/**
* 获取正则匹配结果
*
* @return {@code Matcher} 对象
*/
protected final Matcher getMatcher() { protected final Matcher getMatcher() {
return matcher; return matcher;
} }

View File

@@ -19,6 +19,8 @@ package xyz.zhouxy.plusone.commons.model.dto;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod; import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
import xyz.zhouxy.plusone.commons.collection.CollectionTools; import xyz.zhouxy.plusone.commons.collection.CollectionTools;
@@ -36,25 +38,49 @@ public class PageResult<T> {
private final List<T> content; private final List<T> content;
private PageResult(List<T> content, long total) { private PageResult(@Nullable final List<T> content, final long total) {
this.content = CollectionTools.nullToEmptyList(content); this.content = CollectionTools.nullToEmptyList(content);
this.total = total; this.total = total;
} }
/**
* 创建一个分页查询的结果
*
* @param <T> 内容类型
* @param content 一页数据
* @param total 总数据量
* @return 分页查询的结果
*/
@StaticFactoryMethod(PageResult.class) @StaticFactoryMethod(PageResult.class)
public static <T> PageResult<T> of(List<T> content, long total) { public static <T> PageResult<T> of(@Nullable final List<T> content, final long total) {
return new PageResult<>(content, total); return new PageResult<>(content, total);
} }
/**
* 创建一个空的分页查询的结果
*
* @param <T> 内容类型
* @return 空结果
*/
@StaticFactoryMethod(PageResult.class) @StaticFactoryMethod(PageResult.class)
public static <T> PageResult<T> empty() { public static <T> PageResult<T> empty() {
return new PageResult<>(Collections.emptyList(), 0L); return new PageResult<>(Collections.emptyList(), 0L);
} }
/**
* 总数据量
*
* @return 总数据量
*/
public long getTotal() { public long getTotal() {
return total; return total;
} }
/**
* 一页数据
*
* @return 一页数据
*/
public List<T> getContent() { public List<T> getContent() {
return content; return content;
} }

View File

@@ -21,7 +21,6 @@ import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@@ -51,11 +50,16 @@ public class PagingAndSortingQueryParams {
private Long pageNum; private Long pageNum;
private List<String> orderBy; private List<String> orderBy;
private static final Pattern SORT_STR_PATTERN = Pattern.compile("^[a-zA-Z]\\w+-(desc|asc|DESC|ASC)$"); private static final Pattern SORT_STR_PATTERN = Pattern.compile("^[a-zA-Z][\\w-]{0,63}-(desc|asc|DESC|ASC)$");
private final Map<String, String> sortableProperties; private final Map<String, String> sortableProperties;
public PagingAndSortingQueryParams(@Nonnull Map<String, String> sortableProperties) { /**
* 构造分页排序查询参数
*
* @param sortableProperties 可排序的属性。不可为空。
*/
public PagingAndSortingQueryParams(Map<String, String> sortableProperties) {
AssertTools.checkArgument(CollectionTools.isNotEmpty(sortableProperties), AssertTools.checkArgument(CollectionTools.isNotEmpty(sortableProperties),
"Sortable properties can not be empty."); "Sortable properties can not be empty.");
sortableProperties.forEach((k, v) -> sortableProperties.forEach((k, v) ->
@@ -66,28 +70,58 @@ public class PagingAndSortingQueryParams {
// Setters // Setters
public final void setOrderBy(@Nullable List<String> orderBy) { /**
* 设置排序规则
*
* @param orderBy 排序规则,不能为空
*/
public final void setOrderBy(List<String> orderBy) {
this.orderBy = orderBy; this.orderBy = orderBy;
} }
/**
* 设置每页大小
*
* @param size 每页大小
*/
public final void setSize(@Nullable Integer size) { public final void setSize(@Nullable Integer size) {
this.size = size; this.size = size;
} }
/**
* 设置页码
*
* @param pageNum 页码
*/
public final void setPageNum(@Nullable Long pageNum) { public final void setPageNum(@Nullable Long pageNum) {
this.pageNum = pageNum; this.pageNum = pageNum;
} }
// Setters end // Setters end
/**
* 构建分页参数
*
* @return {@code PagingParams} 对象
*/
public final PagingParams buildPagingParams() { public final PagingParams buildPagingParams() {
final int sizeValue = this.size != null ? this.size : defaultSizeInternal(); final int sizeValue = this.size != null ? this.size : defaultSizeInternal();
final long pageNumValue = this.pageNum != null ? this.pageNum : 1L; final long pageNumValue = this.pageNum != null ? this.pageNum : 1L;
final List<SortableProperty> propertiesToSort = this.orderBy.stream().map(this::generateSortableProperty) AssertTools.checkArgument(CollectionTools.isNotEmpty(this.orderBy),
"The 'orderBy' cannot be empty");
final List<SortableProperty> propertiesToSort = this.orderBy.stream()
.map(this::generateSortableProperty)
.collect(Collectors.toList()); .collect(Collectors.toList());
return new PagingParams(sizeValue, pageNumValue, propertiesToSort); return new PagingParams(sizeValue, pageNumValue, propertiesToSort);
} }
/**
* 默认每页大小
*
* <p>NOTE: 可覆写此方法</p>
*
* @return 默认每页大小
*/
@Virtual @Virtual
protected int defaultSizeInternal() { protected int defaultSizeInternal() {
return DEFAULT_PAGE_SIZE; return DEFAULT_PAGE_SIZE;
@@ -104,6 +138,7 @@ public class PagingAndSortingQueryParams {
} }
private SortableProperty generateSortableProperty(String orderByStr) { private SortableProperty generateSortableProperty(String orderByStr) {
AssertTools.checkArgument(StringTools.isNotBlank(orderByStr));
AssertTools.checkArgument(RegexTools.matches(orderByStr, SORT_STR_PATTERN)); AssertTools.checkArgument(RegexTools.matches(orderByStr, SORT_STR_PATTERN));
String[] propertyNameAndOrderType = orderByStr.split("-"); String[] propertyNameAndOrderType = orderByStr.split("-");
AssertTools.checkArgument(propertyNameAndOrderType.length == 2); AssertTools.checkArgument(propertyNameAndOrderType.length == 2);
@@ -116,6 +151,9 @@ public class PagingAndSortingQueryParams {
return new SortableProperty(propertyName, columnName, orderType); return new SortableProperty(propertyName, columnName, orderType);
} }
/**
* 可排序属性
*/
public static final class SortableProperty { public static final class SortableProperty {
private final String propertyName; private final String propertyName;
private final String columnName; private final String columnName;
@@ -132,18 +170,38 @@ public class PagingAndSortingQueryParams {
this.sqlSnippet = this.propertyName + " " + this.orderType; this.sqlSnippet = this.propertyName + " " + this.orderType;
} }
/**
* 属性名
*
* @return 属性名
*/
public String getPropertyName() { public String getPropertyName() {
return propertyName; return propertyName;
} }
/**
* 对应数据库中列名称
*
* @return 列名称
*/
public String getColumnName() { public String getColumnName() {
return columnName; return columnName;
} }
/**
* 排序方式
*
* @return 排序方式
*/
public String getOrderType() { public String getOrderType() {
return orderType; return orderType;
} }
/**
* SQL 片段
*
* @return SQL 片段
*/
public String getSqlSnippet() { public String getSqlSnippet() {
return sqlSnippet; return sqlSnippet;
} }

View File

@@ -23,9 +23,13 @@ import xyz.zhouxy.plusone.commons.model.dto.PagingAndSortingQueryParams.Sortable
public class PagingParams { public class PagingParams {
/** 每页大小 */
private final int size; private final int size;
/** 当前页码 */
private final long pageNum; private final long pageNum;
/** 偏移量 */
private final long offset; private final long offset;
/** 排序 */
private final List<SortableProperty> orderBy; private final List<SortableProperty> orderBy;
PagingParams(int size, long pageNum, List<SortableProperty> orderBy) { PagingParams(int size, long pageNum, List<SortableProperty> orderBy) {
@@ -37,18 +41,38 @@ public class PagingParams {
// Getters // Getters
/**
* 排序规则
*
* @return 排序规则
*/
public final List<SortableProperty> getOrderBy() { public final List<SortableProperty> getOrderBy() {
return Collections.unmodifiableList(this.orderBy); return Collections.unmodifiableList(this.orderBy);
} }
/**
* 每页大小
*
* @return 每页大小
*/
public final int getSize() { public final int getSize() {
return this.size; return this.size;
} }
/**
* 当前页码
*
* @return 当前页码
*/
public final long getPageNum() { public final long getPageNum() {
return this.pageNum; return this.pageNum;
} }
/**
* 偏移量
*
* @return 偏移量
*/
public final long getOffset() { public final long getOffset() {
return this.offset; return this.offset;
} }

View File

@@ -35,10 +35,23 @@ public class UnifiedResponse<T> {
// #region - Constructors // #region - Constructors
// ================================ // ================================
/**
* 构造 {@code UnifiedResponse}
*
* @param code 状态码
* @param message 响应信息
*/
UnifiedResponse(String code, @Nullable String message) { UnifiedResponse(String code, @Nullable String message) {
this(code, message, null); this(code, message, null);
} }
/**
* 构造 {@code UnifiedResponse}
*
* @param code 状态码
* @param message 响应信息
* @param data 响应数据
*/
UnifiedResponse(String code, @Nullable String message, @Nullable T data) { UnifiedResponse(String code, @Nullable String message, @Nullable T data) {
this.code = Objects.requireNonNull(code); this.code = Objects.requireNonNull(code);
this.message = message == null ? "" : message; this.message = message == null ? "" : message;
@@ -53,14 +66,29 @@ public class UnifiedResponse<T> {
// #region - Getters // #region - Getters
// ================================ // ================================
/**
* 状态码
*
* @return 状态码
*/
public String getCode() { public String getCode() {
return code; return code;
} }
/**
* 响应信息
*
* @return 响应信息
*/
public String getMessage() { public String getMessage() {
return message; return message;
} }
/**
* 响应数据
*
* @return 响应数据
*/
@Nullable @Nullable
public T getData() { public T getData() {
return data; return data;
@@ -76,7 +104,7 @@ public class UnifiedResponse<T> {
this.code, this.message, transValue(this.data)); this.code, this.message, transValue(this.data));
} }
private static String transValue(Object value) { private static String transValue(@Nullable Object value) {
if (value == null) { if (value == null) {
return null; return null;
} }

View File

@@ -34,14 +34,36 @@ public class UnifiedResponses {
// #region - success // #region - success
// ================================ // ================================
/**
* 默认成功响应结果
*
* @return {@code UnifiedResponse} 对象。
* {@code code} = "2000000", {@code message} = "SUCCESS", {@code data} = null
*/
public static UnifiedResponse<Void> success() { public static UnifiedResponse<Void> success() {
return new UnifiedResponse<>(SUCCESS_CODE, DEFAULT_SUCCESS_MSG); return new UnifiedResponse<>(SUCCESS_CODE, DEFAULT_SUCCESS_MSG);
} }
/**
* 使用指定 {@code message} 创建成功响应结果
*
* @param message 成功信息
* @return {@code UnifiedResponse} 对象。
* {@code code} = "2000000", {@code data} = null
*/
public static UnifiedResponse<Void> success(@Nullable String message) { public static UnifiedResponse<Void> success(@Nullable String message) {
return new UnifiedResponse<>(SUCCESS_CODE, message); return new UnifiedResponse<>(SUCCESS_CODE, message);
} }
/**
* 使用指定 {@code message} 和 {@code data} 创建成功响应结果
*
* @param <T> data 类型
* @param message 成功信息
* @param data 携带数据
* @return {@code UnifiedResponse} 对象。
* {@code code} = "2000000"
*/
public static <T> UnifiedResponse<T> success(@Nullable String message, @Nullable T data) { public static <T> UnifiedResponse<T> success(@Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(SUCCESS_CODE, message, data); return new UnifiedResponse<>(SUCCESS_CODE, message, data);
} }
@@ -54,14 +76,39 @@ public class UnifiedResponses {
// #region - error // #region - error
// ================================ // ================================
/**
* 创建错误响应结果
*
* @param code 错误码
* @param message 错误信息
* @return {@code UnifiedResponse} 对象({@code data} 为 {@code null}
*/
public static UnifiedResponse<Void> error(String code, @Nullable String message) { public static UnifiedResponse<Void> error(String code, @Nullable String message) {
return new UnifiedResponse<>(code, message); return new UnifiedResponse<>(code, message);
} }
/**
* 创建错误响应结果
*
* @param <T> data 类型
* @param code 错误码
* @param message 错误信息
* @param data 携带数据
* @return {@code UnifiedResponse} 对象
*/
public static <T> UnifiedResponse<T> error(String code, @Nullable String message, @Nullable T data) { public static <T> UnifiedResponse<T> error(String code, @Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(code, message, data); return new UnifiedResponse<>(code, message, data);
} }
/**
* 创建错误响应结果
*
* @param code 错误码
* @param e 异常
* @return {@code UnifiedResponse} 对象。
* {@code message} 为异常的 {@code message}
* {@code data} 为 {@code null}。
*/
public static UnifiedResponse<Void> error(String code, Throwable e) { public static UnifiedResponse<Void> error(String code, Throwable e) {
return new UnifiedResponse<>(code, e.getMessage()); return new UnifiedResponse<>(code, e.getMessage());
} }
@@ -74,10 +121,26 @@ public class UnifiedResponses {
// #region - of // #region - of
// ================================ // ================================
/**
* 创建响应结果
*
* @param code 状态码
* @param message 响应信息
* @return {@code UnifiedResponse} 对象({@code data} 为 {@code null}
*/
public static UnifiedResponse<Void> of(String code, @Nullable String message) { public static UnifiedResponse<Void> of(String code, @Nullable String message) {
return new UnifiedResponse<>(code, message); return new UnifiedResponse<>(code, message);
} }
/**
* 创建响应结果
*
* @param <T> data 类型
* @param code 状态码
* @param message 响应信息
* @param data 携带数据
* @return {@code UnifiedResponse} 对象
*/
public static <T> UnifiedResponse<T> of(String code, @Nullable String message, @Nullable T data) { public static <T> UnifiedResponse<T> of(String code, @Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(code, message, data); return new UnifiedResponse<>(code, message, data);
} }

View File

@@ -64,4 +64,7 @@
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/ */
@ParametersAreNonnullByDefault
package xyz.zhouxy.plusone.commons.model.dto; package xyz.zhouxy.plusone.commons.model.dto;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -22,4 +22,7 @@
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/ */
@ParametersAreNonnullByDefault
package xyz.zhouxy.plusone.commons.model; package xyz.zhouxy.plusone.commons.model;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -46,6 +46,7 @@ public enum Quarter implements IWithIntCode {
/** 季度值 (1/2/3/4) */ /** 季度值 (1/2/3/4) */
private final int value; private final int value;
/** 月份范围 */
private final Range<Integer> monthRange; private final Range<Integer> monthRange;
/** 常量值 */ /** 常量值 */
@@ -63,7 +64,9 @@ public enum Quarter implements IWithIntCode {
this.monthRange = Range.closed(firstMonth, lastMonth); this.monthRange = Range.closed(firstMonth, lastMonth);
} }
// StaticFactoryMethods // ================================
// #region - StaticFactoryMethods
// ================================
/** /**
* 根据给定的月份值返回对应的季度 * 根据给定的月份值返回对应的季度
@@ -114,23 +117,48 @@ public enum Quarter implements IWithIntCode {
return ENUMS[checkValidIntValue(value) - 1]; return ENUMS[checkValidIntValue(value) - 1];
} }
// StaticFactoryMethods end // ================================
// #endregion - StaticFactoryMethods
// ================================
// computes // ================================
// #region - computes
// ================================
/**
* 加上指定数量的季度
*
* @param quarters 所添加的季度数量
* @return 计算结果
*/
public Quarter plus(long quarters) { public Quarter plus(long quarters) {
final int amount = (int) ((quarters % 4) + 4); final int amount = (int) ((quarters % 4) + 4);
return ENUMS[(ordinal() + amount) % 4]; return ENUMS[(ordinal() + amount) % 4];
} }
/**
* 减去指定数量的季度
*
* @param quarters 所减去的季度数量
* @return 计算结果
*/
public Quarter minus(long quarters) { public Quarter minus(long quarters) {
return plus(-(quarters % 4)); return plus(-(quarters % 4));
} }
// computes end // ================================
// #endregion - computes
// ================================
// Getters // ================================
// #region - Getters
// ================================
/**
* 获取季度值
*
* @return 季度值
*/
public int getValue() { public int getValue() {
return value; return value;
} }
@@ -140,45 +168,92 @@ public enum Quarter implements IWithIntCode {
return getValue(); return getValue();
} }
/**
* 该季度的第一个月
*
* @return {@code Month} 对象
*/
public Month firstMonth() { public Month firstMonth() {
return Month.of(firstMonthValue()); return Month.of(firstMonthValue());
} }
/**
* 该季度的第一个月
*
* @return 月份值从 1 开始1 表示 1月以此类推。
*/
public int firstMonthValue() { public int firstMonthValue() {
return this.monthRange.lowerEndpoint(); return this.monthRange.lowerEndpoint();
} }
/**
* 该季度的最后一个月
*
* @return {@code Month} 对象
*/
public Month lastMonth() { public Month lastMonth() {
return Month.of(lastMonthValue()); return Month.of(lastMonthValue());
} }
/**
* 该季度的最后一个月
*
* @return 月份值从 1 开始1 表示 1月以此类推。
*/
public int lastMonthValue() { public int lastMonthValue() {
return this.monthRange.upperEndpoint(); return this.monthRange.upperEndpoint();
} }
/**
* 该季度的第一天
*
* @return {@code MonthDay} 对象
*/
public MonthDay firstMonthDay() { public MonthDay firstMonthDay() {
return MonthDay.of(firstMonth(), 1); return MonthDay.of(firstMonth(), 1);
} }
/**
* 该季度的最后一天
*
* @return {@code MonthDay} 对象
*/
public MonthDay lastMonthDay() { public MonthDay lastMonthDay() {
// 季度的最后一个月不可能是 2 月 // 季度的最后一个月不可能是 2 月
final Month month = lastMonth(); final Month month = lastMonth();
return MonthDay.of(month, month.maxLength()); return MonthDay.of(month, month.maxLength());
} }
/**
* 计算该季度的第一天为当年的第几天
*
* @param leapYear 是否为闰年
* @return day of year
*/
public int firstDayOfYear(boolean leapYear) { public int firstDayOfYear(boolean leapYear) {
return firstMonth().firstDayOfYear(leapYear); return firstMonth().firstDayOfYear(leapYear);
} }
// Getters end // ================================
// #endregion - Getters
// ================================
/**
* 检查给定的季度值是否有效
*
* @param value 季度值
* @return 如果给定的季度值有效则返回该值
* @throws DateTimeException 如果给定的季度值不在有效范围内1到4将抛出异常
*/
public static int checkValidIntValue(int value) { public static int checkValidIntValue(int value) {
AssertTools.checkCondition(value >= 1 && value <= 4, AssertTools.checkCondition(value >= 1 && value <= 4,
() -> new DateTimeException("Invalid value for Quarter: " + value)); () -> new DateTimeException("Invalid value for Quarter: " + value));
return value; return value;
} }
// Internal // ================================
// #region - Internal
// ================================
/** /**
* 计算给定月份对应的季度值 * 计算给定月份对应的季度值
@@ -189,4 +264,8 @@ public enum Quarter implements IWithIntCode {
private static int computeQuarterValueInternal(int monthValue) { private static int computeQuarterValueInternal(int monthValue) {
return (monthValue - 1) / 3 + 1; return (monthValue - 1) / 3 + 1;
} }
// ================================
// #endregion - Internal
// ================================
} }

View File

@@ -139,6 +139,11 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
return of(yearMonth.getYear(), Quarter.fromMonth(yearMonth.getMonth())); return of(yearMonth.getYear(), Quarter.fromMonth(yearMonth.getMonth()));
} }
/**
* 根据现在的日期,判断所在的年份与季度,创建 {@link YearQuarter} 实例
*
* @return {@link YearQuarter} 实例
*/
@StaticFactoryMethod(YearQuarter.class) @StaticFactoryMethod(YearQuarter.class)
public static YearQuarter now() { public static YearQuarter now() {
return of(LocalDate.now()); return of(LocalDate.now());
@@ -148,46 +153,101 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
// #region - Getters // #region - Getters
/**
* 年份
*
* @return 年份
*/
public int getYear() { public int getYear() {
return this.year; return this.year;
} }
/**
* 季度
*
* @return 季度
*/
public Quarter getQuarter() { public Quarter getQuarter() {
return this.quarter; return this.quarter;
} }
/**
* 季度值。从 1 开始。
*
* @return 季度值
*/
public int getQuarterValue() { public int getQuarterValue() {
return this.quarter.getValue(); return this.quarter.getValue();
} }
/**
* 该季度第一个月
*
* @return {@link YearMonth} 对象
*/
public YearMonth firstYearMonth() { public YearMonth firstYearMonth() {
return YearMonth.of(this.year, this.quarter.firstMonth()); return YearMonth.of(this.year, this.quarter.firstMonth());
} }
/**
* 该季度第一个月
*
* @return {@link Month} 对象
*/
public Month firstMonth() { public Month firstMonth() {
return this.quarter.firstMonth(); return this.quarter.firstMonth();
} }
/**
* 该季度的第一个月
*
* @return 结果。月份值从 1 开始1 表示 1月以此类推。
*/
public int firstMonthValue() { public int firstMonthValue() {
return this.quarter.firstMonthValue(); return this.quarter.firstMonthValue();
} }
/**
* 该季度的最后一个月
*
* @return {@link YearMonth} 对象
*/
public YearMonth lastYearMonth() { public YearMonth lastYearMonth() {
return YearMonth.of(this.year, this.quarter.lastMonth()); return YearMonth.of(this.year, this.quarter.lastMonth());
} }
/**
* 该季度的最后一个月
*
* @return {@link Month} 对象
*/
public Month lastMonth() { public Month lastMonth() {
return this.quarter.lastMonth(); return this.quarter.lastMonth();
} }
/**
* 该季度的最后一个月
*
* @return 结果。月份值从 1 开始1 表示 1月以此类推。
*/
public int lastMonthValue() { public int lastMonthValue() {
return this.quarter.lastMonthValue(); return this.quarter.lastMonthValue();
} }
/**
* 该季度的第一天
*
* @return {@link LocalDate} 对象
*/
public LocalDate firstDate() { public LocalDate firstDate() {
return firstDate; return firstDate;
} }
/**
* 该季度的最后一天
*
* @return {@link LocalDate} 对象
*/
public LocalDate lastDate() { public LocalDate lastDate() {
return lastDate; return lastDate;
} }
@@ -223,7 +283,7 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
if (yearsToAdd == 0L) { if (yearsToAdd == 0L) {
return this; return this;
} }
int newYear = YEAR.checkValidIntValue(this.year + yearsToAdd); // safe overflow int newYear = YEAR.checkValidIntValue(this.year + yearsToAdd); // safe overflow
return new YearQuarter(newYear, this.quarter); return new YearQuarter(newYear, this.quarter);
} }

View File

@@ -470,10 +470,24 @@ public class ArrayTools {
// repeat - char // repeat - char
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static char[] repeat(char[] arr, int times) { public static char[] repeat(char[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static char[] repeat(char[] arr, int times, int maxLength) { public static char[] repeat(char[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -491,10 +505,24 @@ public class ArrayTools {
// repeat - byte // repeat - byte
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static byte[] repeat(byte[] arr, int times) { public static byte[] repeat(byte[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static byte[] repeat(byte[] arr, int times, int maxLength) { public static byte[] repeat(byte[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -512,10 +540,24 @@ public class ArrayTools {
// repeat - short // repeat - short
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static short[] repeat(short[] arr, int times) { public static short[] repeat(short[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static short[] repeat(short[] arr, int times, int maxLength) { public static short[] repeat(short[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -533,10 +575,24 @@ public class ArrayTools {
// repeat - int // repeat - int
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static int[] repeat(int[] arr, int times) { public static int[] repeat(int[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static int[] repeat(int[] arr, int times, int maxLength) { public static int[] repeat(int[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -554,10 +610,24 @@ public class ArrayTools {
// repeat - long // repeat - long
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static long[] repeat(long[] arr, int times) { public static long[] repeat(long[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static long[] repeat(long[] arr, int times, int maxLength) { public static long[] repeat(long[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -575,10 +645,24 @@ public class ArrayTools {
// repeat - float // repeat - float
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static float[] repeat(float[] arr, int times) { public static float[] repeat(float[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static float[] repeat(float[] arr, int times, int maxLength) { public static float[] repeat(float[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -596,10 +680,24 @@ public class ArrayTools {
// repeat - double // repeat - double
/**
* 重复数组中的元素
*
* @param arr 重复内容
* @param times 重复次数
* @return 重复后的数组
*/
public static double[] repeat(double[] arr, int times) { public static double[] repeat(double[] arr, int times) {
return repeat(arr, times, Integer.MAX_VALUE); return repeat(arr, times, Integer.MAX_VALUE);
} }
/**
* 重复数组中的元素
* @param arr 重复内容
* @param times 重复次数
* @param maxLength 最大长度
* @return 重复后的数组
*/
public static double[] repeat(double[] arr, int times, int maxLength) { public static double[] repeat(double[] arr, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(arr)); AssertTools.checkArgument(Objects.nonNull(arr));
AssertTools.checkArgument(times >= 0, AssertTools.checkArgument(times >= 0,
@@ -621,14 +719,34 @@ public class ArrayTools {
// fill - char // fill - char
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(char[] a, @Nullable char[] values) { public static void fill(char[] a, @Nullable char[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(char[] a, @Nullable String values) { public static void fill(char[] a, @Nullable String values) {
fill(a, 0, a.length, values != null ? values.toCharArray() : EMPTY_CHAR_ARRAY); fill(a, 0, a.length, values != null ? values.toCharArray() : EMPTY_CHAR_ARRAY);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(char[] a, int fromIndex, int toIndex, @Nullable char[] values) { public static void fill(char[] a, int fromIndex, int toIndex, @Nullable char[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -654,10 +772,24 @@ public class ArrayTools {
// fill - byte // fill - byte
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(byte[] a, @Nullable byte[] values) { public static void fill(byte[] a, @Nullable byte[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(byte[] a, int fromIndex, int toIndex, @Nullable byte[] values) { public static void fill(byte[] a, int fromIndex, int toIndex, @Nullable byte[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -683,10 +815,24 @@ public class ArrayTools {
// fill - short // fill - short
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(short[] a, @Nullable short[] values) { public static void fill(short[] a, @Nullable short[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(short[] a, int fromIndex, int toIndex, @Nullable short[] values) { public static void fill(short[] a, int fromIndex, int toIndex, @Nullable short[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -712,10 +858,24 @@ public class ArrayTools {
// fill - int // fill - int
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(int[] a, @Nullable int[] values) { public static void fill(int[] a, @Nullable int[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(int[] a, int fromIndex, int toIndex, @Nullable int[] values) { public static void fill(int[] a, int fromIndex, int toIndex, @Nullable int[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -741,10 +901,24 @@ public class ArrayTools {
// fill - long // fill - long
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(long[] a, @Nullable long[] values) { public static void fill(long[] a, @Nullable long[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(long[] a, int fromIndex, int toIndex, @Nullable long[] values) { public static void fill(long[] a, int fromIndex, int toIndex, @Nullable long[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -770,10 +944,24 @@ public class ArrayTools {
// fill - float // fill - float
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(float[] a, @Nullable float[] values) { public static void fill(float[] a, @Nullable float[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(float[] a, int fromIndex, int toIndex, @Nullable float[] values) { public static void fill(float[] a, int fromIndex, int toIndex, @Nullable float[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -799,10 +987,24 @@ public class ArrayTools {
// fill - double // fill - double
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static void fill(double[] a, @Nullable double[] values) { public static void fill(double[] a, @Nullable double[] values) {
fill(a, 0, a.length, values); fill(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static void fill(double[] a, int fromIndex, int toIndex, @Nullable double[] values) { public static void fill(double[] a, int fromIndex, int toIndex, @Nullable double[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -828,14 +1030,36 @@ public class ArrayTools {
// fill - T // fill - T
/**
* 填充数组
*
* @param a 要填充的数组
* @param values 填充内容
*/
public static <T> void fill(T[] a, @Nullable T[] values) { public static <T> void fill(T[] a, @Nullable T[] values) {
fillInternal(a, 0, a.length, values); fillInternal(a, 0, a.length, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
public static <T> void fill(T[] a, int fromIndex, int toIndex, @Nullable T[] values) { public static <T> void fill(T[] a, int fromIndex, int toIndex, @Nullable T[] values) {
fillInternal(a, fromIndex, toIndex, values); fillInternal(a, fromIndex, toIndex, values);
} }
/**
* 填充数组
*
* @param a 要填充的数组
* @param fromIndex 开始位置
* @param toIndex 结束位置
* @param values 填充内容
*/
private static <T> void fillInternal(T[] a, int fromIndex, int toIndex, @Nullable T[] values) { private static <T> void fillInternal(T[] a, int fromIndex, int toIndex, @Nullable T[] values) {
AssertTools.checkArgument(Objects.nonNull(a)); AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) { if (values == null || values.length == 0) {
@@ -863,7 +1087,7 @@ public class ArrayTools {
// #region - indexOf // #region - indexOf
public static <T> int indexOfWithPredicate(@Nullable T[] arr, Predicate<? super T> predicate) { public static <T> int indexOf(@Nullable T[] arr, Predicate<? super T> predicate) {
AssertTools.checkNotNull(predicate); AssertTools.checkNotNull(predicate);
if (arr == null || arr.length == 0) { if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX; return NOT_FOUND_INDEX;
@@ -877,7 +1101,7 @@ public class ArrayTools {
} }
public static <T> int indexOf(@Nullable T[] arr, @Nullable T obj) { public static <T> int indexOf(@Nullable T[] arr, @Nullable T obj) {
return indexOfWithPredicate(arr, item -> Objects.equals(item, obj)); return indexOf(arr, item -> Objects.equals(item, obj));
} }
public static int indexOf(@Nullable char[] arr, char value) { public static int indexOf(@Nullable char[] arr, char value) {
@@ -968,7 +1192,7 @@ public class ArrayTools {
// #region - lastIndexOf // #region - lastIndexOf
public static <T> int lastIndexOfWithPredicate(@Nullable T[] arr, Predicate<? super T> predicate) { public static <T> int lastIndexOf(@Nullable T[] arr, Predicate<? super T> predicate) {
AssertTools.checkNotNull(predicate); AssertTools.checkNotNull(predicate);
if (arr == null || arr.length == 0) { if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX; return NOT_FOUND_INDEX;
@@ -982,7 +1206,7 @@ public class ArrayTools {
} }
public static <T> int lastIndexOf(T[] arr, T obj) { public static <T> int lastIndexOf(T[] arr, T obj) {
return lastIndexOfWithPredicate(arr, item -> Objects.equals(item, obj)); return lastIndexOf(arr, item -> Objects.equals(item, obj));
} }
public static int lastIndexOf(@Nullable char[] arr, char value) { public static int lastIndexOf(@Nullable char[] arr, char value) {
@@ -1106,7 +1330,7 @@ public class ArrayTools {
} }
public static boolean containsValue(@Nullable BigDecimal[] arr, @Nullable BigDecimal obj) { public static boolean containsValue(@Nullable BigDecimal[] arr, @Nullable BigDecimal obj) {
return indexOfWithPredicate(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX; return indexOf(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX;
} }
// #endregion // #endregion

View File

@@ -49,22 +49,46 @@ public class AssertTools {
// #region - Argument // #region - Argument
// ================================ // ================================
/** Throw {@link IllegalArgumentException} if the {@code condition} is false. */ /**
* 检查实参
*
* @param condition 判断参数是否符合条件的结果
* @throws IllegalArgumentException 当条件不满足时抛出
*/
public static void checkArgument(boolean condition) { public static void checkArgument(boolean condition) {
checkCondition(condition, IllegalArgumentException::new); checkCondition(condition, IllegalArgumentException::new);
} }
/** Throw {@link IllegalArgumentException} if the {@code condition} is false. */ /**
* 检查实参
*
* @param condition 判断参数是否符合条件的结果
* @param errMsg 异常信息
* @throws IllegalArgumentException 当条件不满足时抛出
*/
public static void checkArgument(boolean condition, @Nullable String errMsg) { public static void checkArgument(boolean condition, @Nullable String errMsg) {
checkCondition(condition, () -> new IllegalArgumentException(errMsg)); checkCondition(condition, () -> new IllegalArgumentException(errMsg));
} }
/** Throw {@link IllegalArgumentException} if the {@code condition} is false. */ /**
* 检查实参
*
* @param condition 判断参数是否符合条件的结果
* @param messageSupplier 异常信息
* @throws IllegalArgumentException 当条件不满足时抛出
*/
public static void checkArgument(boolean condition, Supplier<String> messageSupplier) { public static void checkArgument(boolean condition, Supplier<String> messageSupplier) {
checkCondition(condition, () -> new IllegalArgumentException(messageSupplier.get())); checkCondition(condition, () -> new IllegalArgumentException(messageSupplier.get()));
} }
/** Throw {@link IllegalArgumentException} if the {@code condition} is false. */ /**
* 检查实参
*
* @param condition 判断参数是否符合条件的结果
* @param format 异常信息模板
* @param args 异常信息参数
* @throws IllegalArgumentException 当条件不满足时抛出
*/
public static void checkArgument(boolean condition, String format, Object... args) { public static void checkArgument(boolean condition, String format, Object... args) {
checkCondition(condition, () -> new IllegalArgumentException(String.format(format, args))); checkCondition(condition, () -> new IllegalArgumentException(String.format(format, args)));
} }
@@ -77,22 +101,46 @@ public class AssertTools {
// #region - State // #region - State
// ================================ // ================================
/** Throw {@link IllegalStateException} if the {@code condition} is false. */ /**
* 检查状态
*
* @param condition 判断状态是否符合条件的结果
* @throws IllegalStateException 当条件不满足时抛出
*/
public static void checkState(boolean condition) { public static void checkState(boolean condition) {
checkCondition(condition, IllegalStateException::new); checkCondition(condition, IllegalStateException::new);
} }
/** Throw {@link IllegalStateException} if the {@code condition} is false. */ /**
* 检查状态
*
* @param condition 判断状态是否符合条件的结果
* @param errMsg 异常信息
* @throws IllegalStateException 当条件不满足时抛出
*/
public static void checkState(boolean condition, @Nullable String errMsg) { public static void checkState(boolean condition, @Nullable String errMsg) {
checkCondition(condition, () -> new IllegalStateException(errMsg)); checkCondition(condition, () -> new IllegalStateException(errMsg));
} }
/** Throw {@link IllegalStateException} if the {@code condition} is false. */ /**
* 检查状态
*
* @param condition 判断状态是否符合条件的结果
* @param messageSupplier 异常信息
* @throws IllegalStateException 当条件不满足时抛出
*/
public static void checkState(boolean condition, Supplier<String> messageSupplier) { public static void checkState(boolean condition, Supplier<String> messageSupplier) {
checkCondition(condition, () -> new IllegalStateException(messageSupplier.get())); checkCondition(condition, () -> new IllegalStateException(messageSupplier.get()));
} }
/** Throw {@link IllegalStateException} if the {@code condition} is false. */ /**
* 检查状态
*
* @param condition 判断状态是否符合条件的结果
* @param format 异常信息模板
* @param args 异常信息参数
* @throws IllegalStateException 当条件不满足时抛出
*/
public static void checkState(boolean condition, String format, Object... args) { public static void checkState(boolean condition, String format, Object... args) {
checkCondition(condition, () -> new IllegalStateException(String.format(format, args))); checkCondition(condition, () -> new IllegalStateException(String.format(format, args)));
} }
@@ -105,22 +153,50 @@ public class AssertTools {
// #region - NotNull // #region - NotNull
// ================================ // ================================
/** Throw {@link NullPointerException} if the {@code obj} is null. */ /**
* 判空
*
* @param <T> 入参类型
* @param obj 入参 *
* @throws NullPointerException 当 {@code obj} 为 {@code null} 时抛出
*/
public static <T> void checkNotNull(@Nullable T obj) { public static <T> void checkNotNull(@Nullable T obj) {
checkCondition(obj != null, NullPointerException::new); checkCondition(obj != null, NullPointerException::new);
} }
/** Throw {@link NullPointerException} if the {@code obj} is null. */ /**
* 判空
*
* @param <T> 入参类型
* @param obj 入参
* @param errMsg 异常信息 *
* @throws NullPointerException 当 {@code obj} 为 {@code null} 时抛出
*/
public static <T> void checkNotNull(@Nullable T obj, String errMsg) { public static <T> void checkNotNull(@Nullable T obj, String errMsg) {
checkCondition(obj != null, () -> new NullPointerException(errMsg)); checkCondition(obj != null, () -> new NullPointerException(errMsg));
} }
/** Throw {@link NullPointerException} if the {@code obj} is null. */ /**
* 判空
*
* @param <T> 入参类型
* @param obj 入参
* @param messageSupplier 异常信息 *
* @throws NullPointerException 当 {@code obj} 为 {@code null} 时抛出
*/
public static <T> void checkNotNull(@Nullable T obj, Supplier<String> messageSupplier) { public static <T> void checkNotNull(@Nullable T obj, Supplier<String> messageSupplier) {
checkCondition(obj != null, () -> new NullPointerException(messageSupplier.get())); checkCondition(obj != null, () -> new NullPointerException(messageSupplier.get()));
} }
/** Throw {@link NullPointerException} if the {@code obj} is null. */ /**
* 判空
*
* @param <T> 入参类型
* @param obj 入参
* @param format 异常信息模板
* @param args 异常信息参数
* @throws NullPointerException 当 {@code obj} 为 {@code null} 时抛出
*/
public static <T> void checkNotNull(@Nullable T obj, String format, Object... args) { public static <T> void checkNotNull(@Nullable T obj, String format, Object... args) {
checkCondition(obj != null, () -> new NullPointerException(String.format(format, args))); checkCondition(obj != null, () -> new NullPointerException(String.format(format, args)));
} }
@@ -133,56 +209,120 @@ public class AssertTools {
// #region - Exists // #region - Exists
// ================================ // ================================
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param obj 入参
* @return 如果 {@code obj} 存在,返回 {@code obj} 本身
* @throws DataNotExistsException 当 {@code obj} 不存在时抛出
*/
public static <T> T checkExists(@Nullable T obj) public static <T> T checkExists(@Nullable T obj)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), DataNotExistsException::new); checkCondition(Objects.nonNull(obj), DataNotExistsException::new);
return obj; return obj;
} }
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param obj 入参
* @param message 异常信息
* @return 如果 {@code obj} 存在,返回 {@code obj} 本身
* @throws DataNotExistsException 当 {@code obj} 不存在时抛出
*/
public static <T> T checkExists(@Nullable T obj, String message) public static <T> T checkExists(@Nullable T obj, String message)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(message)); checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(message));
return obj; return obj;
} }
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param obj 入参
* @param messageSupplier 异常信息
* @return 如果 {@code obj} 存在,返回 {@code obj} 本身
* @throws DataNotExistsException 当 {@code obj} 不存在时抛出
*/
public static <T> T checkExists(@Nullable T obj, Supplier<String> messageSupplier) public static <T> T checkExists(@Nullable T obj, Supplier<String> messageSupplier)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(messageSupplier.get())); checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(messageSupplier.get()));
return obj; return obj;
} }
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param obj 入参
* @param format 异常信息模板
* @param args 异常信息参数
* @return 如果 {@code obj} 存在,返回 {@code obj} 本身
* @throws DataNotExistsException 当 {@code obj} 不存在时抛出
*/
public static <T> T checkExists(@Nullable T obj, String format, Object... args) public static <T> T checkExists(@Nullable T obj, String format, Object... args)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(String.format(format, args))); checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(String.format(format, args)));
return obj; return obj;
} }
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param optional 入参
* @return 如果 {@code optional} 存在,返回 {@code optional} 包含的值
* @throws DataNotExistsException 当 {@code optional} 的值不存在时抛出
*/
public static <T> T checkExists(Optional<T> optional) public static <T> T checkExists(Optional<T> optional)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(optional.isPresent(), DataNotExistsException::new); checkCondition(optional.isPresent(), DataNotExistsException::new);
return optional.get(); return optional.get();
} }
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param optional 入参
* @param message 异常信息
* @return 如果 {@code optional} 存在,返回 {@code optional} 包含的值
* @throws DataNotExistsException 当 {@code optional} 的值不存在时抛出
*/
public static <T> T checkExists(Optional<T> optional, String message) public static <T> T checkExists(Optional<T> optional, String message)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(optional.isPresent(), () -> new DataNotExistsException(message)); checkCondition(optional.isPresent(), () -> new DataNotExistsException(message));
return optional.get(); return optional.get();
} }
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param optional 入参
* @param messageSupplier 异常信息
* @return 如果 {@code optional} 存在,返回 {@code optional} 包含的值
* @throws DataNotExistsException 当 {@code optional} 的值不存在时抛出
*/
public static <T> T checkExists(Optional<T> optional, Supplier<String> messageSupplier) public static <T> T checkExists(Optional<T> optional, Supplier<String> messageSupplier)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(optional.isPresent(), () -> new DataNotExistsException(messageSupplier.get())); checkCondition(optional.isPresent(), () -> new DataNotExistsException(messageSupplier.get()));
return optional.get(); return optional.get();
} }
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */ /**
* 检查数据是否存在
*
* @param <T> 入参类型
* @param optional 入参
* @param format 异常信息模板
* @param args 异常信息参数
* @return 如果 {@code optional} 存在,返回 {@code optional} 包含的值
* @throws DataNotExistsException 当 {@code optional} 的值不存在时抛出
*/
public static <T> T checkExists(Optional<T> optional, String format, Object... args) public static <T> T checkExists(Optional<T> optional, String format, Object... args)
throws DataNotExistsException { throws DataNotExistsException {
checkCondition(optional.isPresent(), () -> new DataNotExistsException(String.format(format, args))); checkCondition(optional.isPresent(), () -> new DataNotExistsException(String.format(format, args)));
@@ -197,78 +337,182 @@ public class AssertTools {
// #region - AffectedRows // #region - AffectedRows
// ================================ // ================================
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
*/
public static void checkAffectedRows(int expectedValue, int result) { public static void checkAffectedRows(int expectedValue, int result) {
checkAffectedRows(expectedValue, result, checkAffectedRows(expectedValue, result,
"The number of rows affected is expected to be %d, but is: %d", expectedValue, result); "The number of rows affected is expected to be %d, but is: %d", expectedValue, result);
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
* @param message 异常信息
*/
public static void checkAffectedRows(int expectedValue, int result, @Nullable String message) { public static void checkAffectedRows(int expectedValue, int result, @Nullable String message) {
checkCondition(expectedValue == result, () -> new DataOperationResultException(message)); checkCondition(expectedValue == result, () -> new DataOperationResultException(message));
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
* @param messageSupplier 异常信息
*/
public static void checkAffectedRows(int expectedValue, int result, public static void checkAffectedRows(int expectedValue, int result,
Supplier<String> messageSupplier) { Supplier<String> messageSupplier) {
checkCondition(expectedValue == result, checkCondition(expectedValue == result,
() -> new DataOperationResultException(messageSupplier.get())); () -> new DataOperationResultException(messageSupplier.get()));
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
* @param format 异常信息模板
* @param args 异常信息参数
*/
public static void checkAffectedRows(int expectedValue, int result, public static void checkAffectedRows(int expectedValue, int result,
String format, Object... args) { String format, Object... args) {
checkCondition(expectedValue == result, checkCondition(expectedValue == result,
() -> new DataOperationResultException(String.format(format, args))); () -> new DataOperationResultException(String.format(format, args)));
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
*/
public static void checkAffectedRows(long expectedValue, long result) { public static void checkAffectedRows(long expectedValue, long result) {
checkAffectedRows(expectedValue, result, checkAffectedRows(expectedValue, result,
"The number of rows affected is expected to be %d, but is: %d", expectedValue, result); "The number of rows affected is expected to be %d, but is: %d", expectedValue, result);
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
* @param message 异常信息
*/
public static void checkAffectedRows(long expectedValue, long result, @Nullable String message) { public static void checkAffectedRows(long expectedValue, long result, @Nullable String message) {
checkCondition(expectedValue == result, () -> new DataOperationResultException(message)); checkCondition(expectedValue == result, () -> new DataOperationResultException(message));
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
* @param messageSupplier 异常信息
*/
public static void checkAffectedRows(long expectedValue, long result, public static void checkAffectedRows(long expectedValue, long result,
Supplier<String> messageSupplier) { Supplier<String> messageSupplier) {
checkCondition(expectedValue == result, checkCondition(expectedValue == result,
() -> new DataOperationResultException(messageSupplier.get())); () -> new DataOperationResultException(messageSupplier.get()));
} }
/**
* 当影响的数据量与预计不同时抛出 {@link DataOperationResultException}。
*
* @param expectedValue 预计的数量
* @param result 实际影响的数据量
* @param format 异常信息模板
* @param args 异常信息参数
*/
public static void checkAffectedRows(long expectedValue, long result, public static void checkAffectedRows(long expectedValue, long result,
String format, Object... args) { String format, Object... args) {
checkCondition(expectedValue == result, checkCondition(expectedValue == result,
() -> new DataOperationResultException(String.format(format, args))); () -> new DataOperationResultException(String.format(format, args)));
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
*/
public static void checkAffectedOneRow(int result) { public static void checkAffectedOneRow(int result) {
checkAffectedRows(1, result, checkAffectedRows(1, result,
() -> "The number of rows affected is expected to be 1, but is: " + result); () -> "The number of rows affected is expected to be 1, but is: " + result);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
* @param message 异常信息
*/
public static void checkAffectedOneRow(int result, String message) { public static void checkAffectedOneRow(int result, String message) {
checkAffectedRows(1, result, message); checkAffectedRows(1, result, message);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
* @param messageSupplier 异常信息
*/
public static void checkAffectedOneRow(int result, Supplier<String> messageSupplier) { public static void checkAffectedOneRow(int result, Supplier<String> messageSupplier) {
checkAffectedRows(1, result, messageSupplier); checkAffectedRows(1, result, messageSupplier);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
* @param format 异常信息模板
* @param args 异常信息参数
*/
public static void checkAffectedOneRow(int result, String format, Object... args) { public static void checkAffectedOneRow(int result, String format, Object... args) {
checkAffectedRows(1, result, format, args); checkAffectedRows(1, result, format, args);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
*/
public static void checkAffectedOneRow(long result) { public static void checkAffectedOneRow(long result) {
checkAffectedRows(1L, result, checkAffectedRows(1L, result,
() -> "The number of rows affected is expected to be 1, but is: " + result); () -> "The number of rows affected is expected to be 1, but is: " + result);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
* @param message 异常信息
*/
public static void checkAffectedOneRow(long result, String message) { public static void checkAffectedOneRow(long result, String message) {
checkAffectedRows(1L, result, message); checkAffectedRows(1L, result, message);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
* @param messageSupplier 异常信息
*/
public static void checkAffectedOneRow(long result, Supplier<String> messageSupplier) { public static void checkAffectedOneRow(long result, Supplier<String> messageSupplier) {
checkAffectedRows(1L, result, messageSupplier); checkAffectedRows(1L, result, messageSupplier);
} }
/**
* 当影响的数据量不为 1 时抛出 {@link DataOperationResultException}。
*
* @param result 实际影响的数据量
* @param format 异常信息模板
* @param args 异常信息参数
*/
public static void checkAffectedOneRow(long result, public static void checkAffectedOneRow(long result,
String format, Object... args) { String format, Object... args) {
checkAffectedRows(1L, result, format, args); checkAffectedRows(1L, result, format, args);
@@ -282,6 +526,14 @@ public class AssertTools {
// #region - Condition // #region - Condition
// ================================ // ================================
/**
* 当条件不满足时抛出异常。
*
* @param <T> 异常类型
* @param condition 条件
* @param e 异常
* @throws T 当条件不满足时抛出异常
*/
public static <T extends Exception> void checkCondition(boolean condition, Supplier<T> e) public static <T extends Exception> void checkCondition(boolean condition, Supplier<T> e)
throws T { throws T {
if (!condition) { if (!condition) {

View File

@@ -35,34 +35,75 @@ import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
*/ */
public class BigDecimals { public class BigDecimals {
/**
* 判断两个 {@code BigDecimal} 的值是否相等
*
* @param a 第一个 {@code BigDecimal}
* @param b 第二个 {@code BigDecimal}
* @return 当两个 {@code BigDecimal} 的值相等时返回 {@code true}
*/
public static boolean equalsValue(@Nullable BigDecimal a, @Nullable BigDecimal b) { public static boolean equalsValue(@Nullable BigDecimal a, @Nullable BigDecimal b) {
return (a == b) || (a != null && b != null && a.compareTo(b) == 0); return (a == b) || (a != null && b != null && a.compareTo(b) == 0);
} }
/**
* 判断两个 {@code BigDecimal} 的值 - 大于
*
* @param a 第一个 {@code BigDecimal}
* @param b 第二个 {@code BigDecimal}
* @return 当 {@code a} 大于 {@code b} 时返回 {@code true}
*/
public static boolean gt(BigDecimal a, BigDecimal b) { public static boolean gt(BigDecimal a, BigDecimal b) {
AssertTools.checkNotNull(a, "Parameter could not be null."); AssertTools.checkNotNull(a, "Parameter could not be null.");
AssertTools.checkNotNull(b, "Parameter could not be null."); AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a != b) && (a.compareTo(b) > 0); return (a != b) && (a.compareTo(b) > 0);
} }
/**
* 判断两个 {@code BigDecimal} 的值 - 大于等于
*
* @param a 第一个 {@code BigDecimal}
* @param b 第二个 {@code BigDecimal}
* @return 当 {@code a} 大于等于 {@code b} 时返回 {@code true}
*/
public static boolean ge(BigDecimal a, BigDecimal b) { public static boolean ge(BigDecimal a, BigDecimal b) {
AssertTools.checkNotNull(a, "Parameter could not be null."); AssertTools.checkNotNull(a, "Parameter could not be null.");
AssertTools.checkNotNull(b, "Parameter could not be null."); AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a == b) || (a.compareTo(b) >= 0); return (a == b) || (a.compareTo(b) >= 0);
} }
/**
* 判断两个 {@code BigDecimal} 的值 - 小于
*
* @param a 第一个 {@code BigDecimal}
* @param b 第二个 {@code BigDecimal}
* @return 当 {@code a} 小于 {@code b} 时返回 {@code true}
*/
public static boolean lt(BigDecimal a, BigDecimal b) { public static boolean lt(BigDecimal a, BigDecimal b) {
AssertTools.checkNotNull(a, "Parameter could not be null."); AssertTools.checkNotNull(a, "Parameter could not be null.");
AssertTools.checkNotNull(b, "Parameter could not be null."); AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a != b) && (a.compareTo(b) < 0); return (a != b) && (a.compareTo(b) < 0);
} }
/**
* 判断两个 {@code BigDecimal} 的值 - 小于等于
*
* @param a 第一个 {@code BigDecimal}
* @param b 第二个 {@code BigDecimal}
* @return 当 {@code a} 小于等于 {@code b} 时返回 {@code true}
*/
public static boolean le(BigDecimal a, BigDecimal b) { public static boolean le(BigDecimal a, BigDecimal b) {
AssertTools.checkNotNull(a, "Parameter could not be null."); AssertTools.checkNotNull(a, "Parameter could not be null.");
AssertTools.checkNotNull(b, "Parameter could not be null."); AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a == b) || (a.compareTo(b) <= 0); return (a == b) || (a.compareTo(b) <= 0);
} }
/**
* 求和
*
* @param numbers {@code BigDecimal} 数组
* @return 求和结果
*/
public static BigDecimal sum(final BigDecimal... numbers) { public static BigDecimal sum(final BigDecimal... numbers) {
if (ArrayTools.isNullOrEmpty(numbers)) { if (ArrayTools.isNullOrEmpty(numbers)) {
return BigDecimal.ZERO; return BigDecimal.ZERO;
@@ -77,11 +118,23 @@ public class BigDecimals {
return result; return result;
} }
/**
* 将 {@code null} 转换为 {@link BigDecimal#ZERO}
*
* @param val BigDecimal 对象
* @return 如果 {@code val} 为 {@code null},则返回 {@link BigDecimal#ZERO},否则返回 {@code val}
*/
@Nonnull @Nonnull
public static BigDecimal nullToZero(@Nullable final BigDecimal val) { public static BigDecimal nullToZero(@Nullable final BigDecimal val) {
return val != null ? val : BigDecimal.ZERO; return val != null ? val : BigDecimal.ZERO;
} }
/**
* 获取字符串所表示的数值转换为 {@code BigDecimal}
*
* @param val 表示数值的字符串
* @return {@code BigDecimal} 对象
*/
@StaticFactoryMethod(BigDecimal.class) @StaticFactoryMethod(BigDecimal.class)
public static BigDecimal of(@Nullable final String val) { public static BigDecimal of(@Nullable final String val) {
return (StringTools.isNotBlank(val)) ? new BigDecimal(val) : BigDecimal.ZERO; return (StringTools.isNotBlank(val)) ? new BigDecimal(val) : BigDecimal.ZERO;

View File

@@ -702,18 +702,43 @@ public class DateTimeTools {
// #region - start & end // #region - start & end
// ================================ // ================================
/**
* 获取指定年份的开始日期
*
* @param year 年份
* @return 指定年份的开始日期
*/
public static LocalDate startDateOfYear(int year) { public static LocalDate startDateOfYear(int year) {
return LocalDate.ofYearDay(year, 1); return LocalDate.ofYearDay(year, 1);
} }
/**
* 获取指定年份的结束日期
*
* @param year 年份
* @return 指定年份的结束日期
*/
public static LocalDate endDateOfYear(int year) { public static LocalDate endDateOfYear(int year) {
return LocalDate.of(year, 12, 31); return LocalDate.of(year, 12, 31);
} }
/**
* 获取指定日期的第二天的开始时间
*
* @param date 日期
* @return 指定日期的第二天的开始时间
*/
public static LocalDateTime startOfNextDate(LocalDate date) { public static LocalDateTime startOfNextDate(LocalDate date) {
return date.plusDays(1L).atStartOfDay(); return date.plusDays(1L).atStartOfDay();
} }
/**
* 获取指定日期的第二天的开始时间
*
* @param date 日期
* @param zone 时区
* @return 指定日期的第二天的开始时间
*/
public static ZonedDateTime startOfNextDate(LocalDate date, ZoneId zone) { public static ZonedDateTime startOfNextDate(LocalDate date, ZoneId zone) {
return date.plusDays(1L).atStartOfDay(zone); return date.plusDays(1L).atStartOfDay(zone);
} }
@@ -726,30 +751,72 @@ public class DateTimeTools {
// #region - isFuture // #region - isFuture
// ================================ // ================================
/**
* 判断指定日期时间是否在将来
*
* @param date 日期时间
* @return 指定日期时间是否在将来
*/
public static boolean isFuture(Date date) { public static boolean isFuture(Date date) {
return date.after(new Date()); return date.after(new Date());
} }
public static boolean isFuture(Calendar date) { /**
return date.after(Calendar.getInstance()); * 判断指定日期时间是否在将来
*
* @param calendar 日期时间
* @return 指定日期时间是否在将来
*/
public static boolean isFuture(Calendar calendar) {
return calendar.after(Calendar.getInstance());
} }
/**
* 判断指定时刻是否在将来
*
* @param instant 时刻
* @return 指定时刻是否在将来
*/
public static boolean isFuture(Instant instant) { public static boolean isFuture(Instant instant) {
return instant.isAfter(Instant.now()); return instant.isAfter(Instant.now());
} }
/**
* 判断指定时间戳是否在将来
*
* @param timeMillis 时间戳
* @return 指定时间戳是否在将来
*/
public static boolean isFuture(long timeMillis) { public static boolean isFuture(long timeMillis) {
return timeMillis > System.currentTimeMillis(); return timeMillis > System.currentTimeMillis();
} }
/**
* 判断指定日期是否在将来
*
* @param date 日期
* @return 指定日期是否在将来
*/
public static boolean isFuture(LocalDate date) { public static boolean isFuture(LocalDate date) {
return date.isAfter(LocalDate.now()); return date.isAfter(LocalDate.now());
} }
/**
* 判断指定日期时间是否在将来
*
* @param dateTime 日期时间
* @return 指定日期时间是否在将来
*/
public static boolean isFuture(LocalDateTime dateTime) { public static boolean isFuture(LocalDateTime dateTime) {
return dateTime.isAfter(LocalDateTime.now()); return dateTime.isAfter(LocalDateTime.now());
} }
/**
* 判断指定日期时间是否在将来
*
* @param dateTime 日期时间
* @return 指定日期时间是否在将来
*/
public static boolean isFuture(ZonedDateTime dateTime) { public static boolean isFuture(ZonedDateTime dateTime) {
return dateTime.isAfter(ZonedDateTime.now()); return dateTime.isAfter(ZonedDateTime.now());
} }
@@ -762,30 +829,72 @@ public class DateTimeTools {
// #region - isPast // #region - isPast
// ================================ // ================================
/**
* 判断指定日期时间是否在过去
*
* @param date 日期时间
* @return 指定日期时间是否在过去
*/
public static boolean isPast(Date date) { public static boolean isPast(Date date) {
return date.before(new Date()); return date.before(new Date());
} }
public static boolean isPast(Calendar date) { /**
return date.before(Calendar.getInstance()); * 判断指定日期时间是否在过去
*
* @param calendar 日期时间
* @return 指定日期时间是否在过去
*/
public static boolean isPast(Calendar calendar) {
return calendar.before(Calendar.getInstance());
} }
/**
* 判断指定时刻是否在过去
*
* @param instant 时刻
* @return 指定时刻是否在过去
*/
public static boolean isPast(Instant instant) { public static boolean isPast(Instant instant) {
return instant.isBefore(Instant.now()); return instant.isBefore(Instant.now());
} }
/**
* 判断指定时间戳是否在过去
*
* @param timeMillis 时间戳
* @return 指定时间戳是否在过去
*/
public static boolean isPast(long timeMillis) { public static boolean isPast(long timeMillis) {
return timeMillis < System.currentTimeMillis(); return timeMillis < System.currentTimeMillis();
} }
/**
* 判断指定日期是否在过去
*
* @param date 日期
* @return 指定日期是否在过去
*/
public static boolean isPast(LocalDate date) { public static boolean isPast(LocalDate date) {
return date.isBefore(LocalDate.now()); return date.isBefore(LocalDate.now());
} }
/**
* 判断指定日期时间是否在过去
*
* @param dateTime 日期时间
* @return 指定日期时间是否在过去
*/
public static boolean isPast(LocalDateTime dateTime) { public static boolean isPast(LocalDateTime dateTime) {
return dateTime.isBefore(LocalDateTime.now()); return dateTime.isBefore(LocalDateTime.now());
} }
/**
* 判断指定日期时间是否在过去
*
* @param dateTime 日期时间
* @return 指定日期时间是否在过去
*/
public static boolean isPast(ZonedDateTime dateTime) { public static boolean isPast(ZonedDateTime dateTime) {
return dateTime.isBefore(ZonedDateTime.now()); return dateTime.isBefore(ZonedDateTime.now());
} }
@@ -798,14 +907,33 @@ public class DateTimeTools {
// #region - others // #region - others
// ================================ // ================================
/**
* 获取指定日期的时间范围
*
* @param date 日期
* @return 指定日期的时间范围
*/
public static Range<LocalDateTime> toDateTimeRange(LocalDate date) { public static Range<LocalDateTime> toDateTimeRange(LocalDate date) {
return Range.closedOpen(date.atStartOfDay(), startOfNextDate(date)); return Range.closedOpen(date.atStartOfDay(), startOfNextDate(date));
} }
/**
* 获取指定日期的时间范围
*
* @param date 日期
* @param zone 时区
* @return 指定日期的时间范围
*/
public static Range<ZonedDateTime> toDateTimeRange(LocalDate date, ZoneId zone) { public static Range<ZonedDateTime> toDateTimeRange(LocalDate date, ZoneId zone) {
return Range.closedOpen(date.atStartOfDay(zone), startOfNextDate(date, zone)); return Range.closedOpen(date.atStartOfDay(zone), startOfNextDate(date, zone));
} }
/**
* 判断指定年份是否为闰年
*
* @param year 年份
* @return 指定年份是否为闰年
*/
public static boolean isLeapYear(int year) { public static boolean isLeapYear(int year) {
return IsoChronology.INSTANCE.isLeapYear(year); return IsoChronology.INSTANCE.isLeapYear(year);
} }
@@ -815,7 +943,7 @@ public class DateTimeTools {
// ================================ // ================================
/** /**
* 私有构造方法,明确标识该常量类的作用。 * 私有构造方法
*/ */
private DateTimeTools() { private DateTimeTools() {
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");

View File

@@ -48,10 +48,20 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
this.name = name; this.name = name;
} }
/**
* 枚举整数码值
*
* @return 整数码值
*/
public final int getId() { public final int getId() {
return id; return id;
} }
/**
* 枚举名称
*
* @return 枚举名称
*/
public final String getName() { public final String getName() {
return name; return name;
} }
@@ -84,6 +94,9 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
return getClass().getSimpleName() + '(' + id + ":" + name + ')'; return getClass().getSimpleName() + '(' + id + ":" + name + ')';
} }
/**
* 枚举值集合
*/
protected static final class ValueSet<T extends Enumeration<T>> { protected static final class ValueSet<T extends Enumeration<T>> {
private final Map<Integer, T> valueMap; private final Map<Integer, T> valueMap;
@@ -91,6 +104,13 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
this.valueMap = valueMap; this.valueMap = valueMap;
} }
/**
* 创建枚举值集合
*
* @param <T> 枚举类型
* @param values 枚举值
* @return 枚举值集合
*/
@StaticFactoryMethod(ValueSet.class) @StaticFactoryMethod(ValueSet.class)
public static <T extends Enumeration<T>> ValueSet<T> of(T[] values) { public static <T extends Enumeration<T>> ValueSet<T> of(T[] values) {
Map<Integer, T> temp = Arrays.stream(values) Map<Integer, T> temp = Arrays.stream(values)
@@ -98,11 +118,22 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
return new ValueSet<>(Collections.unmodifiableMap(temp)); return new ValueSet<>(Collections.unmodifiableMap(temp));
} }
/**
* 根据整数码值获取枚举对象
*
* @param id 整数码
* @return 枚举对象
*/
public T get(int id) { public T get(int id) {
AssertTools.checkArgument(this.valueMap.containsKey(id), "[%s] 对应的值不存在", id); AssertTools.checkArgument(this.valueMap.containsKey(id), "[%s] 对应的值不存在", id);
return this.valueMap.get(id); return this.valueMap.get(id);
} }
/**
* 获取所有枚举对象
*
* @return 所有枚举对象
*/
public Collection<T> getValues() { public Collection<T> getValues() {
return this.valueMap.values(); return this.valueMap.values();
} }

View File

@@ -36,18 +36,39 @@ public class IdGenerator {
// ===== UUID ===== // ===== UUID =====
/**
* 生成 UUID
*
* @return UUID
*/
public static UUID newUuid() { public static UUID newUuid() {
return UUID.randomUUID(); return UUID.randomUUID();
} }
/**
* 生成 UUID 字符串
*
* @return UUID 字符串
*/
public static String uuidString() { public static String uuidString() {
return UUID.randomUUID().toString(); return UUID.randomUUID().toString();
} }
/**
* 生成 UUID 字符串(无分隔符)
*
* @return UUID 字符串
*/
public static String simpleUuidString() { public static String simpleUuidString() {
return toSimpleString(UUID.randomUUID()); return toSimpleString(UUID.randomUUID());
} }
/**
* 生成 UUID 字符串(无分隔符)
*
* @param uuid UUID
* @return UUID 字符串
*/
public static String toSimpleString(UUID uuid) { public static String toSimpleString(UUID uuid) {
AssertTools.checkArgument(Objects.nonNull(uuid)); AssertTools.checkArgument(Objects.nonNull(uuid));
return (uuidDigits(uuid.getMostSignificantBits() >> 32, 8) + return (uuidDigits(uuid.getMostSignificantBits() >> 32, 8) +
@@ -67,11 +88,23 @@ public class IdGenerator {
private static final Map<Long, IdWorker> snowflakePool = new ConcurrentHashMap<>(); private static final Map<Long, IdWorker> snowflakePool = new ConcurrentHashMap<>();
/**
* 生成雪花ID
*
* @param workerId 工作机器ID
* @return 雪花ID
*/
public static long nextSnowflakeId(long workerId) { public static long nextSnowflakeId(long workerId) {
IdWorker generator = getSnowflakeIdGenerator(workerId); IdWorker generator = getSnowflakeIdGenerator(workerId);
return generator.nextId(); return generator.nextId();
} }
/**
* 获取雪花ID生成器
*
* @param workerId 工作机器ID
* @return {@code IdWorker} 对象。来自 Seata 的修改版雪花ID生成器。
*/
public static IdWorker getSnowflakeIdGenerator(long workerId) { public static IdWorker getSnowflakeIdGenerator(long workerId) {
return snowflakePool.computeIfAbsent(workerId, wid -> new IdWorker(workerId)); return snowflakePool.computeIfAbsent(workerId, wid -> new IdWorker(workerId));
} }

View File

@@ -23,7 +23,7 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* Numbers * 数字工具类
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/ */
@@ -31,6 +31,12 @@ public class Numbers {
// #region - sum // #region - sum
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static int sum(final short... numbers) { public static int sum(final short... numbers) {
int result = 0; int result = 0;
for (short number : numbers) { for (short number : numbers) {
@@ -39,6 +45,12 @@ public class Numbers {
return result; return result;
} }
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static long sum(final int... numbers) { public static long sum(final int... numbers) {
long result = 0L; long result = 0L;
for (int number : numbers) { for (int number : numbers) {
@@ -47,6 +59,12 @@ public class Numbers {
return result; return result;
} }
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static long sum(final long... numbers) { public static long sum(final long... numbers) {
long result = 0L; long result = 0L;
for (long number : numbers) { for (long number : numbers) {
@@ -55,6 +73,12 @@ public class Numbers {
return result; return result;
} }
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static double sum(final float... numbers) { public static double sum(final float... numbers) {
double result = 0.00; double result = 0.00;
for (float number : numbers) { for (float number : numbers) {
@@ -63,6 +87,12 @@ public class Numbers {
return result; return result;
} }
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static double sum(final double... numbers) { public static double sum(final double... numbers) {
double result = 0.00; double result = 0.00;
for (double number : numbers) { for (double number : numbers) {
@@ -71,6 +101,12 @@ public class Numbers {
return result; return result;
} }
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static BigInteger sum(final BigInteger... numbers) { public static BigInteger sum(final BigInteger... numbers) {
if (ArrayTools.isNullOrEmpty(numbers)) { if (ArrayTools.isNullOrEmpty(numbers)) {
return BigInteger.ZERO; return BigInteger.ZERO;
@@ -85,6 +121,12 @@ public class Numbers {
return result; return result;
} }
/**
* 求和
*
* @param numbers 数据数组
* @return 求和结果
*/
public static BigDecimal sum(final BigDecimal... numbers) { public static BigDecimal sum(final BigDecimal... numbers) {
return BigDecimals.sum(numbers); return BigDecimals.sum(numbers);
} }
@@ -93,35 +135,83 @@ public class Numbers {
// #region - nullToZero // #region - nullToZero
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
public static byte nullToZero(@Nullable final Byte val) { public static byte nullToZero(@Nullable final Byte val) {
return val != null ? val : 0; return val != null ? val : 0;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
public static short nullToZero(@Nullable final Short val) { public static short nullToZero(@Nullable final Short val) {
return val != null ? val : 0; return val != null ? val : 0;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
public static int nullToZero(@Nullable final Integer val) { public static int nullToZero(@Nullable final Integer val) {
return val != null ? val : 0; return val != null ? val : 0;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
public static long nullToZero(@Nullable final Long val) { public static long nullToZero(@Nullable final Long val) {
return val != null ? val : 0L; return val != null ? val : 0L;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
public static float nullToZero(@Nullable final Float val) { public static float nullToZero(@Nullable final Float val) {
return val != null ? val : 0.0F; return val != null ? val : 0.0F;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
public static double nullToZero(@Nullable final Double val) { public static double nullToZero(@Nullable final Double val) {
return val != null ? val : 0.0; return val != null ? val : 0.0;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
@Nonnull @Nonnull
public static BigInteger nullToZero(@Nullable final BigInteger val) { public static BigInteger nullToZero(@Nullable final BigInteger val) {
return val != null ? val : BigInteger.ZERO; return val != null ? val : BigInteger.ZERO;
} }
/**
* 将 {@code null} 转换为 {@code 0}
*
* @param val 待转换的值
* @return 如果 {@code val} 不为 {@code null},则返回该值;如果值为 {@code null},则返回 {@code 0}
*/
@Nonnull @Nonnull
public static BigDecimal nullToZero(@Nullable final BigDecimal val) { public static BigDecimal nullToZero(@Nullable final BigDecimal val) {
return BigDecimals.nullToZero(val); return BigDecimals.nullToZero(val);

View File

@@ -134,16 +134,34 @@ public class OptionalTools {
return optionalObj.orElse(null); return optionalObj.orElse(null);
} }
/**
* 将 {@link OptionalInt} 转为 {@link Integer}
*
* @param optionalObj optional 对象
* @return {@link Integer} 对象。如果 {@code OptionalInt} 的值缺失,返回 {@code null}。
*/
@Beta @Beta
public static Integer toInteger(OptionalInt optionalObj) { public static Integer toInteger(OptionalInt optionalObj) {
return optionalObj.isPresent() ? optionalObj.getAsInt() : null; return optionalObj.isPresent() ? optionalObj.getAsInt() : null;
} }
/**
* 将 {@link OptionalLong} 转为 {@link Long}
*
* @param optionalObj optional 对象
* @return {@link Long} 对象。如果 {@code OptionalLong} 的值缺失,返回 {@code null}。
*/
@Beta @Beta
public static Long toLong(OptionalLong optionalObj) { public static Long toLong(OptionalLong optionalObj) {
return optionalObj.isPresent() ? optionalObj.getAsLong() : null; return optionalObj.isPresent() ? optionalObj.getAsLong() : null;
} }
/**
* 将 {@link OptionalDouble} 转为 {@link Double}
*
* @param optionalObj optional 对象
* @return {@link Double} 对象。如果 {@code OptionalDouble} 的值缺失,返回 {@code null}。
*/
@Beta @Beta
public static Double toDouble(OptionalDouble optionalObj) { public static Double toDouble(OptionalDouble optionalObj) {
return optionalObj.isPresent() ? optionalObj.getAsDouble() : null; return optionalObj.isPresent() ? optionalObj.getAsDouble() : null;

View File

@@ -18,7 +18,6 @@ package xyz.zhouxy.plusone.commons.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -70,8 +69,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例数组 * @return {@link Pattern} 实例数组
*/ */
public static Pattern[] getPatterns(final String[] patterns, final boolean cachePattern) { public static Pattern[] getPatterns(final String[] patterns, final boolean cachePattern) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
return cachePattern return cachePattern
? cacheAndGetPatternsInternal(patterns) ? cacheAndGetPatternsInternal(patterns)
: getPatternsInternal(patterns); : getPatternsInternal(patterns);
@@ -84,8 +82,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例数组 * @return {@link Pattern} 实例数组
*/ */
public static Pattern[] getPatterns(final String[] patterns) { public static Pattern[] getPatterns(final String[] patterns) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
return getPatternsInternal(patterns); return getPatternsInternal(patterns);
} }
@@ -109,8 +106,7 @@ public final class RegexTools {
* @return 判断结果 * @return 判断结果
*/ */
public static boolean matchesOne(@Nullable final CharSequence input, final Pattern[] patterns) { public static boolean matchesOne(@Nullable final CharSequence input, final Pattern[] patterns) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
return matchesOneInternal(input, patterns); return matchesOneInternal(input, patterns);
} }
@@ -122,8 +118,7 @@ public final class RegexTools {
* @return 判断结果 * @return 判断结果
*/ */
public static boolean matchesAll(@Nullable final CharSequence input, final Pattern[] patterns) { public static boolean matchesAll(@Nullable final CharSequence input, final Pattern[] patterns) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
return matchesAllInternal(input, patterns); return matchesAllInternal(input, patterns);
} }
@@ -166,8 +161,7 @@ public final class RegexTools {
*/ */
public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns, public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns,
final boolean cachePattern) { final boolean cachePattern) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = cachePattern final Pattern[] patternSet = cachePattern
? cacheAndGetPatternsInternal(patterns) ? cacheAndGetPatternsInternal(patterns)
: getPatternsInternal(patterns); : getPatternsInternal(patterns);
@@ -182,8 +176,7 @@ public final class RegexTools {
* @return 判断结果 * @return 判断结果
*/ */
public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns) { public static boolean matchesOne(@Nullable final CharSequence input, final String[] patterns) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = getPatternsInternal(patterns); final Pattern[] patternSet = getPatternsInternal(patterns);
return matchesOneInternal(input, patternSet); return matchesOneInternal(input, patternSet);
} }
@@ -198,8 +191,7 @@ public final class RegexTools {
*/ */
public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns, public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns,
final boolean cachePattern) { final boolean cachePattern) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = cachePattern final Pattern[] patternSet = cachePattern
? cacheAndGetPatternsInternal(patterns) ? cacheAndGetPatternsInternal(patterns)
: getPatternsInternal(patterns); : getPatternsInternal(patterns);
@@ -214,8 +206,7 @@ public final class RegexTools {
* @return 判断结果 * @return 判断结果
*/ */
public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns) { public static boolean matchesAll(@Nullable final CharSequence input, final String[] patterns) {
AssertTools.checkNotNull(patterns); AssertTools.checkArgument(ArrayTools.isAllElementsNotNull(patterns));
AssertTools.checkArgument(allNotNull(patterns));
final Pattern[] patternSet = getPatternsInternal(patterns); final Pattern[] patternSet = getPatternsInternal(patterns);
return matchesAllInternal(input, patternSet); return matchesAllInternal(input, patternSet);
} }
@@ -335,22 +326,32 @@ public final class RegexTools {
return input != null && pattern.matcher(input).matches(); return input != null && pattern.matcher(input).matches();
} }
/**
* 判断 {@code input} 是否匹配至少一个正则。
*
* @param input 输入
* @param patterns 正则表达式
* @return 判断结果
*/
private static boolean matchesOneInternal(@Nullable final CharSequence input, final Pattern[] patterns) { private static boolean matchesOneInternal(@Nullable final CharSequence input, final Pattern[] patterns) {
return input != null return input != null
&& Arrays.stream(patterns) && Arrays.stream(patterns)
.anyMatch(pattern -> pattern.matcher(input).matches()); .anyMatch(pattern -> pattern.matcher(input).matches());
} }
/**
* 判断 {@code input} 是否匹配全部正则。
*
* @param input 输入
* @param patterns 正则表达式
* @return 判断结果
*/
private static boolean matchesAllInternal(@Nullable final CharSequence input, final Pattern[] patterns) { private static boolean matchesAllInternal(@Nullable final CharSequence input, final Pattern[] patterns) {
return input != null return input != null
&& Arrays.stream(patterns) && Arrays.stream(patterns)
.allMatch(pattern -> pattern.matcher(input).matches()); .allMatch(pattern -> pattern.matcher(input).matches());
} }
private static <T> boolean allNotNull(T[] array) {
return Arrays.stream(array).allMatch(Objects::nonNull);
}
private RegexTools() { private RegexTools() {
// 不允许实例化 // 不允许实例化
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");

View File

@@ -34,11 +34,25 @@ import xyz.zhouxy.plusone.commons.constant.PatternConsts;
* </p> * </p>
* *
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a> * @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
*/ */
public class StringTools { public class StringTools {
public static final String EMPTY_STRING = ""; public static final String EMPTY_STRING = "";
/**
* 判断字符串是否非空白
*
* <pre>
* StringTools.isNotBlank(null); // false
* StringTools.isNotBlank(""); // false
* StringTools.isNotBlank(" "); // false
* StringTools.isNotBlank("Hello"); // true
* </pre>
*
* @param cs 检查的字符串
* @return 是否非空白
*/
public static boolean isNotBlank(@Nullable final String cs) { public static boolean isNotBlank(@Nullable final String cs) {
if (cs == null || cs.isEmpty()) { if (cs == null || cs.isEmpty()) {
return false; return false;
@@ -51,6 +65,20 @@ public class StringTools {
return false; return false;
} }
/**
* 判断是否空白字符串
*
* <pre>
* StringTools.isBlank(null); // true
* StringTools.isBlank(""); // true
* StringTools.isBlank(" "); // true
* StringTools.isBlank("Hello"); // false
* </pre>
*
* @param cs 检查的字符串
* @return 是否空白
* @since 1.1.0
*/
public static boolean isBlank(@Nullable String cs) { public static boolean isBlank(@Nullable String cs) {
if (cs == null || cs.isEmpty()) { if (cs == null || cs.isEmpty()) {
return true; return true;
@@ -63,28 +91,89 @@ public class StringTools {
return true; return true;
} }
/**
* 重复字符串
*
* @param str 要重复的字符串
* @param times 重复次数
* @return 结果
*/
public static String repeat(String str, int times) { public static String repeat(String str, int times) {
return repeat(str, times, Integer.MAX_VALUE); return repeat(str, times, Integer.MAX_VALUE);
} }
/**
* 重复字符串
*
* @param str 要重复的字符串
* @param times 重复次数
* @param maxLength 最大长度
* @return 结果
*/
public static String repeat(final String str, int times, int maxLength) { public static String repeat(final String str, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(str)); AssertTools.checkArgument(Objects.nonNull(str));
return String.valueOf(ArrayTools.repeat(str.toCharArray(), times, maxLength)); return String.valueOf(ArrayTools.repeat(str.toCharArray(), times, maxLength));
} }
/**
* 判断字符串是否非空
*
* <pre>
* StringTools.isNotEmpty(null); // false
* StringTools.isNotEmpty(""); // false
* StringTools.isNotEmpty(" "); // true
* StringTools.isNotEmpty("Hello"); // true
* </pre>
*
* @param cs 检查的字符串
* @return 是否非空
* @since 1.1.0
*/
public static boolean isNotEmpty(@Nullable final String cs) { public static boolean isNotEmpty(@Nullable final String cs) {
return cs != null && !cs.isEmpty(); return cs != null && !cs.isEmpty();
} }
/**
* 判断字符串是否为空字符串
*
* <pre>
* StringTools.isEmpty(null); // true
* StringTools.isEmpty(""); // true
* StringTools.isEmpty(" "); // false
* StringTools.isEmpty("Hello"); // false
* </pre>
*
* @param cs 检查的字符串
* @return 是否空字符串
* @since 1.1.0
*/
public static boolean isEmpty(@Nullable final String cs) { public static boolean isEmpty(@Nullable final String cs) {
return cs == null || cs.isEmpty(); return cs == null || cs.isEmpty();
} }
/**
* 判断字符串是否为邮箱地址
*
* @param cs 检查的字符串
* @return 是否是邮箱地址
* @since 1.1.0
*
* @see PatternConsts#EMAIL
*/
@Beta @Beta
public static boolean isEmail(@Nullable final String cs) { public static boolean isEmail(@Nullable final String cs) {
return RegexTools.matches(cs, PatternConsts.EMAIL); return RegexTools.matches(cs, PatternConsts.EMAIL);
} }
/**
* 判断字符串是否为 URL 地址
*
* @param cs 检查的字符串
* @return 是否是 URL
* @since 1.1.0
*
* @see URL
*/
@Beta @Beta
public static boolean isURL(@Nullable final String cs) { public static boolean isURL(@Nullable final String cs) {
try { try {
@@ -95,6 +184,42 @@ public class StringTools {
return true; return true;
} }
/**
* 脱敏
*
* @param src 原字符串
* @param front 前面保留的字符数
* @param end 后面保留的字符数
* @return 脱敏结果
* @since 1.1.0
*/
public static String desensitize(@Nullable final String src, int front, int end) {
return desensitize(src, '*', front, end);
}
/**
* 脱敏
*
* @param src 原字符串
* @param replacedChar 用于替换的字符
* @param front 前面保留的字符数
* @param end 后面保留的字符数
* @return 脱敏结果
* @since 1.1.0
*/
public static String desensitize(@Nullable final String src, char replacedChar, int front, int end) {
if (src == null || src.isEmpty()) {
return EMPTY_STRING;
}
AssertTools.checkArgument(front >= 0 && end >= 0);
AssertTools.checkArgument((front + end) <= src.length(), "需要截取的长度不能大于原字符串长度");
final char[] charArray = src.toCharArray();
for (int i = front; i < charArray.length - end; i++) {
charArray[i] = replacedChar;
}
return String.valueOf(charArray);
}
private StringTools() { private StringTools() {
throw new IllegalStateException("Utility class"); throw new IllegalStateException("Utility class");
} }

View File

@@ -39,11 +39,26 @@ public class TreeBuilder<T, TSubTree extends T, TIdentity> {
private final BiConsumer<TSubTree, T> addChildMethod; private final BiConsumer<TSubTree, T> addChildMethod;
private final Comparator<? super T> defaultComparator; private final Comparator<? super T> defaultComparator;
/**
* 构造一个 {@code TreeBuilder}。不指定用于排序的 {@code Comparator}。
*
* @param identityGetter 从节点中获取其标识的逻辑
* @param parentIdentityGetter 获取父节点标识的逻辑
* @param addChild 添加子节点的逻辑
*/
public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter, public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter,
BiConsumer<TSubTree, T> addChild) { BiConsumer<TSubTree, T> addChild) {
this(identityGetter, parentIdentityGetter, addChild, null); this(identityGetter, parentIdentityGetter, addChild, null);
} }
/**
* 构造一个 {@code TreeBuilder}。
*
* @param identityGetter 从节点中获取其标识的逻辑
* @param parentIdentityGetter 获取父节点标识的逻辑
* @param addChild 添加子节点的逻辑
* @param defaultComparator 默认的 {@code Comparator},用于排序
*/
public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter, public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter,
BiConsumer<TSubTree, T> addChild, @Nullable Comparator<? super T> defaultComparator) { BiConsumer<TSubTree, T> addChild, @Nullable Comparator<? super T> defaultComparator) {
this.identityGetter = identityGetter; this.identityGetter = identityGetter;

View File

@@ -39,6 +39,7 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@SuppressWarnings("deprecation")
class ValidatableStringRecordTests { class ValidatableStringRecordTests {
private static final Logger log = LoggerFactory.getLogger(ValidatableStringRecordTests.class); private static final Logger log = LoggerFactory.getLogger(ValidatableStringRecordTests.class);
@@ -83,6 +84,7 @@ class User {
} }
} }
@SuppressWarnings("deprecation")
@ValueObject @ValueObject
class Email extends ValidatableStringRecord<Email> { class Email extends ValidatableStringRecord<Email> {
private Email(String value) { private Email(String value) {
@@ -95,6 +97,7 @@ class Email extends ValidatableStringRecord<Email> {
} }
} }
@SuppressWarnings("deprecation")
@ValueObject @ValueObject
class Username extends ValidatableStringRecord<Username> { class Username extends ValidatableStringRecord<Username> {
private Username(String username) { private Username(String username) {

View File

@@ -34,6 +34,7 @@ import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.business.BizException; import xyz.zhouxy.plusone.commons.exception.business.BizException;
@Slf4j @Slf4j
@SuppressWarnings("null")
public public
class CustomUnifiedResponseFactoryTests { class CustomUnifiedResponseFactoryTests {

View File

@@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.business.BizException; import xyz.zhouxy.plusone.commons.exception.business.BizException;
@Slf4j @Slf4j
@SuppressWarnings("null")
public public
class UnifiedResponseTests { class UnifiedResponseTests {

View File

@@ -730,37 +730,37 @@ public class ArrayToolsTests {
// ================================ // ================================
@Test @Test
void indexOfWithPredicate_NullPredicate_ThrowsNullPointerException() { void indexOf_NullPredicate_ThrowsNullPointerException() {
assertThrows(NullPointerException.class, () -> ArrayTools.indexOfWithPredicate(new String[] {}, null)); assertThrows(NullPointerException.class, () -> ArrayTools.indexOf(new String[] {}, null));
} }
@Test @Test
void indexOfWithPredicate_NullArray_ReturnsNotFoundIndex() { void indexOf_NullArray_ReturnsNotFoundIndex() {
Predicate<String> predicate = s -> s.equals("test"); Predicate<String> predicate = s -> s.equals("test");
int result = ArrayTools.indexOfWithPredicate(null, predicate); int result = ArrayTools.indexOf(null, predicate);
assertEquals(ArrayTools.NOT_FOUND_INDEX, result); assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
} }
@Test @Test
void indexOfWithPredicate_EmptyArray_ReturnsNotFoundIndex() { void indexOf_EmptyArray_ReturnsNotFoundIndex() {
Predicate<String> predicate = s -> s.equals("test"); Predicate<String> predicate = s -> s.equals("test");
int result = ArrayTools.indexOfWithPredicate(new String[] {}, predicate); int result = ArrayTools.indexOf(new String[] {}, predicate);
assertEquals(ArrayTools.NOT_FOUND_INDEX, result); assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
} }
@Test @Test
void indexOfWithPredicate_ArrayContainsMatchingElement_ReturnsIndex() { void indexOf_ArrayContainsMatchingElement_ReturnsIndex() {
String[] array = { "apple", "banana", "cherry" }; String[] array = { "apple", "banana", "cherry" };
Predicate<String> predicate = s -> s.equals("banana"); Predicate<String> predicate = s -> s.equals("banana");
int result = ArrayTools.indexOfWithPredicate(array, predicate); int result = ArrayTools.indexOf(array, predicate);
assertEquals(1, result); assertEquals(1, result);
} }
@Test @Test
void indexOfWithPredicate_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() { void indexOf_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() {
String[] array = { "apple", "banana", "cherry" }; String[] array = { "apple", "banana", "cherry" };
Predicate<String> predicate = s -> s.equals("orange"); Predicate<String> predicate = s -> s.equals("orange");
int result = ArrayTools.indexOfWithPredicate(array, predicate); int result = ArrayTools.indexOf(array, predicate);
assertEquals(ArrayTools.NOT_FOUND_INDEX, result); assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
} }
@@ -847,12 +847,12 @@ public class ArrayToolsTests {
@Test @Test
void indexOf_NullObject_ReturnsNotFound() { void indexOf_NullObject_ReturnsNotFound() {
assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] { "a", "b", "c" }, null)); assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.indexOf(new String[] { "a", "b", "c" }, (String) null));
} }
@Test @Test
void indexOf_ArrayContainsNull_ReturnsIndex() { void indexOf_ArrayContainsNull_ReturnsIndex() {
assertEquals(1, ArrayTools.indexOf(new String[] { "a", null, "c" }, null)); assertEquals(1, ArrayTools.indexOf(new String[] { "a", null, "c" }, (String) null));
} }
// ================================ // ================================
@@ -864,37 +864,37 @@ public class ArrayToolsTests {
// ================================ // ================================
@Test @Test
void lastIndexOfWithPredicate_NullPredicate_ThrowsNullPointerException() { void lastIndexOf_NullPredicate_ThrowsNullPointerException() {
assertThrows(NullPointerException.class, () -> ArrayTools.lastIndexOfWithPredicate(new String[] {}, null)); assertThrows(NullPointerException.class, () -> ArrayTools.lastIndexOf(new String[] {}, null));
} }
@Test @Test
void lastIndexOfWithPredicate_NullArray_ReturnsNotFoundIndex() { void lastIndexOf_NullArray_ReturnsNotFoundIndex() {
Predicate<String> predicate = s -> s.equals("test"); Predicate<String> predicate = s -> s.equals("test");
int result = ArrayTools.lastIndexOfWithPredicate(null, predicate); int result = ArrayTools.lastIndexOf(null, predicate);
assertEquals(ArrayTools.NOT_FOUND_INDEX, result); assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
} }
@Test @Test
void lastIndexOfWithPredicate_EmptyArray_ReturnsNotFoundIndex() { void lastIndexOf_EmptyArray_ReturnsNotFoundIndex() {
Predicate<String> predicate = s -> s.equals("test"); Predicate<String> predicate = s -> s.equals("test");
int result = ArrayTools.lastIndexOfWithPredicate(new String[] {}, predicate); int result = ArrayTools.lastIndexOf(new String[] {}, predicate);
assertEquals(ArrayTools.NOT_FOUND_INDEX, result); assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
} }
@Test @Test
void lastIndexOfWithPredicate_ArrayContainsMatchingElement_ReturnsIndex() { void lastIndexOf_ArrayContainsMatchingElement_ReturnsIndex() {
String[] array = { "apple", "banana", "banana", "cherry" }; String[] array = { "apple", "banana", "banana", "cherry" };
Predicate<String> predicate = s -> s.equals("banana"); Predicate<String> predicate = s -> s.equals("banana");
int result = ArrayTools.lastIndexOfWithPredicate(array, predicate); int result = ArrayTools.lastIndexOf(array, predicate);
assertEquals(2, result); assertEquals(2, result);
} }
@Test @Test
void lastIndexOfWithPredicate_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() { void lastIndexOf_ArrayDoesNotContainMatchingElement_ReturnsNotFoundIndex() {
String[] array = { "apple", "banana", "cherry" }; String[] array = { "apple", "banana", "cherry" };
Predicate<String> predicate = s -> s.equals("orange"); Predicate<String> predicate = s -> s.equals("orange");
int result = ArrayTools.lastIndexOfWithPredicate(array, predicate); int result = ArrayTools.lastIndexOf(array, predicate);
assertEquals(ArrayTools.NOT_FOUND_INDEX, result); assertEquals(ArrayTools.NOT_FOUND_INDEX, result);
} }
@@ -982,12 +982,12 @@ public class ArrayToolsTests {
@Test @Test
void lastIndexOf_NullObject_ReturnsNotFound() { void lastIndexOf_NullObject_ReturnsNotFound() {
assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] { "a", "b", "c" }, null)); assertEquals(ArrayTools.NOT_FOUND_INDEX, ArrayTools.lastIndexOf(new String[] { "a", "b", "c" }, (String) null));
} }
@Test @Test
void lastIndexOf_ArrayContainsNull_ReturnsIndex() { void lastIndexOf_ArrayContainsNull_ReturnsIndex() {
assertEquals(3, ArrayTools.lastIndexOf(new String[] { "a", null, "c", null, "e" }, null)); assertEquals(3, ArrayTools.lastIndexOf(new String[] { "a", null, "c", null, "e" }, (String) null));
} }
// ================================ // ================================

View File

@@ -75,7 +75,7 @@ class RegexToolsTests {
@Test @Test
void getPatterns_NullPatterns_ThrowsException() { void getPatterns_NullPatterns_ThrowsException() {
assertThrows(NullPointerException.class, () -> { assertThrows(IllegalArgumentException.class, () -> {
RegexTools.getPatterns(null, true); RegexTools.getPatterns(null, true);
}); });
} }

View File

@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Arrays; import java.util.Arrays;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@SuppressWarnings("null") @SuppressWarnings("null")
@@ -184,7 +185,7 @@ class StringToolsTests {
// #region - isURL // #region - isURL
// ================================ // ================================
/** /**
* TC1: 验证标准HTTP协议URL * TC1: 验证标准HTTP协议URL
*/ */
@Test @Test
@@ -298,6 +299,77 @@ class StringToolsTests {
// #endregion - repeat // #endregion - repeat
// ================================ // ================================
// ================================
// #region - desensitize
// ================================
@Test
public void desensitize_NullInput_ReturnsEmptyString() {
String result = StringTools.desensitize(null, '#', 2, 2);
Assertions.assertEquals("", result);
}
@Test
public void desensitize_EmptyString_ReturnsEmptyString() {
String result = StringTools.desensitize("", '#', 2, 2);
Assertions.assertEquals("", result);
}
@Test
public void desensitize_NegativeFront_ThrowsException() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
StringTools.desensitize("123456", '#', -1, 2);
});
}
@Test
public void desensitize_NegativeEnd_ThrowsException() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
StringTools.desensitize("123456", '#', 2, -1);
});
}
@Test
public void desensitize_FrontPlusEndExceedsLength_ThrowsException() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
StringTools.desensitize("123456", '#', 3, 4);
});
}
@Test
public void desensitize_ValidInput_ReturnsDesensitizedString() {
String result = StringTools.desensitize("123456", '#', 2, 2);
Assertions.assertEquals("12##56", result);
}
@Test
public void desensitize_FrontZero_ReturnsDesensitizedString() {
String result = StringTools.desensitize("123456", '#', 0, 2);
Assertions.assertEquals("####56", result);
}
@Test
public void desensitize_EndZero_ReturnsDesensitizedString() {
String result = StringTools.desensitize("123456", '#', 2, 0);
Assertions.assertEquals("12####", result);
}
@Test
public void desensitize_FrontAndEndZero_ReturnsDesensitizedString() {
String result = StringTools.desensitize("123456", '#', 0, 0);
Assertions.assertEquals("######", result);
}
@Test
public void desensitize_ValidInput_DefaultReplacedChar_ReturnsDesensitizedString() {
String result = StringTools.desensitize("123456", 2, 2);
Assertions.assertEquals("12**56", result);
}
// ================================
// #endregion - desensitize
// ================================
@Test @Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() { void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = StringTools.class.getDeclaredConstructors(); Constructor<?>[] constructors = StringTools.class.getDeclaredConstructors();