39 Commits

Author SHA1 Message Date
9fbba72175 docs: 删除 IdWorker 的 author 信息
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/36
2025-03-28 09:29:08 +08:00
88ca20cd6a docs: 改正 OptionalTools 的 javadoc
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/34
2025-03-28 09:02:28 +08:00
e98fe66b65 docs: fix javadoc
fix issue plusone/plusone-commons#29
2025-03-28 08:55:35 +08:00
872d42aef0 docs: fix typos 2025-03-25 14:28:22 +08:00
3f08b08910 docs: 修改 copyright 2025-03-23 13:50:03 +08:00
4c1ee87b4d feat: StringTools 新增工具方法
新增 StringTools#isBlank、StringTools#isEmpty、StringTools#isNotEmpty、StringTools#isURL、StringTools#isEmail
2025-03-23 13:49:23 +08:00
a9c8fec81a feat: DateTimeTools 新增 isFuture 和 isPast。 2025-03-23 13:27:37 +08:00
29519f3489 修改 email 的正则常量
1. fix: Email 的 Pattern 不区分大小写
2. docs: 标注正则表达式的出处
2025-03-22 17:45:02 +08:00
26efd1125e docs: 修改注释和文档 2025-03-22 15:36:17 +08:00
b912f4b063 perf: 优化 JSR305 注解的使用 2025-03-22 15:30:29 +08:00
90da2b8eaa Quarter#fromMonth 新增判空 2025-03-22 15:05:32 +08:00
8d3bbbc56b add README.md. 2025-02-24 23:11:01 +08:00
bdd6e61160 补充各包的 javadoc
在每个包下都创建 package-info.java 文件,编写 javadoc,对每个包进行说明。
2025-02-21 21:50:18 +08:00
f024a08dd2 v1.0.0
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/28
2025-02-20 23:22:16 +08:00
15cea5fb4b v1.0.0 2025-02-20 23:14:56 +08:00
cb2eb0633f 新增 UnifiedResponses
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/27
2025-02-20 23:14:50 +08:00
ff3f80a447 更改 copyright。 2025-02-20 23:05:27 +08:00
e5a57e03b4 新增 UnifiedResponses
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/24
2025-02-20 20:51:29 +08:00
faab942e13 新增 UnifiedResponse 工厂 UnifiedResponses。 2025-02-20 20:26:21 +08:00
0f7ab8fed5 1.0.0-RC3
Merge pull request '1.0.0-RC3' (#21) from 1.x.x into dev
2025-02-14 19:03:05 +08:00
69377f3e67 1.0.0-RC3
Merge pull request '1.x.x' (#20) from ZhouXY108/plusone-commons:1.x.x into 1.x.x
2025-02-14 18:59:31 +08:00
ec4efe5f0f Merge branch '1.x.x' into 1.x.x 2025-02-14 18:58:17 +08:00
d92861284c 整理依赖版本;修改版本号 1.0.0-RC3。 2025-02-14 18:27:43 +08:00
cd88892762 IdGenerator#toSimpleString 添加 @Nonnull 注解 2025-02-14 16:58:33 +08:00
e3ff5a2ab3 CollectionTools#isNotEmpty 支持 guava 的 Table、Multimap、Multiset 和 RangeSet 2025-02-14 16:57:45 +08:00
d217e8b9ac 优化 TreeBuilder 的单元测试代码。 2025-01-30 19:23:25 +08:00
669506e499 删除 YearQuarter#max 和 YearQuarter#min,使用 guava 的 Comparators#max 和 Comparators#min 即可。 2025-01-30 19:21:21 +08:00
79b8b81220 test: 测试工具类的私有构造器 2025-01-22 21:57:53 +08:00
0c4cfd3044 1.0.0-RC2
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/19
2025-01-07 17:18:48 +08:00
52a4011eb1 更新版本号
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/18
2025-01-07 17:15:45 +08:00
0bcc5b895a 新增 ThrowingFunction
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/16
2025-01-07 17:09:52 +08:00
4c0f5095fa 新增 base 和 function 包的 package-info
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/15
2025-01-07 17:06:33 +08:00
044320a2a9 优化 Ref API
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/14
2025-01-07 17:01:57 +08:00
1c940a9c7c IWithCode 相关接口修改方法名,区分重载方法
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/13
2025-01-07 16:52:48 +08:00
5c3923f8af 修改版本号
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/12
2025-01-01 21:10:35 +08:00
0eee05d46a Merge pull request '完成单元测试' (#11) from ZhouXY108/plusone-commons:1.x.x into 1.x.x
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/11
2025-01-01 20:47:38 +08:00
300a2436b1 CollectionTools 新增 nullToEmptyList、nullToEmptySet、nullToEmptyMap 方法
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/10
2024-12-29 22:22:21 +08:00
a6bb069f8a 完成 EnumTools 单元测试
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/9
2024-12-29 22:20:35 +08:00
dbb2ef8c50 修正 InvalidInputExceptionTests 和 ParsingFailureExceptionTests
Merge pull request '修正 InvalidInputExceptionTests 和 ParsingFailureExceptionTests' (#8) from ZhouXY108/plusone-commons:temp/complete-exception-tests into 1.x.x
Reviewed-on: http://zhouxy.xyz:3000/plusone/plusone-commons/pulls/8
2024-12-29 22:18:22 +08:00
51 changed files with 2515 additions and 362 deletions

219
README.md Normal file
View File

@@ -0,0 +1,219 @@
## 一、annotation - 注解
### 1. StaticFactoryMethod
标识静态工厂方法。 *《Effective Java》***Item1** 建议考虑用静态工厂方法替换构造器, 因而考虑有一个注解可以标记一下静态工厂方法,以和其它方法进行区分。
### 2. ReaderMethod 和 WriterMethod
分别标识读方法(如 getter或写方法如 setter
最早是写了一个集合类,为了方便判断使用读写锁时,哪些情况下使用读锁,哪些情况下使用写锁。
### 3. UnsupportedOperation
标识该方法不被支持或没有实现,将抛出 `UnsupportedOperationException`。 为了方便在使用时,不需要点进源码,就能知道该方法没有实现。
### 4. Virtual
Java 非 final 的实例方法,对应 C++/C# 中的虚方法,允许被子类覆写。 Virtual 注解旨在设计父类时,强调该方法父类虽然有默认实现,但子类可以根据自己的需要覆写。
### 5. ValueObject
标记一个类,表示其作为值对象,区别于 Entity。
## 二、base - 基础组件
### 1. Ref
`Ref` 包装了一个值,表示对该值的应用。
灵感来自于 C# 的 ref 参数修饰符。C# 允许通过以下方式,将值返回给调用端:
```C#
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number); // Output: 45
```
`Ref` 使 Java 可以达到类似的效果,如:
```java
void method(Ref<Integer> refArgument) {
refArgument.transformValue(i -> i + 44);
}
Ref<Integer> number = Ref.of(1);
method(number);
System.out.println(number.getValue()); // Output: 45
```
当一个方法需要产生多个结果时,无法有多个返回值,可以使用 `Ref` 作为参数传入,方法内部修改 `Ref` 的值。 调用方在调用方法之后,使用 `getValue()` 获取结果。
```java
String method(Ref<Integer> intRefArgument, Ref<String> strRefArgument) {
intRefArgument.transformValue(i -> i + 44);
strRefArgument.setValue("Hello " + strRefArgument.getValue());
return "Return string";
}
Ref<Integer> number = Ref.of(1);
Ref<String> str = Ref.of("Java");
String result = method(number, str);
System.out.println(number.getValue()); // Output: 45
System.out.println(str.getValue()); // Output: Hello Java
System.out.println(result); // Output: Return string
```
### 2. IWithCode
类似于枚举这样的类型,通常需要设置固定的码值表示对应的含义。 可实现 `IWithCode`、`IWithIntCode`、`IWithLongCode`,便于在需要的地方对这些接口的实现进行处理。
## 三、collection - 集合
### 1. CollectionTools
集合工具类
## 四、constant - 常量
### 1. 正则常量
`RegexConsts` 包含常见正则表达式;`PatternConsts` 包含对应的 `Pattern` 对象
## 五、exception - 异常
### 1. MultiTypesException - 多类型异常
异常在不同场景下被抛出,可以用不同的枚举值,表示不同的场景类型。
异常实现 `MultiTypesException` 的 `MultiTypesException#getType` 方法,返回对应的场景类型。
表示场景类型的枚举实现 `MultiTypesException.ExceptionType`,其中的工厂方法用于创建类型对象。
```java
public final class LoginException
extends RuntimeException
implements MultiTypesException<LoginException, LoginException.Type> {
private final Type type;
private LoginException(@Nonnull Type type, @Nonnull String message) {
super(message);
this.type = type;
}
private LoginException(@Nonnull Type type, @Nonnull Throwable cause) {
super(cause);
this.type = type;
}
private LoginException(@Nonnull Type type,
@Nonnull String message,
@Nonnull Throwable cause) {
super(message, cause);
this.type = type;
}
@Override
public @Nonnull Type getType() {
return this.type;
}
// ...
public enum Type implements ExceptionType {
DEFAULT("00", "当前会话未登录"),
NOT_TOKEN("10", "未提供token"),
INVALID_TOKEN("20", "token无效"),
TOKEN_TIMEOUT("30", "token已过期"),
BE_REPLACED("40", "token已被顶下线"),
KICK_OUT("50", "token已被踢下线"),
;
@Nonnull
private final String code;
@Nonnull
private final String defaultMessage;
Type(@Nonnull String code, @Nonnull String defaultMessage) {
this.code = code;
this.defaultMessage = defaultMessage;
}
@Override
public @Nonnull String getCode() {
return code;
}
@Override
public @Nonnull String getDefaultMessage() {
return defaultMessage;
}
@Override
public @Nonnull LoginException create() {
return new LoginException(this, this.defaultMessage);
}
@Override
public @Nonnull LoginException create(String message) {
return new LoginException(this, message);
}
@Override
public @Nonnull LoginException create(Throwable cause) {
return new LoginException(this, cause);
}
@Override
public @Nonnull LoginException create(String message, Throwable cause) {
return new LoginException(this, message, cause);
}
}
}
```
使用时,可以使用这种方式创建并抛出异常:
```java
throw LoginException.Type.TOKEN_TIMEOUT.create();
```
### 2. 业务异常
预设常见的业务异常。可继承 `BizException` 自定义业务异常。
### 3. 系统异常
预设常见的系统异常。可继承 `SysException` 自定义系统异常。
## 六、function - 函数式编程
### 1. PredicateTools
`PredicateTools` 用于 `Predicate` 的相关操作。
### 2. Functional interfaces
补充可能用得上的函数式接口:
| Group | FunctionalInterface | method |
| ------------- | -------------------- | -------------------------------- |
| UnaryOperator | BoolUnaryOperator | boolean applyAsBool (boolean) |
| UnaryOperator | CharUnaryOperator | char applyAsChar(char) |
| Throwing | Executable | void execute() throws E |
| Throwing | ThrowingConsumer | void accept(T) throws E |
| Throwing | ThrowingFunction | R apply(T) throws E |
| Throwing | ThrowingPredicate | boolean test(T) throws E |
| Throwing | ThrowingSupplier | T get() throws E |
| Optional | OptionalSupplier | Optional<T> get() throws E |
| Optional | ToOptionalBiFunction | Optional<R> apply(T,U) |
| Optional | ToOptionalFunction | Optional<R> apply(T) |
## 七、model - 业务建模组件
包含业务建模可能用到的性别、身份证等元素,也包含数据传输对象,如分页查询参数、响应结果、分页结果等。
### 数据传输对象
#### 1. 分页
分页组件由 `PagingAndSortingQueryParams` 作为入参, 因为分页必须伴随着排序,不然可能出现同一个对象重复出现在不同页,有的对象不被查询到的情况, 所以分页查询的入参必须包含排序条件。
用户可继承 `PagingAndSortingQueryParams` 构建自己的分页查询入参,需在构造器中调用 `PagingAndSortingQueryParams` 的构造器,传入一个 Map 作为白名单, key 是供前端指定用于排序的**属性名**value 是对应数据库中的**字段名**,只有在白名单中指定的属性名才允许作为排序条件。
`PagingAndSortingQueryParams` 包含三个主要的属性:
- **size** - 每页显示的记录数
- **pageNum** - 当前页码
- **orderBy** - 排序条件
其中 `orderBy` 是一个 List可以指定多个排序条件每个排序条件是一个字符串 格式为“**属性名-ASC**”或“**属性名-DESC**”,分别表示升序和降序。
比如前端传入的 orderBy 为 ["name-ASC","age-DESC"],意味着要按 name 进行升序name 相同的情况下则按 age 进行降序。
使用时调用 `PagingAndSortingQueryParams#buildPagingParams()` 方法获取分页参数 `PagingParams`。
分页结果可以存放到 `PageResult` 中,作为出参。
#### 2. UnifiedResponse
UnifiedResponse 对返回给前端的数据进行封装,包含 `code`、`message`、`data。`
可使用 `UnifiedResponses` 快速构建 `UnifiedResponse` 对象。 `UnifiedResponses` 默认的成功代码为 "2000000" 用户按测试类 `CustomUnifiedResponseFactoryTests` 中所示范的,继承 `UnifiedResponses` 实现自己的工厂类, 自定义 `SUCCESS_CODE` 和 `DEFAULT_SUCCESS_MSG` 和工厂方法。 见 [issue#22](http://zhouxy.xyz:3000/plusone/plusone-commons/issues/22)。
## 八、time - 时间 API
### 1. 季度
模仿 JDK 的 `java.time.Month` 和 `java.time.YearMonth` 实现 `Quarter`、`YearQuarter`,对季度进行建模。
## 九、util - 工具类
包含树构建器(`TreeBuilder`)、断言工具(`AssertTools`、ID 生成器(`IdGenerator`)及其它实用工具类。

48
pom.xml
View File

@@ -6,19 +6,35 @@
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-commons</artifactId>
<version>1.0.0-RC2</version>
<version>1.0.0</version>
<properties>
<!-- Basic properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<commons-lang3.version>3.17.0</commons-lang3.version>
<guava.version>33.3.1-jre</guava.version>
<!-- Versions of compile dependencies -->
<guava.version>33.4.0-jre</guava.version>
<joda-time.version>2.13.0</joda-time.version>
<!-- Versions of test dependencies -->
<commons-lang3.version>3.17.0</commons-lang3.version>
<logback.version>1.2.13</logback.version>
<junit.version>5.11.4</junit.version>
<lombok.version>1.18.36</lombok.version>
<hutool.version>5.8.35</hutool.version>
<mybatis.version>3.5.19</mybatis.version>
<h2.version>2.2.224</h2.version>
<jackson.version>2.18.2</jackson.version>
<gson.version>2.12.1</gson.version>
</properties>
<dependencies>
<!-- ========== Compile Dependencies ========== -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -32,7 +48,7 @@
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<!-- ========== Test Dependencies ========== -->
<dependency>
<groupId>org.apache.commons</groupId>
@@ -44,55 +60,55 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
<version>${logback.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.2</version>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
<version>${lombok.version}</version>
<optional>true</optional>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
<artifactId>hutool-core</artifactId>
<version>${hutool.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.17</version>
<version>${mybatis.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
<version>${h2.version}</version>
<scope>test</scope>
</dependency>
@@ -100,20 +116,20 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.5</version>
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.5</version>
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
<version>${gson.version}</version>
<scope>test</scope>
</dependency>

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>注解</h2>
*
* <h3>
* 1. {@link StaticFactoryMethod}
* </h3>
* <p>
* 标识<b>静态工厂方法</b>。
* 《Effective Java》的 Item1 建议考虑用静态工厂方法替换构造器,
* 因而考虑有一个注解可以标记一下静态工厂方法,以和其它方法进行区分。
* </p>
*
* <h3>
* 2. {@link ReaderMethod} 和 {@link WriterMethod}
* </h3>
* <p>
* 分别标识<b>读方法</b>(如 getter或<b>写方法</b>(如 setter
* </p>
* <p>
* 最早是写了一个集合类,为了方便判断使用读写锁时,哪些情况下使用读锁,哪些情况下使用写锁。
* </p>
*
* <h3>
* 3. {@link UnsupportedOperation}
* </h3>
* <p>
* 标识该方法不被支持或没有实现,将抛出 {@link UnsupportedOperationException}。
* 为了方便在使用时,不需要点进源码,就能知道该方法没有实现。
* </p>
*
* <h3>
* 4. {@link Virtual}
* </h3>
* <p>
* Java 非 final 的实例方法,对应 C++/C# 中的虚方法,允许被子类覆写。
* {@link Virtual} 注解旨在设计父类时,强调该方法父类虽然有默认实现,但子类可以根据自己的需要覆写。
* </p>
*
* <h3>
* 5. {@link ValueObject}
* </h3>
* <p>
* 标记一个类,表示其作为值对象,区别于 Entity。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.annotation;

View File

@@ -15,9 +15,9 @@
*/
/**
* 基础组件
* <h2>基础组件</h2>
*
* <h2>Ref</h2>
* <h3>1. Ref</h3>
* <p>
* {@link Ref} 包装了一个值,表示对该值的应用。
* </p>
@@ -61,11 +61,13 @@
* System.out.println(result); // Output: Return string
* </pre>
*
* <h2>IWithCode</h2>
* <h3>2. IWithCode</h3>
* <p>
* 类似于枚举之类的类,通常需要设置固定的码值表示对应的含义。
* 类似于枚举这样的类,通常需要设置固定的码值表示对应的含义。
* 可实现 {@link IWithCode}、{@link IWithIntCode}、{@link IWithLongCode},便于在需要的地方对这些接口的实现进行处理。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
@CheckReturnValue
@ParametersAreNonnullByDefault

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,11 @@ import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.RangeSet;
import com.google.common.collect.Table;
/**
* 集合工具类
*
@@ -33,9 +38,9 @@ import javax.annotation.Nullable;
*/
public class CollectionTools {
// TODO [添加] 新增其它集合类型,如 guava 的扩展集合等
// isEmpty
// ================================
// #region - isEmpty
// ================================
public static boolean isEmpty(@Nullable Collection<?> collection) {
return collection == null || collection.isEmpty();
@@ -45,7 +50,29 @@ public class CollectionTools {
return map == null || map.isEmpty();
}
// isNotEmpty
public static boolean isEmpty(@Nullable Table<?, ?, ?> table) {
return table == null || table.isEmpty();
}
public static boolean isEmpty(@Nullable Multimap<?, ?> map) {
return map == null || map.isEmpty();
}
public static boolean isEmpty(@Nullable Multiset<?> set) {
return set == null || set.isEmpty();
}
public static boolean isEmpty(@Nullable RangeSet<?> set) {
return set == null || set.isEmpty();
}
// ================================
// #endregion - isEmpty
// ================================
// ================================
// #region - isNotEmpty
// ================================
public static boolean isNotEmpty(@Nullable Collection<?> collection) {
return collection != null && !collection.isEmpty();
@@ -55,6 +82,30 @@ public class CollectionTools {
return map != null && !map.isEmpty();
}
public static boolean isNotEmpty(@Nullable Table<?, ?, ?> table) {
return table != null && !table.isEmpty();
}
public static boolean isNotEmpty(@Nullable Multimap<?, ?> map) {
return map != null && !map.isEmpty();
}
public static boolean isNotEmpty(@Nullable Multiset<?> set) {
return set != null && !set.isEmpty();
}
public static boolean isNotEmpty(@Nullable RangeSet<?> set) {
return set != null && !set.isEmpty();
}
// ================================
// #endregion - isNotEmpty
// ================================
// ================================
// #region - nullToEmpty
// ================================
@Nonnull
public static <T> List<T> nullToEmptyList(@Nullable List<T> list) {
return list == null ? Collections.emptyList() : list;
@@ -70,6 +121,10 @@ public class CollectionTools {
return map == null ? Collections.emptyMap() : map;
}
// ================================
// #endregion - nullToEmpty
// ================================
private CollectionTools() {
throw new IllegalStateException("Utility class");
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>集合<h2>
*
* <h3>
* 1. {@link CollectionTools}
* </h3>
* 集合工具类
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
@ParametersAreNonnullByDefault
package xyz.zhouxy.plusone.commons.collection;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ public final class PatternConsts {
*
* @see RegexConsts#EMAIL
*/
public static final Pattern EMAIL = Pattern.compile(RegexConsts.EMAIL);
public static final Pattern EMAIL = Pattern.compile(RegexConsts.EMAIL, Pattern.CASE_INSENSITIVE);
/**
* 中国大陆手机号

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,6 +32,9 @@ public final class RegexConsts {
public static final String CAPTCHA = "^\\w{4,6}$";
/**
* from https://emailregex.com/
*/
public static final String EMAIL
= "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")"
+ "@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])";

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>常量<h2>
*
* <h3>
* 1. 正则常量
* </h3>
* {@link RegexConsts} 包含常见正则表达式;{@link PatternConsts} 包含对应的 {@link Pattern} 对象。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.constant;
import java.util.regex.Pattern;

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 业务异常
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.exception.business;

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>异常<h2>
*
* <h3>1. {@link MultiTypesException} - 多类型异常</h3>
* <p>
* 异常在不同场景下被抛出,可以用不同的枚举值,表示不同的场景类型。
* </p>
* <p>
* 异常实现 {@link MultiTypesException} 的 {@link MultiTypesException#getType} 方法,返回对应的场景类型。
* </p>
* <p>
* 表示场景类型的枚举实现 {@link MultiTypesException.ExceptionType},其中的工厂方法用于创建类型对象。
* </p>
*
* <pre>
* public final class LoginException
* extends RuntimeException
* implements MultiTypesException&lt;LoginException, LoginException.Type&gt; {
* private final Type type;
* private LoginException(&#64;Nonnull Type type, &#64;Nonnull String message) {
* super(message);
* this.type = type;
* }
*
* private LoginException(&#64;Nonnull Type type, &#64;Nonnull Throwable cause) {
* super(cause);
* this.type = type;
* }
*
* private LoginException(&#64;Nonnull Type type,
* &#64;Nonnull String message,
* &#64;Nonnull Throwable cause) {
* super(message, cause);
* this.type = type;
* }
*
* &#64;Override
* public &#64;Nonnull Type getType() {
* return this.type;
* }
*
* // ...
*
* public enum Type implements ExceptionType<LoginException> {
* DEFAULT("00", "当前会话未登录"),
* NOT_TOKEN("10", "未提供token"),
* INVALID_TOKEN("20", "token无效"),
* TOKEN_TIMEOUT("30", "token已过期"),
* BE_REPLACED("40", "token已被顶下线"),
* KICK_OUT("50", "token已被踢下线"),
* ;
*
* &#64;Nonnull
* private final String code;
* &#64;Nonnull
* private final String defaultMessage;
*
* Type(&#64;Nonnull String code, &#64;Nonnull String defaultMessage) {
* this.code = code;
* this.defaultMessage = defaultMessage;
* }
*
* &#64;Override
* public &#64;Nonnull String getCode() {
* return code;
* }
*
* &#64;Override
* public &#64;Nonnull String getDefaultMessage() {
* return defaultMessage;
* }
*
* &#64;Override
* public &#64;Nonnull LoginException create() {
* return new LoginException(this, this.defaultMessage);
* }
*
* &#64;Override
* public &#64;Nonnull LoginException create(String message) {
* return new LoginException(this, message);
* }
*
* &#64;Override
* public &#64;Nonnull LoginException create(Throwable cause) {
* return new LoginException(this, cause);
* }
*
* &#64;Override
* public &#64;Nonnull LoginException create(String message, Throwable cause) {
* return new LoginException(this, message, cause);
* }
* }
* }
* </pre>
*
* 使用时,可以使用这种方式创建并抛出异常:
* <pre>
* throw LoginException.Type.TOKEN_TIMEOUT.create();
* </pre>
* </p>
*
* <h3>2. 业务异常</h3>
* 预设常见的业务异常。可继承 {@link BizException} 自定义业务异常。
*
* <h3>3. 系统异常</h3>
* 预设常见的系统异常。可继承 {@link SysException} 自定义系统异常。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.exception;
import xyz.zhouxy.plusone.commons.exception.business.*;
import xyz.zhouxy.plusone.commons.exception.system.*;

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 系统异常
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.exception.system;

View File

@@ -15,16 +15,16 @@
*/
/**
* 函数式编程
* <h2>函数式编程</h2>
*
* <h2>PredicateTools</h2>
* <h3>1. PredicateTools</h3>
* <p>
* {@link PredicateTools} 用于 {@link java.util.function.Predicate} 的相关操作。
* </p>
*
* <h2>Functional interfaces</h2>
* <h3>2. Functional interfaces</h3>
* <p>
* 补充一些 JDK 没有,而项目中可能用得上的函数式接口:
* 补充可能用得上的函数式接口:
* <pre>
* | Group | FunctionalInterface | method |
* | ------------- | -------------------- | -------------------------------- |
@@ -40,5 +40,7 @@
* | Optional | ToOptionalFunction | Optional&lt;R&gt; apply(T) |
* </pre>
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.function;

View File

@@ -22,10 +22,6 @@ import javax.annotation.Nullable;
/**
* 统一结果,对返回给前端的数据进行封装。
*
* <p>
* <b>SUCCESS: 2000000</b>
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public class UnifiedResponse<T> {
@@ -39,11 +35,11 @@ public class UnifiedResponse<T> {
// #region - Constructors
// ================================
private UnifiedResponse(String code, @Nullable String message) {
UnifiedResponse(String code, @Nullable String message) {
this(code, message, null);
}
private UnifiedResponse(String code, @Nullable String message, @Nullable T data) {
UnifiedResponse(String code, @Nullable String message, @Nullable T data) {
this.code = Objects.requireNonNull(code);
this.message = message == null ? "" : message;
this.data = data;
@@ -53,65 +49,6 @@ public class UnifiedResponse<T> {
// #endregion - Constructors
// ================================
public static final String SUCCESS_CODE = "2000000";
private static final String DEFAULT_SUCCESS_MSG = "SUCCESS";
// ================================
// #region - success
// ================================
public static UnifiedResponse<Void> success() {
return new UnifiedResponse<>(SUCCESS_CODE, DEFAULT_SUCCESS_MSG);
}
public static UnifiedResponse<Void> success(@Nullable String message) {
return new UnifiedResponse<>(SUCCESS_CODE, message);
}
public static <T> UnifiedResponse<T> success(@Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(SUCCESS_CODE, message, data);
}
// ================================
// #endregion - success
// ================================
// ================================
// #region - error
// ================================
public static UnifiedResponse<Void> error(String code, @Nullable String message) {
return new UnifiedResponse<>(code, message);
}
public static <T> UnifiedResponse<T> error(String code, @Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(code, message, data);
}
public static UnifiedResponse<Void> error(String code, Throwable e) {
return new UnifiedResponse<>(code, e.getMessage());
}
// ================================
// #endregion - error
// ================================
// ================================
// #region - of
// ================================
public static UnifiedResponse<Void> of(String code, @Nullable String message) {
return new UnifiedResponse<>(code, message);
}
public static <T> UnifiedResponse<T> of(String code, @Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(code, message, data);
}
// ================================
// #endregion - of
// ================================
// ================================
// #region - Getters
// ================================

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.zhouxy.plusone.commons.model.dto;
import javax.annotation.Nullable;
/**
* UnifiedResponse 工厂
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
* @since 1.0.0
* @see UnifiedResponse
*/
public class UnifiedResponses {
public static final String SUCCESS_CODE = "2000000";
public static final String DEFAULT_SUCCESS_MSG = "SUCCESS";
// ================================
// #region - success
// ================================
public static UnifiedResponse<Void> success() {
return new UnifiedResponse<>(SUCCESS_CODE, DEFAULT_SUCCESS_MSG);
}
public static UnifiedResponse<Void> success(@Nullable String message) {
return new UnifiedResponse<>(SUCCESS_CODE, message);
}
public static <T> UnifiedResponse<T> success(@Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(SUCCESS_CODE, message, data);
}
// ================================
// #endregion - success
// ================================
// ================================
// #region - error
// ================================
public static UnifiedResponse<Void> error(String code, @Nullable String message) {
return new UnifiedResponse<>(code, message);
}
public static <T> UnifiedResponse<T> error(String code, @Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(code, message, data);
}
public static UnifiedResponse<Void> error(String code, Throwable e) {
return new UnifiedResponse<>(code, e.getMessage());
}
// ================================
// #endregion - error
// ================================
// ================================
// #region - of
// ================================
public static UnifiedResponse<Void> of(String code, @Nullable String message) {
return new UnifiedResponse<>(code, message);
}
public static <T> UnifiedResponse<T> of(String code, @Nullable String message, @Nullable T data) {
return new UnifiedResponse<>(code, message, data);
}
// ================================
// #endregion - of
// ================================
protected UnifiedResponses() {
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>数据传输对象</h2>
*
* <h3>1. 分页</h3>
* <p>
* 分页组件由 {@link PagingAndSortingQueryParams} 作为入参,
* 因为分页必须伴随着排序,不然可能出现同一个对象重复出现在不同页,有的对象不被查询到的情况,
* 所以分页查询的入参必须包含排序条件。
* </p>
* <p>
* 用户可继承 {@link PagingAndSortingQueryParams}
* 构建自己的分页查询入参,需在构造器中调用 {@link PagingAndSortingQueryParams} 的构造器,传入一个 Map 作为白名单,
* key 是供前端指定用于排序的属性名value 是对应数据库中的字段名,只有在白名单中指定的属性名才允许作为排序条件。
* </p>
* <p>
* {@link PagingAndSortingQueryParams} 包含三个主要的属性:
* <ul>
* <li>size - 每页显示的记录数</li>
* <li>pageNum - 当前页码</li>
* <li>orderBy - 排序条件</li>
* </ul>
* 其中 orderBy 是一个 List可以指定多个排序条件每个排序条件是一个字符串
* 格式为“属性名-ASC”或“属性名-DESC”分别表示升序和降序。
* </p>
* <p>
* 比如前端传入的 orderBy 为 ["name-ASC","age-DESC"],意味着要按 name 进行升序name 相同的情况下则按 age 进行降序。
* </p>
* <p>
* 使用时调用 {@link PagingAndSortingQueryParams#buildPagingParams()} 方法获取分页参数 {@link PagingParams}。
* </p>
* <p>
* 分页结果可以存放到 {@link PageResult} 中,作为出参。
* </p>
*
* <h3>2. {@link UnifiedResponse}</h3>
* <p>
* {@link UnifiedResponse} 对返回给前端的数据进行封装,包含 code、message、data。
* </p>
* <p>
* 可使用 {@link UnifiedResponses} 快速构建 {@link UnifiedResponse} 对象。
* {@link UnifiedResponses} 默认的成功代码为 "2000000"
* 用户按测试类 CustomUnifiedResponseFactoryTests 中所示范的,继承 {@link UnifiedResponses}
* 实现自己的工厂类,
* 自定义 SUCCESS_CODE 和 DEFAULT_SUCCESS_MSG 和工厂方法。
* 见 <a href="http://zhouxy.xyz:3000/plusone/plusone-commons/issues/22">issue#22</a>。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.model.dto;

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>业务建模组件</h2>
* <p>
* 包含业务建模可能用到的性别、身份证等元素,也包含 DTO 相关类,如分页查询参数,响应结果,分页结果等。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
package xyz.zhouxy.plusone.commons.model;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -86,6 +86,7 @@ public enum Quarter implements IWithIntCode {
*/
@StaticFactoryMethod(Quarter.class)
public static Quarter fromMonth(Month month) {
AssertTools.checkNotNull(month);
final int monthValue = month.getValue();
return of(computeQuarterValueInternal(monthValue));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@ import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.errorprone.annotations.Immutable;
@@ -52,7 +52,7 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
/** 季度结束日期 */
private final LocalDate lastDate;
private YearQuarter(int year, @Nonnull Quarter quarter) {
private YearQuarter(int year, Quarter quarter) {
this.year = year;
this.quarter = quarter;
this.firstDate = quarter.firstMonthDay().atYear(year);
@@ -249,7 +249,7 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
}
@Override
public boolean equals(Object obj) {
public boolean equals(@Nullable Object obj) {
if (this == obj)
return true;
if (obj == null)
@@ -264,6 +264,7 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
// #region - compare
@SuppressWarnings("null")
@Override
public int compareTo(YearQuarter other) {
int cmp = (this.year - other.year);
@@ -281,14 +282,6 @@ public final class YearQuarter implements Comparable<YearQuarter>, Serializable
return this.compareTo(other) > 0;
}
public static YearQuarter min(YearQuarter yearQuarter1, YearQuarter yearQuarter2) {
return yearQuarter1.compareTo(yearQuarter2) <= 0 ? yearQuarter1 : yearQuarter2;
}
public static YearQuarter max(YearQuarter yearQuarter1, YearQuarter yearQuarter2) {
return yearQuarter1.compareTo(yearQuarter2) >= 0 ? yearQuarter1 : yearQuarter2;
}
// #endregion
// #region - toString

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>时间 API<h2>
*
* <h3>1. 季度 API</h3>
*
* 模仿 JDK 的 {@link java.time.Month} 和 {@link java.time.YearMonth}
* 实现 {@link Quarter}{@link YearQuarter},对季度进行建模。
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
@ParametersAreNonnullByDefault
package xyz.zhouxy.plusone.commons.time;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,6 @@ import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
@@ -259,7 +258,7 @@ public class ArrayTools {
*
* @throws IllegalArgumentException 当参数为空时抛出
*/
public static <T> boolean isAllElementsNotNull(@Nonnull final T[] arr) {
public static <T> boolean isAllElementsNotNull(final T[] arr) {
AssertTools.checkArgument(arr != null, "The array cannot be null.");
return Arrays.stream(arr).allMatch(Objects::nonNull);
}
@@ -622,15 +621,15 @@ public class ArrayTools {
// fill - char
public static void fill(char[] a, char[] values) {
public static void fill(char[] a, @Nullable char[] values) {
fill(a, 0, a.length, values);
}
public static void fill(char[] a, String values) {
public static void fill(char[] a, @Nullable String values) {
fill(a, 0, a.length, values != null ? values.toCharArray() : EMPTY_CHAR_ARRAY);
}
public static void fill(char[] a, int fromIndex, int toIndex, char[] values) {
public static void fill(char[] a, int fromIndex, int toIndex, @Nullable char[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -655,11 +654,11 @@ public class ArrayTools {
// fill - byte
public static void fill(byte[] a, byte[] values) {
public static void fill(byte[] a, @Nullable byte[] values) {
fill(a, 0, a.length, values);
}
public static void fill(byte[] a, int fromIndex, int toIndex, byte[] values) {
public static void fill(byte[] a, int fromIndex, int toIndex, @Nullable byte[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -684,11 +683,11 @@ public class ArrayTools {
// fill - short
public static void fill(short[] a, short[] values) {
public static void fill(short[] a, @Nullable short[] values) {
fill(a, 0, a.length, values);
}
public static void fill(short[] a, int fromIndex, int toIndex, short[] values) {
public static void fill(short[] a, int fromIndex, int toIndex, @Nullable short[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -713,11 +712,11 @@ public class ArrayTools {
// fill - int
public static void fill(int[] a, int[] values) {
public static void fill(int[] a, @Nullable int[] values) {
fill(a, 0, a.length, values);
}
public static void fill(int[] a, int fromIndex, int toIndex, int[] values) {
public static void fill(int[] a, int fromIndex, int toIndex, @Nullable int[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -742,11 +741,11 @@ public class ArrayTools {
// fill - long
public static void fill(long[] a, long[] values) {
public static void fill(long[] a, @Nullable long[] values) {
fill(a, 0, a.length, values);
}
public static void fill(long[] a, int fromIndex, int toIndex, long[] values) {
public static void fill(long[] a, int fromIndex, int toIndex, @Nullable long[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -771,11 +770,11 @@ public class ArrayTools {
// fill - float
public static void fill(float[] a, float[] values) {
public static void fill(float[] a, @Nullable float[] values) {
fill(a, 0, a.length, values);
}
public static void fill(float[] a, int fromIndex, int toIndex, float[] values) {
public static void fill(float[] a, int fromIndex, int toIndex, @Nullable float[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -800,11 +799,11 @@ public class ArrayTools {
// fill - double
public static void fill(double[] a, double[] values) {
public static void fill(double[] a, @Nullable double[] values) {
fill(a, 0, a.length, values);
}
public static void fill(double[] a, int fromIndex, int toIndex, double[] values) {
public static void fill(double[] a, int fromIndex, int toIndex, @Nullable double[] values) {
AssertTools.checkArgument(Objects.nonNull(a));
if (values == null || values.length == 0) {
return;
@@ -829,15 +828,15 @@ public class ArrayTools {
// fill - T
public static <T> void fill(@Nonnull T[] a, T[] values) {
public static <T> void fill(T[] a, @Nullable T[] values) {
fillInternal(a, 0, a.length, values);
}
public static <T> void fill(@Nonnull T[] a, int fromIndex, int toIndex, T[] values) {
public static <T> void fill(T[] a, int fromIndex, int toIndex, @Nullable T[] values) {
fillInternal(a, fromIndex, toIndex, values);
}
private static <T> void fillInternal(@Nonnull 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));
if (values == null || values.length == 0) {
return;
@@ -864,9 +863,9 @@ public class ArrayTools {
// #region - indexOf
public static <T> int indexOfWithPredicate(T[] arr, Predicate<? super T> predicate) {
public static <T> int indexOfWithPredicate(@Nullable T[] arr, Predicate<? super T> predicate) {
AssertTools.checkNotNull(predicate);
if (isNullOrEmpty(arr)) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -877,12 +876,12 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static <T> int indexOf(T[] arr, T obj) {
public static <T> int indexOf(@Nullable T[] arr, @Nullable T obj) {
return indexOfWithPredicate(arr, item -> Objects.equals(item, obj));
}
public static int indexOf(char[] arr, char value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable char[] arr, char value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -893,8 +892,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int indexOf(byte[] arr, byte value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable byte[] arr, byte value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -905,8 +904,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int indexOf(short[] arr, short value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable short[] arr, short value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -917,8 +916,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int indexOf(int[] arr, int value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable int[] arr, int value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -929,8 +928,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int indexOf(long[] arr, long value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable long[] arr, long value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -941,8 +940,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int indexOf(float[] arr, float value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable float[] arr, float value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -953,8 +952,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int indexOf(double[] arr, double value) {
if (isNullOrEmpty(arr)) {
public static int indexOf(@Nullable double[] arr, double value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = 0; i < arr.length; i++) {
@@ -969,9 +968,9 @@ public class ArrayTools {
// #region - lastIndexOf
public static <T> int lastIndexOfWithPredicate(T[] arr, @Nonnull Predicate<? super T> predicate) {
public static <T> int lastIndexOfWithPredicate(@Nullable T[] arr, Predicate<? super T> predicate) {
AssertTools.checkNotNull(predicate);
if (isNullOrEmpty(arr)) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -986,8 +985,8 @@ public class ArrayTools {
return lastIndexOfWithPredicate(arr, item -> Objects.equals(item, obj));
}
public static int lastIndexOf(char[] arr, char value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable char[] arr, char value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -998,8 +997,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int lastIndexOf(byte[] arr, byte value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable byte[] arr, byte value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -1010,8 +1009,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int lastIndexOf(short[] arr, short value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable short[] arr, short value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -1022,8 +1021,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int lastIndexOf(int[] arr, int value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable int[] arr, int value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -1034,8 +1033,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int lastIndexOf(long[] arr, long value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable long[] arr, long value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -1046,8 +1045,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int lastIndexOf(float[] arr, float value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable float[] arr, float value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -1058,8 +1057,8 @@ public class ArrayTools {
return NOT_FOUND_INDEX;
}
public static int lastIndexOf(double[] arr, double value) {
if (isNullOrEmpty(arr)) {
public static int lastIndexOf(@Nullable double[] arr, double value) {
if (arr == null || arr.length == 0) {
return NOT_FOUND_INDEX;
}
for (int i = arr.length - 1; i >= 0; i--) {
@@ -1074,39 +1073,39 @@ public class ArrayTools {
// #region - contains
public static <T> boolean contains(T[] arr, T obj) {
public static <T> boolean contains(@Nullable T[] arr, @Nullable T obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(char[] arr, char obj) {
public static boolean contains(@Nullable char[] arr, char obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(byte[] arr, byte obj) {
public static boolean contains(@Nullable byte[] arr, byte obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(short[] arr, short obj) {
public static boolean contains(@Nullable short[] arr, short obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(int[] arr, int obj) {
public static boolean contains(@Nullable int[] arr, int obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(long[] arr, long obj) {
public static boolean contains(@Nullable long[] arr, long obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(float[] arr, float obj) {
public static boolean contains(@Nullable float[] arr, float obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean contains(double[] arr, double obj) {
public static boolean contains(@Nullable double[] arr, double obj) {
return indexOf(arr, obj) > NOT_FOUND_INDEX;
}
public static boolean containsValue(BigDecimal[] arr, BigDecimal obj) {
public static boolean containsValue(@Nullable BigDecimal[] arr, @Nullable BigDecimal obj) {
return indexOfWithPredicate(arr, item -> BigDecimals.equalsValue(item, obj)) > NOT_FOUND_INDEX;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.commons.exception.DataNotExistsException;
import xyz.zhouxy.plusone.commons.exception.system.DataOperationResultException;
@@ -55,12 +55,12 @@ public class AssertTools {
}
/** Throw {@link IllegalArgumentException} if the {@code condition} is false. */
public static void checkArgument(boolean condition, String errMsg) {
public static void checkArgument(boolean condition, @Nullable String errMsg) {
checkCondition(condition, () -> new IllegalArgumentException(errMsg));
}
/** Throw {@link IllegalArgumentException} if the {@code condition} is false. */
public static void checkArgument(boolean condition, @Nonnull Supplier<String> messageSupplier) {
public static void checkArgument(boolean condition, Supplier<String> messageSupplier) {
checkCondition(condition, () -> new IllegalArgumentException(messageSupplier.get()));
}
@@ -83,12 +83,12 @@ public class AssertTools {
}
/** Throw {@link IllegalStateException} if the {@code condition} is false. */
public static void checkState(boolean condition, String errMsg) {
public static void checkState(boolean condition, @Nullable String errMsg) {
checkCondition(condition, () -> new IllegalStateException(errMsg));
}
/** Throw {@link IllegalStateException} if the {@code condition} is false. */
public static void checkState(boolean condition, @Nonnull Supplier<String> messageSupplier) {
public static void checkState(boolean condition, Supplier<String> messageSupplier) {
checkCondition(condition, () -> new IllegalStateException(messageSupplier.get()));
}
@@ -106,22 +106,22 @@ public class AssertTools {
// ================================
/** Throw {@link NullPointerException} if the {@code obj} is null. */
public static <T> void checkNotNull(T obj) {
public static <T> void checkNotNull(@Nullable T obj) {
checkCondition(obj != null, NullPointerException::new);
}
/** Throw {@link NullPointerException} if the {@code obj} is null. */
public static <T> void checkNotNull(T obj, String errMsg) {
public static <T> void checkNotNull(@Nullable T obj, String errMsg) {
checkCondition(obj != null, () -> new NullPointerException(errMsg));
}
/** Throw {@link NullPointerException} if the {@code obj} is null. */
public static <T> void checkNotNull(T obj, @Nonnull Supplier<String> messageSupplier) {
public static <T> void checkNotNull(@Nullable T obj, Supplier<String> messageSupplier) {
checkCondition(obj != null, () -> new NullPointerException(messageSupplier.get()));
}
/** Throw {@link NullPointerException} if the {@code obj} is null. */
public static <T> void checkNotNull(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)));
}
@@ -134,56 +134,56 @@ public class AssertTools {
// ================================
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */
public static <T> T checkExists(T obj)
public static <T> T checkExists(@Nullable T obj)
throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), DataNotExistsException::new);
return obj;
}
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */
public static <T> T checkExists(T obj, String message)
public static <T> T checkExists(@Nullable T obj, String message)
throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(message));
return obj;
}
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */
public static <T> T checkExists(T obj, @Nonnull Supplier<String> messageSupplier)
public static <T> T checkExists(@Nullable T obj, Supplier<String> messageSupplier)
throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(messageSupplier.get()));
return obj;
}
/** Throw {@link DataNotExistsException} if the {@code obj} is null. */
public static <T> T checkExists(T obj, String format, Object... args)
public static <T> T checkExists(@Nullable T obj, String format, Object... args)
throws DataNotExistsException {
checkCondition(Objects.nonNull(obj), () -> new DataNotExistsException(String.format(format, args)));
return obj;
}
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */
public static <T> T checkExists(@Nonnull Optional<T> optional)
public static <T> T checkExists(Optional<T> optional)
throws DataNotExistsException {
checkCondition(optional.isPresent(), DataNotExistsException::new);
return optional.get();
}
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */
public static <T> T checkExists(@Nonnull Optional<T> optional, String message)
public static <T> T checkExists(Optional<T> optional, String message)
throws DataNotExistsException {
checkCondition(optional.isPresent(), () -> new DataNotExistsException(message));
return optional.get();
}
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */
public static <T> T checkExists(@Nonnull Optional<T> optional, @Nonnull Supplier<String> messageSupplier)
public static <T> T checkExists(Optional<T> optional, Supplier<String> messageSupplier)
throws DataNotExistsException {
checkCondition(optional.isPresent(), () -> new DataNotExistsException(messageSupplier.get()));
return optional.get();
}
/** Throw {@link DataNotExistsException} if the {@code optional} is present. */
public static <T> T checkExists(@Nonnull Optional<T> optional, String format, Object... args)
public static <T> T checkExists(Optional<T> optional, String format, Object... args)
throws DataNotExistsException {
checkCondition(optional.isPresent(), () -> new DataNotExistsException(String.format(format, args)));
return optional.get();
@@ -202,17 +202,18 @@ public class AssertTools {
"The number of rows affected is expected to be %d, but is: %d", expectedValue, result);
}
public static void checkAffectedRows(int expectedValue, int result, String message) {
public static void checkAffectedRows(int expectedValue, int result, @Nullable String message) {
checkCondition(expectedValue == result, () -> new DataOperationResultException(message));
}
public static void checkAffectedRows(int expectedValue, int result,
@Nonnull Supplier<String> messageSupplier) {
Supplier<String> messageSupplier) {
checkCondition(expectedValue == result,
() -> new DataOperationResultException(messageSupplier.get()));
}
public static void checkAffectedRows(int expectedValue, int result, String format, Object... args) {
public static void checkAffectedRows(int expectedValue, int result,
String format, Object... args) {
checkCondition(expectedValue == result,
() -> new DataOperationResultException(String.format(format, args)));
}
@@ -222,17 +223,18 @@ public class AssertTools {
"The number of rows affected is expected to be %d, but is: %d", expectedValue, result);
}
public static void checkAffectedRows(long expectedValue, long result, String message) {
public static void checkAffectedRows(long expectedValue, long result, @Nullable String message) {
checkCondition(expectedValue == result, () -> new DataOperationResultException(message));
}
public static void checkAffectedRows(long expectedValue, long result,
@Nonnull Supplier<String> messageSupplier) {
Supplier<String> messageSupplier) {
checkCondition(expectedValue == result,
() -> new DataOperationResultException(messageSupplier.get()));
}
public static void checkAffectedRows(long expectedValue, long result, String format, Object... args) {
public static void checkAffectedRows(long expectedValue, long result,
String format, Object... args) {
checkCondition(expectedValue == result,
() -> new DataOperationResultException(String.format(format, args)));
}
@@ -246,7 +248,7 @@ public class AssertTools {
checkAffectedRows(1, result, message);
}
public static void checkAffectedOneRow(int result, @Nonnull Supplier<String> messageSupplier) {
public static void checkAffectedOneRow(int result, Supplier<String> messageSupplier) {
checkAffectedRows(1, result, messageSupplier);
}
@@ -263,11 +265,12 @@ public class AssertTools {
checkAffectedRows(1L, result, message);
}
public static void checkAffectedOneRow(long result, @Nonnull Supplier<String> messageSupplier) {
public static void checkAffectedOneRow(long result, Supplier<String> messageSupplier) {
checkAffectedRows(1L, result, messageSupplier);
}
public static void checkAffectedOneRow(long result, String format, Object... args) {
public static void checkAffectedOneRow(long result,
String format, Object... args) {
checkAffectedRows(1L, result, format, args);
}
@@ -279,7 +282,7 @@ public class AssertTools {
// #region - Condition
// ================================
public static <T extends Exception> void checkCondition(boolean condition, @Nonnull Supplier<T> e)
public static <T extends Exception> void checkCondition(boolean condition, Supplier<T> e)
throws T {
if (!condition) {
throw e.get();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,9 @@ public class BigDecimals {
}
public static boolean ge(BigDecimal a, BigDecimal b) {
return gt(a, b) || equalsValue(a, b);
AssertTools.checkNotNull(a, "Parameter could not be null.");
AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a == b) || (a.compareTo(b) >= 0);
}
public static boolean lt(BigDecimal a, BigDecimal b) {
@@ -56,7 +58,9 @@ public class BigDecimals {
}
public static boolean le(BigDecimal a, BigDecimal b) {
return lt(a, b) || equalsValue(a, b);
AssertTools.checkNotNull(a, "Parameter could not be null.");
AssertTools.checkNotNull(b, "Parameter could not be null.");
return (a == b) || (a.compareTo(b) <= 0);
}
public static BigDecimal sum(final BigDecimal... numbers) {
@@ -79,7 +83,7 @@ public class BigDecimals {
}
@StaticFactoryMethod(BigDecimal.class)
public static BigDecimal of(final String val) {
public static BigDecimal of(@Nullable final String val) {
return (StringTools.isNotBlank(val)) ? new BigDecimal(val) : BigDecimal.ZERO;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,7 +44,9 @@ import xyz.zhouxy.plusone.commons.time.YearQuarter;
*/
public class DateTimeTools {
// ================================
// #region - toString
// ================================
public static String toYearString(int year) {
return Integer.toString(YEAR.checkValidIntValue(year));
@@ -70,9 +72,13 @@ public class DateTimeTools {
return String.format("%02d", month.getValue());
}
// ================================
// #endregion
// ================================
// ================================
// #region - toDate
// ================================
/**
* 将时间戳转换为 {@link Date} 对象
@@ -137,9 +143,13 @@ public class DateTimeTools {
return Date.from(ZonedDateTime.of(localDate, localTime, zone).toInstant());
}
// ================================
// #endregion
// ================================
// ================================
// #region - toInstant
// ================================
/**
* 将时间戳转换为 {@link Instant} 对象
@@ -192,9 +202,13 @@ public class DateTimeTools {
return ZonedDateTime.of(localDateTime, zone).toInstant();
}
// ================================
// #endregion
// ================================
// ================================
// #region - toZonedDateTime
// ================================
/**
* 获取时间戳在指定时区的地区时间。
@@ -285,9 +299,13 @@ public class DateTimeTools {
return ZonedDateTime.of(localDateTime, zone);
}
// ================================
// #endregion
// ================================
// ================================
// #region - toLocalDateTime
// ================================
/**
* 获取时间戳在指定时区的地区时间。
@@ -355,11 +373,13 @@ public class DateTimeTools {
return LocalDateTime.ofInstant(zonedDateTime.toInstant(), zone);
}
// ================================
// #endregion
// ================================
// ====================
// ================================
// #region - toJodaInstant
// ================================
/**
* 将 {@link java.time.Instant} 转换为 {@link org.joda.time.Instant}
@@ -392,9 +412,13 @@ public class DateTimeTools {
return toJodaInstant(java.time.ZonedDateTime.of(localDateTime, zone));
}
// ================================
// #endregion
// ================================
// ================================
// #region - toJavaInstant
// ================================
/**
* 将 {@link org.joda.time.Instant} 对象转换为 {@link java.time.Instant} 对象
@@ -432,9 +456,13 @@ public class DateTimeTools {
return toJavaInstant(localDateTime.toDateTime(zone));
}
// ================================
// #endregion
// ================================
// ================================
// #region - toJodaDateTime
// ================================
/**
* 将 Java 中表示日期时间的 {@link java.time.ZonedDateTime} 对象
@@ -479,9 +507,13 @@ public class DateTimeTools {
return toJodaInstant(instant).toDateTime(dateTimeZone);
}
// ================================
// #endregion
// ================================
// ================================
// #region - toZonedDateTime
// ================================
/**
* 将 joda-time 中带时区的日期时间,转换为 java.time 中带时区的日期时间
@@ -511,7 +543,7 @@ public class DateTimeTools {
}
/**
* 获取 joda-time 中的 {@link org.joda.time.Instant} 在指定时区的时间,用 Java 8
* 获取 joda-time 中的 {@link org.joda.time.Instant} 在指定时区的时间,用 Java 8+ 的
* {@link java.time.ZonedDateTime} 表示
*
* @param instant joda-time 中的时间戳
@@ -525,9 +557,13 @@ public class DateTimeTools {
return toJavaInstant(instant).atZone(zone);
}
// ================================
// #endregion
// ================================
// ================================
// #region - toJodaLocalDateTime
// ================================
/**
* 将 {@link java.time.LocalDateTime} 转换为 {@link org.joda.time.LocalDateTime}
@@ -541,9 +577,13 @@ public class DateTimeTools {
return toJodaInstant(localDateTime, javaZone).toDateTime(jodaZone).toLocalDateTime();
}
// ================================
// #endregion
// ================================
// ================================
// #region - toJavaLocalDateTime
// ================================
/**
* 将 {@link org.joda.time.LocalDateTime} 转换为 {@link java.time.LocalDateTime}
@@ -557,9 +597,13 @@ public class DateTimeTools {
return toJavaInstant(localDateTime, jodaZone).atZone(javaZone).toLocalDateTime();
}
// ================================
// #endregion
// ================================
// ================================
// #region - ZoneId <--> DateTimeZone
// ================================
/**
* 转换 Java API 和 joda-time API 表示时区的对象
@@ -581,9 +625,13 @@ public class DateTimeTools {
return org.joda.time.DateTimeZone.forID(zone.getId());
}
// ================================
// #endregion
// ================================
// ================================
// #region - YearQuarter & Quarter
// ================================
/**
* 获取指定日期所在季度
@@ -646,10 +694,12 @@ public class DateTimeTools {
return YearQuarter.of(date);
}
// ================================
// #endregion
// ================================
// ================================
// #region - others
// #region - start & end
// ================================
public static LocalDate startDateOfYear(int year) {
@@ -668,6 +718,86 @@ public class DateTimeTools {
return date.plusDays(1L).atStartOfDay(zone);
}
// ================================
// #endregion - start & end
// ================================
// ================================
// #region - isFuture
// ================================
public static boolean isFuture(Date date) {
return date.after(new Date());
}
public static boolean isFuture(Calendar date) {
return date.after(Calendar.getInstance());
}
public static boolean isFuture(Instant instant) {
return instant.isAfter(Instant.now());
}
public static boolean isFuture(long timeMillis) {
return timeMillis > System.currentTimeMillis();
}
public static boolean isFuture(LocalDate date) {
return date.isAfter(LocalDate.now());
}
public static boolean isFuture(LocalDateTime dateTime) {
return dateTime.isAfter(LocalDateTime.now());
}
public static boolean isFuture(ZonedDateTime dateTime) {
return dateTime.isAfter(ZonedDateTime.now());
}
// ================================
// #endregion - isFuture
// ================================
// ================================
// #region - isPast
// ================================
public static boolean isPast(Date date) {
return date.before(new Date());
}
public static boolean isPast(Calendar date) {
return date.before(Calendar.getInstance());
}
public static boolean isPast(Instant instant) {
return instant.isBefore(Instant.now());
}
public static boolean isPast(long timeMillis) {
return timeMillis < System.currentTimeMillis();
}
public static boolean isPast(LocalDate date) {
return date.isBefore(LocalDate.now());
}
public static boolean isPast(LocalDateTime dateTime) {
return dateTime.isBefore(LocalDateTime.now());
}
public static boolean isPast(ZonedDateTime dateTime) {
return dateTime.isBefore(ZonedDateTime.now());
}
// ================================
// #endregion - isPast
// ================================
// ================================
// #region - others
// ================================
public static Range<LocalDateTime> toDateTimeRange(LocalDate date) {
return Range.closedOpen(date.atStartOfDay(), startOfNextDate(date));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package xyz.zhouxy.plusone.commons.util;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
@@ -42,7 +41,7 @@ public final class EnumTools {
* @deprecated 不推荐使用枚举的 ordinal。
*/
@Deprecated
private static <E extends Enum<?>> E valueOfInternal(@Nonnull Class<E> enumType, int ordinal) { // NOSONAR 该方法弃用,但不删掉
private static <E extends Enum<?>> E valueOfInternal(Class<E> enumType, int ordinal) { // NOSONAR 该方法弃用,但不删掉
E[] values = enumType.getEnumConstants();
AssertTools.checkCondition((ordinal >= 0 && ordinal < values.length),
() -> new EnumConstantNotPresentException(enumType, Integer.toString(ordinal)));
@@ -76,7 +75,7 @@ public final class EnumTools {
*/
@Deprecated
public static <E extends Enum<?>> E valueOf(Class<E> enumType, // NOSONAR 该方法弃用,但不删掉
@Nullable Integer ordinal, E defaultValue) {
@Nullable Integer ordinal, @Nullable E defaultValue) {
AssertTools.checkNotNull(enumType);
return null == ordinal ? defaultValue : valueOfInternal(enumType, ordinal);
}
@@ -190,7 +189,7 @@ public final class EnumTools {
*/
@Nullable
private static <E extends Enum<?>> Integer checkOrdinalOrDefaultInternal(
@Nonnull Class<E> enumType,
Class<E> enumType,
@Nullable Integer ordinal,
@Nullable Integer defaultValue) {
return ordinal != null
@@ -200,9 +199,9 @@ public final class EnumTools {
@Nullable
private static <E extends Enum<?>> Integer checkOrdinalOrGetInternal(
@Nonnull Class<E> enumType,
Class<E> enumType,
@Nullable Integer ordinal,
@Nonnull Supplier<Integer> defaultValueSupplier) {
Supplier<Integer> defaultValueSupplier) {
return ordinal != null
? checkOrdinal(enumType, ordinal)
: defaultValueSupplier.get();

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2022-2024 the original author or authors.
* Copyright 2022-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.commons.annotation.StaticFactoryMethod;
/**
@@ -54,6 +56,7 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
return name;
}
@SuppressWarnings("null")
@Override
public final int compareTo(final T o) {
return Integer.compare(this.id, o.id);
@@ -65,7 +68,7 @@ public abstract class Enumeration<T extends Enumeration<T>> // NOSONAR 暂不移
}
@Override
public final boolean equals(final Object obj) {
public final boolean equals(@Nullable final Object obj) {
if (this == obj)
return true;
if (obj == null)

View File

@@ -22,6 +22,8 @@ import java.util.Enumeration;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import xyz.zhouxy.plusone.commons.exception.system.NoAvailableMacFoundException;
/**
@@ -114,7 +116,7 @@ public class IdWorker {
* init workerId
* @param workerId if null, then auto generate one
*/
private void initWorkerId(Long workerId) {
private void initWorkerId(@Nullable Long workerId) {
if (workerId == null) {
workerId = generateWorkerId();
}

View File

@@ -26,7 +26,7 @@ import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
/**
* OptionalUtil
* OptionalTools
*
* <p>
* 提供一些 Optional 相关的方法

View File

@@ -22,8 +22,6 @@ import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import javax.annotation.Nonnull;
/**
* 随机工具类
* <p>
@@ -68,20 +66,20 @@ public final class RandomTools {
* @param length 字符串长度
* @return 随机字符串
*/
public static String randomStr(@Nonnull Random random, @Nonnull char[] sourceCharacters, int length) {
public static String randomStr(Random random, char[] sourceCharacters, int length) {
AssertTools.checkArgument(Objects.nonNull(random), "Random cannot be null.");
AssertTools.checkArgument(Objects.nonNull(sourceCharacters), "Source characters cannot be null.");
AssertTools.checkArgument(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(random, sourceCharacters, length);
}
public static String randomStr(@Nonnull char[] sourceCharacters, int length) {
public static String randomStr(char[] sourceCharacters, int length) {
AssertTools.checkArgument(Objects.nonNull(sourceCharacters), "Source characters cannot be null.");
AssertTools.checkArgument(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(ThreadLocalRandom.current(), sourceCharacters, length);
}
public static String secureRandomStr(@Nonnull char[] sourceCharacters, int length) {
public static String secureRandomStr(char[] sourceCharacters, int length) {
AssertTools.checkArgument(Objects.nonNull(sourceCharacters), "Source characters cannot be null.");
AssertTools.checkArgument(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
@@ -97,20 +95,20 @@ public final class RandomTools {
* @param length 字符串长度
* @return 随机字符串
*/
public static String randomStr(@Nonnull Random random, @Nonnull String sourceCharacters, int length) {
public static String randomStr(Random random, String sourceCharacters, int length) {
AssertTools.checkArgument(Objects.nonNull(random), "Random cannot be null.");
AssertTools.checkArgument(Objects.nonNull(sourceCharacters), "Source characters cannot be null.");
AssertTools.checkArgument(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(random, sourceCharacters, length);
}
public static String randomStr(@Nonnull String sourceCharacters, int length) {
public static String randomStr(String sourceCharacters, int length) {
AssertTools.checkArgument(Objects.nonNull(sourceCharacters), "Source characters cannot be null.");
AssertTools.checkArgument(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(ThreadLocalRandom.current(), sourceCharacters, length);
}
public static String secureRandomStr(@Nonnull String sourceCharacters, int length) {
public static String secureRandomStr(String sourceCharacters, int length) {
AssertTools.checkArgument(Objects.nonNull(sourceCharacters), "Source characters cannot be null.");
AssertTools.checkArgument(length >= 0, "The length should be greater than or equal to zero.");
return randomStrInternal(DEFAULT_SECURE_RANDOM, sourceCharacters, length);
@@ -126,7 +124,7 @@ public final class RandomTools {
* @param length 字符串长度
* @return 随机字符串
*/
private static String randomStrInternal(@Nonnull Random random, @Nonnull char[] sourceCharacters, int length) {
private static String randomStrInternal(Random random, char[] sourceCharacters, int length) {
if (length == 0) {
return StringTools.EMPTY_STRING;
}
@@ -147,7 +145,7 @@ public final class RandomTools {
* @param length 字符串长度
* @return 随机字符串
*/
private static String randomStrInternal(@Nonnull Random random, @Nonnull String sourceCharacters, int length) {
private static String randomStrInternal(Random random, String sourceCharacters, int length) {
if (length == 0) {
return StringTools.EMPTY_STRING;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -272,7 +272,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例
*/
@Nonnull
private static Pattern cacheAndGetPatternInternal(@Nonnull final String pattern) {
private static Pattern cacheAndGetPatternInternal(final String pattern) {
if (PATTERN_CACHE.size() < MAX_CACHE_SIZE) {
return PATTERN_CACHE.computeIfAbsent(pattern, Pattern::compile);
}
@@ -290,7 +290,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例
*/
@Nonnull
private static Pattern getPatternInternal(@Nonnull final String pattern) {
private static Pattern getPatternInternal(final String pattern) {
Pattern result = PATTERN_CACHE.get(pattern);
if (result == null) {
result = Pattern.compile(pattern);
@@ -305,7 +305,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例数组
*/
@Nonnull
private static Pattern[] cacheAndGetPatternsInternal(@Nonnull final String[] patterns) {
private static Pattern[] cacheAndGetPatternsInternal(final String[] patterns) {
return Arrays.stream(patterns)
.map(RegexTools::cacheAndGetPatternInternal)
.toArray(Pattern[]::new);
@@ -318,7 +318,7 @@ public final class RegexTools {
* @return {@link Pattern} 实例数组
*/
@Nonnull
private static Pattern[] getPatternsInternal(@Nonnull final String[] patterns) {
private static Pattern[] getPatternsInternal(final String[] patterns) {
return Arrays.stream(patterns)
.map(RegexTools::getPatternInternal)
.toArray(Pattern[]::new);
@@ -331,17 +331,17 @@ public final class RegexTools {
* @param pattern 正则
* @return 判断结果
*/
private static boolean matchesInternal(@Nullable final CharSequence input, @Nonnull final Pattern pattern) {
private static boolean matchesInternal(@Nullable final CharSequence input, final Pattern pattern) {
return input != null && pattern.matcher(input).matches();
}
private static boolean matchesOneInternal(@Nullable final CharSequence input, @Nonnull final Pattern[] patterns) {
private static boolean matchesOneInternal(@Nullable final CharSequence input, final Pattern[] patterns) {
return input != null
&& Arrays.stream(patterns)
.anyMatch(pattern -> pattern.matcher(input).matches());
}
private static boolean matchesAllInternal(@Nullable final CharSequence input, @Nonnull final Pattern[] patterns) {
private static boolean matchesAllInternal(@Nullable final CharSequence input, final Pattern[] patterns) {
return input != null
&& Arrays.stream(patterns)
.allMatch(pattern -> pattern.matcher(input).matches());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,16 +16,30 @@
package xyz.zhouxy.plusone.commons.util;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Objects;
import javax.annotation.Nullable;
import com.google.common.annotations.Beta;
@Beta
import xyz.zhouxy.plusone.commons.constant.PatternConsts;
/**
* StringTools
*
* <p>
* 字符串工具类。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
public class StringTools {
public static final String EMPTY_STRING = "";
public static boolean isNotBlank(final String cs) {
public static boolean isNotBlank(@Nullable final String cs) {
if (cs == null || cs.isEmpty()) {
return false;
}
@@ -37,15 +51,50 @@ public class StringTools {
return false;
}
public static boolean isBlank(@Nullable String cs) {
if (cs == null || cs.isEmpty()) {
return true;
}
for (int i = 0; i < cs.length(); i++) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
}
public static String repeat(String str, int times) {
return repeat(str, times, Integer.MAX_VALUE);
}
public static String repeat(String str, int times, int maxLength) {
public static String repeat(final String str, int times, int maxLength) {
AssertTools.checkArgument(Objects.nonNull(str));
return String.valueOf(ArrayTools.repeat(str.toCharArray(), times, maxLength));
}
public static boolean isNotEmpty(@Nullable final String cs) {
return cs != null && !cs.isEmpty();
}
public static boolean isEmpty(@Nullable final String cs) {
return cs == null || cs.isEmpty();
}
@Beta
public static boolean isEmail(@Nullable final String cs) {
return RegexTools.matches(cs, PatternConsts.EMAIL);
}
@Beta
public static boolean isURL(@Nullable final String cs) {
try {
new URL(cs);
} catch (MalformedURLException e) {
return false;
}
return true;
}
private StringTools() {
throw new IllegalStateException("Utility class");
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,7 @@ public class TreeBuilder<T, TSubTree extends T, TIdentity> {
}
public TreeBuilder(Function<T, TIdentity> identityGetter, Function<T, Optional<TIdentity>> parentIdentityGetter,
BiConsumer<TSubTree, T> addChild, Comparator<? super T> defaultComparator) {
BiConsumer<TSubTree, T> addChild, @Nullable Comparator<? super T> defaultComparator) {
this.identityGetter = identityGetter;
this.parentIdentityGetter = parentIdentityGetter;
this.addChildMethod = addChild;

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* <h2>工具类</h2>
* <p>
* 包含树构建器({@link TreeBuilder})、断言工具({@link AssertTools}、ID 生成器({@link IdGenerator})及其它实用工具类。
* </p>
*
* @author <a href="http://zhouxy.xyz:3000/ZhouXY108">ZhouXY</a>
*/
@ParametersAreNonnullByDefault
package xyz.zhouxy.plusone.commons.util;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,14 @@ package xyz.zhouxy.plusone.commons.collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -30,14 +34,24 @@ import java.util.Set;
import org.junit.jupiter.api.Test;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.collect.TreeRangeSet;
public class CollectionToolsTests {
@Test
void testIsEmpty() {
// Collection
List<String> list = new ArrayList<>();
assertTrue(CollectionTools.isEmpty(list));
assertFalse(CollectionTools.isNotEmpty(list));
@@ -46,6 +60,7 @@ public class CollectionToolsTests {
assertFalse(CollectionTools.isEmpty(list));
assertTrue(CollectionTools.isNotEmpty(list));
// Map
Map<String, Integer> map = new HashMap<>();
assertTrue(CollectionTools.isEmpty(map));
assertFalse(CollectionTools.isNotEmpty(map));
@@ -53,6 +68,43 @@ public class CollectionToolsTests {
map.put("2", 2);
assertFalse(CollectionTools.isEmpty(map));
assertTrue(CollectionTools.isNotEmpty(map));
// Table
Table<String, String, Integer> table = HashBasedTable.create();
assertTrue(CollectionTools.isEmpty(table));
assertFalse(CollectionTools.isNotEmpty(table));
table.put("ABC", "d", 4);
assertFalse(CollectionTools.isEmpty(table));
assertTrue(CollectionTools.isNotEmpty(table));
// Multimap
Multimap<String, String> multimap = HashMultimap.create();
assertTrue(CollectionTools.isEmpty(multimap));
assertFalse(CollectionTools.isNotEmpty(multimap));
multimap.put("ABC", "d");
assertFalse(CollectionTools.isEmpty(multimap));
assertTrue(CollectionTools.isNotEmpty(multimap));
// Multiset
Multiset<String> multiset = HashMultiset.create();
assertTrue(CollectionTools.isEmpty(multiset));
assertFalse(CollectionTools.isNotEmpty(multiset));
multiset.add("ABC");
assertFalse(CollectionTools.isEmpty(multiset));
assertTrue(CollectionTools.isNotEmpty(multiset));
// RangeSet
RangeSet<Integer> rangeSet = TreeRangeSet.create();
assertTrue(CollectionTools.isEmpty(rangeSet));
assertFalse(CollectionTools.isNotEmpty(rangeSet));
rangeSet.add(Range.closed(0, 100));
rangeSet.add(Range.openClosed(100, 200));
assertFalse(CollectionTools.isEmpty(rangeSet));
assertTrue(CollectionTools.isNotEmpty(rangeSet));
}
@Test
@@ -69,4 +121,18 @@ public class CollectionToolsTests {
assertSame(map, CollectionTools.nullToEmptyMap(map));
assertEquals(Collections.emptyMap(), CollectionTools.nullToEmptyMap(null));
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = CollectionTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package xyz.zhouxy.plusone.commons.constant;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.regex.Matcher;
import org.junit.jupiter.api.Test;
@@ -232,4 +234,38 @@ class PatternConstsTests {
// ================================
// #endregion - Chinese2ndIdCardNumber
// ================================
// ================================
// #region - invoke constructor
// ================================
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors;
constructors = RegexConsts.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
constructors = PatternConsts.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
// ================================
// #endregion - invoke constructor
// ================================
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,13 @@
package xyz.zhouxy.plusone.commons.function;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Predicate;
@@ -33,4 +38,18 @@ class FunctionTests {
.and(StringUtils::isNotBlank);
assertFalse(predicate.test(str), "校验应是不通过");
}
@Test
void test_constructorOfPredicateTools_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = PredicateTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -0,0 +1,577 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package xyz.zhouxy.plusone.commons.model.dto;
import static org.junit.jupiter.api.Assertions.*;
import javax.annotation.Nullable;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import xyz.zhouxy.plusone.commons.exception.business.BizException;
@Slf4j
public
class CustomUnifiedResponseFactoryTests {
static final ObjectMapper jackson = new ObjectMapper();
static final Gson gson = new Gson();
static final PageResult<User> pageResult = PageResult.of(Lists.newArrayList(
new User("zhouxy1", "zhouxy1@gmail.com"),
new User("zhouxy2", "zhouxy2@gmail.com")
), 108);
static {
jackson.setSerializationInclusion(Include.NON_NULL);
}
@Test
void testSuccess_WithoutArgument() throws Exception {
// 1. success without argument
UnifiedResponse<Void> success = CustomUnifiedResponses.success();
assertEquals("0000000", success.getCode());
assertEquals("成功", success.getMessage());
assertNull(success.getData());
String jacksonSuccess = jackson.writeValueAsString(success);
log.info("jacksonSuccess: {}", jacksonSuccess);
assertEquals("{\"code\":\"0000000\",\"message\":\"成功\"}", jacksonSuccess);
}
@Test
void testSuccess_WithMessage() throws Exception {
// 2. success with message
UnifiedResponse<Void> successWithMessage = CustomUnifiedResponses.success("成功");
assertEquals("0000000", successWithMessage.getCode());
assertEquals("成功", successWithMessage.getMessage());
assertNull(successWithMessage.getData());
String jacksonSuccessWithMessage = jackson.writeValueAsString(successWithMessage);
log.info("jacksonSuccessWithMessage: {}", jacksonSuccessWithMessage);
assertEquals("{\"code\":\"0000000\",\"message\":\"成功\"}", jacksonSuccessWithMessage);
}
@Test
void testSuccess_WithMessageAndNullData() throws Exception {
// success with message and null data
final UnifiedResponse<Void> successWithMessageAndNullData = CustomUnifiedResponses.success("查询成功", null);
assertEquals("0000000", successWithMessageAndNullData.getCode());
assertEquals("查询成功", successWithMessageAndNullData.getMessage());
assertNull(successWithMessageAndNullData.getData());
final String jacksonSuccessWithMessageAndNullData = jackson.writeValueAsString(successWithMessageAndNullData);
log.info("jacksonSuccessWithMessageAndNullData: {}", jacksonSuccessWithMessageAndNullData);
assertEquals("{\"code\":\"0000000\",\"message\":\"查询成功\"}", jacksonSuccessWithMessageAndNullData);
assertEquals("{code: \"0000000\", message: \"查询成功\", data: null}", successWithMessageAndNullData.toString());
}
@Test
void testSuccess_WithMessageAndStringData() throws Exception {
UnifiedResponse<String> successWithStringData = CustomUnifiedResponses.success("查询成功", "zhouxy");
assertEquals("0000000", successWithStringData.getCode());
assertEquals("查询成功", successWithStringData.getMessage());
assertEquals("zhouxy", successWithStringData.getData());
String jacksonSuccessWithStringData = jackson.writeValueAsString(successWithStringData);
log.info("jacksonSuccessWithStringData: {}", jacksonSuccessWithStringData);
assertEquals("{\"code\":\"0000000\",\"message\":\"查询成功\",\"data\":\"zhouxy\"}", jacksonSuccessWithStringData);
assertEquals("{code: \"0000000\", message: \"查询成功\", data: \"zhouxy\"}", successWithStringData.toString());
}
@Test
void testSuccess_WithMessageAndIntegerData() throws Exception {
final UnifiedResponse<Integer> successWithIntegerData = CustomUnifiedResponses.success("查询成功", 1);
assertEquals("0000000", successWithIntegerData.getCode());
assertEquals("查询成功", successWithIntegerData.getMessage());
assertEquals(1, successWithIntegerData.getData());
final String jacksonSuccessWithIntegerData = jackson.writeValueAsString(successWithIntegerData);
log.info("jacksonSuccessWithIntegerData: {}", jacksonSuccessWithIntegerData);
assertEquals("{\"code\":\"0000000\",\"message\":\"查询成功\",\"data\":1}", jacksonSuccessWithIntegerData);
assertEquals("{code: \"0000000\", message: \"查询成功\", data: 1}", successWithIntegerData.toString());
}
@Test
void testSuccess_WithMessageAndData() throws Exception {
UnifiedResponse<PageResult<User>> successWithData = CustomUnifiedResponses.success("查询成功", pageResult);
assertEquals("0000000", successWithData.getCode());
assertEquals("查询成功", successWithData.getMessage());
assertNotNull(successWithData.getData());
assertEquals(pageResult, successWithData.getData());
String jacksonSuccessWithData = jackson.writeValueAsString(successWithData);
log.info("jacksonSuccessWithData: {}", jacksonSuccessWithData);
assertEquals("{\"code\":\"0000000\",\"message\":\"查询成功\",\"data\":{\"total\":108,\"content\":[{\"username\":\"zhouxy1\",\"email\":\"zhouxy1@gmail.com\"},{\"username\":\"zhouxy2\",\"email\":\"zhouxy2@gmail.com\"}]}}", jacksonSuccessWithData);
}
@Test
void testSuccess_WithNullMessage() throws Exception {
// 3. success with null message
UnifiedResponse<Void> successWithNullMessage = CustomUnifiedResponses.success(null);
assertEquals("0000000", successWithNullMessage.getCode());
assertEquals("", successWithNullMessage.getMessage());
assertNull(successWithNullMessage.getData());
String jacksonSuccessWithNullMessage = jackson.writeValueAsString(successWithNullMessage);
log.info("jacksonSuccessWithNullMessage: {}", jacksonSuccessWithNullMessage);
assertEquals("{\"code\":\"0000000\",\"message\":\"\"}", jacksonSuccessWithNullMessage);
}
// success with null message and null data
@Test
void testSuccess_WithNullMessageAndNullData() throws Exception {
final UnifiedResponse<Void> successWithNullMessageAndNullData = CustomUnifiedResponses.success(null, null);
assertEquals("0000000", successWithNullMessageAndNullData.getCode());
assertEquals("", successWithNullMessageAndNullData.getMessage());
assertNull(successWithNullMessageAndNullData.getData());
final String jacksonSuccessWithNullMessageAndNullData = jackson.writeValueAsString(successWithNullMessageAndNullData);
log.info("jacksonSuccessWithNullMessageAndNullData: {}", jacksonSuccessWithNullMessageAndNullData);
assertEquals("{\"code\":\"0000000\",\"message\":\"\"}", jacksonSuccessWithNullMessageAndNullData);
assertEquals("{code: \"0000000\", message: \"\", data: null}", successWithNullMessageAndNullData.toString());
}
@Test
void testSuccess_WithNullMessageAndData() throws Exception {
// success with null message and data
final User user = new User("zhouxy", "zhouxy@code108.cn");
final UnifiedResponse<User> successWithNullMessageAndData = CustomUnifiedResponses.success(null, user);
assertEquals("0000000", successWithNullMessageAndData.getCode());
assertEquals("", successWithNullMessageAndData.getMessage());
assertEquals(user, successWithNullMessageAndData.getData());
final String jacksonSuccessWithNullMessageAndData = jackson.writeValueAsString(successWithNullMessageAndData);
log.info("jacksonSuccessWithNullMessageAndData: {}", jacksonSuccessWithNullMessageAndData);
assertEquals("{\"code\":\"0000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@code108.cn\"}}",
jacksonSuccessWithNullMessageAndData);
}
@Test
void testSuccess_WithEmptyMessage() throws Exception {
// 4. success with empty message
UnifiedResponse<Void> successWithEmptyMessage = CustomUnifiedResponses.success("");
assertEquals("0000000", successWithEmptyMessage.getCode());
assertEquals("", successWithEmptyMessage.getMessage());
assertNull(successWithEmptyMessage.getData());
String jacksonSuccessWithEmptyMessage = jackson.writeValueAsString(successWithEmptyMessage);
log.info("jacksonSuccessWithEmptyMessage: {}", jacksonSuccessWithEmptyMessage);
assertEquals("{\"code\":\"0000000\",\"message\":\"\"}", jacksonSuccessWithEmptyMessage);
}
// success with empty message and null data
@Test
void testSuccess_WithEmptyMessageAndNullData() throws Exception {
final UnifiedResponse<Void> successWithEmptyMessageAndNullData = CustomUnifiedResponses.success("", null);
assertEquals("0000000", successWithEmptyMessageAndNullData.getCode());
assertEquals("", successWithEmptyMessageAndNullData.getMessage());
assertNull(successWithEmptyMessageAndNullData.getData());
final String jacksonSuccessWithEmptyMessageAndNullData = jackson.writeValueAsString(successWithEmptyMessageAndNullData);
log.info("jacksonSuccessWithEmptyMessageAndNullData: {}", jacksonSuccessWithEmptyMessageAndNullData);
assertEquals("{\"code\":\"0000000\",\"message\":\"\"}", jacksonSuccessWithEmptyMessageAndNullData);
assertEquals("{code: \"0000000\", message: \"\", data: null}", successWithEmptyMessageAndNullData.toString());
}
// success with empty message and data
@Test
void testSuccess_WithEmptyMessageAndData() throws Exception {
final User user = new User("zhouxy", "zhouxy@gmail.com");
final UnifiedResponse<User> successWithEmptyMessageAndData = CustomUnifiedResponses.success("", user);
assertEquals("0000000", successWithEmptyMessageAndData.getCode());
assertEquals("", successWithEmptyMessageAndData.getMessage());
assertEquals(user, successWithEmptyMessageAndData.getData());
final String jacksonSuccessWithEmptyMessageAndData = jackson.writeValueAsString(successWithEmptyMessageAndData);
log.info("jacksonSuccessWithEmptyMessageAndData: {}", jacksonSuccessWithEmptyMessageAndData);
assertEquals("{\"code\":\"0000000\",\"message\":\"\",\"data\":{\"username\":\"zhouxy\",\"email\":\"zhouxy@gmail.com\"}}", jacksonSuccessWithEmptyMessageAndData);
}
@Test
void testError_WithStatusAndMessage() throws Exception {
final UnifiedResponse<Void> errorWithStatusAndMessage = CustomUnifiedResponses.error("108", "查询失败");
assertEquals("108", errorWithStatusAndMessage.getCode());
assertEquals("查询失败", errorWithStatusAndMessage.getMessage());
assertNull(errorWithStatusAndMessage.getData());
assertEquals("{code: \"108\", message: \"查询失败\", data: null}", errorWithStatusAndMessage.toString());
final String jacksonErrorWithStatusAndMessage = jackson.writeValueAsString(errorWithStatusAndMessage);
log.info("jacksonErrorWithStatusAndMessage: {}", jacksonErrorWithStatusAndMessage);
assertEquals("{\"code\":\"108\",\"message\":\"查询失败\"}", jacksonErrorWithStatusAndMessage);
final String gsonErrorWithStatusAndMessage = gson.toJson(errorWithStatusAndMessage);
assertEquals("{\"code\":\"108\",\"message\":\"查询失败\"}", gsonErrorWithStatusAndMessage);
}
@Test
void testError_WithStatusAndMessage_AndNullData() throws Exception {
final UnifiedResponse<Void> errorWithStatusAndMessageAndNullData = CustomUnifiedResponses.error("108", "查询失败", null);
assertEquals("108", errorWithStatusAndMessageAndNullData.getCode());
assertEquals("查询失败", errorWithStatusAndMessageAndNullData.getMessage());
assertNull(errorWithStatusAndMessageAndNullData.getData());
assertEquals("{code: \"108\", message: \"查询失败\", data: null}", errorWithStatusAndMessageAndNullData.toString());
final String jacksonErrorWithStatusAndMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndMessageAndNullData);
log.info("jacksonErrorWithStatusAndMessage: {}", jacksonErrorWithStatusAndMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"查询失败\"}", jacksonErrorWithStatusAndMessageAndNullData);
final String gsonErrorWithStatusAndMessageAndNullData = gson.toJson(errorWithStatusAndMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"查询失败\"}", gsonErrorWithStatusAndMessageAndNullData);
}
@Test
void testError_WithStatusAndMessage_AndData() throws Exception {
final PageResult<User> emptyPageResult = PageResult.empty();
final UnifiedResponse<PageResult<User>> errorWithStatusAndMessageAndData = CustomUnifiedResponses.error("108", "查询失败", emptyPageResult);
assertEquals("108", errorWithStatusAndMessageAndData.getCode());
assertEquals("查询失败", errorWithStatusAndMessageAndData.getMessage());
assertEquals(emptyPageResult, errorWithStatusAndMessageAndData.getData());
assertEquals("{code: \"108\", message: \"查询失败\", data: PageResult [total=0, content=[]]}", errorWithStatusAndMessageAndData.toString());
final String jacksonErrorWithStatusAndMessageAndData = jackson.writeValueAsString(errorWithStatusAndMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"查询失败\",\"data\":{\"total\":0,\"content\":[]}}",
jacksonErrorWithStatusAndMessageAndData);
final String gsonErrorWithStatusAndMessageAndData = gson.toJson(errorWithStatusAndMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"查询失败\",\"data\":{\"total\":0,\"content\":[]}}",
gsonErrorWithStatusAndMessageAndData);
}
@Test
void testError_WithStatusAndNullMessage() throws Exception {
UnifiedResponse<Void> errorWithStatusAndNullMessage = CustomUnifiedResponses.error("500", (String) null);
assertEquals("500", errorWithStatusAndNullMessage.getCode());
assertEquals("", errorWithStatusAndNullMessage.getMessage());
assertNull(errorWithStatusAndNullMessage.getData());
final String jacksonErrorWithStatusAndNullMessage = jackson.writeValueAsString(errorWithStatusAndNullMessage);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", jacksonErrorWithStatusAndNullMessage);
final String gsonErrorWithStatusAndNullMessage = gson.toJson(errorWithStatusAndNullMessage);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", gsonErrorWithStatusAndNullMessage);
}
@Test
void testError_WithStatusAndNullMessage_AndNullData() throws Exception {
UnifiedResponse<Void> errorWithStatusAndNullMessageAndNullData = CustomUnifiedResponses.error("500", (String) null, null);
assertEquals("500", errorWithStatusAndNullMessageAndNullData.getCode());
assertEquals("", errorWithStatusAndNullMessageAndNullData.getMessage());
assertNull(errorWithStatusAndNullMessageAndNullData.getData());
final String jacksonErrorWithStatusAndNullMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndNullMessageAndNullData);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", jacksonErrorWithStatusAndNullMessageAndNullData);
final String gsonErrorWithStatusAndNullMessageAndNullData = gson.toJson(errorWithStatusAndNullMessageAndNullData);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", gsonErrorWithStatusAndNullMessageAndNullData);
}
@Test
void testError_WithStatusAndNullMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> errorWithStatusAndNullMessageAndData = CustomUnifiedResponses.error("500", (String) null, emptyPageResult);
assertEquals("500", errorWithStatusAndNullMessageAndData.getCode());
assertEquals("", errorWithStatusAndNullMessageAndData.getMessage());
assertEquals(emptyPageResult, errorWithStatusAndNullMessageAndData.getData());
final String jacksonErrorWithStatusAndNullMessageAndData = jackson.writeValueAsString(errorWithStatusAndNullMessageAndData);
assertEquals("{\"code\":\"500\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonErrorWithStatusAndNullMessageAndData);
final String gsonErrorWithStatusAndNullMessageAndData = gson.toJson(errorWithStatusAndNullMessageAndData);
assertEquals("{\"code\":\"500\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonErrorWithStatusAndNullMessageAndData);
}
@Test
void testError_WithStatusAndEmptyMessage() throws Exception {
UnifiedResponse<Void> errorWithStatusAndEmptyMessage = CustomUnifiedResponses.error("500", "");
assertEquals("500", errorWithStatusAndEmptyMessage.getCode());
assertEquals("", errorWithStatusAndEmptyMessage.getMessage());
assertNull(errorWithStatusAndEmptyMessage.getData());
final String jacksonErrorWithStatusAndEmptyMessage = jackson.writeValueAsString(errorWithStatusAndEmptyMessage);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", jacksonErrorWithStatusAndEmptyMessage);
final String gsonErrorWithStatusAndEmptyMessage = gson.toJson(errorWithStatusAndEmptyMessage);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", gsonErrorWithStatusAndEmptyMessage);
}
@Test
void testError_WithStatusAndEmptyMessage_AndNullData() throws Exception {
UnifiedResponse<Void> errorWithStatusAndEmptyMessageAndNullData = CustomUnifiedResponses.error("500", "", null);
assertEquals("500", errorWithStatusAndEmptyMessageAndNullData.getCode());
assertEquals("", errorWithStatusAndEmptyMessageAndNullData.getMessage());
assertNull(errorWithStatusAndEmptyMessageAndNullData.getData());
final String jacksonErrorWithStatusAndEmptyMessageAndNullData = jackson.writeValueAsString(errorWithStatusAndEmptyMessageAndNullData);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", jacksonErrorWithStatusAndEmptyMessageAndNullData);
final String gsonErrorWithStatusAndEmptyMessageAndNullData = gson.toJson(errorWithStatusAndEmptyMessageAndNullData);
assertEquals("{\"code\":\"500\",\"message\":\"\"}", gsonErrorWithStatusAndEmptyMessageAndNullData);
}
@Test
void testError_WithStatusAndEmptyMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> errorWithStatusAndEmptyMessageAndData = CustomUnifiedResponses.error("500", "", emptyPageResult);
assertEquals("500", errorWithStatusAndEmptyMessageAndData.getCode());
assertEquals("", errorWithStatusAndEmptyMessageAndData.getMessage());
assertEquals(emptyPageResult, errorWithStatusAndEmptyMessageAndData.getData());
final String jacksonErrorWithStatusAndEmptyMessageAndData = jackson.writeValueAsString(errorWithStatusAndEmptyMessageAndData);
assertEquals("{\"code\":\"500\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonErrorWithStatusAndEmptyMessageAndData);
final String gsonErrorWithStatusAndEmptyMessageAndData = gson.toJson(errorWithStatusAndEmptyMessageAndData);
assertEquals("{\"code\":\"500\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonErrorWithStatusAndEmptyMessageAndData);
}
@Test
void testError_WithStatusAndThrowable() throws Exception {
final IllegalArgumentException e = new IllegalArgumentException("ID cannot be null");
final UnifiedResponse<Void> errorWithStatusThrowable = CustomUnifiedResponses.error("500", e);
assertEquals("500", errorWithStatusThrowable.getCode());
assertEquals("ID cannot be null", errorWithStatusThrowable.getMessage());
assertNull(errorWithStatusThrowable.getData());
assertEquals("{\"code\":\"500\",\"message\":\"ID cannot be null\"}", jackson.writeValueAsString(errorWithStatusThrowable));
assertEquals("{\"code\":\"500\",\"message\":\"ID cannot be null\"}", gson.toJson(errorWithStatusThrowable));
}
@Test
void testError_WithStatusAndNullThrowable() {
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error("500", (Throwable) null));
}
@Test
void testError_WithNullStatus() {
final String nullStatus = null;
final String nullMessage = null;
final User user = new User("zhouxy", "zhouxy@gmail.com");
// message
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "查询失败"));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "查询失败", user));
// empty message
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, ""));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "", null));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "", user));
// null message
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, nullMessage));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, "查询失败", user));
// Throwable
BizException bizException = new BizException("业务异常");
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, bizException));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.error(nullStatus, (Throwable) null));
}
@Test
void testOf_WithStatusAndMessage() throws Exception {
final UnifiedResponse<Void> ofWithStatusAndMessage = CustomUnifiedResponses.of("108", "This is a message.");
assertEquals("108", ofWithStatusAndMessage.getCode());
assertEquals("This is a message.", ofWithStatusAndMessage.getMessage());
assertNull(ofWithStatusAndMessage.getData());
final String jacksonOfWithStatusAndMessage = jackson.writeValueAsString(ofWithStatusAndMessage);
log.info("jacksonOfWithStatusAndMessage: {}", jacksonOfWithStatusAndMessage);
assertEquals("{\"code\":\"108\",\"message\":\"This is a message.\"}", jacksonOfWithStatusAndMessage);
assertEquals("{code: \"108\", message: \"This is a message.\", data: null}", ofWithStatusAndMessage.toString());
final String gsonOfWithStatusAndMessage = gson.toJson(ofWithStatusAndMessage);
assertEquals("{\"code\":\"108\",\"message\":\"This is a message.\"}", gsonOfWithStatusAndMessage);
}
@Test
void testOf_WithStatusAndMessage_AndNullData() throws Exception {
final UnifiedResponse<Void> ofWithStatusAndMessageAndNullData = CustomUnifiedResponses.of("108", "This is a message.", null);
assertEquals("108", ofWithStatusAndMessageAndNullData.getCode());
assertEquals("This is a message.", ofWithStatusAndMessageAndNullData.getMessage());
assertNull(ofWithStatusAndMessageAndNullData.getData());
final String jacksonOfWithStatusAndMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndMessageAndNullData);
log.info("jacksonOfWithStatusAndMessage: {}", jacksonOfWithStatusAndMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"This is a message.\"}", jacksonOfWithStatusAndMessageAndNullData);
assertEquals("{code: \"108\", message: \"This is a message.\", data: null}", ofWithStatusAndMessageAndNullData.toString());
final String gsonOfWithStatusAndMessageAndNullData = gson.toJson(ofWithStatusAndMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"This is a message.\"}", gsonOfWithStatusAndMessageAndNullData);
}
@Test
void testOf_WithStatusAndMessage_AndData() throws Exception {
final PageResult<User> emptyPageResult = PageResult.empty();
final UnifiedResponse<PageResult<User>> ofWithStatusAndMessageAndData
= CustomUnifiedResponses.of("108", "This is a message.", emptyPageResult);
assertEquals("{code: \"108\", message: \"This is a message.\", data: PageResult [total=0, content=[]]}",
ofWithStatusAndMessageAndData.toString());
assertEquals("108", ofWithStatusAndMessageAndData.getCode());
assertEquals("This is a message.", ofWithStatusAndMessageAndData.getMessage());
assertEquals(emptyPageResult, ofWithStatusAndMessageAndData.getData());
final String jacksonOfWithStatusAndMessageAndData = jackson.writeValueAsString(ofWithStatusAndMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"This is a message.\",\"data\":{\"total\":0,\"content\":[]}}",
jacksonOfWithStatusAndMessageAndData);
final String gsonOfWithStatusAndMessageAndData = gson.toJson(ofWithStatusAndMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"This is a message.\",\"data\":{\"total\":0,\"content\":[]}}",
gsonOfWithStatusAndMessageAndData);
}
@Test
void testOf_WithStatusAndNullMessage() throws Exception {
UnifiedResponse<Void> ofWithStatusAndNullMessage = CustomUnifiedResponses.of("108", (String) null);
assertEquals("108", ofWithStatusAndNullMessage.getCode());
assertEquals("", ofWithStatusAndNullMessage.getMessage());
assertNull(ofWithStatusAndNullMessage.getData());
final String jacksonOfWithStatusAndNullMessage = jackson.writeValueAsString(ofWithStatusAndNullMessage);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", jacksonOfWithStatusAndNullMessage);
final String gsonOfWithStatusAndNullMessage = gson.toJson(ofWithStatusAndNullMessage);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", gsonOfWithStatusAndNullMessage);
}
@Test
void testOf_WithStatusAndNullMessage_AndNullData() throws Exception {
UnifiedResponse<Void> ofWithStatusAndNullMessageAndNullData = CustomUnifiedResponses.of("108", (String) null, null);
assertEquals("108", ofWithStatusAndNullMessageAndNullData.getCode());
assertEquals("", ofWithStatusAndNullMessageAndNullData.getMessage());
assertNull(ofWithStatusAndNullMessageAndNullData.getData());
final String jacksonOfWithStatusAndNullMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndNullMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", jacksonOfWithStatusAndNullMessageAndNullData);
final String gsonOfWithStatusAndNullMessageAndNullData = gson.toJson(ofWithStatusAndNullMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", gsonOfWithStatusAndNullMessageAndNullData);
}
@Test
void testOf_WithStatusAndNullMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> ofWithStatusAndNullMessageAndData = CustomUnifiedResponses.of("108", (String) null, emptyPageResult);
assertEquals("108", ofWithStatusAndNullMessageAndData.getCode());
assertEquals("", ofWithStatusAndNullMessageAndData.getMessage());
assertEquals(emptyPageResult, ofWithStatusAndNullMessageAndData.getData());
final String jacksonOfWithStatusAndNullMessageAndData = jackson.writeValueAsString(ofWithStatusAndNullMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonOfWithStatusAndNullMessageAndData);
final String gsonOfWithStatusAndNullMessageAndData = gson.toJson(ofWithStatusAndNullMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonOfWithStatusAndNullMessageAndData);
}
@Test
void testOf_WithStatusAndEmptyMessage() throws Exception {
UnifiedResponse<Void> ofWithStatusAndEmptyMessage = CustomUnifiedResponses.of("108", "");
assertEquals("108", ofWithStatusAndEmptyMessage.getCode());
assertEquals("", ofWithStatusAndEmptyMessage.getMessage());
assertNull(ofWithStatusAndEmptyMessage.getData());
final String jacksonOfWithStatusAndEmptyMessage = jackson.writeValueAsString(ofWithStatusAndEmptyMessage);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", jacksonOfWithStatusAndEmptyMessage);
final String gsonOfWithStatusAndEmptyMessage = gson.toJson(ofWithStatusAndEmptyMessage);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", gsonOfWithStatusAndEmptyMessage);
}
@Test
void testOf_WithStatusAndEmptyMessage_AndNullData() throws Exception {
UnifiedResponse<Void> ofWithStatusAndEmptyMessageAndNullData = CustomUnifiedResponses.of("108", "", null);
assertEquals("108", ofWithStatusAndEmptyMessageAndNullData.getCode());
assertEquals("", ofWithStatusAndEmptyMessageAndNullData.getMessage());
assertNull(ofWithStatusAndEmptyMessageAndNullData.getData());
final String jacksonOfWithStatusAndEmptyMessageAndNullData = jackson.writeValueAsString(ofWithStatusAndEmptyMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", jacksonOfWithStatusAndEmptyMessageAndNullData);
final String gsonOfWithStatusAndEmptyMessageAndNullData = gson.toJson(ofWithStatusAndEmptyMessageAndNullData);
assertEquals("{\"code\":\"108\",\"message\":\"\"}", gsonOfWithStatusAndEmptyMessageAndNullData);
}
@Test
void testOf_WithStatusAndEmptyMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> ofWithStatusAndEmptyMessageAndData = CustomUnifiedResponses.of("108", "", emptyPageResult);
assertEquals("108", ofWithStatusAndEmptyMessageAndData.getCode());
assertEquals("", ofWithStatusAndEmptyMessageAndData.getMessage());
assertEquals(emptyPageResult, ofWithStatusAndEmptyMessageAndData.getData());
final String jacksonOfWithStatusAndEmptyMessageAndData = jackson.writeValueAsString(ofWithStatusAndEmptyMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", jacksonOfWithStatusAndEmptyMessageAndData);
final String gsonOfWithStatusAndEmptyMessageAndData = gson.toJson(ofWithStatusAndEmptyMessageAndData);
assertEquals("{\"code\":\"108\",\"message\":\"\",\"data\":{\"total\":0,\"content\":[]}}", gsonOfWithStatusAndEmptyMessageAndData);
}
@Test
void testOf_WithNullStatus() {
final String nullStatus = null;
final String nullMessage = null;
final User user = new User("zhouxy", "zhouxy@gmail.com");
// message
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "查询失败"));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "查询失败", user));
// empty message
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, ""));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "", null));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "", user));
// null message
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, nullMessage));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> CustomUnifiedResponses.of(nullStatus, "查询失败", user));
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class User {
private String username;
private String email;
}
public static class CustomUnifiedResponses extends UnifiedResponses {
public static final String SUCCESS_CODE = "0000000";
public static final String DEFAULT_SUCCESS_MSG = "成功";
public static UnifiedResponse<Void> success() {
return of(SUCCESS_CODE, DEFAULT_SUCCESS_MSG);
}
public static UnifiedResponse<Void> success(@Nullable String message) {
return of(SUCCESS_CODE, message);
}
public static <T> UnifiedResponse<T> success(@Nullable String message, @Nullable T data) {
return of(SUCCESS_CODE, message, data);
}
private CustomUnifiedResponses() {
super();
}
}
}

View File

@@ -51,7 +51,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithoutArgument() throws Exception {
// 1. success without argument
UnifiedResponse<Void> success = UnifiedResponse.success();
UnifiedResponse<Void> success = UnifiedResponses.success();
assertEquals("2000000", success.getCode());
assertEquals("SUCCESS", success.getMessage());
assertNull(success.getData());
@@ -63,7 +63,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithMessage() throws Exception {
// 2. success with message
UnifiedResponse<Void> successWithMessage = UnifiedResponse.success("成功");
UnifiedResponse<Void> successWithMessage = UnifiedResponses.success("成功");
assertEquals("2000000", successWithMessage.getCode());
assertEquals("成功", successWithMessage.getMessage());
assertNull(successWithMessage.getData());
@@ -75,7 +75,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithMessageAndNullData() throws Exception {
// success with message and null data
final UnifiedResponse<Void> successWithMessageAndNullData = UnifiedResponse.success("查询成功", null);
final UnifiedResponse<Void> successWithMessageAndNullData = UnifiedResponses.success("查询成功", null);
assertEquals("2000000", successWithMessageAndNullData.getCode());
assertEquals("查询成功", successWithMessageAndNullData.getMessage());
assertNull(successWithMessageAndNullData.getData());
@@ -88,7 +88,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithMessageAndStringData() throws Exception {
UnifiedResponse<String> successWithStringData = UnifiedResponse.success("查询成功", "zhouxy");
UnifiedResponse<String> successWithStringData = UnifiedResponses.success("查询成功", "zhouxy");
assertEquals("2000000", successWithStringData.getCode());
assertEquals("查询成功", successWithStringData.getMessage());
assertEquals("zhouxy", successWithStringData.getData());
@@ -101,7 +101,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithMessageAndIntegerData() throws Exception {
final UnifiedResponse<Integer> successWithIntegerData = UnifiedResponse.success("查询成功", 1);
final UnifiedResponse<Integer> successWithIntegerData = UnifiedResponses.success("查询成功", 1);
assertEquals("2000000", successWithIntegerData.getCode());
assertEquals("查询成功", successWithIntegerData.getMessage());
assertEquals(1, successWithIntegerData.getData());
@@ -114,7 +114,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithMessageAndData() throws Exception {
UnifiedResponse<PageResult<User>> successWithData = UnifiedResponse.success("查询成功", pageResult);
UnifiedResponse<PageResult<User>> successWithData = UnifiedResponses.success("查询成功", pageResult);
assertEquals("2000000", successWithData.getCode());
assertEquals("查询成功", successWithData.getMessage());
assertNotNull(successWithData.getData());
@@ -127,7 +127,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithNullMessage() throws Exception {
// 3. success with null message
UnifiedResponse<Void> successWithNullMessage = UnifiedResponse.success(null);
UnifiedResponse<Void> successWithNullMessage = UnifiedResponses.success(null);
assertEquals("2000000", successWithNullMessage.getCode());
assertEquals("", successWithNullMessage.getMessage());
assertNull(successWithNullMessage.getData());
@@ -139,7 +139,7 @@ class UnifiedResponseTests {
// success with null message and null data
@Test
void testSuccess_WithNullMessageAndNullData() throws Exception {
final UnifiedResponse<Void> successWithNullMessageAndNullData = UnifiedResponse.success(null, null);
final UnifiedResponse<Void> successWithNullMessageAndNullData = UnifiedResponses.success(null, null);
assertEquals("2000000", successWithNullMessageAndNullData.getCode());
assertEquals("", successWithNullMessageAndNullData.getMessage());
assertNull(successWithNullMessageAndNullData.getData());
@@ -155,7 +155,7 @@ class UnifiedResponseTests {
void testSuccess_WithNullMessageAndData() throws Exception {
// success with null message and data
final User user = new User("zhouxy", "zhouxy@code108.cn");
final UnifiedResponse<User> successWithNullMessageAndData = UnifiedResponse.success(null, user);
final UnifiedResponse<User> successWithNullMessageAndData = UnifiedResponses.success(null, user);
assertEquals("2000000", successWithNullMessageAndData.getCode());
assertEquals("", successWithNullMessageAndData.getMessage());
assertEquals(user, successWithNullMessageAndData.getData());
@@ -168,7 +168,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithEmptyMessage() throws Exception {
// 4. success with empty message
UnifiedResponse<Void> successWithEmptyMessage = UnifiedResponse.success("");
UnifiedResponse<Void> successWithEmptyMessage = UnifiedResponses.success("");
assertEquals("2000000", successWithEmptyMessage.getCode());
assertEquals("", successWithEmptyMessage.getMessage());
assertNull(successWithEmptyMessage.getData());
@@ -180,7 +180,7 @@ class UnifiedResponseTests {
// success with empty message and null data
@Test
void testSuccess_WithEmptyMessageAndNullData() throws Exception {
final UnifiedResponse<Void> successWithEmptyMessageAndNullData = UnifiedResponse.success("", null);
final UnifiedResponse<Void> successWithEmptyMessageAndNullData = UnifiedResponses.success("", null);
assertEquals("2000000", successWithEmptyMessageAndNullData.getCode());
assertEquals("", successWithEmptyMessageAndNullData.getMessage());
assertNull(successWithEmptyMessageAndNullData.getData());
@@ -196,7 +196,7 @@ class UnifiedResponseTests {
@Test
void testSuccess_WithEmptyMessageAndData() throws Exception {
final User user = new User("zhouxy", "zhouxy@gmail.com");
final UnifiedResponse<User> successWithEmptyMessageAndData = UnifiedResponse.success("", user);
final UnifiedResponse<User> successWithEmptyMessageAndData = UnifiedResponses.success("", user);
assertEquals("2000000", successWithEmptyMessageAndData.getCode());
assertEquals("", successWithEmptyMessageAndData.getMessage());
assertEquals(user, successWithEmptyMessageAndData.getData());
@@ -208,7 +208,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndMessage() throws Exception {
final UnifiedResponse<Void> errorWithStatusAndMessage = UnifiedResponse.error("108", "查询失败");
final UnifiedResponse<Void> errorWithStatusAndMessage = UnifiedResponses.error("108", "查询失败");
assertEquals("108", errorWithStatusAndMessage.getCode());
assertEquals("查询失败", errorWithStatusAndMessage.getMessage());
assertNull(errorWithStatusAndMessage.getData());
@@ -224,7 +224,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndMessage_AndNullData() throws Exception {
final UnifiedResponse<Void> errorWithStatusAndMessageAndNullData = UnifiedResponse.error("108", "查询失败", null);
final UnifiedResponse<Void> errorWithStatusAndMessageAndNullData = UnifiedResponses.error("108", "查询失败", null);
assertEquals("108", errorWithStatusAndMessageAndNullData.getCode());
assertEquals("查询失败", errorWithStatusAndMessageAndNullData.getMessage());
assertNull(errorWithStatusAndMessageAndNullData.getData());
@@ -241,7 +241,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndMessage_AndData() throws Exception {
final PageResult<User> emptyPageResult = PageResult.empty();
final UnifiedResponse<PageResult<User>> errorWithStatusAndMessageAndData = UnifiedResponse.error("108", "查询失败", emptyPageResult);
final UnifiedResponse<PageResult<User>> errorWithStatusAndMessageAndData = UnifiedResponses.error("108", "查询失败", emptyPageResult);
assertEquals("108", errorWithStatusAndMessageAndData.getCode());
assertEquals("查询失败", errorWithStatusAndMessageAndData.getMessage());
assertEquals(emptyPageResult, errorWithStatusAndMessageAndData.getData());
@@ -258,7 +258,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndNullMessage() throws Exception {
UnifiedResponse<Void> errorWithStatusAndNullMessage = UnifiedResponse.error("500", (String) null);
UnifiedResponse<Void> errorWithStatusAndNullMessage = UnifiedResponses.error("500", (String) null);
assertEquals("500", errorWithStatusAndNullMessage.getCode());
assertEquals("", errorWithStatusAndNullMessage.getMessage());
assertNull(errorWithStatusAndNullMessage.getData());
@@ -272,7 +272,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndNullMessage_AndNullData() throws Exception {
UnifiedResponse<Void> errorWithStatusAndNullMessageAndNullData = UnifiedResponse.error("500", (String) null, null);
UnifiedResponse<Void> errorWithStatusAndNullMessageAndNullData = UnifiedResponses.error("500", (String) null, null);
assertEquals("500", errorWithStatusAndNullMessageAndNullData.getCode());
assertEquals("", errorWithStatusAndNullMessageAndNullData.getMessage());
@@ -288,7 +288,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndNullMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> errorWithStatusAndNullMessageAndData = UnifiedResponse.error("500", (String) null, emptyPageResult);
UnifiedResponse<PageResult<User>> errorWithStatusAndNullMessageAndData = UnifiedResponses.error("500", (String) null, emptyPageResult);
assertEquals("500", errorWithStatusAndNullMessageAndData.getCode());
assertEquals("", errorWithStatusAndNullMessageAndData.getMessage());
assertEquals(emptyPageResult, errorWithStatusAndNullMessageAndData.getData());
@@ -300,7 +300,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndEmptyMessage() throws Exception {
UnifiedResponse<Void> errorWithStatusAndEmptyMessage = UnifiedResponse.error("500", "");
UnifiedResponse<Void> errorWithStatusAndEmptyMessage = UnifiedResponses.error("500", "");
assertEquals("500", errorWithStatusAndEmptyMessage.getCode());
assertEquals("", errorWithStatusAndEmptyMessage.getMessage());
assertNull(errorWithStatusAndEmptyMessage.getData());
@@ -314,7 +314,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndEmptyMessage_AndNullData() throws Exception {
UnifiedResponse<Void> errorWithStatusAndEmptyMessageAndNullData = UnifiedResponse.error("500", "", null);
UnifiedResponse<Void> errorWithStatusAndEmptyMessageAndNullData = UnifiedResponses.error("500", "", null);
assertEquals("500", errorWithStatusAndEmptyMessageAndNullData.getCode());
assertEquals("", errorWithStatusAndEmptyMessageAndNullData.getMessage());
@@ -330,7 +330,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndEmptyMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> errorWithStatusAndEmptyMessageAndData = UnifiedResponse.error("500", "", emptyPageResult);
UnifiedResponse<PageResult<User>> errorWithStatusAndEmptyMessageAndData = UnifiedResponses.error("500", "", emptyPageResult);
assertEquals("500", errorWithStatusAndEmptyMessageAndData.getCode());
assertEquals("", errorWithStatusAndEmptyMessageAndData.getMessage());
assertEquals(emptyPageResult, errorWithStatusAndEmptyMessageAndData.getData());
@@ -343,7 +343,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndThrowable() throws Exception {
final IllegalArgumentException e = new IllegalArgumentException("ID cannot be null");
final UnifiedResponse<Void> errorWithStatusThrowable = UnifiedResponse.error("500", e);
final UnifiedResponse<Void> errorWithStatusThrowable = UnifiedResponses.error("500", e);
assertEquals("500", errorWithStatusThrowable.getCode());
assertEquals("ID cannot be null", errorWithStatusThrowable.getMessage());
assertNull(errorWithStatusThrowable.getData());
@@ -353,7 +353,7 @@ class UnifiedResponseTests {
@Test
void testError_WithStatusAndNullThrowable() {
assertThrows(NullPointerException.class, () -> UnifiedResponse.error("500", (Throwable) null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error("500", (Throwable) null));
}
@Test
@@ -363,30 +363,30 @@ class UnifiedResponseTests {
final User user = new User("zhouxy", "zhouxy@gmail.com");
// message
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败"));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败", user));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "查询失败"));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "查询失败", user));
// empty message
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, ""));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "", null));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "", user));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, ""));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "", null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "", user));
// null message
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, nullMessage));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, "查询失败", user));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, nullMessage));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, "查询失败", user));
// Throwable
BizException bizException = new BizException("业务异常");
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, bizException));
assertThrows(NullPointerException.class, () -> UnifiedResponse.error(nullStatus, (Throwable) null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, bizException));
assertThrows(NullPointerException.class, () -> UnifiedResponses.error(nullStatus, (Throwable) null));
}
@Test
void testOf_WithStatusAndMessage() throws Exception {
final UnifiedResponse<Void> ofWithStatusAndMessage = UnifiedResponse.of("108", "This is a message.");
final UnifiedResponse<Void> ofWithStatusAndMessage = UnifiedResponses.of("108", "This is a message.");
assertEquals("108", ofWithStatusAndMessage.getCode());
assertEquals("This is a message.", ofWithStatusAndMessage.getMessage());
assertNull(ofWithStatusAndMessage.getData());
@@ -403,7 +403,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndMessage_AndNullData() throws Exception {
final UnifiedResponse<Void> ofWithStatusAndMessageAndNullData = UnifiedResponse.of("108", "This is a message.", null);
final UnifiedResponse<Void> ofWithStatusAndMessageAndNullData = UnifiedResponses.of("108", "This is a message.", null);
assertEquals("108", ofWithStatusAndMessageAndNullData.getCode());
assertEquals("This is a message.", ofWithStatusAndMessageAndNullData.getMessage());
assertNull(ofWithStatusAndMessageAndNullData.getData());
@@ -422,7 +422,7 @@ class UnifiedResponseTests {
void testOf_WithStatusAndMessage_AndData() throws Exception {
final PageResult<User> emptyPageResult = PageResult.empty();
final UnifiedResponse<PageResult<User>> ofWithStatusAndMessageAndData
= UnifiedResponse.of("108", "This is a message.", emptyPageResult);
= UnifiedResponses.of("108", "This is a message.", emptyPageResult);
assertEquals("{code: \"108\", message: \"This is a message.\", data: PageResult [total=0, content=[]]}",
ofWithStatusAndMessageAndData.toString());
assertEquals("108", ofWithStatusAndMessageAndData.getCode());
@@ -438,7 +438,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndNullMessage() throws Exception {
UnifiedResponse<Void> ofWithStatusAndNullMessage = UnifiedResponse.of("108", (String) null);
UnifiedResponse<Void> ofWithStatusAndNullMessage = UnifiedResponses.of("108", (String) null);
assertEquals("108", ofWithStatusAndNullMessage.getCode());
assertEquals("", ofWithStatusAndNullMessage.getMessage());
assertNull(ofWithStatusAndNullMessage.getData());
@@ -452,7 +452,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndNullMessage_AndNullData() throws Exception {
UnifiedResponse<Void> ofWithStatusAndNullMessageAndNullData = UnifiedResponse.of("108", (String) null, null);
UnifiedResponse<Void> ofWithStatusAndNullMessageAndNullData = UnifiedResponses.of("108", (String) null, null);
assertEquals("108", ofWithStatusAndNullMessageAndNullData.getCode());
assertEquals("", ofWithStatusAndNullMessageAndNullData.getMessage());
@@ -468,7 +468,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndNullMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> ofWithStatusAndNullMessageAndData = UnifiedResponse.of("108", (String) null, emptyPageResult);
UnifiedResponse<PageResult<User>> ofWithStatusAndNullMessageAndData = UnifiedResponses.of("108", (String) null, emptyPageResult);
assertEquals("108", ofWithStatusAndNullMessageAndData.getCode());
assertEquals("", ofWithStatusAndNullMessageAndData.getMessage());
assertEquals(emptyPageResult, ofWithStatusAndNullMessageAndData.getData());
@@ -480,7 +480,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndEmptyMessage() throws Exception {
UnifiedResponse<Void> ofWithStatusAndEmptyMessage = UnifiedResponse.of("108", "");
UnifiedResponse<Void> ofWithStatusAndEmptyMessage = UnifiedResponses.of("108", "");
assertEquals("108", ofWithStatusAndEmptyMessage.getCode());
assertEquals("", ofWithStatusAndEmptyMessage.getMessage());
assertNull(ofWithStatusAndEmptyMessage.getData());
@@ -494,7 +494,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndEmptyMessage_AndNullData() throws Exception {
UnifiedResponse<Void> ofWithStatusAndEmptyMessageAndNullData = UnifiedResponse.of("108", "", null);
UnifiedResponse<Void> ofWithStatusAndEmptyMessageAndNullData = UnifiedResponses.of("108", "", null);
assertEquals("108", ofWithStatusAndEmptyMessageAndNullData.getCode());
assertEquals("", ofWithStatusAndEmptyMessageAndNullData.getMessage());
@@ -510,7 +510,7 @@ class UnifiedResponseTests {
@Test
void testOf_WithStatusAndEmptyMessage_AndData() throws Exception {
PageResult<User> emptyPageResult = PageResult.empty();
UnifiedResponse<PageResult<User>> ofWithStatusAndEmptyMessageAndData = UnifiedResponse.of("108", "", emptyPageResult);
UnifiedResponse<PageResult<User>> ofWithStatusAndEmptyMessageAndData = UnifiedResponses.of("108", "", emptyPageResult);
assertEquals("108", ofWithStatusAndEmptyMessageAndData.getCode());
assertEquals("", ofWithStatusAndEmptyMessageAndData.getMessage());
assertEquals(emptyPageResult, ofWithStatusAndEmptyMessageAndData.getData());
@@ -527,19 +527,19 @@ class UnifiedResponseTests {
final User user = new User("zhouxy", "zhouxy@gmail.com");
// message
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败"));
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", user));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "查询失败"));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "查询失败", user));
// empty message
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, ""));
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "", null));
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "", user));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, ""));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "", null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "", user));
// null message
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, nullMessage));
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponse.of(nullStatus, "查询失败", user));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, nullMessage));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "查询失败", null));
assertThrows(NullPointerException.class, () -> UnifiedResponses.of(nullStatus, "查询失败", user));
}
@Data

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Slf4j
@SuppressWarnings("null")
public class YearQuarterTests {
// ================================
@@ -588,7 +589,7 @@ public class YearQuarterTests {
Year.MIN_VALUE,
Year.MAX_VALUE,
})
void of_ValidYearMonth_CreatesYearMnoth_Q1(int year) {
void of_ValidYearMonth_CreatesYearMonth_Q1(int year) {
{
YearMonth yearMonth = YearMonth.of(year, 1);
YearQuarter yearQuarter = YearQuarter.of(yearMonth);
@@ -619,7 +620,7 @@ public class YearQuarterTests {
Year.MIN_VALUE,
Year.MAX_VALUE,
})
void of_ValidYearMonth_CreatesYearMnoth_Q2(int year) {
void of_ValidYearMonth_CreatesYearMonth_Q2(int year) {
{
YearMonth yearMonth = YearMonth.of(year, 4);
YearQuarter yearQuarter = YearQuarter.of(yearMonth);
@@ -650,7 +651,7 @@ public class YearQuarterTests {
Year.MIN_VALUE,
Year.MAX_VALUE,
})
void of_ValidYearMonth_CreatesYearMnoth_Q3(int year) {
void of_ValidYearMonth_CreatesYearMonth_Q3(int year) {
{
YearMonth yearMonth = YearMonth.of(year, 7);
YearQuarter yearQuarter = YearQuarter.of(yearMonth);
@@ -681,7 +682,7 @@ public class YearQuarterTests {
Year.MIN_VALUE,
Year.MAX_VALUE,
})
void of_ValidYearMonth_CreatesYearMnoth_Q4(int year) {
void of_ValidYearMonth_CreatesYearMonth_Q4(int year) {
{
YearMonth yearMonth = YearMonth.of(year, 10);
YearQuarter yearQuarter = YearQuarter.of(yearMonth);
@@ -712,7 +713,7 @@ public class YearQuarterTests {
Year.MIN_VALUE,
Year.MAX_VALUE,
})
void of_NullYearMonth_CreatesYearMnoth_Q4(int year) {
void of_NullYearMonth_CreatesYearMonth_Q4(int year) {
YearMonth yearMonth = null;
assertThrows(NullPointerException.class,
() -> YearQuarter.of(yearMonth));
@@ -1173,19 +1174,4 @@ public class YearQuarterTests {
}
}
}
@ParameterizedTest
@ValueSource(ints = { -1, 0, 1, 1900, 2000, 2023, 2024, Year.MAX_VALUE, Year.MIN_VALUE })
void test_min_And_max_sameYear(int year) {
YearQuarter yq1 = YearQuarter.of(year, 1);
YearQuarter anotherYq1 = YearQuarter.of(year, 1);
assertEquals(yq1, YearQuarter.max(yq1, anotherYq1));
assertEquals(yq1, YearQuarter.min(yq1, anotherYq1));
YearQuarter yq2 = YearQuarter.of(year, 2);
assertEquals(yq2, YearQuarter.max(yq1, yq2));
assertEquals(yq1, YearQuarter.min(yq1, yq2));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,9 +20,11 @@ import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -1087,4 +1089,26 @@ public class ArrayToolsTests {
// #endregion
// ================================
// ================================
// #region - invoke constructor
// ================================
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = ArrayTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
// ================================
// #endregion - invoke constructor
// ================================
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,9 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.Constructor;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Supplier;
@@ -939,4 +941,25 @@ public class AssertToolsTests {
// #endregion - Condition
// ================================
// #region - invoke constructor
// ================================
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = AssertTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
// ================================
// #endregion - invoke constructor
// ================================
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,16 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings("null")
public class BigDecimalsTests {
@Test
@@ -202,4 +205,17 @@ public class BigDecimalsTests {
assertEquals(bd, BigDecimals.of("10"));
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = BigDecimals.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,12 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
@@ -32,6 +34,7 @@ import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
@@ -432,32 +435,117 @@ class DateTimeToolsTests {
// ================================
// ================================
// #region - others
// #region - start & end
// ================================
@Test
void startDateOfYear() {
void testStartAndEndDateOfYear() {
assertEquals(LocalDate.of(2008, 1, 1), DateTimeTools.startDateOfYear(2008));
assertEquals(LocalDate.of(2008, 12, 31), DateTimeTools.endDateOfYear(2008));
}
@Test
void testStartOfNextDate() {
assertEquals(LocalDateTime.of(2024, 12, 30, 0, 0, 0),
DateTimeTools.startOfNextDate(LOCAL_DATE));
assertEquals(LocalDateTime.of(2024, 12, 30, 0, 0, 0).atZone(SYS_ZONE_ID),
DateTimeTools.startOfNextDate(LOCAL_DATE, SYS_ZONE_ID));
assertEquals(LocalDateTime.of(2024, 12, 30, 0, 0, 0).atZone(ZONE_ID),
DateTimeTools.startOfNextDate(LOCAL_DATE, ZONE_ID));
}
// ================================
// #endregion - start & end
// ================================
// ================================
// #region - isFuture & isPast
// ================================
@Test
void test_isFuture_And_isPast_WhenFuture() {
Date date = new Date(Instant.now().plusSeconds(10).toEpochMilli());
assertTrue(DateTimeTools.isFuture(date));
assertFalse(DateTimeTools.isPast(date));
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("+01:00"));
calendar.add(Calendar.SECOND, 10);
assertTrue(DateTimeTools.isFuture(calendar));
assertFalse(DateTimeTools.isPast(calendar));
Instant instant = Instant.now().plusSeconds(10);
assertTrue(DateTimeTools.isFuture(instant));
assertFalse(DateTimeTools.isPast(instant));
long timeMillis = Instant.now().plusSeconds(10).toEpochMilli();
assertTrue(DateTimeTools.isFuture(timeMillis));
assertFalse(DateTimeTools.isPast(timeMillis));
LocalDate localDate = LocalDate.now().plusDays(1);
assertTrue(DateTimeTools.isFuture(localDate));
assertFalse(DateTimeTools.isPast(localDate));
LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(10);
assertTrue(DateTimeTools.isFuture(localDateTime));
assertFalse(DateTimeTools.isPast(localDateTime));
ZonedDateTime zonedDateTime = Instant.now().plusSeconds(10).atZone(ZoneId.of("+01:00"));
assertTrue(DateTimeTools.isFuture(zonedDateTime));
assertFalse(DateTimeTools.isPast(zonedDateTime));
}
@Test
void test_isFuture_And_isPast_WhenPast() {
Date date = new Date(Instant.now().minusSeconds(10).toEpochMilli());
assertFalse(DateTimeTools.isFuture(date));
assertTrue(DateTimeTools.isPast(date));
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("+01:00"));
calendar.add(Calendar.SECOND, -10);
assertFalse(DateTimeTools.isFuture(calendar));
assertTrue(DateTimeTools.isPast(calendar));
Instant instant = Instant.now().minusSeconds(10);
assertFalse(DateTimeTools.isFuture(instant));
assertTrue(DateTimeTools.isPast(instant));
long timeMillis = Instant.now().minusSeconds(10).toEpochMilli();
assertFalse(DateTimeTools.isFuture(timeMillis));
assertTrue(DateTimeTools.isPast(timeMillis));
LocalDate localDate = LocalDate.now().minusDays(1);
assertFalse(DateTimeTools.isFuture(localDate));
assertTrue(DateTimeTools.isPast(localDate));
LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(10);
assertFalse(DateTimeTools.isFuture(localDateTime));
assertTrue(DateTimeTools.isPast(localDateTime));
ZonedDateTime zonedDateTime = Instant.now().minusSeconds(10).atZone(ZoneId.of("+01:00"));
assertFalse(DateTimeTools.isFuture(zonedDateTime));
assertTrue(DateTimeTools.isPast(zonedDateTime));
}
// ================================
// #endregion - isFuture & isPast
// ================================
// ================================
// #region - others
// ================================
@Test
void startDateTimeRange() {
Range<LocalDateTime> localDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE);
assertEquals(LOCAL_DATE.atStartOfDay(), localDateTimeRange.lowerEndpoint());
assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay(), localDateTimeRange.upperEndpoint());
assertTrue(localDateTimeRange.contains(LOCAL_DATE.atStartOfDay()));
assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay(), localDateTimeRange.upperEndpoint());
assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay(), localDateTimeRange.upperEndpoint());
assertFalse(localDateTimeRange.contains(LocalDate.of(2024, 12, 30).atStartOfDay()));
Range<ZonedDateTime> zonedDateTimeRange = DateTimeTools.toDateTimeRange(LOCAL_DATE, SYS_ZONE_ID);
assertEquals(LOCAL_DATE.atStartOfDay().atZone(SYS_ZONE_ID), zonedDateTimeRange.lowerEndpoint());
assertEquals(LocalDate.of(2024, 12, 30).atStartOfDay().atZone(SYS_ZONE_ID), zonedDateTimeRange.upperEndpoint());
assertTrue(zonedDateTimeRange.contains(LOCAL_DATE.atStartOfDay().atZone(SYS_ZONE_ID)));
assertEquals(ZonedDateTime.of(LocalDate.of(2024, 12, 30).atStartOfDay(), SYS_ZONE_ID), zonedDateTimeRange.upperEndpoint());
assertEquals(ZonedDateTime.of(LocalDate.of(2024, 12, 30).atStartOfDay(), SYS_ZONE_ID), zonedDateTimeRange.upperEndpoint());
assertFalse(zonedDateTimeRange.contains(LocalDate.of(2024, 12, 30).atStartOfDay().atZone(SYS_ZONE_ID)));
}
@@ -477,6 +565,10 @@ class DateTimeToolsTests {
assertThrows(DateTimeException.class, () -> DateTimeTools.toYearString(Year.MIN_VALUE - 1));
assertThrows(DateTimeException.class, () -> DateTimeTools.toYearString(Year.MAX_VALUE + 1));
assertEquals("2024", DateTimeTools.toYearString(Year.of(2024)));
assertEquals("999999999", DateTimeTools.toYearString(Year.of(Year.MAX_VALUE)));
assertEquals("-999999999", DateTimeTools.toYearString(Year.of(Year.MIN_VALUE)));
assertEquals("01", DateTimeTools.toMonthStringMM(1));
assertEquals("02", DateTimeTools.toMonthStringMM(2));
assertEquals("3", DateTimeTools.toMonthStringM(3));
@@ -493,4 +585,27 @@ class DateTimeToolsTests {
// ================================
// #endregion - toString
// ================================
// ================================
// #region - invoke constructor
// ================================
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = DateTimeTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
// ================================
// #endregion - invoke constructor
// ================================
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,20 @@
package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation", "null"})
public
class EnumToolsTests {
@@ -222,4 +227,18 @@ class EnumToolsTests {
assertSame(MyEnum.VALUE_0, EnumTools.valueOf(MyEnum.class, null, MyEnum.VALUE_0));
assertNull(EnumTools.valueOf(MyEnum.class, null, null));
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = EnumTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,12 @@
package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
@@ -83,4 +88,17 @@ public class IdGeneratorTests {
IdGenerator.toSimpleString(id));
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = IdGenerator.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,16 @@
package xyz.zhouxy.plusone.commons.util;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
public
class NumbersTests {
@@ -184,4 +191,18 @@ class NumbersTests {
BigDecimal result = Numbers.nullToZero(value);
assertEquals(BigDecimal.ZERO, result);
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = Numbers.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,13 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
@@ -32,6 +35,7 @@ import org.junit.jupiter.api.Test;
/**
* {@link OptionalTools} 单元测试
*/
@SuppressWarnings("null")
public
class OptionalToolsTests {
@@ -184,4 +188,18 @@ class OptionalToolsTests {
Double result = OptionalTools.toDouble(OptionalDouble.of(10.0));
assertEquals(10.0, result, 0.0001);
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = OptionalTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,13 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.lang.reflect.Constructor;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Random;
import org.junit.jupiter.api.BeforeAll;
@@ -102,4 +106,18 @@ public class RandomToolsTests {
String result = RandomTools.secureRandomStr(sourceCharactersString, 5);
assertEquals(5, result.length());
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = RandomTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.*;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -26,6 +28,7 @@ import org.junit.jupiter.api.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings("null")
public
class RegexToolsTests {
@@ -162,4 +165,18 @@ class RegexToolsTests {
RegexTools.getMatcher("abc", pattern);
});
}
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = RegexTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 the original author or authors.
* Copyright 2024-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,15 +18,24 @@ package xyz.zhouxy.plusone.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
@SuppressWarnings("null")
public
class StringToolsTests {
// ================================
// #region - isNotBlank
// ================================
@Test
void isNotBlank_NullString_ReturnsFalse() {
assertFalse(StringTools.isNotBlank(null));
@@ -47,6 +56,206 @@ class StringToolsTests {
assertTrue(StringTools.isNotBlank("Hello"));
}
// ================================
// #endregion - isNotBlank
// ================================
// ================================
// #region - isBlank
// ================================
@Test
void isBlank_NullString_ReturnsTrue() {
assertTrue(StringTools.isBlank(null));
}
@Test
void isBlank_EmptyString_ReturnsTrue() {
assertTrue(StringTools.isBlank(""));
}
@Test
void isBlank_WhitespaceString_ReturnsTrue() {
assertTrue(StringTools.isBlank(" "));
}
@Test
void isBlank_NonWhitespaceString_ReturnsFalse() {
assertFalse(StringTools.isBlank("Hello"));
}
// ================================
// #endregion - isBlank
// ================================
// ================================
// #region - isNotEmpty
// ================================
@Test
void isNotEmpty_NullString_ReturnsFalse() {
assertFalse(StringTools.isNotEmpty(null));
}
@Test
void isNotEmpty_EmptyString_ReturnsFalse() {
assertFalse(StringTools.isNotEmpty(""));
}
@Test
void isNotEmpty_WhitespaceString_ReturnsTrue() {
assertTrue(StringTools.isNotEmpty(" "));
}
@Test
void isNotEmpty_NonWhitespaceString_ReturnsTrue() {
assertTrue(StringTools.isNotEmpty("Hello"));
}
// ================================
// #endregion - isNotEmpty
// ================================
// ================================
// #region - isEmpty
// ================================
@Test
void isEmpty_NullString_ReturnsTrue() {
assertTrue(StringTools.isEmpty(null));
}
@Test
void isEmpty_EmptyString_ReturnsTrue() {
assertTrue(StringTools.isEmpty(""));
}
@Test
void isEmpty_WhitespaceString_ReturnsFalse() {
assertFalse(StringTools.isEmpty(" "));
}
@Test
void isEmpty_NonWhitespaceString_ReturnsFalse() {
assertFalse(StringTools.isEmpty("Hello"));
}
// ================================
// #endregion - isEmpty
// ================================
// ================================
// #region - EMAIL
// ================================
@Test
public void testValidEmails() {
assertTrue(StringTools.isEmail("test@example.com"));
assertTrue(StringTools.isEmail("user.name+tag+sorting@example.com"));
assertTrue(StringTools.isEmail("user@sub.example.com"));
assertTrue(StringTools.isEmail("user@123.123.123.123"));
}
@Test
public void testInvalidEmails() {
assertFalse(StringTools.isEmail(".username@example.com"));
assertFalse(StringTools.isEmail("@missingusername.com"));
assertFalse(StringTools.isEmail("plainaddress"));
assertFalse(StringTools.isEmail("username..username@example.com"));
assertFalse(StringTools.isEmail("username.@example.com"));
assertFalse(StringTools.isEmail("username@-example.com"));
assertFalse(StringTools.isEmail("username@-example.com"));
assertFalse(StringTools.isEmail("username@.com.com"));
assertFalse(StringTools.isEmail("username@.com.my"));
assertFalse(StringTools.isEmail("username@.com"));
assertFalse(StringTools.isEmail("username@com."));
assertFalse(StringTools.isEmail("username@com"));
assertFalse(StringTools.isEmail("username@example..com"));
assertFalse(StringTools.isEmail("username@example.com-"));
assertFalse(StringTools.isEmail("username@example.com."));
assertFalse(StringTools.isEmail("username@example"));
}
// ================================
// #endregion - EMAIL
// ================================
// ================================
// #region - isURL
// ================================
/**
* TC1: 验证标准HTTP协议URL
*/
@Test
void isURL_ValidHttpURL_ReturnsTrue() {
assertTrue(StringTools.isURL("http://example.com"));
}
/**
* TC2: 验证带路径参数的HTTPS复杂URL
*/
@Test
void isURL_ValidHttpsComplexURL_ReturnsTrue() {
assertTrue(StringTools.isURL("https://test.com:8080/api/v1?param=value#anchor"));
}
/**
* TC3: 验证FTP协议URL
*/
@Test
void isURL_ValidFtpURL_ReturnsTrue() {
assertTrue(StringTools.isURL("ftp://files.example.com/directory/"));
}
/**
* TC4: 验证非法协议处理
*/
@Test
void isURL_InvalidProtocol_ReturnsFalse() {
assertFalse(StringTools.isURL("httpx://invalid.com"));
}
/**
* TC5: 验证null输入处理
*/
@Test
void isURL_NullInput_ReturnsFalse() {
assertFalse(StringTools.isURL(null));
}
/**
* TC6: 验证空字符串处理
*/
@Test
void isURL_EmptyString_ReturnsFalse() {
assertFalse(StringTools.isURL(StringTools.EMPTY_STRING));
}
/**
* TC7: 验证缺失协议头处理
*/
@Test
void isURL_MissingProtocol_ReturnsFalse() {
assertFalse(StringTools.isURL("www.example.com/path"));
}
/**
* TC8: 验证未编码特殊字符处理
*/
@Test
void isURL_UnencodedSpecialChars_ReturnsTrue() {
assertTrue(StringTools.isURL("http://example.com/测试"));
}
// ================================
// #endregion - isURL
// ================================
// ================================
// #region - repeat
// ================================
@Test
void repeat_NullString_ThrowsException() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
@@ -84,4 +293,22 @@ class StringToolsTests {
void repeat_ZeroTimes_ReturnsEmptyString() {
assertEquals("", StringTools.repeat("Hello", 0));
}
// ================================
// #endregion - repeat
// ================================
@Test
void test_constructor_isNotAccessible_ThrowsIllegalStateException() {
Constructor<?>[] constructors = StringTools.class.getDeclaredConstructors();
Arrays.stream(constructors)
.forEach(constructor -> {
assertFalse(constructor.isAccessible());
constructor.setAccessible(true);
Throwable cause = assertThrows(Exception.class, constructor::newInstance)
.getCause();
assertInstanceOf(IllegalStateException.class, cause);
assertEquals("Utility class", cause.getMessage());
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 the original author or authors.
* Copyright 2023-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@ import cn.hutool.core.util.ObjectUtil;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@SuppressWarnings("null")
class TreeBuilderTests {
private static final Logger log = LoggerFactory.getLogger(TreeBuilderTests.class);
@@ -62,7 +63,7 @@ class TreeBuilderTests {
Menu::getMenuCode,
menu -> Optional.ofNullable(menu.parentMenuCode),
MenuList::addChild,
Menu.orderNumComparator);
Comparator.comparing(Menu::getOrderNum));
@Test
void testBuildTreeAndSortedByOrderNum() {
@@ -70,31 +71,35 @@ class TreeBuilderTests {
List<Menu> menuTreeSortedByOrderNum = treeBuilder.buildTree(clonedMenus);
log.info("menuTreeSortedByOrderNum: {}", new Gson().toJson(menuTreeSortedByOrderNum));
assertEquals(clonedMenus.stream()
assertEquals(
clonedMenus.stream()
.filter(menu -> menu.getParentMenuCode() == null)
.sorted(Menu.orderNumComparator)
.sorted(Comparator.comparing(Menu::getOrderNum))
.collect(Collectors.toList()),
menuTreeSortedByOrderNum);
menuTreeSortedByOrderNum);
Map<String, Menu> menuMap = new HashMap<>();
for (Menu element : clonedMenus) {
menuMap.put(element.getMenuCode(), element);
}
assertEquals(Arrays.stream(new Menu[] { B001, B002, B003, B004 })
.sorted(Menu.orderNumComparator)
assertEquals(
Arrays.stream(new Menu[] { B001, B002, B003, B004 })
.sorted(Comparator.comparing(Menu::getOrderNum))
.collect(Collectors.toList()),
MenuList.class.cast(menuMap.get("B")).children);
MenuList.class.cast(menuMap.get("B")).children);
assertEquals(Arrays.stream(new Menu[] { C1, C2, C3 })
.sorted(Menu.orderNumComparator)
assertEquals(
Arrays.stream(new Menu[] { C1, C2, C3 })
.sorted(Comparator.comparing(Menu::getOrderNum))
.collect(Collectors.toList()),
MenuList.class.cast(menuMap.get("C")).children);
MenuList.class.cast(menuMap.get("C")).children);
assertEquals(Arrays.stream(new Menu[] { C1001, C1002 })
.sorted(Menu.orderNumComparator)
assertEquals(
Arrays.stream(new Menu[] { C1001, C1002 })
.sorted(Comparator.comparing(Menu::getOrderNum))
.collect(Collectors.toList()),
MenuList.class.cast(menuMap.get("C1")).children);
MenuList.class.cast(menuMap.get("C1")).children);
}
@@ -103,16 +108,16 @@ class TreeBuilderTests {
List<Menu> clonedMenus;
clonedMenus = menus.stream().map(ObjectUtil::clone).collect(Collectors.toList());
List<Menu> menuTreeSortedByMenuCode = treeBuilder.buildTree(
clonedMenus,
(a, b) -> a.getMenuCode().compareTo(b.getMenuCode()));
List<Menu> menuTreeSortedByMenuCode = treeBuilder
.buildTree(clonedMenus, Comparator.comparing(Menu::getMenuCode));
log.info("menuTreeSortedByMenuCode: {}", new Gson().toJson(menuTreeSortedByMenuCode));
assertEquals(clonedMenus.stream()
assertEquals(
clonedMenus.stream()
.filter(menu -> menu.getParentMenuCode() == null)
.sorted((a, b) -> a.getMenuCode().compareTo(b.getMenuCode()))
.sorted(Comparator.comparing(Menu::getMenuCode))
.collect(Collectors.toList()),
menuTreeSortedByMenuCode);
menuTreeSortedByMenuCode);
Map<String, Menu> menuMap = new HashMap<>();
for (Menu element : clonedMenus) {
@@ -160,9 +165,6 @@ class TreeBuilderTests {
return orderNum;
}
public static Comparator<Menu> orderNumComparator =
(a, b) -> Integer.compare(a.getOrderNum(), b.getOrderNum());
private static final long serialVersionUID = 20240917181424L;
}